From 973af2a4bbf71c0f63a104389fe8777ddb76f60e Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Mon, 15 Jul 2024 16:20:47 +0200 Subject: [PATCH 01/25] SNOW-1519397- Added structured types test --- test/integration/testStructuredType.js | 94 ++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 test/integration/testStructuredType.js diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js new file mode 100644 index 000000000..13bd1cc19 --- /dev/null +++ b/test/integration/testStructuredType.js @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015-2024 Snowflake Computing Inc. All rights reserved. + */ + +const async = require('async'); +const GlobalConfig = require('./../../lib/global_config'); +const snowflake = require('./../../lib/snowflake'); +const testUtil = require('./testUtil'); +const sharedStatement = require('./sharedStatements'); +const bigInt = require('big-integer'); + +describe('Test DataType', function () { + let connection; + const selectObject = 'select {\'string\':\'a\'}::OBJECT(string VARCHAR) as result'; + // const selectNumber = 'select * from testNumber'; + // const selectVariant = 'select * from testVariant'; + // const selectArray = 'select * from testArray'; + // const selectDate = 'select * from testDate'; + // const selectTime = 'select * from testTime'; + // const selectTimestamp = 'select * from testTimestamp'; + // const selectBoolean = 'select * from testBoolean'; + // const selectString = 'select * from testString'; + + before(function (done) { + connection = testUtil.createConnection({ 'proxyHost': '127.0.0.1', 'proxyPort': 8080 }); + async.series([ + function (callback) { + snowflake.configure({ 'insecureConnect': true }); + GlobalConfig.setInsecureConnect(true); + testUtil.connect(connection, callback); + }], + done + ); + }); + + // after(function (done) { + // async.series([ + // function (callback) { + // testUtil.executeCmd(connection, selectObject, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithVariant, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithArray, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithNumber, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithDouble, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithDate, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithTime, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithTimestamp, callback); + // }, + // function (callback) { + // testUtil.executeCmd(connection, dropTableWithBoolean, callback); + // }, + // function (callback) { + // testUtil.destroyConnection(connection, callback); + // }], + // done + // ); + // }); + + describe('testNumber', function () { + it('testObject', function (done) { + async.series([ + function (callback) { + testUtil.executeCmd(connection, 'alter session set ENABLE_STRUCTURED_TYPES_IN_CLIENT_RESPONSE = true', callback); + }, + function (callback) { + testUtil.executeCmd(connection, 'alter session set IGNORE_CLIENT_VESRION_IN_STRUCTURED_TYPES_RESPONSE = true', callback); + }, + function (callback) { + testUtil.executeQueryAndVerify( + connection, + selectObject, + [{ 'string': 'a' }], + callback + ); + }], + done + ); + }); + }); + +}); From bc8e42550fea2c076b8531ca824bf58dbc524dd5 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 18 Jul 2024 10:19:26 +0200 Subject: [PATCH 02/25] SNOW-1332640 - structured type - simple types --- lib/connection/result/column.js | 89 ++++- lib/connection/result/data_types.js | 11 +- .../result/datetime_format_converter.js | 104 ++++++ lib/connection/result/sf_timestamp.js | 33 +- lib/core.js | 3 + test/integration/testStructuredType.js | 330 +++++++++++++++--- test/integration/testUtil.js | 2 + 7 files changed, 484 insertions(+), 88 deletions(-) create mode 100644 lib/connection/result/datetime_format_converter.js diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index afa831e84..4981487a0 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -10,7 +10,10 @@ const Logger = require('../../logger'); const SfTimestamp = require('./sf_timestamp'); const DataTypes = require('./data_types'); const SqlTypes = require('./data_types').SqlTypes; +const dateTimeFormatConverter = require('./datetime_format_converter'); const bigInt = require('big-integer'); +const moment = require('moment'); +const momentTimezone = require('moment-timezone'); /** * Creates a new Column. @@ -28,6 +31,7 @@ function Column(options, index, statementParameters, resultVersion) { const scale = options.scale; const type = options.type; const precision = options.precision; + const metadataFields = options.fields; /** * Returns the name of this column. @@ -168,6 +172,9 @@ function Column(options, index, statementParameters, resultVersion) { } else if (this.isVariant()) { convert = convertRawVariant; toString = toStringFromVariant; + } else if (this.isObject()) { + convert = convertRawObject; + toString = toStringFromVariant; } else { // column is of type string, so leave value as is convert = noop; @@ -183,7 +190,8 @@ function Column(options, index, statementParameters, resultVersion) { toString: toString, format: format, resultVersion: resultVersion, - statementParameters: statementParameters + statementParameters: statementParameters, + metadataFields: metadataFields }; /** @@ -268,14 +276,89 @@ function convertRawBigInt(rawColumnValue) { function convertRawBoolean(rawColumnValue) { let ret; - if ((rawColumnValue === '1') || (rawColumnValue === 'TRUE')) { + if (rawColumnValue === true || (rawColumnValue === '1') || (rawColumnValue === 'TRUE')) { ret = true; - } else if ((rawColumnValue === '0') || (rawColumnValue === 'FALSE')) { + } else if (rawColumnValue === false || (rawColumnValue === '0') || (rawColumnValue === 'FALSE')) { ret = false; } return ret; } +/** + * Converts a raw column value of structured type object to javascript Object + * + * @param {String} rawColumnValue + * + * @returns {Object} + */ +function convertRawObject(rawColumnValue, column, context) { + const metadata = context.metadataFields.reduce(function (map, obj) { + map[obj.name] = obj; + return map; + }, {}); + const json = JSON.parse(rawColumnValue); + const result = {}; + + Object.keys(json).forEach(function (key) { + result[key] = mapStructuredTypeFieldValue(json[key], metadata[key], context, column); + }); + + return result; + +} + +function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, column) { + let value; + switch (columnMetadata.type) { + case 'text': + value = columnValue; + break; + case 'real': + value = toValueFromNumber(convertRawNumber(columnValue)); + break; + case 'fixed': + value = toValueFromNumber(convertRawNumber(columnValue)); + break; + case 'boolean': + value = convertRawBoolean(columnValue); + break; + case 'timestamp_ltz': + context.format = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; + value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + break; + case 'timestamp_ntz': + context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; + value = convertDateTimeString(columnValue, context.format, moment.tz.guess(), column.getScale()).toSfDate(); + break; + case 'timestamp_tz': + context.format = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; + value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + break; + case 'date': + context.format = context.statementParameters['DATE_OUTPUT_FORMAT']; + value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + break; + case 'time': + context.format = context.statementParameters['TIME_OUTPUT_FORMAT']; + value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfTime(); + break; + case 'binary': + context.format = context.statementParameters['BINARY_OUTPUT_FORMAT']; + // } + value = convertRawBinary(columnValue, column, context).toJSON().data; + break; + default: + Logger.getInstance().info(`Column type not supported: ${columnMetadata.type}`); + throw new Error('Algorithm was not recognized!'); + } + return value; +} + +const convertDateTimeString = function (stringValue, formatSql, timezone, scale) { + const formatMoment = dateTimeFormatConverter.convertSnowflakeFormatToMomentFormat(formatSql, scale); + const epochSeconds = momentTimezone(stringValue, formatMoment).unix(); + return new SfTimestamp(epochSeconds, 0, scale, timezone, formatSql); +}; /** * Converts a raw column value of type Date to a Snowflake Date. diff --git a/lib/connection/result/data_types.js b/lib/connection/result/data_types.js index ba9aa4a0b..8c87cc64e 100644 --- a/lib/connection/result/data_types.js +++ b/lib/connection/result/data_types.js @@ -145,7 +145,6 @@ const sqlTypes = */ isVariant: function (sqlType) { return (sqlType === this.values.VARIANT) || - (sqlType === this.values.OBJECT) || (sqlType === this.values.ARRAY); }, @@ -181,7 +180,10 @@ const nativeTypes = NUMBER: 'NUMBER', DATE: 'DATE', JSON: 'JSON', - BUFFER: 'BUFFER' + BUFFER: 'BUFFER', + OBJECT: 'OBJECT', + ARRAY: 'ARRAY', + MAP: 'MAP' }, /** @@ -246,8 +248,9 @@ MAP_SQL_TO_NATIVE[sqlTypeValues.TIMESTAMP_LTZ] = nativeTypeValues.DATE; MAP_SQL_TO_NATIVE[sqlTypeValues.TIMESTAMP_NTZ] = nativeTypeValues.DATE; MAP_SQL_TO_NATIVE[sqlTypeValues.TIMESTAMP_TZ] = nativeTypeValues.DATE; MAP_SQL_TO_NATIVE[sqlTypeValues.VARIANT] = nativeTypeValues.JSON; -MAP_SQL_TO_NATIVE[sqlTypeValues.OBJECT] = nativeTypeValues.JSON; -MAP_SQL_TO_NATIVE[sqlTypeValues.ARRAY] = nativeTypeValues.JSON; +MAP_SQL_TO_NATIVE[sqlTypeValues.OBJECT] = nativeTypeValues.OBJECT; +MAP_SQL_TO_NATIVE[sqlTypeValues.ARRAY] = nativeTypeValues.ARRAY; +MAP_SQL_TO_NATIVE[sqlTypeValues.MAP] = nativeTypeValues.MAP; exports.SqlTypes = sqlTypes; exports.NativeTypes = nativeTypes; diff --git a/lib/connection/result/datetime_format_converter.js b/lib/connection/result/datetime_format_converter.js new file mode 100644 index 000000000..971ebc888 --- /dev/null +++ b/lib/connection/result/datetime_format_converter.js @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015-2024 Snowflake Computing Inc. All rights reserved. + */ + +function formatTagsMap() { + return [ + // proper mappings + ['YYYY', 'YYYY'], + ['YY', 'YY'], + ['MM', 'MM'], + ['MON', 'MMM'], + ['DD', 'DD'], + ['DY', 'ddd'], + ['HH24', 'HH'], + ['HH12', 'hh'], + ['HH', 'HH'], + ['AM', 'A'], + ['PM', 'A'], + ['MI', 'mm'], + ['SS', 'ss'], + ['TZH:TZM', 'Z'], + ['TZHTZM', 'ZZ'], + + // special code needed + ['TZH', ''], + ['TZM', ''], + ['FF', ''] + ]; +} + +function convertSnowflakeFormatToMomentFormat(formatSql, scale) { + const tags = formatTagsMap(); + + // get an upper-case version of the input sql format + const formatSqlUpper = formatSql.toUpperCase(); + let moment; + + // iterate over the format string + const length = formatSql.length; + let formatMoment = ''; + for (let pos = 0; pos < length;) { + let tag = null; + let out = null; + + // at each position, check if there's a tag at that position; if so, use + // 'out' as the replacement + for (let index = 0; index < tags.length; index++) { + if (formatSqlUpper.substr(pos).indexOf(tags[index][0]) === 0) { + tag = tags[index][0]; + out = tags[index][1]; + break; + } + } + + // if we didn't find a match, just insert the character after escaping it + // (by wrapping it in square brackets) + if (out === null) { + formatMoment += formatSql[pos]; + pos++; + } else { + // we found one of our special tags + if (out === '') { + if (tag === 'TZH') { + // format the moment to get the timezone string and extract the + // hours; for example, '-0700' will be converted to '-07' + out = moment.format('ZZ').substr(0, 3); + } else if (tag === 'TZM') { + // format the moment to get the timezone string and extract the + // minutes; for example, '-0700' will be converted to '00 + out = moment.format('ZZ').substr(3); + } else if (tag === 'FF') { + // if 'FF' is followed by a digit, use the digit as the scale + let digit = null; + if (pos + tag.length < length) { + const matches = formatSql[pos + tag.length].match(/[0-9]/); + if (matches) { + digit = matches[0]; + } + } + if (digit !== null) { + pos++; // skip the digit as well + } + + // if we need to include fractional seconds + if (scale > 0) { + // divide the nanoSeconds to get the requested number of + // meaningful digits + // pad with the appropriate number of leading zeros + out = (new Array(9).join('S')).substr(-scale); + } + } + } + + // append the 'out' text to the moment format and update the position + formatMoment += out; + pos += tag.length; + } + } + return formatMoment; +} + +module.exports.formatTagsMap = formatTagsMap; +module.exports.convertSnowflakeFormatToMomentFormat = convertSnowflakeFormatToMomentFormat; + diff --git a/lib/connection/result/sf_timestamp.js b/lib/connection/result/sf_timestamp.js index 232fe6a76..3947af244 100644 --- a/lib/connection/result/sf_timestamp.js +++ b/lib/connection/result/sf_timestamp.js @@ -4,36 +4,7 @@ const Moment = require('moment-timezone'); const Util = require('../../util'); - -/** - * An array of tag mappings to convert a sql format to a moment.js format. If - * the 2nd element is empty, special code is needed. - */ -const CONST_TAGS = - [ - // proper mappings - ['YYYY', 'YYYY'], - ['YY', 'YY'], - ['MM', 'MM'], - ['MON', 'MMM'], - ['DD', 'DD'], - ['DY', 'ddd'], - ['HH24', 'HH'], - ['HH12', 'hh'], - ['HH', 'HH'], - ['AM', 'A'], - ['PM', 'A'], - ['MI', 'mm'], - ['SS', 'ss'], - ['TZH:TZM', 'Z'], - ['TZHTZM', 'ZZ'], - - // special code needed - ['TZH', ''], - ['TZM', ''], - ['FF', ''] - ]; - +const datetimeFormatConverter = require('./datetime_format_converter'); /** * Creates a new SfTimestamp instance. * @@ -94,7 +65,7 @@ SfTimestamp.prototype.toString = function () { // get an upper-case version of the input sql format const formatSqlUpper = formatSql.toUpperCase(); - const tags = CONST_TAGS; + const tags = datetimeFormatConverter.formatTagsMap(); // iterate over the format string const length = formatSql.length; diff --git a/lib/core.js b/lib/core.js index 4c3685ce8..13ef6c33e 100644 --- a/lib/core.js +++ b/lib/core.js @@ -247,6 +247,9 @@ function Core(options) { BOOLEAN: { value: nativeTypeValues.BOOLEAN }, NUMBER: { value: nativeTypeValues.NUMBER }, DATE: { value: nativeTypeValues.DATE }, + OBJECT: { value: nativeTypeValues.OBJECT }, + ARRAY: { value: nativeTypeValues.ARRAY }, + MAP: { value: nativeTypeValues.MAP }, JSON: { value: nativeTypeValues.JSON } }); diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 13bd1cc19..e97eece70 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -7,19 +7,10 @@ const GlobalConfig = require('./../../lib/global_config'); const snowflake = require('./../../lib/snowflake'); const testUtil = require('./testUtil'); const sharedStatement = require('./sharedStatements'); -const bigInt = require('big-integer'); +const assert = require('assert'); -describe('Test DataType', function () { +describe('Test Structured types', function () { let connection; - const selectObject = 'select {\'string\':\'a\'}::OBJECT(string VARCHAR) as result'; - // const selectNumber = 'select * from testNumber'; - // const selectVariant = 'select * from testVariant'; - // const selectArray = 'select * from testArray'; - // const selectDate = 'select * from testDate'; - // const selectTime = 'select * from testTime'; - // const selectTimestamp = 'select * from testTimestamp'; - // const selectBoolean = 'select * from testBoolean'; - // const selectString = 'select * from testString'; before(function (done) { connection = testUtil.createConnection({ 'proxyHost': '127.0.0.1', 'proxyPort': 8080 }); @@ -33,44 +24,10 @@ describe('Test DataType', function () { ); }); - // after(function (done) { - // async.series([ - // function (callback) { - // testUtil.executeCmd(connection, selectObject, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithVariant, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithArray, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithNumber, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithDouble, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithDate, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithTime, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithTimestamp, callback); - // }, - // function (callback) { - // testUtil.executeCmd(connection, dropTableWithBoolean, callback); - // }, - // function (callback) { - // testUtil.destroyConnection(connection, callback); - // }], - // done - // ); - // }); - - describe('testNumber', function () { - it('testObject', function (done) { + describe('test object', function () { + it('test simple object', function (done) { + const selectObject = 'select {\'string\':\'a\'}::OBJECT(string VARCHAR) as result'; + async.series([ function (callback) { testUtil.executeCmd(connection, 'alter session set ENABLE_STRUCTURED_TYPES_IN_CLIENT_RESPONSE = true', callback); @@ -82,13 +39,286 @@ describe('Test DataType', function () { testUtil.executeQueryAndVerify( connection, selectObject, - [{ 'string': 'a' }], + [{ RESULT: { 'string': 'a' } }], + callback + ); + }], + done + ); + }); + + it('test timestamp ltz', function (done) { + const selectObject = 'select {' + + '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + + '}' + + '::OBJECT(timestamp_ltz TIMESTAMP_LTZ) As RESULT'; + async.series([ + function (callback) { + const executeOptions = {}; + executeOptions.sqlText = selectObject; + executeOptions.complete = function (err, stmt) { + assert.ok(!err, JSON.stringify(err)); + let rowCount = 0; + const stream = stmt.streamRows(); + + stream.on('readable', function () { + let row; + while ((row = stream.read()) !== null) { + const narmalizedRow = {}; + const expected = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + expected.timestamp_ltz = '2021-12-22 09:43:44.000 -0800'; + assert.deepStrictEqual(narmalizedRow.RESULT, expected); + rowCount++; + } + }); + stream.on('error', function (err) { + assert.ok(!err, JSON.stringify(err)); + }); + stream.on('end', function () { + assert.strictEqual(rowCount, 1); + callback(); + done(); + }); + }; + connection.execute(executeOptions); + } + ] + ); + + }); + + it('test timestamp ltz fetch as string', function (done) { + const selectObject = 'select {' + + '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + + '}' + + '::OBJECT(timestamp_ltz TIMESTAMP_LTZ) As RESULT'; + async.series([ + function (callback) { + const executeOptions = {}; + executeOptions.sqlText = selectObject; + executeOptions.fetchAsString = [snowflake.OBJECT]; + executeOptions.complete = function (err, stmt) { + assert.ok(!err, JSON.stringify(err)); + let rowCount = 0; + const stream = stmt.streamRows(); + stream.on('readable', function () { + let row; + while ((row = stream.read()) !== null) { + const expected = '{"timestamp_ltz":"2021-12-22 09:43:44.000 -0800"}'; + assert.deepStrictEqual(row.RESULT, expected); + rowCount++; + rowCount++; + } + }); + stream.on('error', function (err) { + assert.ok(!err, JSON.stringify(err)); + }); + stream.on('end', function () { + callback(); + }); + }; + connection.execute(executeOptions); + }], + done + ); + }); + + it('test timestamp ntz', function (done) { + const selectObject = 'select {' + + '\'timestamp_ntz\': \'2021-12-22 09:43:44\'::TIMESTAMP_NTZ' + + '}' + + '::OBJECT(timestamp_ntz TIMESTAMP_NTZ) AS RESULT'; + async.series([ + function (callback) { + const executeOptions = {}; + executeOptions.sqlText = selectObject; + executeOptions.complete = function (err, stmt) { + assert.ok(!err, JSON.stringify(err)); + let rowCount = 0; + const stream = stmt.streamRows(); + + stream.on('readable', function () { + let row; + while ((row = stream.read()) !== null) { + const narmalizedRow = {}; + const expected = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + expected.timestamp_ntz = '2021-12-22 09:43:44.000'; + console.log(narmalizedRow); + assert.deepStrictEqual(narmalizedRow.RESULT, expected); + rowCount++; + } + }); + stream.on('error', function (err) { + assert.ok(!err, JSON.stringify(err)); + }); + stream.on('end', function () { + assert.strictEqual(rowCount, 1); + callback(); + done(); + }); + }; + connection.execute(executeOptions); + } + ] + ); + + }); + + it('test timestamp tz', function (done) { + const selectObject = 'select {' + + '\'timestamp_tz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ' + + '}' + + '::OBJECT(timestamp_tz TIMESTAMP_TZ) AS RESULT'; + async.series([ + function (callback) { + testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); + }, + function (callback) { + const executeOptions = {}; + executeOptions.sqlText = selectObject; + executeOptions.complete = function (err, stmt) { + assert.ok(!err, JSON.stringify(err)); + let rowCount = 0; + const stream = stmt.streamRows(); + + stream.on('readable', function () { + let row; + while ((row = stream.read()) !== null) { + const narmalizedRow = {}; + const expected = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + expected.timestamp_tz = '2021-12-24 09:45:45.000 -0800'; + assert.deepStrictEqual(narmalizedRow.RESULT, expected); + rowCount++; + } + }); + stream.on('error', function (err) { + assert.ok(!err, JSON.stringify(err)); + }); + stream.on('end', function () { + assert.strictEqual(rowCount, 1); + callback(); + done(); + }); + }; + connection.execute(executeOptions); + } + ] + ); + + }); + + it('test binary', function (done) { + const selectObject = 'select {' + + '\'binary\': TO_BINARY(\'616263\', \'HEX\')' + + '}' + + '::OBJECT(binary BINARY) As RESULT'; + async.series([ + function (callback) { + testUtil.executeQueryAndVerify( + connection, + selectObject, + [{ RESULT: { 'binary': [97, 98, 99] } }], callback ); }], done ); }); + + it('test object all types', function (done) { + const selectObject = 'select {\'string\': \'a\'' + + ', \'b\': 1, ' + + '\'s\': 2, ' + + '\'i\': 3, ' + + '\'l\': 4,' + + ' \'f\': 1.1,' + + ' \'d\': 2.2,' + + ' \'bd\': 3.3, ' + + '\'bool\': true, ' + + '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + + ' \'timestamp_ntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + + '\'timestamp_tz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ,' + + ' \'date\': \'2023-12-24\'::DATE, ' + + '\'time\': \'12:34:56\'::TIME, ' + + '\'binary\': TO_BINARY(\'616263\', \'HEX\') ' + + '}' + + '::OBJECT(string VARCHAR' + + ', b TINYINT, ' + + 's SMALLINT, ' + + 'i INTEGER, ' + + 'l BIGINT, ' + + 'f FLOAT, ' + + 'd DOUBLE, ' + + 'bd DOUBLE, ' + + 'bool BOOLEAN,' + + 'timestamp_ltz TIMESTAMP_LTZ,' + + 'timestamp_ntz TIMESTAMP_NTZ, ' + + 'timestamp_tz TIMESTAMP_TZ, ' + + 'date DATE, time TIME, ' + + 'binary BINARY' + + ') AS RESULT'; + + const expected = { RESULT: {} }; + expected.RESULT = { string: 'a', + b: 1, + s: 2, + i: 3, + l: 4, + f: 1.1, + d: 2.2, + bd: 3.3, + bool: true, + timestamp_ltz: '2021-12-22 09:43:44.000 -0800', + timestamp_ntz: '2021-12-23 09:44:44.000', + timestamp_tz: '2021-12-24 09:45:45.000 -0800', + date: '2023-12-23', + time: '03:34:56', + binary: [97, 98, 99] }; + + async.series([ + function (callback) { + testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); + }, + function (callback) { + const executeOptions = {}; + executeOptions.sqlText = selectObject; + executeOptions.complete = function (err, stmt) { + assert.ok(!err, JSON.stringify(err)); + let rowCount = 0; + const stream = stmt.streamRows(); + stream.on('readable', function () { + let row; + while ((row = stream.read()) !== null) { + const narmalizedRow = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(narmalizedRow, expected); + rowCount++; + } + }); + stream.on('error', function (err) { + assert.ok(!err, JSON.stringify(err)); + }); + stream.on('end', function () { + assert.strictEqual(rowCount, 1); + callback(); + }); + }; + connection.execute(executeOptions); + }], + done + ); + }); }); }); diff --git a/test/integration/testUtil.js b/test/integration/testUtil.js index 6d6e4fdf8..a2f2f7025 100644 --- a/test/integration/testUtil.js +++ b/test/integration/testUtil.js @@ -313,3 +313,5 @@ module.exports.createRandomFileName = function ( option = { prefix: '', postfix: const fileName = `${option.prefix || ''}${randomName}${option.postfix || ''}${option.extension || ''}`; return fileName; }; + +module.exports.normalizeRowObject = normalizeRowObject; \ No newline at end of file From 4a8e4a66d75f6c7842c759c0c5b0440ea59a5617 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 18 Jul 2024 10:30:02 +0200 Subject: [PATCH 03/25] SNOW-1332640 - structured type - simple types --- test/integration/testStructuredType.js | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index e97eece70..b69282ed7 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -319,6 +319,76 @@ describe('Test Structured types', function () { done ); }); + + it('test object all types fetch as string', function (done) { + const selectObject = 'select {\'string\': \'a\'' + + ', \'b\': 1, ' + + '\'s\': 2, ' + + '\'i\': 3, ' + + '\'l\': 4,' + + ' \'f\': 1.1,' + + ' \'d\': 2.2,' + + ' \'bd\': 3.3, ' + + '\'bool\': true, ' + + '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + + ' \'timestamp_ntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + + '\'timestamp_tz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ,' + + ' \'date\': \'2023-12-24\'::DATE, ' + + '\'time\': \'12:34:56\'::TIME, ' + + '\'binary\': TO_BINARY(\'616263\', \'HEX\') ' + + '}' + + '::OBJECT(string VARCHAR' + + ', b TINYINT, ' + + 's SMALLINT, ' + + 'i INTEGER, ' + + 'l BIGINT, ' + + 'f FLOAT, ' + + 'd DOUBLE, ' + + 'bd DOUBLE, ' + + 'bool BOOLEAN,' + + 'timestamp_ltz TIMESTAMP_LTZ,' + + 'timestamp_ntz TIMESTAMP_NTZ, ' + + 'timestamp_tz TIMESTAMP_TZ, ' + + 'date DATE, time TIME, ' + + 'binary BINARY' + + ') AS RESULT'; + + const expected = { + "RESULT": "{\"string\":\"a\",\"b\":1,\"s\":2,\"i\":3,\"l\":4,\"f\":1.1,\"d\":2.2,\"bd\":3.3,\"bool\":true,\"timestamp_ltz\":\"2021-12-22 09:43:44.000 -0800\",\"timestamp_ntz\":\"2021-12-23 09:44:44.000\",\"timestamp_tz\":\"2021-12-24 09:45:45.000 -0800\",\"date\":\"2023-12-23\",\"time\":\"03:34:56\",\"binary\":[97,98,99]}" + } + + async.series([ + function (callback) { + testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); + }, + function (callback) { + const executeOptions = {}; + executeOptions.sqlText = selectObject; + executeOptions.fetchAsString = [snowflake.OBJECT]; + executeOptions.complete = function (err, stmt) { + assert.ok(!err, JSON.stringify(err)); + let rowCount = 0; + const stream = stmt.streamRows(); + stream.on('readable', function () { + let row; + while ((row = stream.read()) !== null) { + assert.deepStrictEqual(row, expected); + rowCount++; + } + }); + stream.on('error', function (err) { + assert.ok(!err, JSON.stringify(err)); + }); + stream.on('end', function () { + assert.strictEqual(rowCount, 1); + callback(); + }); + }; + connection.execute(executeOptions); + }], + done + ); + }); }); }); From f1b3740d2da17a32624f855ad5254699813c5171 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 18 Jul 2024 10:34:08 +0200 Subject: [PATCH 04/25] SNOW-1332640 - structured type - simple types --- lib/connection/result/column.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 4981487a0..c42350093 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -281,7 +281,6 @@ function convertRawBoolean(rawColumnValue) { } else if (rawColumnValue === false || (rawColumnValue === '0') || (rawColumnValue === 'FALSE')) { ret = false; } - return ret; } /** @@ -304,7 +303,6 @@ function convertRawObject(rawColumnValue, column, context) { }); return result; - } function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, column) { @@ -344,7 +342,6 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum break; case 'binary': context.format = context.statementParameters['BINARY_OUTPUT_FORMAT']; - // } value = convertRawBinary(columnValue, column, context).toJSON().data; break; default: From 767fc2f1d669d24e8457806dc469d8994330fe2b Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 18 Jul 2024 10:52:38 +0200 Subject: [PATCH 05/25] SNOW-1332640 - structured type - simple types --- lib/connection/result/column.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index c42350093..678db611a 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -346,7 +346,7 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum break; default: Logger.getInstance().info(`Column type not supported: ${columnMetadata.type}`); - throw new Error('Algorithm was not recognized!'); + throw new Error(`Column type not supported: ${columnMetadata.type}`); } return value; } From eb90431ce476f81eb840737d15bcea2048392768 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 18 Jul 2024 11:57:43 +0200 Subject: [PATCH 06/25] SNOW-1332640 - structured type - simple types --- lib/connection/result/column.js | 3 + test/integration/testStructuredType.js | 348 +++++++++++-------------- 2 files changed, 153 insertions(+), 198 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 678db611a..0ad955bd4 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -344,6 +344,9 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum context.format = context.statementParameters['BINARY_OUTPUT_FORMAT']; value = convertRawBinary(columnValue, column, context).toJSON().data; break; + case 'object': + value = convertRawObject(columnValue, column, context).toJSON().data; + break; default: Logger.getInstance().info(`Column type not supported: ${columnMetadata.type}`); throw new Error(`Column type not supported: ${columnMetadata.type}`); diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index b69282ed7..4d502c5b9 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -19,6 +19,12 @@ describe('Test Structured types', function () { snowflake.configure({ 'insecureConnect': true }); GlobalConfig.setInsecureConnect(true); testUtil.connect(connection, callback); + }, + function (callback) { + testUtil.executeCmd(connection, 'alter session set ENABLE_STRUCTURED_TYPES_IN_CLIENT_RESPONSE = true', callback); + }, + function (callback) { + testUtil.executeCmd(connection, 'alter session set IGNORE_CLIENT_VESRION_IN_STRUCTURED_TYPES_RESPONSE = true', callback); }], done ); @@ -29,12 +35,6 @@ describe('Test Structured types', function () { const selectObject = 'select {\'string\':\'a\'}::OBJECT(string VARCHAR) as result'; async.series([ - function (callback) { - testUtil.executeCmd(connection, 'alter session set ENABLE_STRUCTURED_TYPES_IN_CLIENT_RESPONSE = true', callback); - }, - function (callback) { - testUtil.executeCmd(connection, 'alter session set IGNORE_CLIENT_VESRION_IN_STRUCTURED_TYPES_RESPONSE = true', callback); - }, function (callback) { testUtil.executeQueryAndVerify( connection, @@ -52,42 +52,26 @@ describe('Test Structured types', function () { '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + '}' + '::OBJECT(timestamp_ltz TIMESTAMP_LTZ) As RESULT'; + const expected = { timestamp_ltz: '2021-12-22 09:43:44.000 -0800' }; async.series([ function (callback) { - const executeOptions = {}; - executeOptions.sqlText = selectObject; - executeOptions.complete = function (err, stmt) { - assert.ok(!err, JSON.stringify(err)); - let rowCount = 0; - const stream = stmt.streamRows(); - - stream.on('readable', function () { - let row; - while ((row = stream.read()) !== null) { - const narmalizedRow = {}; - const expected = {}; - Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); - }); - expected.timestamp_ltz = '2021-12-22 09:43:44.000 -0800'; - assert.deepStrictEqual(narmalizedRow.RESULT, expected); - rowCount++; - } - }); - stream.on('error', function (err) { - assert.ok(!err, JSON.stringify(err)); - }); - stream.on('end', function () { - assert.strictEqual(rowCount, 1); + connection.execute({ + sqlText: selectObject, + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const narmalizedRow = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(narmalizedRow.RESULT, expected); callback(); - done(); - }); - }; - connection.execute(executeOptions); + } + }); } - ] + ], + done ); - }); it('test timestamp ltz fetch as string', function (done) { @@ -95,33 +79,26 @@ describe('Test Structured types', function () { '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + '}' + '::OBJECT(timestamp_ltz TIMESTAMP_LTZ) As RESULT'; + const expected = '{"timestamp_ltz":"2021-12-22 09:43:44.000 -0800"}'; + async.series([ function (callback) { - const executeOptions = {}; - executeOptions.sqlText = selectObject; - executeOptions.fetchAsString = [snowflake.OBJECT]; - executeOptions.complete = function (err, stmt) { - assert.ok(!err, JSON.stringify(err)); - let rowCount = 0; - const stream = stmt.streamRows(); - stream.on('readable', function () { - let row; - while ((row = stream.read()) !== null) { - const expected = '{"timestamp_ltz":"2021-12-22 09:43:44.000 -0800"}'; - assert.deepStrictEqual(row.RESULT, expected); - rowCount++; - rowCount++; - } - }); - stream.on('error', function (err) { - assert.ok(!err, JSON.stringify(err)); - }); - stream.on('end', function () { + connection.execute({ + sqlText: selectObject, + fetchAsString: [snowflake.OBJECT], + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const narmalizedRow = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(row.RESULT, expected); callback(); - }); - }; - connection.execute(executeOptions); - }], + } + }); + }, + ], done ); }); @@ -131,41 +108,26 @@ describe('Test Structured types', function () { '\'timestamp_ntz\': \'2021-12-22 09:43:44\'::TIMESTAMP_NTZ' + '}' + '::OBJECT(timestamp_ntz TIMESTAMP_NTZ) AS RESULT'; + const expected = { timestamp_ntz: '2021-12-22 09:43:44.000' }; + async.series([ function (callback) { - const executeOptions = {}; - executeOptions.sqlText = selectObject; - executeOptions.complete = function (err, stmt) { - assert.ok(!err, JSON.stringify(err)); - let rowCount = 0; - const stream = stmt.streamRows(); - - stream.on('readable', function () { - let row; - while ((row = stream.read()) !== null) { - const narmalizedRow = {}; - const expected = {}; - Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); - }); - expected.timestamp_ntz = '2021-12-22 09:43:44.000'; - console.log(narmalizedRow); - assert.deepStrictEqual(narmalizedRow.RESULT, expected); - rowCount++; - } - }); - stream.on('error', function (err) { - assert.ok(!err, JSON.stringify(err)); - }); - stream.on('end', function () { - assert.strictEqual(rowCount, 1); + connection.execute({ + sqlText: selectObject, + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const narmalizedRow = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(narmalizedRow.RESULT, expected); callback(); - done(); - }); - }; - connection.execute(executeOptions); - } - ] + } + }); + }, + ], + done ); }); @@ -175,43 +137,29 @@ describe('Test Structured types', function () { '\'timestamp_tz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ' + '}' + '::OBJECT(timestamp_tz TIMESTAMP_TZ) AS RESULT'; + const expected = { timestamp_tz: '2021-12-24 09:45:45.000 -0800' }; + async.series([ function (callback) { testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); }, function (callback) { - const executeOptions = {}; - executeOptions.sqlText = selectObject; - executeOptions.complete = function (err, stmt) { - assert.ok(!err, JSON.stringify(err)); - let rowCount = 0; - const stream = stmt.streamRows(); - - stream.on('readable', function () { - let row; - while ((row = stream.read()) !== null) { - const narmalizedRow = {}; - const expected = {}; - Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); - }); - expected.timestamp_tz = '2021-12-24 09:45:45.000 -0800'; - assert.deepStrictEqual(narmalizedRow.RESULT, expected); - rowCount++; - } - }); - stream.on('error', function (err) { - assert.ok(!err, JSON.stringify(err)); - }); - stream.on('end', function () { - assert.strictEqual(rowCount, 1); + connection.execute({ + sqlText: selectObject, + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const narmalizedRow = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(narmalizedRow.RESULT, expected); callback(); - done(); - }); - }; - connection.execute(executeOptions); - } - ] + } + }); + }, + ], + done ); }); @@ -221,14 +169,24 @@ describe('Test Structured types', function () { '\'binary\': TO_BINARY(\'616263\', \'HEX\')' + '}' + '::OBJECT(binary BINARY) As RESULT'; + + const expected = { RESULT: { 'binary': [97, 98, 99] } }; + async.series([ function (callback) { - testUtil.executeQueryAndVerify( - connection, - selectObject, - [{ RESULT: { 'binary': [97, 98, 99] } }], - callback - ); + connection.execute({ + sqlText: selectObject, + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const narmalizedRow = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(narmalizedRow, expected); + callback(); + } + }); }], done ); @@ -267,55 +225,46 @@ describe('Test Structured types', function () { 'binary BINARY' + ') AS RESULT'; - const expected = { RESULT: {} }; - expected.RESULT = { string: 'a', - b: 1, - s: 2, - i: 3, - l: 4, - f: 1.1, - d: 2.2, - bd: 3.3, - bool: true, - timestamp_ltz: '2021-12-22 09:43:44.000 -0800', - timestamp_ntz: '2021-12-23 09:44:44.000', - timestamp_tz: '2021-12-24 09:45:45.000 -0800', - date: '2023-12-23', - time: '03:34:56', - binary: [97, 98, 99] }; + const expected = { + RESULT: { + string: 'a', + b: 1, + s: 2, + i: 3, + l: 4, + f: 1.1, + d: 2.2, + bd: 3.3, + bool: true, + timestamp_ltz: '2021-12-22 09:43:44.000 -0800', + timestamp_ntz: '2021-12-23 09:44:44.000', + timestamp_tz: '2021-12-24 09:45:45.000 -0800', + date: '2023-12-23', + time: '03:34:56', + binary: [97, 98, 99] + } + }; async.series([ function (callback) { testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); }, function (callback) { - const executeOptions = {}; - executeOptions.sqlText = selectObject; - executeOptions.complete = function (err, stmt) { - assert.ok(!err, JSON.stringify(err)); - let rowCount = 0; - const stream = stmt.streamRows(); - stream.on('readable', function () { - let row; - while ((row = stream.read()) !== null) { - const narmalizedRow = {}; - Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); - }); - assert.deepStrictEqual(narmalizedRow, expected); - rowCount++; - } - }); - stream.on('error', function (err) { - assert.ok(!err, JSON.stringify(err)); - }); - stream.on('end', function () { - assert.strictEqual(rowCount, 1); + connection.execute({ + sqlText: selectObject, + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const narmalizedRow = {}; + Object.keys(row).forEach((key) => { + narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(narmalizedRow, expected); callback(); - }); - }; - connection.execute(executeOptions); - }], + } + }); + }, + ], done ); }); @@ -354,41 +303,44 @@ describe('Test Structured types', function () { ') AS RESULT'; const expected = { - "RESULT": "{\"string\":\"a\",\"b\":1,\"s\":2,\"i\":3,\"l\":4,\"f\":1.1,\"d\":2.2,\"bd\":3.3,\"bool\":true,\"timestamp_ltz\":\"2021-12-22 09:43:44.000 -0800\",\"timestamp_ntz\":\"2021-12-23 09:44:44.000\",\"timestamp_tz\":\"2021-12-24 09:45:45.000 -0800\",\"date\":\"2023-12-23\",\"time\":\"03:34:56\",\"binary\":[97,98,99]}" - } + 'RESULT': '{"string":"a","b":1,"s":2,"i":3,"l":4,"f":1.1,"d":2.2,"bd":3.3,"bool":true,"timestamp_ltz":"2021-12-22 09:43:44.000 -0800","timestamp_ntz":"2021-12-23 09:44:44.000","timestamp_tz":"2021-12-24 09:45:45.000 -0800","date":"2023-12-23","time":"03:34:56","binary":[97,98,99]}' + }; async.series([ function (callback) { testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); }, function (callback) { - const executeOptions = {}; - executeOptions.sqlText = selectObject; - executeOptions.fetchAsString = [snowflake.OBJECT]; - executeOptions.complete = function (err, stmt) { - assert.ok(!err, JSON.stringify(err)); - let rowCount = 0; - const stream = stmt.streamRows(); - stream.on('readable', function () { - let row; - while ((row = stream.read()) !== null) { - assert.deepStrictEqual(row, expected); - rowCount++; - } - }); - stream.on('error', function (err) { - assert.ok(!err, JSON.stringify(err)); - }); - stream.on('end', function () { - assert.strictEqual(rowCount, 1); + connection.execute({ + sqlText: selectObject, + fetchAsString: [snowflake.OBJECT], + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + assert.deepStrictEqual(row, expected); callback(); - }); - }; - connection.execute(executeOptions); - }], + } + }); + }, + ], done ); }); - }); + // it('test nested object', function (done) { + // const selectObject = 'select {\'inside\': {\'string\':\'a\'}}::OBJECT(inside OBJECT(string VARCHAR)) as result'; + // + // async.series([ + // function (callback) { + // testUtil.executeQueryAndVerify( + // connection, + // selectObject, + // [{ RESULT: { 'string': 'a' } }], + // callback + // ); + // }], + // done + // ); + // }); + }); }); From e6ac44c484dde8bceb311006d3498681eccddd57 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Tue, 30 Jul 2024 11:53:52 +0200 Subject: [PATCH 07/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 2 +- test/integration/testStructuredType.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 0ad955bd4..96a9842dd 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -326,7 +326,7 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum break; case 'timestamp_ntz': context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; - value = convertDateTimeString(columnValue, context.format, moment.tz.guess(), column.getScale()).toSfDate(); + value = convertDateTimeString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfDate(); break; case 'timestamp_tz': context.format = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 4d502c5b9..28cbce622 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -80,7 +80,7 @@ describe('Test Structured types', function () { '}' + '::OBJECT(timestamp_ltz TIMESTAMP_LTZ) As RESULT'; const expected = '{"timestamp_ltz":"2021-12-22 09:43:44.000 -0800"}'; - + async.series([ function (callback) { connection.execute({ @@ -109,7 +109,7 @@ describe('Test Structured types', function () { '}' + '::OBJECT(timestamp_ntz TIMESTAMP_NTZ) AS RESULT'; const expected = { timestamp_ntz: '2021-12-22 09:43:44.000' }; - + async.series([ function (callback) { connection.execute({ @@ -149,11 +149,11 @@ describe('Test Structured types', function () { complete: function (err, stmt, rows) { testUtil.checkError(err); const row = rows[0]; - const narmalizedRow = {}; + const normalizedRow = {}; Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); }); - assert.deepStrictEqual(narmalizedRow.RESULT, expected); + assert.deepStrictEqual(normalizedRow.RESULT, expected); callback(); } }); @@ -169,9 +169,9 @@ describe('Test Structured types', function () { '\'binary\': TO_BINARY(\'616263\', \'HEX\')' + '}' + '::OBJECT(binary BINARY) As RESULT'; - + const expected = { RESULT: { 'binary': [97, 98, 99] } }; - + async.series([ function (callback) { connection.execute({ From 1a541d0afff89b0f5c495c5b7f3f16399f17ab14 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Tue, 30 Jul 2024 18:21:28 +0200 Subject: [PATCH 08/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 28 ++++++++--- lib/connection/result/sf_timestamp.js | 8 ++- test/integration/testStructuredType.js | 68 +++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 96a9842dd..5749b4073 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -322,23 +322,23 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum break; case 'timestamp_ltz': context.format = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; - value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'timestamp_ntz': context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; - value = convertDateTimeString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfDate(); + value = convertTimestampNtzString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfDate(); break; case 'timestamp_tz': context.format = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; - value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'date': context.format = context.statementParameters['DATE_OUTPUT_FORMAT']; - value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'time': context.format = context.statementParameters['TIME_OUTPUT_FORMAT']; - value = convertDateTimeString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfTime(); + value = convertTimeString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfTime(); break; case 'binary': context.format = context.statementParameters['BINARY_OUTPUT_FORMAT']; @@ -354,12 +354,28 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum return value; } -const convertDateTimeString = function (stringValue, formatSql, timezone, scale) { +const convertTimestampTzString = function (stringValue, formatSql, timezone, scale) { const formatMoment = dateTimeFormatConverter.convertSnowflakeFormatToMomentFormat(formatSql, scale); const epochSeconds = momentTimezone(stringValue, formatMoment).unix(); return new SfTimestamp(epochSeconds, 0, scale, timezone, formatSql); }; +const convertTimestampNtzString = function (stringValue, formatSql, timezone, scale) { + const formatMoment = dateTimeFormatConverter.convertSnowflakeFormatToMomentFormat(formatSql, scale); + const epochSeconds = momentTimezone.utc(stringValue, formatMoment).unix(); + return new SfTimestamp(epochSeconds, 0, scale, timezone, formatSql); +}; + + +const convertTimeString = function (stringValue, formatSql, timezone, scale) { + const formatMoment = dateTimeFormatConverter.convertSnowflakeFormatToMomentFormat(formatSql, scale); + const moment = momentTimezone(stringValue, formatMoment); + const epochSeconds = moment.hours() * 3600 + moment.minutes() * 60 + moment.seconds(); + const time = new SfTimestamp(epochSeconds, 0, scale, timezone, formatSql); + time._valueAsString = stringValue; + return time; +}; + /** * Converts a raw column value of type Date to a Snowflake Date. * diff --git a/lib/connection/result/sf_timestamp.js b/lib/connection/result/sf_timestamp.js index 3947af244..23179dd08 100644 --- a/lib/connection/result/sf_timestamp.js +++ b/lib/connection/result/sf_timestamp.js @@ -131,9 +131,13 @@ SfTimestamp.prototype.toString = function () { pos += tag.length; } } - // format the moment and cache the result - this._valueAsString = moment.format(formatMoment); + const timezone = this.timezone.name || this.timezone; + if (timezone) { + this._valueAsString = moment.tz(timezone).format(formatMoment); + } else { + this._valueAsString = moment.format(formatMoment); + } return this._valueAsString; }; diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 28cbce622..aae820646 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -164,6 +164,70 @@ describe('Test Structured types', function () { }); + it('test date', function (done) { + const selectObject = 'select {' + + '\'date\': to_date(\'2023-12-24\')::DATE' + + '}' + + '::OBJECT(date DATE) AS RESULT'; + const expected = { date: '2023-12-23' }; + + async.series([ + function (callback) { + testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); + }, + function (callback) { + connection.execute({ + sqlText: selectObject, + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const normalizedRow = {}; + Object.keys(row).forEach((key) => { + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(normalizedRow.RESULT, expected); + callback(); + } + }); + }, + ], + done + ); + + }); + + it('test time', function (done) { + const selectObject = 'select {' + + '\'time\': \'09:45:45\'::TIME' + + '}' + + '::OBJECT(time TIME) AS RESULT'; + const expected = { time: '09:45:45' }; + + async.series([ + function (callback) { + testUtil.executeCmd(connection, sharedStatement.setTimezoneAndTimestamps, callback); + }, + function (callback) { + connection.execute({ + sqlText: selectObject, + complete: function (err, stmt, rows) { + testUtil.checkError(err); + const row = rows[0]; + const normalizedRow = {}; + Object.keys(row).forEach((key) => { + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); + }); + assert.deepStrictEqual(normalizedRow.RESULT, expected); + callback(); + } + }); + }, + ], + done + ); + + }); + it('test binary', function (done) { const selectObject = 'select {' + '\'binary\': TO_BINARY(\'616263\', \'HEX\')' + @@ -240,7 +304,7 @@ describe('Test Structured types', function () { timestamp_ntz: '2021-12-23 09:44:44.000', timestamp_tz: '2021-12-24 09:45:45.000 -0800', date: '2023-12-23', - time: '03:34:56', + time: '12:34:56', binary: [97, 98, 99] } }; @@ -303,7 +367,7 @@ describe('Test Structured types', function () { ') AS RESULT'; const expected = { - 'RESULT': '{"string":"a","b":1,"s":2,"i":3,"l":4,"f":1.1,"d":2.2,"bd":3.3,"bool":true,"timestamp_ltz":"2021-12-22 09:43:44.000 -0800","timestamp_ntz":"2021-12-23 09:44:44.000","timestamp_tz":"2021-12-24 09:45:45.000 -0800","date":"2023-12-23","time":"03:34:56","binary":[97,98,99]}' + 'RESULT': '{"string":"a","b":1,"s":2,"i":3,"l":4,"f":1.1,"d":2.2,"bd":3.3,"bool":true,"timestamp_ltz":"2021-12-22 09:43:44.000 -0800","timestamp_ntz":"2021-12-23 09:44:44.000","timestamp_tz":"2021-12-24 09:45:45.000 -0800","date":"2023-12-23","time":"12:34:56","binary":[97,98,99]}' }; async.series([ From 7004a261db205f810e15130e3342910504e98c50 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 31 Jul 2024 00:02:45 +0200 Subject: [PATCH 09/25] SNOW-1332640 - structured type - object --- .../result/datetime_format_converter_test.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/unit/connection/result/datetime_format_converter_test.js diff --git a/test/unit/connection/result/datetime_format_converter_test.js b/test/unit/connection/result/datetime_format_converter_test.js new file mode 100644 index 000000000..da7094a18 --- /dev/null +++ b/test/unit/connection/result/datetime_format_converter_test.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015-2024 Snowflake Computing Inc. All rights reserved. + */ + +const dateTimeFormatConverter = require('./../../../../lib/connection/result/datetime_format_converter'); +const assert = require('assert'); + +describe('Datetime format converter test', function () { + [ + { + inputFormat: 'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM', + output: 'YYYY-MM-DD HH:mm:ss. Z', + }, + { + inputFormat: 'YY-MM-DD HH12:MI:SS,FF5AM TZHTZM', + output: 'YY-MM-DD hh:mm:ss,A ZZ', + }, + { + inputFormat: 'MMMM DD, YYYY DY HH24:MI:SS.FF9 TZH:TZM', + output: 'MMMM DD, YYYY ddd HH:mm:ss. Z', + }, + { + inputFormat: 'MON DD, YYYY HH12:MI:SS,FF9PM TZH:TZM', + output: 'MMM DD, YYYY hh:mm:ss,A Z', + }, + { + inputFormat: 'HH24:MI:SS.FF3 HH12:MI:SS,FF9', + output: 'HH:mm:ss. hh:mm:ss,', + } + ].forEach(({ inputFormat, output }) => { + it('valid result stream', function () { + const converted = dateTimeFormatConverter.convertSnowflakeFormatToMomentFormat(inputFormat); + assert.equal(converted, output); + }); + }); +}); \ No newline at end of file From dc8de82df8a5e4fc08c62ead76bf0001019fd1d0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 31 Jul 2024 13:30:30 +0200 Subject: [PATCH 10/25] SNOW-1332640 - structured type - object --- lib/connection/result/sf_timestamp.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/connection/result/sf_timestamp.js b/lib/connection/result/sf_timestamp.js index 23179dd08..d822a8973 100644 --- a/lib/connection/result/sf_timestamp.js +++ b/lib/connection/result/sf_timestamp.js @@ -134,7 +134,11 @@ SfTimestamp.prototype.toString = function () { // format the moment and cache the result const timezone = this.timezone.name || this.timezone; if (timezone) { - this._valueAsString = moment.tz(timezone).format(formatMoment); + if (typeof timezone === 'number') { + this._valueAsString = moment.utcOffset(timezone).format(formatMoment); + } else { + this._valueAsString = moment.tz(timezone).format(formatMoment); + } } else { this._valueAsString = moment.format(formatMoment); } From b8c6aa07f76d184f6151cc2b6c1c19570ebaf4d4 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 1 Aug 2024 13:48:01 +0200 Subject: [PATCH 11/25] SNOW-1332640 - structured type - object --- test/integration/sharedStatements.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/sharedStatements.js b/test/integration/sharedStatements.js index 8a132e840..159fa967f 100644 --- a/test/integration/sharedStatements.js +++ b/test/integration/sharedStatements.js @@ -8,6 +8,8 @@ const setTimestampNTZOutputFormat = 'alter session set TIMESTAMP_NTZ_OUTPUT_FORM const setTimezoneAndTimestamps = 'alter session set TIMEZONE=\'America/Los_Angeles\', ' + 'TIMESTAMP_OUTPUT_FORMAT=\'YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM\', ' + + 'TIMESTAMP_LTZ_OUTPUT_FORMAT=\'YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM\', ' + + 'TIMESTAMP_TZ_OUTPUT_FORMAT=\'YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM\', ' + 'TIMESTAMP_NTZ_OUTPUT_FORMAT=\'YYYY-MM-DD HH24:MI:SS.FF3\''; exports.setTimezoneToPST = setTimezoneToPST; From 9fbf0c2a791b87874e51971cfbe0c4cd6228fda5 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 1 Aug 2024 16:42:52 +0200 Subject: [PATCH 12/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 6 ++++++ lib/connection/result/sf_timestamp.js | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 5749b4073..3b5bef909 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -321,14 +321,20 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum value = convertRawBoolean(columnValue); break; case 'timestamp_ltz': + Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'timestamp_ntz': + Logger.getInstance().info(`TIMESTAMP_NTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampNtzString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfDate(); break; case 'timestamp_tz': + Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; diff --git a/lib/connection/result/sf_timestamp.js b/lib/connection/result/sf_timestamp.js index d822a8973..26208fb2f 100644 --- a/lib/connection/result/sf_timestamp.js +++ b/lib/connection/result/sf_timestamp.js @@ -5,6 +5,7 @@ const Moment = require('moment-timezone'); const Util = require('../../util'); const datetimeFormatConverter = require('./datetime_format_converter'); +const Logger = require('../../logger'); /** * Creates a new SfTimestamp instance. * @@ -142,7 +143,8 @@ SfTimestamp.prototype.toString = function () { } else { this._valueAsString = moment.format(formatMoment); } - + Logger.getInstance().info(`Format: ${formatMoment}`); + Logger.getInstance().info(`VAlue: ${this._valueAsString}`); return this._valueAsString; }; From 3ad9bff18cc76474d32bd79630b9f5861208a4d5 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 1 Aug 2024 23:22:06 +0200 Subject: [PATCH 13/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 3b5bef909..0ac7e0f74 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -321,20 +321,22 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum value = convertRawBoolean(columnValue); break; case 'timestamp_ltz': - Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); + console.log.info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'timestamp_ntz': - Logger.getInstance().info(`TIMESTAMP_NTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_NTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampNtzString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfDate(); break; case 'timestamp_tz': - Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; From b4fed54a9f1f9d36b9320511c48f64457a51f785 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 1 Aug 2024 23:23:30 +0200 Subject: [PATCH 14/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 0ac7e0f74..1a4c8d5ff 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -324,7 +324,7 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum console.log(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); console.log(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); console.log(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); - console.log.info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + console.log(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; From 80fa405c00ff42e9a5e53f52b8de3414f8581403 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 00:59:33 +0200 Subject: [PATCH 15/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 1a4c8d5ff..55836879f 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -321,22 +321,23 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum value = convertRawBoolean(columnValue); break; case 'timestamp_ltz': - console.log(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); - console.log(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - console.log(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); - console.log(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'timestamp_ntz': - console.log(`TIMESTAMP_NTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT']}`); - console.log(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_NTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampNtzString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfDate(); break; case 'timestamp_tz': - console.log(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - console.log(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); + Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); context.format = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; From 2c641eb6a894defafbbd385cad3dca9ecf5d7e84 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 08:26:28 +0200 Subject: [PATCH 16/25] SNOW-1332640 - structured type - object --- test/integration/testStructuredType.js | 61 ++++++++++++++------------ 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index aae820646..1fa264cf7 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -9,7 +9,7 @@ const testUtil = require('./testUtil'); const sharedStatement = require('./sharedStatements'); const assert = require('assert'); -describe('Test Structured types', function () { +describe.only('Test Structured types', function () { let connection; before(function (done) { @@ -25,6 +25,9 @@ describe('Test Structured types', function () { }, function (callback) { testUtil.executeCmd(connection, 'alter session set IGNORE_CLIENT_VESRION_IN_STRUCTURED_TYPES_RESPONSE = true', callback); + }, + function (callback) { + testUtil.executeCmd(connection, sharedStatement.setTimestampOutputFormat, callback); }], done ); @@ -49,10 +52,10 @@ describe('Test Structured types', function () { it('test timestamp ltz', function (done) { const selectObject = 'select {' + - '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + + '\'timestampltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + '}' + - '::OBJECT(timestamp_ltz TIMESTAMP_LTZ) As RESULT'; - const expected = { timestamp_ltz: '2021-12-22 09:43:44.000 -0800' }; + '::OBJECT(timestampltz TIMESTAMP_LTZ) As RESULT'; + const expected = { timestampltz: '2021-12-22 09:43:44.000 -0800' }; async.series([ function (callback) { connection.execute({ @@ -76,10 +79,10 @@ describe('Test Structured types', function () { it('test timestamp ltz fetch as string', function (done) { const selectObject = 'select {' + - '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + + '\'timestampltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ' + '}' + - '::OBJECT(timestamp_ltz TIMESTAMP_LTZ) As RESULT'; - const expected = '{"timestamp_ltz":"2021-12-22 09:43:44.000 -0800"}'; + '::OBJECT(timestampltz TIMESTAMP_LTZ) As RESULT'; + const expected = '{"timestampltz":"2021-12-22 09:43:44.000 -0800"}'; async.series([ function (callback) { @@ -105,10 +108,10 @@ describe('Test Structured types', function () { it('test timestamp ntz', function (done) { const selectObject = 'select {' + - '\'timestamp_ntz\': \'2021-12-22 09:43:44\'::TIMESTAMP_NTZ' + + '\'timestampntz\': \'2021-12-22 09:43:44\'::TIMESTAMP_NTZ' + '}' + - '::OBJECT(timestamp_ntz TIMESTAMP_NTZ) AS RESULT'; - const expected = { timestamp_ntz: '2021-12-22 09:43:44.000' }; + '::OBJECT(timestampntz TIMESTAMP_NTZ) AS RESULT'; + const expected = { timestampntz: '2021-12-22 09:43:44.000' }; async.series([ function (callback) { @@ -134,10 +137,10 @@ describe('Test Structured types', function () { it('test timestamp tz', function (done) { const selectObject = 'select {' + - '\'timestamp_tz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ' + + '\'timestamptz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ' + '}' + - '::OBJECT(timestamp_tz TIMESTAMP_TZ) AS RESULT'; - const expected = { timestamp_tz: '2021-12-24 09:45:45.000 -0800' }; + '::OBJECT(timestamptz TIMESTAMP_TZ) AS RESULT'; + const expected = { timestamptz: '2021-12-24 09:45:45.000 -0800' }; async.series([ function (callback) { @@ -266,9 +269,9 @@ describe('Test Structured types', function () { ' \'d\': 2.2,' + ' \'bd\': 3.3, ' + '\'bool\': true, ' + - '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + - ' \'timestamp_ntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + - '\'timestamp_tz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ,' + + '\'timestampltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + + ' \'timestampntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + + '\'timestamptz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ,' + ' \'date\': \'2023-12-24\'::DATE, ' + '\'time\': \'12:34:56\'::TIME, ' + '\'binary\': TO_BINARY(\'616263\', \'HEX\') ' + @@ -282,9 +285,9 @@ describe('Test Structured types', function () { 'd DOUBLE, ' + 'bd DOUBLE, ' + 'bool BOOLEAN,' + - 'timestamp_ltz TIMESTAMP_LTZ,' + - 'timestamp_ntz TIMESTAMP_NTZ, ' + - 'timestamp_tz TIMESTAMP_TZ, ' + + 'timestampltz TIMESTAMP_LTZ,' + + 'timestampntz TIMESTAMP_NTZ, ' + + 'timestamptz TIMESTAMP_TZ, ' + 'date DATE, time TIME, ' + 'binary BINARY' + ') AS RESULT'; @@ -300,9 +303,9 @@ describe('Test Structured types', function () { d: 2.2, bd: 3.3, bool: true, - timestamp_ltz: '2021-12-22 09:43:44.000 -0800', - timestamp_ntz: '2021-12-23 09:44:44.000', - timestamp_tz: '2021-12-24 09:45:45.000 -0800', + timestampltz: '2021-12-22 09:43:44.000 -0800', + timestampntz: '2021-12-23 09:44:44.000', + timestamptz: '2021-12-24 09:45:45.000 -0800', date: '2023-12-23', time: '12:34:56', binary: [97, 98, 99] @@ -343,9 +346,9 @@ describe('Test Structured types', function () { ' \'d\': 2.2,' + ' \'bd\': 3.3, ' + '\'bool\': true, ' + - '\'timestamp_ltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + - ' \'timestamp_ntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + - '\'timestamp_tz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ,' + + '\'timestampltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + + ' \'timestampntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + + '\'timestamptz\': \'2021-12-24 09:45:45 -0800\'::TIMESTAMP_TZ,' + ' \'date\': \'2023-12-24\'::DATE, ' + '\'time\': \'12:34:56\'::TIME, ' + '\'binary\': TO_BINARY(\'616263\', \'HEX\') ' + @@ -359,15 +362,15 @@ describe('Test Structured types', function () { 'd DOUBLE, ' + 'bd DOUBLE, ' + 'bool BOOLEAN,' + - 'timestamp_ltz TIMESTAMP_LTZ,' + - 'timestamp_ntz TIMESTAMP_NTZ, ' + - 'timestamp_tz TIMESTAMP_TZ, ' + + 'timestampltz TIMESTAMP_LTZ,' + + 'timestampntz TIMESTAMP_NTZ, ' + + 'timestamptz TIMESTAMP_TZ, ' + 'date DATE, time TIME, ' + 'binary BINARY' + ') AS RESULT'; const expected = { - 'RESULT': '{"string":"a","b":1,"s":2,"i":3,"l":4,"f":1.1,"d":2.2,"bd":3.3,"bool":true,"timestamp_ltz":"2021-12-22 09:43:44.000 -0800","timestamp_ntz":"2021-12-23 09:44:44.000","timestamp_tz":"2021-12-24 09:45:45.000 -0800","date":"2023-12-23","time":"12:34:56","binary":[97,98,99]}' + 'RESULT': '{"string":"a","b":1,"s":2,"i":3,"l":4,"f":1.1,"d":2.2,"bd":3.3,"bool":true,"timestampltz":"2021-12-22 09:43:44.000 -0800","timestampntz":"2021-12-23 09:44:44.000","timestamptz":"2021-12-24 09:45:45.000 -0800","date":"2023-12-23","time":"12:34:56","binary":[97,98,99]}' }; async.series([ From adbb03e0a2ad62302f86c26a42251cae6fa3d07f Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 09:10:48 +0200 Subject: [PATCH 17/25] SNOW-1332640 - structured type - object --- test/integration/testStructuredType.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 1fa264cf7..80c18122d 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -13,11 +13,13 @@ describe.only('Test Structured types', function () { let connection; before(function (done) { - connection = testUtil.createConnection({ 'proxyHost': '127.0.0.1', 'proxyPort': 8080 }); + connection = testUtil.createConnection( + // { 'proxyHost': '127.0.0.1', 'proxyPort': 8080 } + ); async.series([ function (callback) { - snowflake.configure({ 'insecureConnect': true }); - GlobalConfig.setInsecureConnect(true); + // snowflake.configure({ 'insecureConnect': true }); + // GlobalConfig.setInsecureConnect(true); testUtil.connect(connection, callback); }, function (callback) { From 1649874bd69dacfa82ee2930b3be4d32119dfe59 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 10:06:00 +0200 Subject: [PATCH 18/25] SNOW-1332640 - structured type - object --- test/integration/testStructuredType.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 80c18122d..a20167bd1 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -113,7 +113,7 @@ describe.only('Test Structured types', function () { '\'timestampntz\': \'2021-12-22 09:43:44\'::TIMESTAMP_NTZ' + '}' + '::OBJECT(timestampntz TIMESTAMP_NTZ) AS RESULT'; - const expected = { timestampntz: '2021-12-22 09:43:44.000' }; + const expected = { timestampntz: '2021-12-22 09:43:44.000 +0000' }; async.series([ function (callback) { @@ -269,7 +269,6 @@ describe.only('Test Structured types', function () { '\'l\': 4,' + ' \'f\': 1.1,' + ' \'d\': 2.2,' + - ' \'bd\': 3.3, ' + '\'bool\': true, ' + '\'timestampltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + ' \'timestampntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + @@ -285,7 +284,6 @@ describe.only('Test Structured types', function () { 'l BIGINT, ' + 'f FLOAT, ' + 'd DOUBLE, ' + - 'bd DOUBLE, ' + 'bool BOOLEAN,' + 'timestampltz TIMESTAMP_LTZ,' + 'timestampntz TIMESTAMP_NTZ, ' + @@ -303,7 +301,6 @@ describe.only('Test Structured types', function () { l: 4, f: 1.1, d: 2.2, - bd: 3.3, bool: true, timestampltz: '2021-12-22 09:43:44.000 -0800', timestampntz: '2021-12-23 09:44:44.000', @@ -346,7 +343,6 @@ describe.only('Test Structured types', function () { '\'l\': 4,' + ' \'f\': 1.1,' + ' \'d\': 2.2,' + - ' \'bd\': 3.3, ' + '\'bool\': true, ' + '\'timestampltz\': \'2021-12-22 09:43:44\'::TIMESTAMP_LTZ,' + ' \'timestampntz\': \'2021-12-23 09:44:44\'::TIMESTAMP_NTZ, ' + @@ -362,7 +358,6 @@ describe.only('Test Structured types', function () { 'l BIGINT, ' + 'f FLOAT, ' + 'd DOUBLE, ' + - 'bd DOUBLE, ' + 'bool BOOLEAN,' + 'timestampltz TIMESTAMP_LTZ,' + 'timestampntz TIMESTAMP_NTZ, ' + @@ -372,7 +367,7 @@ describe.only('Test Structured types', function () { ') AS RESULT'; const expected = { - 'RESULT': '{"string":"a","b":1,"s":2,"i":3,"l":4,"f":1.1,"d":2.2,"bd":3.3,"bool":true,"timestampltz":"2021-12-22 09:43:44.000 -0800","timestampntz":"2021-12-23 09:44:44.000","timestamptz":"2021-12-24 09:45:45.000 -0800","date":"2023-12-23","time":"12:34:56","binary":[97,98,99]}' + 'RESULT': '{"string":"a","b":1,"s":2,"i":3,"l":4,"f":1.1,"d":2.2,"bool":true,"timestampltz":"2021-12-22 09:43:44.000 -0800","timestampntz":"2021-12-23 09:44:44.000","timestamptz":"2021-12-24 09:45:45.000 -0800","date":"2023-12-23","time":"12:34:56","binary":[97,98,99]}' }; async.series([ From 2ac4511a8cc346113c2b559535ebdd51c3b4ae2f Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 11:04:38 +0200 Subject: [PATCH 19/25] SNOW-1332640 - structured type - object --- test/integration/testStructuredType.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index a20167bd1..dc40e1be4 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -30,6 +30,9 @@ describe.only('Test Structured types', function () { }, function (callback) { testUtil.executeCmd(connection, sharedStatement.setTimestampOutputFormat, callback); + }, + function (callback) { + testUtil.executeCmd(connection, sharedStatement.setTimestampNTZOutputFormat, callback); }], done ); @@ -113,7 +116,7 @@ describe.only('Test Structured types', function () { '\'timestampntz\': \'2021-12-22 09:43:44\'::TIMESTAMP_NTZ' + '}' + '::OBJECT(timestampntz TIMESTAMP_NTZ) AS RESULT'; - const expected = { timestampntz: '2021-12-22 09:43:44.000 +0000' }; + const expected = { timestampntz: '2021-12-22 09:43:44.000' }; async.series([ function (callback) { From e3518dff7e557c0458fd28c0bf544894f3323595 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 15:34:01 +0200 Subject: [PATCH 20/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 32 +++++++++++--------------- lib/connection/result/sf_timestamp.js | 3 --- test/integration/testStructuredType.js | 4 ---- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 55836879f..ae7b4cbc0 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -31,7 +31,7 @@ function Column(options, index, statementParameters, resultVersion) { const scale = options.scale; const type = options.type; const precision = options.precision; - const metadataFields = options.fields; + const fieldsMetadata = options.fields; /** * Returns the name of this column. @@ -191,7 +191,7 @@ function Column(options, index, statementParameters, resultVersion) { format: format, resultVersion: resultVersion, statementParameters: statementParameters, - metadataFields: metadataFields + fieldsMetadata: fieldsMetadata }; /** @@ -280,6 +280,8 @@ function convertRawBoolean(rawColumnValue) { ret = true; } else if (rawColumnValue === false || (rawColumnValue === '0') || (rawColumnValue === 'FALSE')) { ret = false; + } else { + throw new Error(`Value could not be converted to boolean: ${rawColumnValue}`); } return ret; } @@ -291,7 +293,7 @@ function convertRawBoolean(rawColumnValue) { * @returns {Object} */ function convertRawObject(rawColumnValue, column, context) { - const metadata = context.metadataFields.reduce(function (map, obj) { + const fieldsByName = context.fieldsMetadata.reduce(function (map, obj) { map[obj.name] = obj; return map; }, {}); @@ -299,13 +301,16 @@ function convertRawObject(rawColumnValue, column, context) { const result = {}; Object.keys(json).forEach(function (key) { - result[key] = mapStructuredTypeFieldValue(json[key], metadata[key], context, column); + result[key] = mapStructuredTypeFieldValue(json[key], fieldsByName[key], context, column); }); return result; } function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, column) { + const formatLtz = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; + const formatTz = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; + const formatNtz = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT']; let value; switch (columnMetadata.type) { case 'text': @@ -321,25 +326,14 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum value = convertRawBoolean(columnValue); break; case 'timestamp_ltz': - Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_LTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - context.format = context.statementParameters['TIMESTAMP_LTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; - value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + value = convertTimestampTzString(columnValue, formatLtz, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'timestamp_ntz': - Logger.getInstance().info(`TIMESTAMP_NTZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; - value = convertTimestampNtzString(columnValue, context.format, moment.tz.zone('UTC'), column.getScale()).toSfDate(); + // context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; + value = convertTimestampNtzString(columnValue, formatNtz, moment.tz.zone('UTC'), column.getScale()).toSfDate(); break; case 'timestamp_tz': - Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - Logger.getInstance().info(`TIMESTAMP_TZ_OUTPUT_FORMAT : ${context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']}`); - context.format = context.statementParameters['TIMESTAMP_TZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; - value = convertTimestampTzString(columnValue, context.format, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); + value = convertTimestampTzString(columnValue, formatTz, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'date': context.format = context.statementParameters['DATE_OUTPUT_FORMAT']; diff --git a/lib/connection/result/sf_timestamp.js b/lib/connection/result/sf_timestamp.js index 26208fb2f..e03aebbf1 100644 --- a/lib/connection/result/sf_timestamp.js +++ b/lib/connection/result/sf_timestamp.js @@ -5,7 +5,6 @@ const Moment = require('moment-timezone'); const Util = require('../../util'); const datetimeFormatConverter = require('./datetime_format_converter'); -const Logger = require('../../logger'); /** * Creates a new SfTimestamp instance. * @@ -143,8 +142,6 @@ SfTimestamp.prototype.toString = function () { } else { this._valueAsString = moment.format(formatMoment); } - Logger.getInstance().info(`Format: ${formatMoment}`); - Logger.getInstance().info(`VAlue: ${this._valueAsString}`); return this._valueAsString; }; diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index dc40e1be4..3068cde81 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -3,7 +3,6 @@ */ const async = require('async'); -const GlobalConfig = require('./../../lib/global_config'); const snowflake = require('./../../lib/snowflake'); const testUtil = require('./testUtil'); const sharedStatement = require('./sharedStatements'); @@ -14,12 +13,9 @@ describe.only('Test Structured types', function () { before(function (done) { connection = testUtil.createConnection( - // { 'proxyHost': '127.0.0.1', 'proxyPort': 8080 } ); async.series([ function (callback) { - // snowflake.configure({ 'insecureConnect': true }); - // GlobalConfig.setInsecureConnect(true); testUtil.connect(connection, callback); }, function (callback) { From 5a9d8325005ba29e463011f3a689332fb4318c1d Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 16:07:50 +0200 Subject: [PATCH 21/25] SNOW-1332640 - structured type - object --- test/integration/testStructuredType.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 3068cde81..230756022 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -8,7 +8,7 @@ const testUtil = require('./testUtil'); const sharedStatement = require('./sharedStatements'); const assert = require('assert'); -describe.only('Test Structured types', function () { +describe('Test Structured types', function () { let connection; before(function (done) { From 0ad6bb5df8188e0cbe058a4778cd7194426066d1 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 17:11:39 +0200 Subject: [PATCH 22/25] SNOW-1332640 - structured type - object --- lib/connection/result/column.js | 6 +++--- lib/connection/result/data_types.js | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index ae7b4cbc0..494a649cb 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -108,9 +108,9 @@ function Column(options, index, statementParameters, resultVersion) { this.isTimestampLtz = createFnIsColumnOfType(type, SqlTypes.isTimestampLtz, SqlTypes); this.isTimestampNtz = createFnIsColumnOfType(type, SqlTypes.isTimestampNtz, SqlTypes); this.isTimestampTz = createFnIsColumnOfType(type, SqlTypes.isTimestampTz, SqlTypes); - this.isVariant = createFnIsColumnOfType(type, SqlTypes.isVariant, SqlTypes); - this.isObject = createFnIsColumnOfType(type, SqlTypes.isObject, SqlTypes); - this.isArray = createFnIsColumnOfType(type, SqlTypes.isArray, SqlTypes); + this.isVariant = createFnIsColumnOfType(type, () => SqlTypes.isVariant(type, fieldsMetadata), SqlTypes); + this.isObject = createFnIsColumnOfType(type, () => SqlTypes.isObject(type, fieldsMetadata), SqlTypes); + this.isArray = createFnIsColumnOfType(type, () => SqlTypes.isArray(type, fieldsMetadata), SqlTypes); let convert; let toString; diff --git a/lib/connection/result/data_types.js b/lib/connection/result/data_types.js index 8c87cc64e..5c105812b 100644 --- a/lib/connection/result/data_types.js +++ b/lib/connection/result/data_types.js @@ -143,8 +143,9 @@ const sqlTypes = * * @returns {Boolean} */ - isVariant: function (sqlType) { + isVariant: function (sqlType, fieldsMetadata) { return (sqlType === this.values.VARIANT) || + (sqlType === this.values.OBJECT && fieldsMetadata == null) || (sqlType === this.values.ARRAY); }, @@ -155,8 +156,8 @@ const sqlTypes = * * @returns {Boolean} */ - isObject: function (sqlType) { - return (sqlType === this.values.OBJECT); + isObject: function (sqlType, fieldsMetadata) { + return (sqlType === this.values.OBJECT && fieldsMetadata != null); }, /** @@ -166,8 +167,8 @@ const sqlTypes = * * @returns {Boolean} */ - isArray: function (sqlType) { - return (sqlType === this.values.ARRAY); + isArray: function (sqlType, fieldsMetadata) { + return (sqlType === this.values.ARRAY && fieldsMetadata != null); } }; From f117631ec9052559fe4fbcb915d442c5c6ba011e Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Fri, 2 Aug 2024 19:07:14 +0200 Subject: [PATCH 23/25] SNOW-1332640 - structured type - object --- test/integration/testConnection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/testConnection.js b/test/integration/testConnection.js index 77a6c026d..847849783 100644 --- a/test/integration/testConnection.js +++ b/test/integration/testConnection.js @@ -47,7 +47,7 @@ describe('Connection test', function () { assert.ok(!connection.isUp(), 'still active'); }); - it('Wrong Username', function (done) { + it.skip('Wrong Username', function (done) { const connection = snowflake.createConnection(connOption.wrongUserName); connection.connect(function (err) { assert.ok(err, 'Username is an empty string'); From f984cda2c60a4bdb49b9ad985bc8ca3c16ac3d77 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 29 Aug 2024 16:23:02 +0200 Subject: [PATCH 24/25] SNOW-1332640-structured-types-support --- lib/connection/result/column.js | 1 - test/integration/testConnection.js | 2 +- test/integration/testStructuredType.js | 16 ---------------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js index 494a649cb..095f250a2 100644 --- a/lib/connection/result/column.js +++ b/lib/connection/result/column.js @@ -329,7 +329,6 @@ function mapStructuredTypeFieldValue(columnValue, columnMetadata, context, colum value = convertTimestampTzString(columnValue, formatLtz, context.statementParameters['TIMEZONE'], column.getScale()).toSfDate(); break; case 'timestamp_ntz': - // context.format = context.statementParameters['TIMESTAMP_NTZ_OUTPUT_FORMAT'] || context.statementParameters['TIMESTAMP_OUTPUT_FORMAT']; value = convertTimestampNtzString(columnValue, formatNtz, moment.tz.zone('UTC'), column.getScale()).toSfDate(); break; case 'timestamp_tz': diff --git a/test/integration/testConnection.js b/test/integration/testConnection.js index 847849783..77a6c026d 100644 --- a/test/integration/testConnection.js +++ b/test/integration/testConnection.js @@ -47,7 +47,7 @@ describe('Connection test', function () { assert.ok(!connection.isUp(), 'still active'); }); - it.skip('Wrong Username', function (done) { + it('Wrong Username', function (done) { const connection = snowflake.createConnection(connOption.wrongUserName); connection.connect(function (err) { assert.ok(err, 'Username is an empty string'); diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 230756022..66e725e08 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -389,21 +389,5 @@ describe('Test Structured types', function () { done ); }); - - // it('test nested object', function (done) { - // const selectObject = 'select {\'inside\': {\'string\':\'a\'}}::OBJECT(inside OBJECT(string VARCHAR)) as result'; - // - // async.series([ - // function (callback) { - // testUtil.executeQueryAndVerify( - // connection, - // selectObject, - // [{ RESULT: { 'string': 'a' } }], - // callback - // ); - // }], - // done - // ); - // }); }); }); From 0fbb24e0caa17e7d4cb988f04e0d13e66d27461d Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Tue, 3 Sep 2024 10:11:16 +0200 Subject: [PATCH 25/25] SNOW-1332640-structured-types-support --- test/integration/testStructuredType.js | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/integration/testStructuredType.js b/test/integration/testStructuredType.js index 66e725e08..f9e05da71 100644 --- a/test/integration/testStructuredType.js +++ b/test/integration/testStructuredType.js @@ -64,11 +64,11 @@ describe('Test Structured types', function () { complete: function (err, stmt, rows) { testUtil.checkError(err); const row = rows[0]; - const narmalizedRow = {}; + const normalizedRow = {}; Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); }); - assert.deepStrictEqual(narmalizedRow.RESULT, expected); + assert.deepStrictEqual(normalizedRow.RESULT, expected); callback(); } }); @@ -93,9 +93,9 @@ describe('Test Structured types', function () { complete: function (err, stmt, rows) { testUtil.checkError(err); const row = rows[0]; - const narmalizedRow = {}; + const normalizedRow = {}; Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); }); assert.deepStrictEqual(row.RESULT, expected); callback(); @@ -121,11 +121,11 @@ describe('Test Structured types', function () { complete: function (err, stmt, rows) { testUtil.checkError(err); const row = rows[0]; - const narmalizedRow = {}; + const normalizedRow = {}; Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); }); - assert.deepStrictEqual(narmalizedRow.RESULT, expected); + assert.deepStrictEqual(normalizedRow.RESULT, expected); callback(); } }); @@ -247,11 +247,11 @@ describe('Test Structured types', function () { complete: function (err, stmt, rows) { testUtil.checkError(err); const row = rows[0]; - const narmalizedRow = {}; + const normalizedRow = {}; Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); }); - assert.deepStrictEqual(narmalizedRow, expected); + assert.deepStrictEqual(normalizedRow, expected); callback(); } }); @@ -320,11 +320,11 @@ describe('Test Structured types', function () { complete: function (err, stmt, rows) { testUtil.checkError(err); const row = rows[0]; - const narmalizedRow = {}; + const normalizedRow = {}; Object.keys(row).forEach((key) => { - narmalizedRow[key] = testUtil.normalizeRowObject(row[key]); + normalizedRow[key] = testUtil.normalizeRowObject(row[key]); }); - assert.deepStrictEqual(narmalizedRow, expected); + assert.deepStrictEqual(normalizedRow, expected); callback(); } });