diff --git a/index.d.ts b/index.d.ts index e2c56ecb4..0d4e17bea 100644 --- a/index.d.ts +++ b/index.d.ts @@ -131,6 +131,7 @@ declare module 'snowflake-sdk' { ERR_CONN_EXEC_STMT_INVALID_FETCH_AS_STRING_VALUES = 409012, ERR_CONN_EXEC_STMT_INVALID_REQUEST_ID = 409013, ERR_CONN_EXEC_STMT_INVALID_ASYNC_EXEC = 409014, + ERR_CONN_EXEC_STMT_INVALID_DESCRIBE_ONLY = 409015, // 410001 ERR_CONN_FETCH_RESULT_MISSING_OPTIONS = 410001, @@ -652,6 +653,11 @@ declare module 'snowflake-sdk' { * that is different from the connector directory. */ cwd?: string; + + /** + * `true` to enable a describe only query. + */ + describeOnly?: boolean; } export interface RowStatement { diff --git a/lib/connection/statement.js b/lib/connection/statement.js index fef7b2cbb..b67f81357 100644 --- a/lib/connection/statement.js +++ b/lib/connection/statement.js @@ -353,6 +353,12 @@ function createContextPreExec( ErrorCodes.ERR_CONN_EXEC_STMT_INVALID_ASYNC_EXEC); } + // if a describeOnly flag is specified, make sure it's boolean + if (Util.exists(statementOptions.describeOnly)) { + Errors.checkArgumentValid(Util.isBoolean(statementOptions.describeOnly), + ErrorCodes.ERR_CONN_EXEC_STMT_INVALID_DESCRIBE_ONLY); + } + // create a statement context const statementContext = createStatementContext(); @@ -384,6 +390,11 @@ function createContextPreExec( statementContext.cwd = statementOptions.cwd; } + // if the describeOnly flag is specified, add it to the statement context + if (Util.exists(statementOptions.describeOnly)) { + statementContext.describeOnly = statementOptions.describeOnly; + } + // validate non-user-specified arguments Errors.assertInternal(Util.isObject(services)); Errors.assertInternal(Util.isObject(connectionConfig)); @@ -1244,6 +1255,11 @@ function sendRequestPreExec(statementContext, onResultAvailable) { json.asyncExec = statementContext.asyncExec; } + // include describeOnly flag if a value was specified + if (Util.exists(statementContext.describeOnly)) { + json.describeOnly = statementContext.describeOnly; + } + // use the snowflake service to issue the request sendSfRequest(statementContext, { diff --git a/lib/constants/error_messages.js b/lib/constants/error_messages.js index 4a1c34145..2effda456 100644 --- a/lib/constants/error_messages.js +++ b/lib/constants/error_messages.js @@ -126,6 +126,7 @@ exports[409011] = 'Invalid fetchAsString value. The specified value must be an A exports[409012] = 'Invalid fetchAsString type: %s. The supported types are: String, Boolean, Number, Date, Buffer, and JSON.'; exports[409013] = 'Invalid requestId. The specified value must be a string.'; exports[409014] = 'Invalid asyncExec. The specified value must be a boolean.'; +exports[409015] = 'Invalid describeOnly. The specified value must be a boolean.'; // 410001 exports[410001] = 'Fetch-result options must be specified.'; diff --git a/lib/errors.js b/lib/errors.js index b57e4dc39..6a00ad982 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -130,6 +130,7 @@ codes.ERR_CONN_EXEC_STMT_INVALID_FETCH_AS_STRING = 409011; codes.ERR_CONN_EXEC_STMT_INVALID_FETCH_AS_STRING_VALUES = 409012; codes.ERR_CONN_EXEC_STMT_INVALID_REQUEST_ID = 409013; codes.ERR_CONN_EXEC_STMT_INVALID_ASYNC_EXEC = 409014; +codes.ERR_CONN_EXEC_STMT_INVALID_DESCRIBE_ONLY = 409015; // 410001 codes.ERR_CONN_FETCH_RESULT_MISSING_OPTIONS = 410001; diff --git a/test/integration/testExecute.js b/test/integration/testExecute.js index f2e515976..5f62b7a58 100644 --- a/test/integration/testExecute.js +++ b/test/integration/testExecute.js @@ -137,6 +137,54 @@ describe('Execute test', function () { done ); }); + + describe('testDescribeOnly', async function () { + const selectWithDescribeOnly = 'SELECT 1.0::NUMBER(30,2) as C1, 2::NUMBER(38,0) AS C2, \'t3\' AS C3, 4.2::DOUBLE AS C4, \'abcd\'::BINARY(8388608) AS C5, true AS C6'; + const expectedRows = [{ 'C1': 1, 'C2': 2, 'C3': 't3', 'C4': 4.2, 'C5': { 'type': 'Buffer', 'data': [171, 205] }, 'C6': true }]; + const testCases = + [ + { + name: 'describeOnly - true', + describeOnly: true, + expectedRows: [] + }, + { + name: 'describeOnly - false', + describeOnly: false, + expectedRows: expectedRows + }, + { + name: 'describeOnly - undefined', + describeOnly: undefined, + expectedRows: expectedRows + }, + ]; + + const executeQueryAndVerifyResultDependOnDescribeOnly = async (describeOnly, expectedReturnedRows) => { + return new Promise((resolve, reject) => { + connection.execute({ + sqlText: selectWithDescribeOnly, + describeOnly: describeOnly, + complete: (err, stmt, rows) => { + if (err) { + return reject(err); + } + assert.strictEqual(stmt.getColumns().length, 6); + assert.strictEqual(rows.length, expectedReturnedRows.length); + if (rows.length > 0) { + const columnsNamesInMetadata = stmt.getColumns().map(cl => cl.getName()); + const columnsNames = Object.keys(rows[0]); + columnsNames.every((element, index) => assert.strictEqual(element, columnsNamesInMetadata[index])); + } + return resolve(rows); + } + }); + } + ); + }; + + testCases.forEach(testCase => it(testCase.name, () => executeQueryAndVerifyResultDependOnDescribeOnly(testCase.describeOnly, testCase.expectedRows))); + }); }); describe('Execute test - variant', function () { diff --git a/test/unit/connection/statement_test.js b/test/unit/connection/statement_test.js index eaba007bf..7405b64af 100644 --- a/test/unit/connection/statement_test.js +++ b/test/unit/connection/statement_test.js @@ -205,6 +205,16 @@ describe('Statement.execute()', function () { connectionConfig: null }, errorCode: ErrorCodes.ERR_CONN_EXEC_STMT_MISSING_SQL_TEXT + }, + { + name: 'execute() invalid describeOnly', + options: { + statementOptions: { + sqlText: '', + describeOnly: 1, + }, + }, + errorCode: ErrorCodes.ERR_CONN_EXEC_STMT_INVALID_DESCRIBE_ONLY } ]; @@ -404,4 +414,4 @@ describe('Statement.fetchResult()', function () { testCase = testCases[index]; it(testCase.name, createItCallback(testCase)); } -}); \ No newline at end of file +});