oracle / node-oracledb

Oracle Database driver for Node.js maintained by Oracle Corp.
http://oracle.github.io/node-oracledb/
Other
2.24k stars 1.07k forks source link

oracledb v6.2.0 parses multi-line comment into extra bind vars #1625

Closed jameschenjav closed 6 months ago

jameschenjav commented 7 months ago
  1. What versions are you using?

Oracle DB 21c XE

process.platform 'linux' process.version 'v20.9.0' process.arch 'x64' require('oracledb').versionString '6.2.0' require('oracledb').oracleClientVersionString undefined

  1. Is it an error or a hang or a crash?

An error. I have tried v6.1.0 and it works without a problem.

  1. What error(s) or behavior you are seeing?
Uncaught TypeError: Cannot read properties of null (reading 'type')
    at ExecuteMessage.writeColumnMetadata (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/protocol/messages/withData.js:617:33)
    at ExecuteMessage.processBindParams (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/protocol/messages/withData.js:611:10)
    at ExecuteMessage.writeExecuteMessage (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/protocol/messages/execute.js:216:19)
    at ExecuteMessage.encode (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/protocol/messages/execute.js:287:32)
    at Protocol._encodeMessage (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/protocol/protocol.js:108:20)
    at Protocol._processMessage (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/protocol/protocol.js:151:18)
    at ThinConnectionImpl._execute (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/connection.js:195:28)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async ThinConnectionImpl.execute (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/thin/connection.js:927:14)
    at async Connection.execute (./node_modules/.pnpm/oracledb@6.2.0/node_modules/oracledb/lib/connection.js:861:16)
  processBindParams(buf, params) {
    const bindVars = [];
    const nonReturningParams = [];
    for (const bindInfo of params) {
      if (!bindInfo.isReturnBind) {
        nonReturningParams.push(bindInfo);
      }
      bindVars.push(bindInfo.bindVar);
    }
+    console.log(params, bindVars); // added on line 610, see the log below
    this.writeColumnMetadata(buf, bindVars);
    return nonReturningParams;
  }
  1. Include a runnable Node.js script that shows the problem.
const oracledb = require('oracledb');
const sql = `
/************************************************************************************************
 * Get COLUMN of tables
 *
 * Returns:
 *  - table_name: string
 *  - column_name: string
 *  - data_type: number
 *  - data_type: 'BLOB' | 'CHAR' | 'CLOB' | 'DATE' | 'FLOAT' | 'NUMBER' | 'TIMESTAMP(6)' | 'TIMESTAMP(6) WITH LOCAL TIME ZONE' | 'VARCHAR2'
 *  - data_length: number
 *  - data_precision: number | null
 *  - data_scale: number | null
 *  - nullable: 'Y' | 'N'
 *  - data_default: long | null
 *  - column_id: number
 ************************************************************************************************/
  SELECT  table_name
        , column_name
        , char_length
        , char_used
        , data_type
        , data_length
        , data_precision
        , data_scale
        , nullable
        , data_default
        , column_id
  FROM all_tab_cols
  WHERE owner = UPPER(:schema)
    AND hidden_column = 'NO'
    AND virtual_column = 'NO'
    AND user_generated = 'YES'
    AND table_name NOT IN (
      SELECT mview_name FROM all_mviews WHERE owner = UPPER(:schema)
      UNION SELECT view_name FROM all_views WHERE owner = UPPER(:schema)
      UNION SELECT log_table FROM all_mview_logs WHERE log_owner = UPPER(:schema)
    )
  ORDER BY table_name, column_id
`;

oracledb.getConnection({})
  .then((conn) => {
    return conn.execute(sql, { schema: 'foo' });
  });

It looks like it parsed the comment into bind vars (with regex?):

/************************************************************************************************
 * Get COLUMN of tables
 *
 * Returns:
 *  - table_name: string
 *  - column_name: string
 *  - data_type: number
 *  - data_type: 'BLOB' | 'CHAR' | 'CLOB' | 'DATE' | 'FLOAT' | 'NUMBER' | 'TIMESTAMP(6)' | 'TIMESTAMP(6) WITH LOCAL TIME ZONE' | 'VARCHAR2'
 *  - data_length: number
 *  - data_precision: number | null
 *  - data_scale: number | null
 *  - nullable: 'Y' | 'N'
 *  - data_default: long | null
 *  - column_id: number
 ************************************************************************************************/

// from `console.log`

