From 89796b3e9898cf80ac086c96143cdaefb6283769 Mon Sep 17 00:00:00 2001 From: he-is-harry Date: Mon, 31 Mar 2025 14:02:22 -0400 Subject: [PATCH 1/3] Fixed TIMESTAMP and TIME truncation - Updated TIMESTAMP and TIME types to truncate milliseconds instead of rounding - Changed the regex so that fractions of seconds will be read exactly for TIME, TIMESTAMP, and LONGDATE types - The fractions are truncated to nearest millisecond for TIME and TIMESTAMP, and nanosecond for LONGDATE - Added some unit tests for truncating DATETIME types - Updated integration tests for DFV 4 types to also be run on DFV 1 --- lib/protocol/Writer.js | 20 +- test/acceptance/db.DataType.js | 741 ++++++++++++++++++++------------ test/fixtures/parametersData.js | 43 +- test/lib.Writer.js | 4 +- 4 files changed, 516 insertions(+), 292 deletions(-) diff --git a/lib/protocol/Writer.js b/lib/protocol/Writer.js index 28cf501..1636be5 100644 --- a/lib/protocol/Writer.js +++ b/lib/protocol/Writer.js @@ -33,8 +33,8 @@ exports = module.exports = Writer; var REGEX = { DATE: /(\d{4})-(\d{2})-(\d{2})/, - TIME: /(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)/, - TIMESTAMP: /(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)/, + TIME: /(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?/, + TIMESTAMP: /(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?/, // DECIMAL will match "" and ".", both of which are invalid, requires an // additional check DECIMAL: /^([+-])?(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/ @@ -728,7 +728,8 @@ Writer.prototype[TypeCode.TIME] = function writeTime(value) { } hours = ~~time[1]; minutes = ~~time[2]; - milliseconds = Math.round(time[3] * 1000); + var decMilli = strToNumLen(time[4], 3); + milliseconds = time[3] * 1000 + decMilli; } else { throw createInputError('TIME'); } @@ -776,7 +777,8 @@ Writer.prototype[TypeCode.TIMESTAMP] = function writeTimestamp(value) { day = ~~ts[3]; hours = ~~ts[4]; minutes = ~~ts[5]; - milliseconds = Math.round(ts[6] * 1000); + var decMilli = strToNumLen(ts[7], 3); + milliseconds = ts[6] * 1000 + decMilli; } else { throw createInputError('TIMESTAMP'); } @@ -861,7 +863,7 @@ Writer.prototype[TypeCode.LONGDATE] = function writeLongDate(value) { hours = ~~ts[4]; minutes = ~~ts[5]; seconds = ~~ts[6]; - nanoseconds = ~~((ts[6] * 1000000000) % 1000000000); + nanoseconds = strToNumLen(ts[7], 9); } else { throw createInputError('LONGDATE'); } @@ -1105,6 +1107,14 @@ function truncateDecimalToExp(decimal, minExp) { }; } +// Truncates / pads a decimal string to get a fixed number of decimal places as an integer +function strToNumLen(str, len) { + if (str === undefined) { + return 0; + } + return Number(str.length > len ? str.substring(0, len) : str + util.ZEROS[len - str.length]); +} + function createInputError(type) { return new Error(util.format('Wrong input for %s type', type)); } diff --git a/test/acceptance/db.DataType.js b/test/acceptance/db.DataType.js index 26f71b2..5becde3 100644 --- a/test/acceptance/db.DataType.js +++ b/test/acceptance/db.DataType.js @@ -130,7 +130,8 @@ describe('db', function () { } function insert(cb) { - statement.exec(values, function (rowsAffected) { + statement.exec(values, function (err, rowsAffected) { + if (err) cb(err); statement.drop(); cb(); }); @@ -181,12 +182,16 @@ describe('db', function () { async.waterfall([prepare, insert, drop], callback); } - // Functions used to setup and drop tables only when the db is a RemoteDB - function setUpTableRemoteDB(tableName, columns, dataFormatRequired) { + // Indicator to skip tests and test setup (some tests are only run on HANA cloud / on premise) + var skipTests = false; + + // Functions used to setup and drop tables only when the db is a RemoteDB, we are not skipping the test, + // and the data format is greater or equal to that required. + function setUpTable(tableName, columns, dataFormatRequired) { // Returns a closure which has access to the parameters above and the 'this' // argument will be the describe context when used in a mocha before hook return function setUpTableClosure(done) { - if (isRemoteDB && db.getDataFormatVersion2() >= dataFormatRequired) { + if (isRemoteDB && !skipTests && db.getDataFormatVersion2() >= dataFormatRequired) { // Tables have an ID column auto generated to preserve order between elements setCurTableCols(columns); db.createTable.bind(db)(tableName, ['ID INT GENERATED BY DEFAULT AS IDENTITY'].concat(columns), null, done); @@ -197,9 +202,9 @@ describe('db', function () { } } - function dropTableRemoteDB(tableName, dataFormatRequired) { + function dropTable(tableName, dataFormatRequired) { return function dropTableClosure(done) { - if (isRemoteDB && db.getDataFormatVersion2() >= dataFormatRequired) { + if (isRemoteDB && !skipTests && db.getDataFormatVersion2() >= dataFormatRequired) { db.dropTable.bind(db)(tableName, done); } else { done(); @@ -207,6 +212,22 @@ describe('db', function () { } } + // Function used to skip all tests if running on HANA cloud until reset skip is called + function setSkipHANACloud (done) { + var version = db.getHANAFullVersion(); + if (version === undefined || version.startsWith("4.")) { // Skip tests on HANA cloud + skipTests = true; + this.test.parent.pending = true; + this.skip(); + } + done(); + } + + function resetSkip(done) { + skipTests = false; + done(); + } + // Function used to reconnect to the db with a new data format version function changeDataFormatSupport(newDataFormat) { return function DFVReconnect(done) { @@ -221,195 +242,197 @@ describe('db', function () { } } - describeRemoteDB('DATES', function () { - describeRemoteDB('DAYDATE', function () { - // Setup a daydate table only if the db is a RemoteDB and dataFormatVersion is at least 4 - before(setUpTableRemoteDB('DAYDATE_TABLE', ['A DAYDATE'], 4)); - after(dropTableRemoteDB('DAYDATE_TABLE', 4)); - - it('should return valid date via callback', function (done) { - var insertValues = [ - ['2023-05-04'], - ['1990-12-31'], - ['0001-01-01'], - ['2001-02-12 08:30:00'], - ['9999-12-31'], - [null], - ['1582-10-15'], - ['1582-10-04'], - ['1582-10-10'], - ]; - var expected = [{A: '2023-05-04'}, {A: '1990-12-31'}, {A: '0001-01-01'}, {A: '2001-02-12'}, - {A: '9999-12-31'}, {A: null}, {A: '1582-10-15'}, {A: '1582-10-04'}, {A: '1582-10-20'}]; - testDataTypeValid('DAYDATE_TABLE', insertValues, [63], expected, done); - }); - - it('should raise input type error', function (done) { - var invalidTestData = [{value: '2460684', errMessage: "Cannot set parameter at row: 1. Wrong input for DATE type"}, - {value: '10000-01-12', errMessage: "Cannot set parameter at row: 1. Wrong input for DAYDATE type"}, - {value: '2020-14-20', errMessage: "Cannot set parameter at row: 1. Wrong input for DAYDATE type"}]; - async.each(invalidTestData, testDataTypeError.bind(null, 'DAYDATE_TABLE'), done); - }); - }); - - describeRemoteDB('SECONDDATE', function () { - before(setUpTableRemoteDB('SECONDDATE_TABLE', ['A SECONDDATE'], 4)); - after(dropTableRemoteDB('SECONDDATE_TABLE', 4)); - - it('should return valid dates via callback', function (done) { - var insertValues = [ - ['2025-01-13 04:27:32.7891234'], - ['1990-12-03 10:20:29'], - ['0001-01-01 00:00:00'], - ['2011-01-01 11:12:13.167832'], - ['2000-02-29 23:59:59.9999999'], - ['9999-12-31 23:59:59'], - [null], - ['1582-10-15 00:00:00'], - ['1582-10-04 23:59:59'], - ['1582-10-10 12:00:00'], - ]; - var expected = [{A: '2025-01-13 04:27:32'}, {A: '1990-12-03 10:20:29'}, {A: '0001-01-01 00:00:00'}, - {A: '2011-01-01 11:12:13'}, {A: '2000-02-29 23:59:59'}, {A: '9999-12-31 23:59:59'}, {A: null}, - {A: '1582-10-15 00:00:00'}, {A: '1582-10-04 23:59:59'}, {A: '1582-10-20 12:00:00'}]; - testDataTypeValid('SECONDDATE_TABLE', insertValues, [62], expected, done); - }); + describeRemoteDB('DFV 4 types', function () { + // DAYDATE + var daydateInsertValues = [ + ['2023-05-04'], + ['1990-12-31'], + ['0001-01-01'], + ['2001-02-12 08:30:00'], + ['9999-12-31'], + [null], + ['1582-10-15'], + ['1582-10-04'], + ['1582-10-10'], + ]; + var daydateExpected = [{A: '2023-05-04'}, {A: '1990-12-31'}, {A: '0001-01-01'}, {A: '2001-02-12'}, + {A: '9999-12-31'}, {A: null}, {A: '1582-10-15'}, {A: '1582-10-04'}, {A: '1582-10-20'}]; + var daydateInvalidTestDataDFV4 = [ + {value: '2460684', errMessage: "Cannot set parameter at row: 1. Wrong input for DATE type"}, + {value: '10000-01-12', errMessage: "Cannot set parameter at row: 1. Wrong input for DAYDATE type"}, + {value: '2020-14-20', errMessage: "Cannot set parameter at row: 1. Wrong input for DAYDATE type"} + ]; + // In DFV1, some error messages are derived from the server + var daydateInvalidTestDataDFV1 = [ + {value: '2460684', errMessage: "Cannot set parameter at row: 1. Wrong input for DATE type"}, + {value: '10000-01-12', errMessage: "invalid DATE, TIME or TIMESTAMP value: year must be between " + + "-4713 and +9999, and not be 0: 0: type_code=14, index=1"}, + {value: '2020-14-20', errMessage: "invalid DATE, TIME or TIMESTAMP value: not a valid month: 14: " + + "type_code=14, index=1"} + ]; - it('should raise input type error', function (done) { - var invalidValues = ['2015-02-10', '10000-01-12 14:01:02', '1998-06-25 25:59:59']; - // Add the same expected error message to the values - var invalidTestData = invalidValues.map(function (testValue) { - return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for SECONDDATE type"}; - }); - async.each(invalidTestData, testDataTypeError.bind(null, 'SECONDDATE_TABLE'), done); - }); + // SECONDDATE + var secondDateInsertValues = [ + ['2025-01-13 04:27:32.7891234'], + ['1990-12-03 10:20:29'], + ['0001-01-01 00:00:00'], + ['2011-01-01 11:12:13.167832'], + ['2000-02-29 23:59:59.9999999'], + ['9999-12-31 23:59:59'], + [null], + ['1582-10-15 00:00:00'], + ['1582-10-04 23:59:59'], + ['1582-10-10 12:00:00'], + ]; + var secondDateExpectedDFV4 = [{A: '2025-01-13 04:27:32'}, {A: '1990-12-03 10:20:29'}, {A: '0001-01-01 00:00:00'}, + {A: '2011-01-01 11:12:13'}, {A: '2000-02-29 23:59:59'}, {A: '9999-12-31 23:59:59'}, {A: null}, + {A: '1582-10-15 00:00:00'}, {A: '1582-10-04 23:59:59'}, {A: '1582-10-20 12:00:00'}]; + // In DFV1, values are returned as TIMESTAMP's which have a T instead of space between + var secondDateExpectedDFV1 = secondDateExpectedDFV4.map(function (row) { + if (row.A) { + return {A: row.A.replace(' ', 'T')}; + } else { + return row; + } }); - - describeRemoteDB('LONGDATE', function () { - before(setUpTableRemoteDB('LONGDATE_TABLE', ['A LONGDATE'], 4)); - after(dropTableRemoteDB('LONGDATE_TABLE', 4)); - - it('should return valid dates via callback', function (done) { - var insertValues = [ - ['2025-01-13 04:27:32.7891234'], - ['1990-12-03 10:20:29'], - ['0001-01-01 00:00:00'], - ['2011-01-01 11:12:13.167832'], - ['2000-02-29 23:59:59.9999999'], - ['9999-12-31 23:59:59.9999999'], - [null], - ['1582-10-15 00:00:00.1234567'], - ['1582-10-04 23:59:59.5819212'], - ['1582-10-10 12:00:00'], - ]; - var expected = [{A: '2025-01-13 04:27:32.789123400'}, {A: '1990-12-03 10:20:29.000000000'}, - {A: '0001-01-01 00:00:00.000000000'}, {A: '2011-01-01 11:12:13.167832000'}, {A: '2000-02-29 23:59:59.999999900'}, - {A: '9999-12-31 23:59:59.999999900'}, {A: null}, {A: '1582-10-15 00:00:00.123456700'}, - {A: '1582-10-04 23:59:59.581921200'}, {A: '1582-10-20 12:00:00.000000000'}]; - testDataTypeValid('LONGDATE_TABLE', insertValues, [61], expected, done); - }); - - it('should raise input type error', function (done) { - var invalidValues = ['2015-02-10', '10000-01-12 14:01:02', '1998-06-25 25:59:59', '1900-02-29 12:00:00', '0000-00-00 12:00:00']; - // Add the same expected error message to the values - var invalidTestData = invalidValues.map(function (testValue) { - return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for LONGDATE type"}; - }); - async.each(invalidTestData, testDataTypeError.bind(null, 'LONGDATE_TABLE'), done); - }); + var secondDateInvalidValues = ['2015-02-10', '10000-01-12 14:01:02', '1998-06-25 25:59:59']; + // Add the same expected error message to the values for DFV4 + var secondDateInvalidTestDataDFV4 = secondDateInvalidValues.map(function (testValue) { + return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for SECONDDATE type"}; }); + var secondDateInvalidTestDataDFV1 = [ + {value: '2015-02-10', errMessage: "Cannot set parameter at row: 1. Wrong input for TIMESTAMP type"}, + {value: '10000-01-12 14:01:02', errMessage: "invalid DATE, TIME or TIMESTAMP value: year must be between " + + "-4713 and +9999, and not be 0: 0: type_code=16, index=1"}, + {value: '1998-06-25 25:59:59', errMessage: "invalid DATE, TIME or TIMESTAMP value: not a valid hour: 25: " + + "type_code=16, index=1"}, + ]; - describeRemoteDB('SECONDTIME', function () { - before(setUpTableRemoteDB('SECONDTIME_TABLE', ['A SECONDTIME'], 4)); - after(dropTableRemoteDB('SECONDTIME_TABLE', 4)); - - it('should return valid times via callback', function (done) { - var insertValues = [ - ['04:27:32.7891234'], - ['10:20:29'], - ['00:00:00'], - ['11:12:13.167832'], - ['23:59:59.9999999'], - [null], - // ['9:9:9 AM'], - // ['3:28:03 PM'] - ]; - var expected = [{A: '04:27:32'}, {A: '10:20:29'}, {A: '00:00:00'}, {A: '11:12:13'}, - {A: '23:59:59'}, {A: null}]; - // When AM / PM are added, the result should instead be - // var expected = [{A: '04:27:32'}, {A: '10:20:29'}, {A: '00:00:00'}, {A: '11:12:13'}, - // {A: '23:59:59'}, {A: null}, {A: '09:09:09'}, {A: '15:28:03'}]; - testDataTypeValid('SECONDTIME_TABLE', insertValues, [64], expected, done); - }); - - it('should raise input type error', function (done) { - var invalidValues = ['2015-02-10', '11:50:62', '24:00:01', '00:00-01', '11:60:02']; - // Add the same expected error message to the values - var invalidTestData = invalidValues.map(function (testValue) { - return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for SECONDTIME type"}; - }); - async.each(invalidTestData, testDataTypeError.bind(null, 'SECONDTIME_TABLE'), done); - }); + // LONGDATE + var longDateInsertValues = [ + ['2025-01-13 04:27:32.7891234'], + ['1990-12-03 10:20:29'], + ['0001-01-01 00:00:00'], + ['2011-01-01 11:12:13.167832'], + ['2000-02-29 23:59:59.9999999'], + ['9999-12-31 23:59:59.9999999'], + [null], + ['1582-10-15 00:00:00.1234567'], + ['1582-10-04 23:59:59.5819212'], + ['1582-10-10 12:00:00'], + ]; + var longDateExpectedDFV4 = [{A: '2025-01-13 04:27:32.789123400'}, {A: '1990-12-03 10:20:29.000000000'}, + {A: '0001-01-01 00:00:00.000000000'}, {A: '2011-01-01 11:12:13.167832000'}, {A: '2000-02-29 23:59:59.999999900'}, + {A: '9999-12-31 23:59:59.999999900'}, {A: null}, {A: '1582-10-15 00:00:00.123456700'}, + {A: '1582-10-04 23:59:59.581921200'}, {A: '1582-10-20 12:00:00.000000000'}]; + // In DFV1, values are TIMESTAMP's which have lower precision and have a T instead of a space + var longDateExpectedDFV1 = [{A: '2025-01-13T04:27:32.789'}, {A: '1990-12-03T10:20:29'}, + {A: '0001-01-01T00:00:00'}, {A: '2011-01-01T11:12:13.167'}, {A: '2000-02-29T23:59:59.999'}, + {A: '9999-12-31T23:59:59.999'}, {A: null}, {A: '1582-10-15T00:00:00.123'}, + {A: '1582-10-04T23:59:59.581'}, {A: '1582-10-20T12:00:00'}]; + var longDateInvalidValues = ['2015-02-10', '10000-01-12 14:01:02', '1998-06-25 25:59:59', '1900-02-29 12:00:00', + '0000-00-00 12:00:00']; + var longDateInvalidTestDataDFV4 = longDateInvalidValues.map(function (testValue) { + return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for LONGDATE type"}; }); - }); - - describeRemoteDB('ALPHANUM, TEXT, SHORTTEXT, BINTEXT (only tested on on-premise HANA)', function () { - var skipTests = false; - before(function (done) { - var version = db.getHANAFullVersion(); - if (version === undefined || version.startsWith("4.")) { // Skip tests on HANA cloud - skipTests = true; - this.test.parent.pending = true; - this.skip(); - } - done(); + var longDateInvalidTestDataDFV1 = [ + {value: '2015-02-10', errMessage: "Cannot set parameter at row: 1. Wrong input for TIMESTAMP type"}, + {value: '10000-01-12 14:01:02', errMessage: "invalid DATE, TIME or TIMESTAMP value: year must be between " + + "-4713 and +9999, and not be 0: 0: type_code=16, index=1"}, + {value: '1998-06-25 25:59:59', errMessage: "invalid DATE, TIME or TIMESTAMP value: not a valid hour: 25: " + + "type_code=16, index=1"}, + {value: '1900-02-29 12:00:00', errMessage: "invalid DATE, TIME or TIMESTAMP value: day of month must be " + + "between 1 and last day of month: 29: type_code=16, index=1"}, + {value: '0000-00-00 12:00:00', errMessage: "invalid DATE, TIME or TIMESTAMP value: year must be between " + + "-4713 and +9999, and not be 0: 0: type_code=16, index=1"}, + ]; + + // SECONDTIME + var secondTimeInsertValues = [ + ['04:27:32.7891234'], + ['10:20:29'], + ['00:00:00'], + ['11:12:13.167832'], + ['23:59:59.9999999'], + [null], + // ['9:9:9 AM'], + // ['3:28:03 PM'] + ]; + var secondTimeExpected = [{A: '04:27:32'}, {A: '10:20:29'}, {A: '00:00:00'}, {A: '11:12:13'}, + {A: '23:59:59'}, {A: null}]; + // When AM / PM are added, the result should instead be + // var secondTimeExpected = [{A: '04:27:32'}, {A: '10:20:29'}, {A: '00:00:00'}, {A: '11:12:13'}, + // {A: '23:59:59'}, {A: null}, {A: '09:09:09'}, {A: '15:28:03'}]; + var secondTimeInvalidValues = ['2015-02-10', '11:50:62', '24:00:01', '00:00-01', '11:60:02']; + var secondTimeInvalidTestDataDFV4 = secondTimeInvalidValues.map(function (testValue) { + return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for SECONDTIME type"}; }); + var secondTimeInvalidTestDataDFV1 = [ + {value: '2015-02-10', errMessage: "Cannot set parameter at row: 1. Wrong input for TIME type"}, + {value: '11:50:62', errMessage: "invalid DATE, TIME or TIMESTAMP value: not a valid millisecond: " + + "62000: type_code=15, index=1"}, + {value: '24:00:01', errMessage: "invalid DATE, TIME or TIMESTAMP value: not a valid hour: 24: type_code=15, index=1"}, + {value: '00:00-01', errMessage: "Cannot set parameter at row: 1. Wrong input for TIME type"}, + {value: '11:60:02', errMessage: "invalid DATE, TIME or TIMESTAMP value: not a valid minute: 60: type_code=15, index=1"}, + ]; - function setUpTable(tableName, columns, dataFormatRequired) { - // Returns a closure which has access to the parameters above and the 'this' - // argument will be the describe context when used in a mocha before hook - return function setUpTableClosure(done) { - if (skipTests || db.getDataFormatVersion2() < dataFormatRequired) { - this.skip(); - done(); - } else { - setCurTableCols(columns); - db.createTable.bind(db)(tableName, ['ID INT GENERATED BY DEFAULT AS IDENTITY'].concat(columns), null, done); - } - } - } - - function dropTable(tableName, dataFormatRequired) { - return function dropTableClosure(done) { - if (skipTests || db.getDataFormatVersion2() < dataFormatRequired) { - done(); - } else { - db.dropTable.bind(db)(tableName, done); - } + // ALPHANUM + var alphanumInsertValues = [ + ['ABC123'], + ['9017123461226781'], + ['12893'], + [''], + [null], + ]; + var alphanumExpected = [{A: 'ABC123'}, {A: '9017123461226781'}, {A: '0000000000012893'}, {A: ''}, {A: null}]; + var alphanumInvalidTestData = [{value: 'ABCDEFG1234567890', + errMessage: 'inserted value too large for column: Failed in "A" column with the value \'ABCDEFG1234567890\''}]; + + // TEXT + var textInsertValues = [ + ['Some regular length strings'], + ['!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890'], + [lorem.LONG], + [''], + [Buffer.from('Buffers can also be inserted for text', 'utf8')], + [null], + ]; + var textExpected = [{A: Buffer.from('Some regular length strings', "utf-8")}, + {A: Buffer.from('!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890', "utf-8")}, {A: Buffer.from(lorem.LONG, "utf-8")}, + {A: Buffer.from('', "utf-8")}, {A: Buffer.from('Buffers can also be inserted for text', "utf8")}, + {A: null}]; + + // SHORTTEXT + var shortTextInsertValues = [ + ['Some regular length strings'], + ['!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890'], + ['50 length-----------------------------------------'], + [''], + [null], + ]; + var shortTextExpected = [{A: 'Some regular length strings'}, {A: '!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890'}, + {A: '50 length-----------------------------------------'}, {A: ''}, {A: null}]; + var shortTextInvalidTestData = [{value: 'Too large in length (51)---------------------------', + errMessage: 'inserted value too large for column: Failed in "A" column with the value \'Too large in length (51)---------------------------\''}]; + + // BINTEXT + var binTextInsertValues = [ + [Buffer.from("Here is a regular string", "utf-8")], + [Buffer.alloc(0)], + ['Strings can also be used as input'], + [''], + [null], + ]; + var binTextExpected = [{A: Buffer.from("Here is a regular string", "utf-8")}, {A: Buffer.alloc(0)}, + {A: Buffer.from("Strings can also be used as input", "utf-8")}, {A: Buffer.from("", "utf-8")}, {A: null}]; + var binTextInvalidValues = [Buffer.from('61c7', 'hex'), Buffer.from('c7', 'hex'), Buffer.from('f09f9880f09f9988', 'hex'), + Buffer.from('010100000083b2f2ffadfaa3430564e1fc74b64643', 'hex'), false, 123]; + var binTextInvalidTestData = binTextInvalidValues.map(function (testValue) { + if (Buffer.isBuffer(testValue)) { + return {value: testValue, errMessage: "fatal error: invalid CESU-8 encoding for Unicode string"}; + } else { + return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for LOB type"}; } - } - - describe('ALPHANUM', function () { - before(setUpTable('ALPHANUM_TABLE', ['A ALPHANUM(16)'], 4)); - after(dropTable('ALPHANUM_TABLE', 4)); - - it('should return valid alphanums via callback', function (done) { - var insertValues = [ - ['ABC123'], - ['9017123461226781'], - ['12893'], - [''], - [null], - ]; - var expected = [{A: 'ABC123'}, {A: '9017123461226781'}, {A: '0000000000012893'}, {A: ''}, {A: null}]; - testDataTypeValid('ALPHANUM_TABLE', insertValues, [55], expected, done); - }); - - it('should raise input type error', function (done) { - var invalidTestData = [{value: 'ABCDEFG1234567890', - errMessage: 'inserted value too large for column: Failed in "A" column with the value \'ABCDEFG1234567890\''}]; - async.each(invalidTestData, testDataTypeError.bind(null, 'ALPHANUM_TABLE'), done); - }); }); function testFuzzySearch(tableName, dataTypeCode, done) { @@ -469,102 +492,254 @@ describe('db', function () { async.waterfall([prepareInsert, insert, prepareSelect, select, drop], done); } - describe('TEXT', function () { - beforeEach(setUpTable('TEXT_TABLE', ['A TEXT'], 4)); - afterEach(dropTable('TEXT_TABLE', 4)); - - it('should insert and return valid text via callback', function (done) { - var insertValues = [ - ['Some regular length strings'], - ['!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890'], - [lorem.LONG], - [''], - [Buffer.from('Buffers can also be inserted for text', 'utf8')], - [null], - ]; - var expected = [{A: Buffer.from('Some regular length strings', "utf-8")}, - {A: Buffer.from('!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890', "utf-8")}, {A: Buffer.from(lorem.LONG, "utf-8")}, - {A: Buffer.from('', "utf-8")}, {A: Buffer.from('Buffers can also be inserted for text', "utf8")}, - {A: null}]; - testDataTypeValid('TEXT_TABLE', insertValues, [51], expected, done); - }); - - it('should support fuzzy search', function (done) { - testFuzzySearch('TEXT_TABLE', 51, done); - }); - }); + describeRemoteDB('DFV >= 4', function () { + describeRemoteDB('DATES', function () { + describeRemoteDB('DAYDATE', function () { + // Setup a daydate table only if the db is a RemoteDB and dataFormatVersion is at least 4 + before(setUpTable('DAYDATE_TABLE', ['A DAYDATE'], 4)); + after(dropTable('DAYDATE_TABLE', 4)); + + it('should return valid date via callback', function (done) { + testDataTypeValid('DAYDATE_TABLE', daydateInsertValues, [63], daydateExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(daydateInvalidTestDataDFV4, testDataTypeError.bind(null, 'DAYDATE_TABLE'), done); + }); + }); - describe('SHORTTEXT', function () { - beforeEach(setUpTable('SHORTTEXT_TABLE', ['A SHORTTEXT(50)'], 4)); - afterEach(dropTable('SHORTTEXT_TABLE', 4)); - - it('should insert and return valid text via callback', function (done) { - var insertValues = [ - ['Some regular length strings'], - ['!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890'], - ['50 length-----------------------------------------'], - [''], - [null], - ]; - var expected = [{A: 'Some regular length strings'}, {A: '!@#$%^&*()-_=`~|}{\\:"\'<>,.?/1234567890'}, - {A: '50 length-----------------------------------------'}, {A: ''}, {A: null}]; - testDataTypeValid('SHORTTEXT_TABLE', insertValues, [52], expected, done); - }); - - it('should support fuzzy search', function (done) { - testFuzzySearch('SHORTTEXT_TABLE', 52, done); + describeRemoteDB('SECONDDATE', function () { + before(setUpTable('SECONDDATE_TABLE', ['A SECONDDATE'], 4)); + after(dropTable('SECONDDATE_TABLE', 4)); + + it('should return valid dates via callback', function (done) { + testDataTypeValid('SECONDDATE_TABLE', secondDateInsertValues, [62], secondDateExpectedDFV4, done); + }); + + it('should raise input type error', function (done) { + async.each(secondDateInvalidTestDataDFV4, testDataTypeError.bind(null, 'SECONDDATE_TABLE'), done); + }); + }); + + describeRemoteDB('LONGDATE', function () { + before(setUpTable('LONGDATE_TABLE', ['A LONGDATE'], 4)); + after(dropTable('LONGDATE_TABLE', 4)); + + it('should return valid dates via callback', function (done) { + testDataTypeValid('LONGDATE_TABLE', longDateInsertValues, [61], longDateExpectedDFV4, done); + }); + + it('should raise input type error', function (done) { + async.each(longDateInvalidTestDataDFV4, testDataTypeError.bind(null, 'LONGDATE_TABLE'), done); + }); + }); + + describeRemoteDB('SECONDTIME', function () { + before(setUpTable('SECONDTIME_TABLE', ['A SECONDTIME'], 4)); + after(dropTable('SECONDTIME_TABLE', 4)); + + it('should return valid times via callback', function (done) { + testDataTypeValid('SECONDTIME_TABLE', secondTimeInsertValues, [64], secondTimeExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(secondTimeInvalidTestDataDFV4, testDataTypeError.bind(null, 'SECONDTIME_TABLE'), done); + }); + }); }); - it('should raise input type error', function (done) { - var invalidTestData = [{value: 'Too large in length (51)---------------------------', - errMessage: 'inserted value too large for column: Failed in "A" column with the value \'Too large in length (51)---------------------------\''}]; - async.each(invalidTestData, testDataTypeError.bind(null, 'SHORTTEXT_TABLE'), done); + describeRemoteDB('ALPHANUM, TEXT, SHORTTEXT, BINTEXT (only tested on on-premise HANA)', function () { + before(setSkipHANACloud); + after(resetSkip); + + describe('ALPHANUM', function () { + before(setUpTable('ALPHANUM_TABLE', ['A ALPHANUM(16)'], 4)); + after(dropTable('ALPHANUM_TABLE', 4)); + + it('should return valid alphanums via callback', function (done) { + testDataTypeValid('ALPHANUM_TABLE', alphanumInsertValues, [55], alphanumExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(alphanumInvalidTestData, testDataTypeError.bind(null, 'ALPHANUM_TABLE'), done); + }); + }); + + describe('TEXT', function () { + beforeEach(setUpTable('TEXT_TABLE', ['A TEXT'], 4)); + afterEach(dropTable('TEXT_TABLE', 4)); + + it('should insert and return valid text via callback', function (done) { + testDataTypeValid('TEXT_TABLE', textInsertValues, [51], textExpected, done); + }); + + it('should support fuzzy search', function (done) { + testFuzzySearch('TEXT_TABLE', 51, done); + }); + }); + + describe('SHORTTEXT', function () { + beforeEach(setUpTable('SHORTTEXT_TABLE', ['A SHORTTEXT(50)'], 4)); + afterEach(dropTable('SHORTTEXT_TABLE', 4)); + + it('should insert and return valid text via callback', function (done) { + testDataTypeValid('SHORTTEXT_TABLE', shortTextInsertValues, [52], shortTextExpected, done); + }); + + it('should support fuzzy search', function (done) { + testFuzzySearch('SHORTTEXT_TABLE', 52, done); + }); + + it('should raise input type error', function (done) { + async.each(shortTextInvalidTestData, testDataTypeError.bind(null, 'SHORTTEXT_TABLE'), done); + }); + }); + + describe('BINTEXT', function () { + beforeEach(setUpTable('BINTEXT_TABLE', ['A BINTEXT'], 6)); + afterEach(dropTable('BINTEXT_TABLE', 6)); + + it('should insert and return valid text via callback', function (done) { + testDataTypeValid('BINTEXT_TABLE', binTextInsertValues, [53], binTextExpected, done); + }); + + it('should insert binary data', function (done) { + var expected = [{ID: 1, A: Buffer.from("6162636465666768696a6b6c6d6e6fc3a1", "hex")}]; + function insert(cb) { + client.exec("INSERT INTO BINTEXT_TABLE VALUES(1, x'6162636465666768696a6b6c6d6e6fc3a1')", function (err, rowsAffected) { + if (err) { + done(err); + } + cb(); + }); + } + async.waterfall([insert, validateDataSql.bind(null, "select * from BINTEXT_TABLE", [3, 53], expected)], done); + }); + + it('should raise input type error', function (done) { + // Character conversion validations are done on the server one at a time, so this test will take longer + this.timeout(3000); + async.each(binTextInvalidTestData, testDataTypeError.bind(null, 'BINTEXT_TABLE'), done); + }); + }); }); }); - describe('BINTEXT', function () { - beforeEach(setUpTable('BINTEXT_TABLE', ['A BINTEXT'], 6)); - afterEach(dropTable('BINTEXT_TABLE', 6)); - - it('should insert and return valid text via callback', function (done) { - var insertValues = [ - [Buffer.from("Here is a regular string", "utf-8")], - [Buffer.alloc(0)], - ['Strings can also be used as input'], - [''], - [null], - ]; - var expected = [{A: Buffer.from("Here is a regular string", "utf-8")}, {A: Buffer.alloc(0)}, - {A: Buffer.from("Strings can also be used as input", "utf-8")}, {A: Buffer.from("", "utf-8")}, {A: null}]; - testDataTypeValid('BINTEXT_TABLE', insertValues, [53], expected, done); - }); + describeRemoteDB('DFV 1', function () { + before(changeDataFormatSupport(1)); + after(changeDataFormatSupport(ORGINAL_DATA_FORMAT)); - it('should insert binary data', function (done) { - var expected = [{ID: 1, A: Buffer.from("6162636465666768696a6b6c6d6e6fc3a1", "hex")}]; - function insert(cb) { - client.exec("INSERT INTO BINTEXT_TABLE VALUES(1, x'6162636465666768696a6b6c6d6e6fc3a1')", function (err, rowsAffected) { - if (err) { - done(err); - } - cb(); + describeRemoteDB('DATES', function () { + describeRemoteDB('DAYDATE', function () { + before(setUpTable('DAYDATE_TABLE', ['A DAYDATE'], 1)); + after(dropTable('DAYDATE_TABLE', 1)); + + it('should return valid date via callback', function (done) { + testDataTypeValid('DAYDATE_TABLE', daydateInsertValues, [14], daydateExpected, done); }); - } - async.waterfall([insert, validateDataSql.bind(null, "select * from BINTEXT_TABLE", [3, 53], expected)], done); + + it('should raise input type error', function (done) { + async.each(daydateInvalidTestDataDFV1, testDataTypeError.bind(null, 'DAYDATE_TABLE'), done); + }); + }); + + describeRemoteDB('SECONDDATE', function () { + before(setUpTable('SECONDDATE_TABLE', ['A SECONDDATE'], 1)); + after(dropTable('SECONDDATE_TABLE', 1)); + + it('should return valid dates via callback', function (done) { + testDataTypeValid('SECONDDATE_TABLE', secondDateInsertValues, [16], secondDateExpectedDFV1, done); + }); + + it('should raise input type error', function (done) { + async.each(secondDateInvalidTestDataDFV1, testDataTypeError.bind(null, 'SECONDDATE_TABLE'), done); + }); + }); + + describeRemoteDB('LONGDATE', function () { + before(setUpTable('LONGDATE_TABLE', ['A LONGDATE'], 1)); + after(dropTable('LONGDATE_TABLE', 1)); + + it('should return valid dates via callback', function (done) { + testDataTypeValid('LONGDATE_TABLE', longDateInsertValues, [16], longDateExpectedDFV1, done); + }); + + it('should raise input type error', function (done) { + // In DFV1, most error validations are done on the server so this test can take longer + this.timeout(3000); + async.each(longDateInvalidTestDataDFV1, testDataTypeError.bind(null, 'LONGDATE_TABLE'), done); + }); + }); + + describeRemoteDB('SECONDTIME', function () { + before(setUpTable('SECONDTIME_TABLE', ['A SECONDTIME'], 1)); + after(dropTable('SECONDTIME_TABLE', 1)); + + it('should return valid times via callback', function (done) { + testDataTypeValid('SECONDTIME_TABLE', secondTimeInsertValues, [15], secondTimeExpected, done); + }); + + it('should raise input type error', function (done) { + // Same as LONGDATE, in DFV1, most error validations are done on the server, so this test can take longer + this.timeout(3000); + async.each(secondTimeInvalidTestDataDFV1, testDataTypeError.bind(null, 'SECONDTIME_TABLE'), done); + }); + }); }); - it('should raise input type error', function (done) { - // Character conversion validations are done on the server one at a time, so this test will take longer - this.timeout(3000); - var invalidValues = [Buffer.from('61c7', 'hex'), Buffer.from('c7', 'hex'), Buffer.from('f09f9880f09f9988', 'hex'), - Buffer.from('010100000083b2f2ffadfaa3430564e1fc74b64643', 'hex'), false, 123]; - var invalidTestData = invalidValues.map(function (testValue) { - if (Buffer.isBuffer(testValue)) { - return {value: testValue, errMessage: "fatal error: invalid CESU-8 encoding for Unicode string"}; - } else { - return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for LOB type"}; - } + describeRemoteDB('ALPHANUM, TEXT, SHORTTEXT, BINTEXT (only tested on on-premise HANA)', function () { + before(setSkipHANACloud); + after(resetSkip); + + describe('ALPHANUM', function () { + before(setUpTable('ALPHANUM_TABLE', ['A ALPHANUM(16)'], 1)); + after(dropTable('ALPHANUM_TABLE', 1)); + + it('should return valid alphanums via callback', function (done) { + testDataTypeValid('ALPHANUM_TABLE', alphanumInsertValues, [11], alphanumExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(alphanumInvalidTestData, testDataTypeError.bind(null, 'ALPHANUM_TABLE'), done); + }); + }); + + describe('TEXT', function () { + beforeEach(setUpTable('TEXT_TABLE', ['A TEXT'], 1)); + afterEach(dropTable('TEXT_TABLE', 1)); + + it('should insert and return valid text via callback', function (done) { + testDataTypeValid('TEXT_TABLE', textInsertValues, [26], textExpected, done); + }); + }); + + describe('SHORTTEXT', function () { + beforeEach(setUpTable('SHORTTEXT_TABLE', ['A SHORTTEXT(50)'], 1)); + afterEach(dropTable('SHORTTEXT_TABLE', 1)); + + it('should insert and return valid text via callback', function (done) { + testDataTypeValid('SHORTTEXT_TABLE', shortTextInsertValues, [11], shortTextExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(shortTextInvalidTestData, testDataTypeError.bind(null, 'SHORTTEXT_TABLE'), done); + }); + }); + + describe('BINTEXT', function () { + beforeEach(setUpTable('BINTEXT_TABLE', ['A BINTEXT'], 1)); + afterEach(dropTable('BINTEXT_TABLE', 1)); + + it('should insert and return valid text via callback', function (done) { + testDataTypeValid('BINTEXT_TABLE', binTextInsertValues, [26], binTextExpected, done); + }); + + it('should raise input type error', function (done) { + // Character conversion validations are done on the server one at a time, so this test will take longer + this.timeout(3000); + async.each(binTextInvalidTestData, testDataTypeError.bind(null, 'BINTEXT_TABLE'), done); + }); }); - async.each(invalidTestData, testDataTypeError.bind(null, 'BINTEXT_TABLE'), done); }); }); }); @@ -572,8 +747,8 @@ describe('db', function () { describeRemoteDB('Spatial', function () { describeRemoteDB('default spatialTypes', function () { describeRemoteDB('ST_GEOMETRY', function () { - beforeEach(setUpTableRemoteDB('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY'], 5)); - afterEach(dropTableRemoteDB('ST_GEOMETRY_TABLE', 5)); + beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY'], 5)); + afterEach(dropTable('ST_GEOMETRY_TABLE', 5)); it('should return valid ST_GEOMETRY types', function (done) { var values = [ @@ -649,8 +824,8 @@ describe('db', function () { }); describeRemoteDB('ST_POINT', function () { - beforeEach(setUpTableRemoteDB('ST_POINT_TABLE', ['A ST_POINT'], 5)); - afterEach(dropTableRemoteDB('ST_POINT_TABLE', 5)); + beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 5)); + afterEach(dropTable('ST_POINT_TABLE', 5)); it('should return valid ST_POINT types', function (done) { var values = [ @@ -692,8 +867,8 @@ describe('db', function () { }); describeRemoteDB('ST_GEOMETRY', function () { - beforeEach(setUpTableRemoteDB('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY(4326)'], 5)); - afterEach(dropTableRemoteDB('ST_GEOMETRY_TABLE', 5)); + beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY(4326)'], 5)); + afterEach(dropTable('ST_GEOMETRY_TABLE', 5)); it('should return valid ST_GEOMETRY types', function (done) { var insertValues = [ @@ -780,8 +955,8 @@ describe('db', function () { }); describeRemoteDB('ST_POINT', function () { - beforeEach(setUpTableRemoteDB('ST_POINT_TABLE', ['A ST_POINT'], 5)); - afterEach(dropTableRemoteDB('ST_POINT_TABLE', 5)); + beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 5)); + afterEach(dropTable('ST_POINT_TABLE', 5)); it('should return valid ST_POINT types', function (done) { var insertValues = [ @@ -820,8 +995,8 @@ describe('db', function () { }); describeRemoteDB('BOOLEAN', function () { - before(setUpTableRemoteDB('BOOLEAN_TABLE', ['A BOOLEAN'], 7)); - after(dropTableRemoteDB('BOOLEAN_TABLE', 7)); + before(setUpTable('BOOLEAN_TABLE', ['A BOOLEAN'], 7)); + after(dropTable('BOOLEAN_TABLE', 7)); it('should add valid booleans using different parameter types', function (done) { var insertValues = [ @@ -1287,4 +1462,4 @@ describe('db', function () { }); }); }); -}); \ No newline at end of file +}); diff --git a/test/fixtures/parametersData.js b/test/fixtures/parametersData.js index bbe1322..513a4ac 100644 --- a/test/fixtures/parametersData.js +++ b/test/fixtures/parametersData.js @@ -787,6 +787,10 @@ exports.DATETIME = { '3eb9830a990e000000' + '3e0100000000000000' + '3e80db887749000000' + + '3e80db887749000000' + + '3e81fd409f0b000000' + + '3e353019df0e000000' + + '3e0ea6b3c40e000000' + '8e' + '3f9e120b00' + '3f439d0600' + @@ -795,7 +799,16 @@ exports.DATETIME = { '8f' + '4039880000' + '4080510100' + - '4001000000', 'hex') + '4001000000' + + '40b53e0000' + + '408e9d0000' + + '4080510100' + + '10338c0105830a24db' + + '10e887071e920b0e45' + + '10d2870b11932f3219' + + '0f830a24db' + + '0f920b0e45' + + '0f932f3219', 'hex') }, types: [ TypeCode.LONGDATE, @@ -808,6 +821,10 @@ exports.DATETIME = { TypeCode.SECONDDATE, TypeCode.SECONDDATE, TypeCode.SECONDDATE, + TypeCode.SECONDDATE, + TypeCode.SECONDDATE, + TypeCode.SECONDDATE, + TypeCode.SECONDDATE, TypeCode.DAYDATE, TypeCode.DAYDATE, TypeCode.DAYDATE, @@ -817,6 +834,15 @@ exports.DATETIME = { TypeCode.SECONDTIME, TypeCode.SECONDTIME, TypeCode.SECONDTIME, + TypeCode.SECONDTIME, + TypeCode.SECONDTIME, + TypeCode.SECONDTIME, + TypeCode.TIMESTAMP, + TypeCode.TIMESTAMP, + TypeCode.TIMESTAMP, + TypeCode.TIME, + TypeCode.TIME, + TypeCode.TIME, ], values: [ null, @@ -829,6 +855,10 @@ exports.DATETIME = { '1987-10-16 09:41:12', '0001-01-01 00:00:00', '9999-12-31 23:59:59', + '9999-12-31 23:59:59.999999999999', + '1582-10-15 00:00:00.890123', + '2025-01-13 04:27:32.7891234', + '2011-01-01 11:12:13.167832', null, '1987-10-16', '1187-10-16', @@ -837,7 +867,16 @@ exports.DATETIME = { null, '09:41:12', '23:59:59', - '00:00:00' + '00:00:00', + '04:27:32.7891234', + '11:12:13.167832', + '23:59:59.9999999', + '3123-02-05T03:10:56.1', + '2024-08-30T18:11:17.678', + '2002-12-17T19:47:06.45059', + '03:10:56.1', + '18:11:17.678', + '19:47:06.45059', ] }; diff --git a/test/lib.Writer.js b/test/lib.Writer.js index a65e944..12ce16b 100644 --- a/test/lib.Writer.js +++ b/test/lib.Writer.js @@ -195,14 +195,14 @@ describe('Lib', function () { }); }); - it('should propertly round DATETIME ms value', function() { + it('should propertly truncate DATETIME ms value', function() { var writer = new Writer({ types: [TypeCode.TIMESTAMP] }); var dt = '2018-05-17T12:38:02.002Z'; writer.setValues([dt]); writer._buffers[0][7].should.equal(210); }); - it('should propertly round TIME ms value', function() { + it('should propertly truncate TIME ms value', function() { var writer = new Writer({ types: [TypeCode.TIME] }); var dt = '12:38:02.002Z'; writer.setValues([dt]); From ca4b31ad737f29be03e54e20771f0f37b3443e89 Mon Sep 17 00:00:00 2001 From: he-is-harry Date: Wed, 2 Apr 2025 16:03:46 -0400 Subject: [PATCH 2/3] Changed DFV5 and DFV7 integration tests to also run on DFV 1 - Updated the spatial types and boolean integration tests to also be run on DFV 1 (BINTEXT DFV6 integration tests were updated in the previous commit) - Wrote a comment about a BOOLEAN behaviour change in DFV 7, where the empty string '' will result in null in DFV 7 but in DFV 1 it results in false --- test/acceptance/db.DataType.js | 657 +++++++++++++++++++++------------ 1 file changed, 418 insertions(+), 239 deletions(-) diff --git a/test/acceptance/db.DataType.js b/test/acceptance/db.DataType.js index 5becde3..da5d2ca 100644 --- a/test/acceptance/db.DataType.js +++ b/test/acceptance/db.DataType.js @@ -242,6 +242,7 @@ describe('db', function () { } } + // DFV 4 types also includes BINTEXT (DFV 6) to group it within the on-premise HANA only types describeRemoteDB('DFV 4 types', function () { // DAYDATE var daydateInsertValues = [ @@ -746,114 +747,182 @@ describe('db', function () { describeRemoteDB('Spatial', function () { describeRemoteDB('default spatialTypes', function () { - describeRemoteDB('ST_GEOMETRY', function () { - beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY'], 5)); - afterEach(dropTable('ST_GEOMETRY_TABLE', 5)); + // ST_GEOMETRY (default spatialTypes) + var geometryValuesDFV5 = [ + null, + '010100000000000000000000000000000000000000', + Buffer.from("0103000000020000000400000000000000000014c00000000" + + "0000014c0000000000000144000000000000014c00000000000000000" + + "000000000000144000000000000014c000000000000014c0050000000" + + "0000000000000c000000000000000c000000000000000c00000000000" + + "000000000000000000004000000000000000000000000000000040000" + + "00000000000c000000000000000c000000000000000c0", "hex"), + Buffer.from("0106000000020000000103000000020000000400000000000" + + "000000014c000000000000014c0000000000000144000000000000014" + + "c00000000000000000000000000000144000000000000014c00000000" + + "0000014c00500000000000000000000c000000000000000c000000000" + + "000000c00000000000000000000000000000004000000000000000000" + + "00000000000004000000000000000c000000000000000c00000000000" + + "0000c0010300000001000000040000000000000000002440000000000" + + "00014c00000000000002e400000000000001440000000000000144000" + + "00000000001440000000000000244000000000000014c0", "hex"), + Buffer.from("01ed0300000200000001ea030000020000000000000000002" + + "440000000000000244000000000000024400000000000002840000000" + + "0000002840000000000000284001ea030000020000000000000000002" + + "c40000000000000244000000000000024400000000000003040000000" + + "00000028400000000000002840", "hex"), + ]; + var geometryInsertValuesDFV5 = geometryValuesDFV5.map(function (val) { return [val]; }); + var geometryExpected = geometryValuesDFV5.map(function (val) { + if (util.isString(val)) { + return {A: Buffer.from(val, "hex")}; + } else { + return {A: val}; + } + }); + // In DFV1, the parameter type becomes BINARY, so string input is not allowed + var geometryInsertValuesDFV1 = geometryExpected.map(function (row) { return [row.A]; }); + // EWKT conversion + var geometryEWKTInsertValues = [ + Buffer.from("0108000000030000000000000000000000000000000000000" + + "0000000000000f03f000000000000f03f000000000000000000000000" + + "00000040", "hex") + ]; + var geometryEWKTExpected = [{EWKTEXT: Buffer.from("SRID=0;CIRCULARSTRING (0 0,1 1,0 2)", "utf8")}]; + // Large ST_GEOMETRY + // Generate a multiline's hex that is 29282 bytes + var hexStr = "0105000000910100000"; + for (var i = 0; i < 400; i++) { + hexStr += "1020000000400000000000000000000000000000000000000000000000000000000000000" + + "0000f03f000000000000f03f000000000000f03f000000000000f03f00000000000000000"; + } + hexStr += "1020000000400000000000000000000000000000000000000000000000000000000000000" + + "0000f03f000000000000f03f000000000000f03f000000000000f03f0000000000000000"; + var geometryLargeInsertValuesDFV5 = [[hexStr]]; + // In DFV1, only buffers are supported as input + var geometryLargeInsertValuesDFV1 = [[Buffer.from(hexStr, 'hex')]]; + var geometryLargeExpected = [{A: Buffer.from(hexStr, 'hex')}]; + // Invalid ST_GEOMETRY + var geometryInvalidTestDataDFV5 = [ + { + value: 'Strings must be hex in this option', + errMessage: 'spatial error: Unexpected end of WKB at position 0: type_code=12, index=1' + }, + {value: 12345, errMessage: 'Cannot set parameter at row: 1. Argument must be a string or Buffer'} + ]; + var geometryInvalidTestDataDFV1 = [ + { + value: Buffer.from('Strings must be hex in this option', 'hex'), + errMessage: 'spatial error: Unexpected end of WKB at position 0: type_code=12, index=1' + }, + {value: 12345, errMessage: 'Cannot set parameter at row: 1. Wrong input for BINARY type'} + ]; + + // ST_POINT (default spatialTypes) + var pointValuesDFV5 = [ + null, + Buffer.from("010100000000000000000024400000000000003440", "hex"), + '0101000000000000c078e91b400000000000b6a5c0', + Buffer.from("010100000083b2f2ffadfaa3430564e1fc74b64643", "hex"), + ]; + var pointInsertValuesDFV5 = pointValuesDFV5.map(function (val) { return [val]; }); + var pointExpected = pointValuesDFV5.map(function (val) { + if (util.isString(val)) { + return {A: Buffer.from(val, "hex")}; + } else { + return {A: val}; + } + }); + // In DFV1, the parameter type becomes BINARY, so string input is not allowed + var pointInsertValuesDFV1 = pointExpected.map(function (row) { return [row.A]; }); + + // Invalid ST_POINT + // Keep the same tests as in ST_GEOMETRY + var pointInvalidTestDataDFV5 = geometryInvalidTestDataDFV5.slice(); + var pointInvalidTestDataDFV1 = geometryInvalidTestDataDFV1.slice(); + // Add a test for a ST_POINT that does not match + var mismatchPointInvalidTest = { + value: Buffer.from('01e9030000000000000000244000000000000034400000000000003e40', 'hex'), + errMessage: "spatial error: exception 1620502: The geometry type 'ST_Point' with dimension 'XYZ' is not allowed in column " + + "of type ST_POINT due to column constraints, which only allows 2-dimensional ST_Point types\n: type_code=12, index=1" + }; + pointInvalidTestDataDFV1.push(mismatchPointInvalidTest); + pointInvalidTestDataDFV5.push(mismatchPointInvalidTest); + + describeRemoteDB('DFV >= 5', function () { + describeRemoteDB('ST_GEOMETRY', function () { + beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY'], 5)); + afterEach(dropTable('ST_GEOMETRY_TABLE', 5)); + + it('should return valid ST_GEOMETRY types', function (done) { + testDataTypeValid('ST_GEOMETRY_TABLE', geometryInsertValuesDFV5, [74], geometryExpected, done); + }); + + it('should return valid EWKT conversions', function (done) { + testDataTypeValidSql('ST_GEOMETRY_TABLE', "SELECT A.ST_AsEWKT() EWKTEXT FROM ST_GEOMETRY_TABLE", + geometryEWKTInsertValues, [[25, 26]], geometryEWKTExpected, done); + }); - it('should return valid ST_GEOMETRY types', function (done) { - var values = [ - null, - '010100000000000000000000000000000000000000', - Buffer.from("0103000000020000000400000000000000000014c00000000" - + "0000014c0000000000000144000000000000014c00000000000000000" - + "000000000000144000000000000014c000000000000014c0050000000" - + "0000000000000c000000000000000c000000000000000c00000000000" - + "000000000000000000004000000000000000000000000000000040000" - + "00000000000c000000000000000c000000000000000c0", "hex"), - Buffer.from("0106000000020000000103000000020000000400000000000" - + "000000014c000000000000014c0000000000000144000000000000014" - + "c00000000000000000000000000000144000000000000014c00000000" - + "0000014c00500000000000000000000c000000000000000c000000000" - + "000000c00000000000000000000000000000004000000000000000000" - + "00000000000004000000000000000c000000000000000c00000000000" - + "0000c0010300000001000000040000000000000000002440000000000" - + "00014c00000000000002e400000000000001440000000000000144000" - + "00000000001440000000000000244000000000000014c0", "hex"), - Buffer.from("01ed0300000200000001ea030000020000000000000000002" - + "440000000000000244000000000000024400000000000002840000000" - + "0000002840000000000000284001ea030000020000000000000000002" - + "c40000000000000244000000000000024400000000000003040000000" - + "00000028400000000000002840", "hex"), - ]; - var insertValues = values.map(function (val) { return [val]; }); - var expected = values.map(function (val) { - if (util.isString(val)) { - return {A: Buffer.from(val, "hex")}; - } else { - return {A: val}; - } + it('should insert and return large ST_GEOMETRY types', function (done) { + testDataTypeValid('ST_GEOMETRY_TABLE', geometryLargeInsertValuesDFV5, [74], geometryLargeExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(geometryInvalidTestDataDFV5, testDataTypeError.bind(null, 'ST_GEOMETRY_TABLE'), done); }); - testDataTypeValid('ST_GEOMETRY_TABLE', insertValues, [74], expected, done); }); - - it('should return valid EWKT conversions', function (done) { - var insertValues = [ - Buffer.from("0108000000030000000000000000000000000000000000000" - + "0000000000000f03f000000000000f03f000000000000000000000000" - + "00000040", "hex") - ]; - var expected = [{EWKTEXT: Buffer.from("SRID=0;CIRCULARSTRING (0 0,1 1,0 2)", "utf8")}]; - testDataTypeValidSql('ST_GEOMETRY_TABLE', "SELECT A.ST_AsEWKT() EWKTEXT FROM ST_GEOMETRY_TABLE", insertValues, [[25, 26]], expected, done); + + describeRemoteDB('ST_POINT', function () { + beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 5)); + afterEach(dropTable('ST_POINT_TABLE', 5)); + + it('should return valid ST_POINT types', function (done) { + testDataTypeValid('ST_POINT_TABLE', pointInsertValuesDFV5, [75], pointExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(pointInvalidTestDataDFV5, testDataTypeError.bind(null, 'ST_POINT_TABLE'), done); + }); }); + }); - it('should insert and return large ST_GEOMETRY types', function (done) { - // Generate a multiline's hex that is 29282 bytes - var hexStr = "0105000000910100000"; - for (var i = 0; i < 400; i++) { - hexStr += "1020000000400000000000000000000000000000000000000000000000000000000000000" - + "0000f03f000000000000f03f000000000000f03f000000000000f03f00000000000000000"; - } - hexStr += "1020000000400000000000000000000000000000000000000000000000000000000000000" - + "0000f03f000000000000f03f000000000000f03f000000000000f03f0000000000000000"; + describeRemoteDB('DFV 1', function () { + before(changeDataFormatSupport(1)); + after(changeDataFormatSupport(ORGINAL_DATA_FORMAT)); - var insertValues = [[hexStr]]; - var expected = [{A: Buffer.from(hexStr, 'hex')}]; - testDataTypeValid('ST_GEOMETRY_TABLE', insertValues, [74], expected, done); - }); - - it('should raise input type error', function (done) { - var invalidTestData = [ - { - value: 'Strings must be hex in this option', - errMessage: 'spatial error: Unexpected end of WKB at position 0: type_code=12, index=1' - }, - {value: 12345, errMessage: 'Cannot set parameter at row: 1. Argument must be a string or Buffer'} - ]; - async.each(invalidTestData, testDataTypeError.bind(null, 'ST_GEOMETRY_TABLE'), done); - }); - }); - - describeRemoteDB('ST_POINT', function () { - beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 5)); - afterEach(dropTable('ST_POINT_TABLE', 5)); + describeRemoteDB('ST_GEOMETRY', function () { + beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY'], 1)); + afterEach(dropTable('ST_GEOMETRY_TABLE', 1)); + + it('should return valid ST_GEOMETRY types', function (done) { + testDataTypeValid('ST_GEOMETRY_TABLE', geometryInsertValuesDFV1, [13], geometryExpected, done); + }); + + it('should return valid EWKT conversions', function (done) { + testDataTypeValidSql('ST_GEOMETRY_TABLE', "SELECT A.ST_AsEWKT() EWKTEXT FROM ST_GEOMETRY_TABLE", + geometryEWKTInsertValues, [[25, 26]], geometryEWKTExpected, done); + }); - it('should return valid ST_POINT types', function (done) { - var values = [ - null, - Buffer.from("010100000000000000000024400000000000003440", "hex"), - '0101000000000000c078e91b400000000000b6a5c0', - Buffer.from("010100000083b2f2ffadfaa3430564e1fc74b64643", "hex"), - ]; - var insertValues = values.map(function (val) { return [val]; }); - var expected = values.map(function (val) { - if (util.isString(val)) { - return {A: Buffer.from(val, "hex")}; - } else { - return {A: val}; - } + it('should insert and return large ST_GEOMETRY types', function (done) { + testDataTypeValid('ST_GEOMETRY_TABLE', geometryLargeInsertValuesDFV1, [13], geometryLargeExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(geometryInvalidTestDataDFV1, testDataTypeError.bind(null, 'ST_GEOMETRY_TABLE'), done); }); - testDataTypeValid('ST_POINT_TABLE', insertValues, [75], expected, done); }); - - it('should raise input type error', function (done) { - var invalidTestData = [ - { - value: 'Strings must be hex in this option', - errMessage: 'spatial error: Unexpected end of WKB at position 0: type_code=12, index=1' - }, - {value: 12345, errMessage: 'Cannot set parameter at row: 1. Argument must be a string or Buffer'} - ]; - async.each(invalidTestData, testDataTypeError.bind(null, 'ST_POINT_TABLE'), done); + + describeRemoteDB('ST_POINT', function () { + beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 1)); + afterEach(dropTable('ST_POINT_TABLE', 1)); + + it('should return valid ST_POINT types', function (done) { + testDataTypeValid('ST_POINT_TABLE', pointInsertValuesDFV1, [13], pointExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(pointInvalidTestDataDFV1, testDataTypeError.bind(null, 'ST_POINT_TABLE'), done); + }); }); }); }); @@ -866,166 +935,276 @@ describe('db', function () { }) }); - describeRemoteDB('ST_GEOMETRY', function () { - beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY(4326)'], 5)); - afterEach(dropTable('ST_GEOMETRY_TABLE', 5)); + // ST_GEOMETRY (spatialTypes 1) + var geometryInsertValuesDFV5 = [ + [null], + [Buffer.from("010100000000000000000024400000000000002440", "hex")], + ['MultiLineString ((10 10, 12 12), (14 10, 16 12))'], + ['GeometryCollection (LineString(5 10, 10 12, 15 10), Polygon ((10 -5, 15 5, 5 5, 10 -5)))'], + ['MultiPolygon Z(((-5 -5 4, 5 -5 7, 0 5 1, -5 -5 4), (-2 -2 9, -2 0 4, 2 0 4, 2 -2 1, -2 -2 9)), ((10 -5 2, 15 5 2, 5 5 3, 10 -5 2)))'], + ]; + var geometryExpected = [ + {A: null}, + {A: Buffer.from("010100000000000000000024400000000000002440", "hex")}, + {A: Buffer.from("010500000002000000010200000002000000000000000" + + "000244000000000000024400000000000002840000000000000284001" + + "02000000020000000000000000002c400000000000002440000000000" + + "00030400000000000002840", "hex")}, + {A: Buffer.from("010700000002000000010200000003000000000000000" + + "000144000000000000024400000000000002440000000000000284000" + + "00000000002e400000000000002440010300000001000000040000000" + + "00000000000244000000000000014c00000000000002e400000000000" + + "001440000000000000144000000000000014400000000000002440000" + + "00000000014c0", "hex")}, + {A: Buffer.from("01ee0300000200000001eb03000002000000040000000" + + "0000000000014c000000000000014c000000000000010400000000000" + + "00144000000000000014c00000000000001c400000000000000000000" + + "0000000001440000000000000f03f00000000000014c0000000000000" + + "14c000000000000010400500000000000000000000c00000000000000" + + "0c0000000000000224000000000000000c00000000000000000000000" + + "000000104000000000000000400000000000000000000000000000104" + + "0000000000000004000000000000000c0000000000000f03f00000000" + + "000000c000000000000000c0000000000000224001eb0300000100000" + + "004000000000000000000244000000000000014c00000000000000040" + + "0000000000002e4000000000000014400000000000000040000000000" + + "000144000000000000014400000000000000840000000000000244000" + + "000000000014c00000000000000040", "hex")} + ]; + // Even in spatialTypes 1, DFV 1 only supports buffers since the parameter type + // becomes BINARY + var geometryInsertValuesDFV1 = geometryExpected.map(function (row) { + return [row.A]; + }); + // EWKT conversion + var geometryEWKTInsertValuesDFV5 = [ + ['LineString ZM(0 0 3 6, 5 10 4 8)'], + ['MultiPoint ZM((10 10 12 1), (12 12 14 1), (14 10 10 1))'], + ]; + var geometryEWKTExpectedDFV5 = [ + {EWKTEXT: Buffer.from("SRID=4326;LINESTRING ZM (0 0 3 6,5 10 4 8)", "utf8")}, + {EWKTEXT: Buffer.from("SRID=4326;MULTIPOINT ZM ((10 10 12 1),(12 12 14 1),(14 10 10 1))", "utf8")}, + ]; + // Large ST_GEOMETRY + var xMax = 30; + var yMax = 30; + + // Generate multilines that are 21,155 bytes long + var lines = "MULTILINESTRING ((0 0,0 1,1 1,1 0)"; + for (var x = 0; x < xMax; x++) { + var x1 = x + 1; + for (var y = 0; y < yMax; y++) { + var y1 = y + 1; + lines += ",(" + x + " " + y + "," + x + " " + y1 + "," + y1 + " " + x1 + + "," + x1 + " " + y + ")"; + } + } + lines += ")"; + var geometryLargeInsertValuesDFV5 = [[lines]]; + var geometryLargeExpectedDFV5 = [{EWKTEXT: Buffer.from("SRID=4326;" + lines, "utf8")}]; + // Invalid ST_GEOMETRY + var geometryInvalidTestDataDFV5 = [ + { + value: '010100000000000000000000000000000000000000', + errMessage: "spatial error: Invalid or unsupported geometry type '010100000000000000000000000000000000000000' at " + + "position 0 of WKT 010100000000000000000000000000000000000000: type_code=29, index=1" + }, + {value: 12345, errMessage: 'Cannot set parameter at row: 1. Argument must be a string or Buffer'} + ]; + var geometryInvalidTestDataDFV1 = [ + { + value: 'MultiLineString ((10 10, 12 12), (14 10, 16 12))', // Check that strings are not valid input in DFV 1 + errMessage: "Cannot set parameter at row: 1. Wrong input for BINARY type" + }, + {value: 12345, errMessage: 'Cannot set parameter at row: 1. Wrong input for BINARY type'} + ]; + + // ST_POINT (spatialTypes 1) + var pointInsertValuesDFV5 = [ + [null], + [Buffer.from("010100000000000000000000400000000000000840", "hex")], + ['Point (-10 0)'], + ['Point (0.5 0.5)'], + ]; + var pointExpected = [ + {A: null}, + {A: Buffer.from("010100000000000000000000400000000000000840", "hex")}, + {A: Buffer.from("010100000000000000000024c00000000000000000", "hex")}, + {A: Buffer.from("0101000000000000000000e03f000000000000e03f", "hex")} + ]; + // Same as before, even in spatialTypes 1, DFV 1 only supports buffers + var pointInsertValuesDFV1 = pointExpected.map(function (row) { + return [row.A]; + }); + // Invalid ST_POINT + var pointInvalidTestDataDFV5 = [ + { + value: '010100000000000000000024c00000000000000000', + errMessage: "spatial error: Invalid or unsupported geometry type '010100000000000000000024c00000000000000000' at " + + "position 0 of WKT 010100000000000000000024c00000000000000000: type_code=29, index=1" + }, + {value: 12345, errMessage: 'Cannot set parameter at row: 1. Argument must be a string or Buffer'}, + { + value: 'Point Z(10 20 30)', + errMessage: "spatial error: exception 1620502: The geometry type 'ST_Point' with dimension 'XYZ' is not allowed in " + + "column of type ST_POINT due to column constraints, which only allows 2-dimensional ST_Point types\n: type_code=29, index=1" + } + ]; + var pointInvalidTestDataDFV1 = [ + // Check that strings are not valid input in DFV 1 + {value: 'Point (25 25)', errMessage: "Cannot set parameter at row: 1. Wrong input for BINARY type"}, + {value: 12345, errMessage: 'Cannot set parameter at row: 1. Wrong input for BINARY type'}, + { + value: Buffer.from('01e9030000000000000000244000000000000034400000000000003e40', 'hex'), + errMessage: "spatial error: exception 1620502: The geometry type 'ST_Point' with dimension 'XYZ' is not allowed in " + + "column of type ST_POINT due to column constraints, which only allows 2-dimensional ST_Point types\n: type_code=12, index=1" + } + ]; + + describeRemoteDB('DFV >= 5', function () { + describeRemoteDB('ST_GEOMETRY', function () { + beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY(4326)'], 5)); + afterEach(dropTable('ST_GEOMETRY_TABLE', 5)); + + it('should return valid ST_GEOMETRY types', function (done) { + testDataTypeValid('ST_GEOMETRY_TABLE', geometryInsertValuesDFV5, [74], geometryExpected, done); + }); + + it('should return valid EWKT conversions', function (done) { + testDataTypeValidSql('ST_GEOMETRY_TABLE', "SELECT A.ST_AsEWKT() EWKTEXT FROM ST_GEOMETRY_TABLE", + geometryEWKTInsertValuesDFV5, [[25, 26]], geometryEWKTExpectedDFV5, done); + }); - it('should return valid ST_GEOMETRY types', function (done) { - var insertValues = [ - [null], - [Buffer.from("010100000000000000000024400000000000002440", "hex")], - ['MultiLineString ((10 10, 12 12), (14 10, 16 12))'], - ['GeometryCollection (LineString(5 10, 10 12, 15 10), Polygon ((10 -5, 15 5, 5 5, 10 -5)))'], - ['MultiPolygon Z(((-5 -5 4, 5 -5 7, 0 5 1, -5 -5 4), (-2 -2 9, -2 0 4, 2 0 4, 2 -2 1, -2 -2 9)), ((10 -5 2, 15 5 2, 5 5 3, 10 -5 2)))'], - ]; - var expected = [ - {A: null}, - {A: Buffer.from("010100000000000000000024400000000000002440", "hex")}, - {A: Buffer.from("010500000002000000010200000002000000000000000" - + "000244000000000000024400000000000002840000000000000284001" - + "02000000020000000000000000002c400000000000002440000000000" - + "00030400000000000002840", "hex")}, - {A: Buffer.from("010700000002000000010200000003000000000000000" - + "000144000000000000024400000000000002440000000000000284000" - + "00000000002e400000000000002440010300000001000000040000000" - + "00000000000244000000000000014c00000000000002e400000000000" - + "001440000000000000144000000000000014400000000000002440000" - + "00000000014c0", "hex")}, - {A: Buffer.from("01ee0300000200000001eb03000002000000040000000" - + "0000000000014c000000000000014c000000000000010400000000000" - + "00144000000000000014c00000000000001c400000000000000000000" - + "0000000001440000000000000f03f00000000000014c0000000000000" - + "14c000000000000010400500000000000000000000c00000000000000" - + "0c0000000000000224000000000000000c00000000000000000000000" - + "000000104000000000000000400000000000000000000000000000104" - + "0000000000000004000000000000000c0000000000000f03f00000000" - + "000000c000000000000000c0000000000000224001eb0300000100000" - + "004000000000000000000244000000000000014c00000000000000040" - + "0000000000002e4000000000000014400000000000000040000000000" - + "000144000000000000014400000000000000840000000000000244000" - + "000000000014c00000000000000040", "hex")} - ]; - testDataTypeValid('ST_GEOMETRY_TABLE', insertValues, [74], expected, done); + it('should insert and return large ST_GEOMETRY types', function (done) { + testDataTypeValidSql('ST_GEOMETRY_TABLE', "SELECT A.ST_AsEWKT() EWKTEXT FROM ST_GEOMETRY_TABLE", + geometryLargeInsertValuesDFV5, [[25, 26]], geometryLargeExpectedDFV5, done); + }); + + it('should raise input type error', function (done) { + async.each(geometryInvalidTestDataDFV5, testDataTypeError.bind(null, 'ST_GEOMETRY_TABLE'), done); + }); }); - it('should return valid EWKT conversions', function (done) { - var insertValues = [ - ['LineString ZM(0 0 3 6, 5 10 4 8)'], - ['MultiPoint ZM((10 10 12 1), (12 12 14 1), (14 10 10 1))'], - ]; - var expected = [ - {EWKTEXT: Buffer.from("SRID=4326;LINESTRING ZM (0 0 3 6,5 10 4 8)", "utf8")}, - {EWKTEXT: Buffer.from("SRID=4326;MULTIPOINT ZM ((10 10 12 1),(12 12 14 1),(14 10 10 1))", "utf8")}, - ]; - testDataTypeValidSql('ST_GEOMETRY_TABLE', "SELECT A.ST_AsEWKT() EWKTEXT FROM ST_GEOMETRY_TABLE", insertValues, [[25, 26]], expected, done); + describeRemoteDB('ST_POINT', function () { + beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 5)); + afterEach(dropTable('ST_POINT_TABLE', 5)); + + it('should return valid ST_POINT types', function (done) { + testDataTypeValid('ST_POINT_TABLE', pointInsertValuesDFV5, [75], pointExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(pointInvalidTestDataDFV5, testDataTypeError.bind(null, 'ST_POINT_TABLE'), done); + }); }); + }); - it('should insert and return large ST_GEOMETRY types', function (done) { - var xMax = 30; - var yMax = 30; - - // Generate multilines that are 21,155 bytes long - var lines = "MULTILINESTRING ((0 0,0 1,1 1,1 0)"; - for (var x = 0; x < xMax; x++) { - var x1 = x + 1; - for (var y = 0; y < yMax; y++) { - var y1 = y + 1; - lines += ",(" + x + " " + y + "," + x + " " + y1 + "," + y1 + " " + x1 - + "," + x1 + " " + y + ")"; - } - } - lines += ")"; + describeRemoteDB('DFV 1', function () { + before(changeDataFormatSupport(1)); + after(changeDataFormatSupport(ORGINAL_DATA_FORMAT)); - var insertValues = [[lines]]; - var expected = [{EWKTEXT: Buffer.from("SRID=4326;" + lines, "utf8")}]; - testDataTypeValidSql('ST_GEOMETRY_TABLE', "SELECT A.ST_AsEWKT() EWKTEXT FROM ST_GEOMETRY_TABLE", insertValues, [[25, 26]], expected, done); + describeRemoteDB('ST_GEOMETRY', function () { + beforeEach(setUpTable('ST_GEOMETRY_TABLE', ['A ST_GEOMETRY(4326)'], 1)); + afterEach(dropTable('ST_GEOMETRY_TABLE', 1)); + + it('should return valid ST_GEOMETRY types', function (done) { + testDataTypeValid('ST_GEOMETRY_TABLE', geometryInsertValuesDFV1, [13], geometryExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(geometryInvalidTestDataDFV1, testDataTypeError.bind(null, 'ST_GEOMETRY_TABLE'), done); + }); }); - it('should raise input type error', function (done) { - var invalidTestData = [ - { - value: '010100000000000000000000000000000000000000', - errMessage: "spatial error: Invalid or unsupported geometry type '010100000000000000000000000000000000000000' at " - + "position 0 of WKT 010100000000000000000000000000000000000000: type_code=29, index=1" - }, - {value: 12345, errMessage: 'Cannot set parameter at row: 1. Argument must be a string or Buffer'} - ]; - async.each(invalidTestData, testDataTypeError.bind(null, 'ST_GEOMETRY_TABLE'), done); + describeRemoteDB('ST_POINT', function () { + beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 1)); + afterEach(dropTable('ST_POINT_TABLE', 1)); + + it('should return valid ST_POINT types', function (done) { + testDataTypeValid('ST_POINT_TABLE', pointInsertValuesDFV1, [13], pointExpected, done); + }); + + it('should raise input type error', function (done) { + async.each(pointInvalidTestDataDFV1, testDataTypeError.bind(null, 'ST_POINT_TABLE'), done); + }); }); }); + }); + }); - describeRemoteDB('ST_POINT', function () { - beforeEach(setUpTable('ST_POINT_TABLE', ['A ST_POINT'], 5)); - afterEach(dropTable('ST_POINT_TABLE', 5)); - - it('should return valid ST_POINT types', function (done) { - var insertValues = [ - [null], - [Buffer.from("010100000000000000000000400000000000000840", "hex")], - ['Point (-10 0)'], - ['Point (0.5 0.5)'], - ]; - var expected = [ - {A: null}, - {A: Buffer.from("010100000000000000000000400000000000000840", "hex")}, - {A: Buffer.from("010100000000000000000024c00000000000000000", "hex")}, - {A: Buffer.from("0101000000000000000000e03f000000000000e03f", "hex")} - ]; - testDataTypeValid('ST_POINT_TABLE', insertValues, [75], expected, done); + describeRemoteDB('DFV 7 (BOOLEAN)', function () { + var booleanInsertValuesDFV7 = [ + [true], + [null], + [false], + [1], + [10.5], + [0], + ['TRUE'], + ['FAlsE'], + ['UNknOwn'], + ['1'], + ['0'], + [''], + ]; + var booleanExpectedDFV7 = [{A: true}, {A: null}, {A: false}, {A: true}, {A: true}, + {A: false}, {A: true}, {A: false}, {A: null}, {A: true}, + {A: false}, {A: null}]; + // In DFV1, non number strings are not supported ('true', 'unknown'). There is also a behaviour change + // for '' which returns null in DFV7 but 0 (false) in DFV1. + var booleanInsertValuesDFV1 = [ + [true], + [null], + [false], + [1], + [10.5], + [0], + ['1'], + ['0'], + [''], + ]; + var booleanExpectedDFV1 = [{A: 1}, {A: null}, {A: 0}, {A: 1}, {A: 1}, {A: 0}, {A: 1}, {A: 0}, {A: 0}]; + var booleanInvalidValuesDFV7 = ['String not boolean', Buffer.from("01", "hex")]; + // Add the same expected error message to the values + var booleanInvalidTestDataDFV7 = booleanInvalidValuesDFV7.map(function (testValue) { + return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for BOOLEAN type"}; + }); + // In DFV1, the error message is for the TINYINT type and boolean strings like 'true' cannot be added + var booleanInvalidValuesDFV1 = ['true', 'String not boolean', Buffer.from("01", "hex")]; + var booleanInvalidTestDataDFV1 = booleanInvalidValuesDFV1.map(function (testValue) { + return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for TINYINT type"}; + }); + + describeRemoteDB('DFV >= 7', function () { + describeRemoteDB('BOOLEAN', function () { + before(setUpTable('BOOLEAN_TABLE', ['A BOOLEAN'], 7)); + after(dropTable('BOOLEAN_TABLE', 7)); + + it('should add valid booleans using different parameter types', function (done) { + testDataTypeValid('BOOLEAN_TABLE', booleanInsertValuesDFV7, [28], booleanExpectedDFV7, done); }); - + it('should raise input type error', function (done) { - var invalidTestData = [ - { - value: '010100000000000000000024c00000000000000000', - errMessage: "spatial error: Invalid or unsupported geometry type '010100000000000000000024c00000000000000000' at " - + "position 0 of WKT 010100000000000000000024c00000000000000000: type_code=29, index=1" - }, - {value: 12345, errMessage: 'Cannot set parameter at row: 1. Argument must be a string or Buffer'}, - { - value: 'Point Z(10 20 30)', - errMessage: "spatial error: exception 1620502: The geometry type 'ST_Point' with dimension 'XYZ' is not allowed in " - + "column of type ST_POINT due to column constraints, which only allows 2-dimensional ST_Point types\n: type_code=29, index=1" - } - ]; - async.each(invalidTestData, testDataTypeError.bind(null, 'ST_POINT_TABLE'), done); + async.each(booleanInvalidTestDataDFV7, testDataTypeError.bind(null, 'BOOLEAN_TABLE'), done); }); }); }); - }); - describeRemoteDB('BOOLEAN', function () { - before(setUpTable('BOOLEAN_TABLE', ['A BOOLEAN'], 7)); - after(dropTable('BOOLEAN_TABLE', 7)); + describeRemoteDB('DFV 1', function () { + before(changeDataFormatSupport(1)); + after(changeDataFormatSupport(ORGINAL_DATA_FORMAT)); - it('should add valid booleans using different parameter types', function (done) { - var insertValues = [ - [true], - [null], - [false], - [1], - [10.5], - [0], - ['TRUE'], - ['FAlsE'], - ['UNknOwn'], - ['1'], - ['0'], - [''], - ]; - var expected = [{A: true}, {A: null}, {A: false}, {A: true}, {A: true}, - {A: false}, {A: true}, {A: false}, {A: null}, {A: true}, - {A: false}, {A: null}]; - testDataTypeValid('BOOLEAN_TABLE', insertValues, [28], expected, done); - }); + describeRemoteDB('BOOLEAN', function () { + before(setUpTable('BOOLEAN_TABLE', ['A BOOLEAN'], 1)); + after(dropTable('BOOLEAN_TABLE', 1)); + + it('should add valid booleans using different parameter types', function (done) { + testDataTypeValid('BOOLEAN_TABLE', booleanInsertValuesDFV1, [1], booleanExpectedDFV1, done); + }); - it('should raise input type error', function (done) { - var invalidValues = ['String not boolean', Buffer.from("01", "hex")]; - // Add the same expected error message to the values - var invalidTestData = invalidValues.map(function (testValue) { - return {value: testValue, errMessage: "Cannot set parameter at row: 1. Wrong input for BOOLEAN type"}; + it('should raise input type error', function (done) { + async.each(booleanInvalidTestDataDFV1, testDataTypeError.bind(null, 'BOOLEAN_TABLE'), done); + }); }); - async.each(invalidTestData, testDataTypeError.bind(null, 'BOOLEAN_TABLE'), done); }); }); From cbc8b0aa27f584d66475669961562c640b137818 Mon Sep 17 00:00:00 2001 From: he-is-harry Date: Thu, 24 Apr 2025 14:33:45 -0400 Subject: [PATCH 3/3] Removed comment about empty string boolean input - Removed the comment about the behaviour change in DFV 7 which affects empty string inputs for BOOLEAN. - The behaviour will be clear in the test change for BOOLEAN in a separate pull request Bug: 342310 --- test/acceptance/db.DataType.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/test/acceptance/db.DataType.js b/test/acceptance/db.DataType.js index da5d2ca..d325f69 100644 --- a/test/acceptance/db.DataType.js +++ b/test/acceptance/db.DataType.js @@ -1149,8 +1149,7 @@ describe('db', function () { var booleanExpectedDFV7 = [{A: true}, {A: null}, {A: false}, {A: true}, {A: true}, {A: false}, {A: true}, {A: false}, {A: null}, {A: true}, {A: false}, {A: null}]; - // In DFV1, non number strings are not supported ('true', 'unknown'). There is also a behaviour change - // for '' which returns null in DFV7 but 0 (false) in DFV1. + // In DFV1, non number strings are not supported ('true', 'unknown'). var booleanInsertValuesDFV1 = [ [true], [null], @@ -1368,8 +1367,8 @@ describe('db', function () { describeRemoteDB('DFV >= 8', function () { describeRemoteDB('FIXED8', function () { - before(setUpTableRemoteDB('FIXED8_TABLE', ['A DECIMAL(15, 5)'], 8)); - after(dropTableRemoteDB('FIXED8_TABLE', 8)); + before(setUpTable('FIXED8_TABLE', ['A DECIMAL(15, 5)'], 8)); + after(dropTable('FIXED8_TABLE', 8)); it('should insert and return valid FIXED8 decimals', function (done) { testDataTypeValid('FIXED8_TABLE', fixed8InsertValues, [81], fixed8Expected, done); @@ -1383,8 +1382,8 @@ describe('db', function () { }); describeRemoteDB('FIXED12', function () { - before(setUpTableRemoteDB('FIXED12_TABLE', ['A DECIMAL(23, 5)'], 8)); - after(dropTableRemoteDB('FIXED12_TABLE', 8)); + before(setUpTable('FIXED12_TABLE', ['A DECIMAL(23, 5)'], 8)); + after(dropTable('FIXED12_TABLE', 8)); it('should insert and return valid FIXED12 decimals', function (done) { testDataTypeValid('FIXED12_TABLE', fixed12InsertValues, [82], fixed12Expected, done); @@ -1398,8 +1397,8 @@ describe('db', function () { }); describeRemoteDB('FIXED16', function () { - before(setUpTableRemoteDB('FIXED16_TABLE', ['A DECIMAL(35, 5)'], 8)); - after(dropTableRemoteDB('FIXED16_TABLE', 8)); + before(setUpTable('FIXED16_TABLE', ['A DECIMAL(35, 5)'], 8)); + after(dropTable('FIXED16_TABLE', 8)); it('should insert and return valid FIXED16 decimals', function (done) { testDataTypeValid('FIXED16_TABLE', fixed16InsertValues, [76], fixed16ExpectedDFV8, done); @@ -1418,8 +1417,8 @@ describe('db', function () { after(changeDataFormatSupport(ORGINAL_DATA_FORMAT)); describeRemoteDB('FIXED8', function () { - before(setUpTableRemoteDB('FIXED8_TABLE', ['A DECIMAL(15, 5)'], 7)); - after(dropTableRemoteDB('FIXED8_TABLE', 7)); + before(setUpTable('FIXED8_TABLE', ['A DECIMAL(15, 5)'], 7)); + after(dropTable('FIXED8_TABLE', 7)); it('should insert and return valid FIXED8 decimals', function (done) { testDataTypeValid('FIXED8_TABLE', fixed8InsertValues, [5], fixed8Expected, done); @@ -1434,8 +1433,8 @@ describe('db', function () { }); describeRemoteDB('FIXED12', function () { - before(setUpTableRemoteDB('FIXED12_TABLE', ['A DECIMAL(23, 5)'], 7)); - after(dropTableRemoteDB('FIXED12_TABLE', 7)); + before(setUpTable('FIXED12_TABLE', ['A DECIMAL(23, 5)'], 7)); + after(dropTable('FIXED12_TABLE', 7)); it('should insert and return valid FIXED12 decimals', function (done) { testDataTypeValid('FIXED12_TABLE', fixed12InsertValues, [5], fixed12Expected, done); @@ -1449,8 +1448,8 @@ describe('db', function () { }); describeRemoteDB('FIXED16', function () { - before(setUpTableRemoteDB('FIXED16_TABLE', ['A DECIMAL(35, 5)'], 7)); - after(dropTableRemoteDB('FIXED16_TABLE', 7)); + before(setUpTable('FIXED16_TABLE', ['A DECIMAL(35, 5)'], 7)); + after(dropTable('FIXED16_TABLE', 7)); it('should insert and return valid FIXED16 decimals', function (done) { testDataTypeValid('FIXED16_TABLE', fixed16InsertValues, [5], fixed16ExpectedDFV7, done);