[
  BindInfo { // table_name: string
    bindName: 'STRING',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo { // column_name: string
    bindName: 'STRING',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo { // data_type: number
    bindName: 'NUMBER',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo { // data_length: number
    bindName: 'NUMBER',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo { // data_precision: number
    bindName: 'NUMBER',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo { // data_scale: number
    bindName: 'NUMBER',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo { // data_default: long
    bindName: 'LONG',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo { // column_id: number
    bindName: 'NUMBER',
    isReturnBind: false,
    maxSize: 0,
    numElements: 0,
    maxArraySize: 0,
    type: null,
    isArray: false,
    dir: 3001,
    bindVar: null
  },
  BindInfo {
    bindName: 'SCHEMA',
    isReturnBind: false,
    maxSize: 6,
    numElements: 1,
    maxArraySize: undefined,
    type: [DbType DB_TYPE_VARCHAR],
    isArray: false,
    dir: 3001,
    bindVar: {
      name: 'schema',
      values: [Array],
      isArray: false,
      dir: 3001,
      type: [DbType DB_TYPE_VARCHAR],
      maxSize: 6
    }
  },
  BindInfo {
    bindName: 'SCHEMA',
    isReturnBind: false,
    maxSize: 6,
    numElements: 1,
    maxArraySize: undefined,
    type: [DbType DB_TYPE_VARCHAR],
    isArray: false,
    dir: 3001,
    bindVar: {
      name: 'schema',
      values: [Array],
      isArray: false,
      dir: 3001,
      type: [DbType DB_TYPE_VARCHAR],
      maxSize: 6
    }
  },
  BindInfo {
    bindName: 'SCHEMA',
    isReturnBind: false,
    maxSize: 6,
    numElements: 1,
    maxArraySize: undefined,
    type: [DbType DB_TYPE_VARCHAR],
    isArray: false,
    dir: 3001,
    bindVar: {
      name: 'schema',
      values: [Array],
      isArray: false,
      dir: 3001,
      type: [DbType DB_TYPE_VARCHAR],
      maxSize: 6
    }
  },
  BindInfo {
    bindName: 'SCHEMA',
    isReturnBind: false,
    maxSize: 6,
    numElements: 1,
    maxArraySize: undefined,
    type: [DbType DB_TYPE_VARCHAR],
    isArray: false,
    dir: 3001,
    bindVar: {
      name: 'schema',
      values: [Array],
      isArray: false,
      dir: 3001,
      type: [DbType DB_TYPE_VARCHAR],
      maxSize: 6
    }
  }
] [
  null,
  null,
  null,
  null,
  null,
  null,
  null,
  null,
  {
    name: 'schema',
    values: [ 'foo' ],
    isArray: false,
    dir: 3001,
    type: [DbType DB_TYPE_VARCHAR],
    maxSize: 6
  },
  {
    name: 'schema',
    values: [ 'foo' ],
    isArray: false,
    dir: 3001,
    type: [DbType DB_TYPE_VARCHAR],
    maxSize: 6
  },
  {
    name: 'schema',
    values: [ 'foo' ],
    isArray: false,
    dir: 3001,
    type: [DbType DB_TYPE_VARCHAR],
    maxSize: 6
  },
  {
    name: 'schema',
    values: [ 'foo' ],
    isArray: false,
    dir: 3001,
    type: [DbType DB_TYPE_VARCHAR],
    maxSize: 6
  }
]
cjbj commented 7 months ago

Thanks for the report.

sharadraju commented 7 months ago

We have identified the issue and working on a fix.

sharadraju commented 7 months ago

@jameschenjav Please apply the following patch in lib/thin/statement.js and let us know:

diff --git a/lib/thin/statement.js b/lib/thin/statement.js
index 9740a85ae..96e09580d 100644
--- a/lib/thin/statement.js
+++ b/lib/thin/statement.js
@@ -142,7 +142,7 @@ class Parser {
           break;
         }
         inComment = true;
-      } else if (!exitingComment && ch === '*') {
+      } else if (ch === '*') {
         exitingComment = true;
       } else if (exitingComment) {
         if (ch === '/') {
jameschenjav commented 7 months ago

@jameschenjav Please apply the following patch in lib/thin/statement.js and let us know:

diff --git a/lib/thin/statement.js b/lib/thin/statement.js
index 9740a85ae..96e09580d 100644
--- a/lib/thin/statement.js
+++ b/lib/thin/statement.js
@@ -142,7 +142,7 @@ class Parser {
           break;
         }
         inComment = true;
-      } else if (!exitingComment && ch === '*') {
+      } else if (ch === '*') {
         exitingComment = true;
       } else if (exitingComment) {
         if (ch === '/') {

thanks. it's working now

sharadraju commented 6 months ago

This has been fixed as part of the 6.3 release.