Create a flag oldJsonColumnAsObj inside a new future object for ensuring backward compatibility before converting the IS JSON type column values to JSON object
This commit is contained in:
parent
0c129d3eec
commit
8a2973adb0
|
@ -13,6 +13,16 @@ node-oracledb `v6.3.0 <https://github.com/oracle/node-oracledb/compare/v6.2.0...
|
|||
Common Changes
|
||||
++++++++++++++
|
||||
|
||||
#) VARCHAR2 and LOB columns which contain JSON, and have the "IS JSON" check
|
||||
constraint enabled, can now be fetched in the same way as columns of type
|
||||
JSON. In node-oracledb :ref:`Thick mode <enablingthick>` this requires
|
||||
Oracle Client 19 or higher. Applications can get this new fetch behaviour
|
||||
by setting the new oracledb property
|
||||
:attr:`oracledb.future.oldJsonColumnAsObj` to `true`. The default value
|
||||
for this property is `false` which retains the existing fetch behaviour.
|
||||
In a future version, the new fetch behaviour will become default and
|
||||
setting this property will no longer be needed.
|
||||
|
||||
#) Added constant ``oracledb.DB_TYPE_XMLTYPE`` to represent data of type
|
||||
``SYS.XMLTYPE`` in metadata ``fetchType`` and ``dbType`` attributes.
|
||||
Previously the constant used was ``oracledb.DB_TYPE_LONG`` in Thick mode.
|
||||
|
@ -21,13 +31,6 @@ Common Changes
|
|||
Software Development Kits (SDKs) to generate
|
||||
:ref:`authentication tokens <tokenbasedauthentication>`.
|
||||
|
||||
#) VARCHAR2 and LOB columns which contain JSON, and have the "IS JSON" check
|
||||
constraint enabled, are now fetched in the same way as columns of type
|
||||
JSON. In node-oracledb :ref:`Thick mode <enablingthick>` this requires
|
||||
Oracle Client 19 or higher. Applications can get the old behaviour by
|
||||
using :attr:`oracledb.fetchTypeHandler` to replace the new default
|
||||
conversion.
|
||||
|
||||
#) Added new connection properties :attr:`connection.dbDomain`,
|
||||
:attr:`connection.dbName`, :attr:`connection.maxOpenCursors`,
|
||||
:attr:`connection.serviceName` and :attr:`connection.transactionInProgress`
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2023, Oracle and/or its affiliates.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// This software is dual-licensed to you under the Universal Permissive License
|
||||
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
||||
// either license.
|
||||
//
|
||||
// If you elect to accept the software under the Apache License, Version 2.0,
|
||||
// the following applies:
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
'use strict';
|
||||
|
||||
const errors = require('./errors.js');
|
||||
|
||||
// future object used for managing backwards incompatible changes.
|
||||
class Future {
|
||||
|
||||
constructor() {
|
||||
this._featureFlags = {};
|
||||
this._featureFlags.oldJsonColumnAsObj = false;
|
||||
}
|
||||
|
||||
get oldJsonColumnAsObj() {
|
||||
return this._featureFlags.oldJsonColumnAsObj;
|
||||
}
|
||||
|
||||
// fetch VARCHAR2 and LOB columns that contain JSON data (and have
|
||||
// the "IS JSON" constraint enabled) in the same way that columns
|
||||
// of type JSON (which requires Oracle Database 21 and higher) are fetched.
|
||||
set oldJsonColumnAsObj(value) {
|
||||
errors.assertPropValue(typeof value === 'boolean', "oldJsonColumnAsObj");
|
||||
this._featureFlags.oldJsonColumnAsObj = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new Future;
|
|
@ -30,6 +30,7 @@ const constants = require('../constants.js');
|
|||
const errors = require('../errors.js');
|
||||
const nodbUtil = require('../util.js');
|
||||
const settings = require('../settings.js');
|
||||
const future = require('../future.js');
|
||||
const types = require('../types.js');
|
||||
const Lob = require('../lob.js');
|
||||
|
||||
|
@ -106,7 +107,7 @@ class ResultSetImpl {
|
|||
|
||||
// If IsJson is set convert to JSON objects unless
|
||||
// user defined output type handler overwrites it.
|
||||
if (metadata.isJson && metadata.dbType !== types.DB_TYPE_JSON
|
||||
if (future.oldJsonColumnAsObj && metadata.isJson && metadata.dbType !== types.DB_TYPE_JSON
|
||||
&& userConverter === undefined) {
|
||||
const outConverter = async function(val) {
|
||||
if (!val) {
|
||||
|
|
|
@ -48,6 +48,7 @@ const AqDeqOptions = require('./aqDeqOptions.js');
|
|||
const AqEnqOptions = require('./aqEnqOptions.js');
|
||||
const AqMessage = require('./aqMessage.js');
|
||||
const AqQueue = require('./aqQueue.js');
|
||||
const future = require('./future.js');
|
||||
const BaseDbObject = require('./dbObject.js');
|
||||
const Connection = require('./connection.js');
|
||||
const Lob = require('./lob.js');
|
||||
|
@ -1013,6 +1014,9 @@ module.exports = {
|
|||
ARRAY: constants.OUT_FORMAT_ARRAY,
|
||||
OBJECT: constants.OUT_FORMAT_OBJECT,
|
||||
|
||||
// Instances
|
||||
future,
|
||||
|
||||
// property getters
|
||||
get autoCommit() {
|
||||
return settings.autoCommit;
|
||||
|
|
|
@ -199,7 +199,6 @@ describe('3. examples.js', function() {
|
|||
let conn = null;
|
||||
const testData = { "userId": 1, "userName": "Chris", "location": "Australia" };
|
||||
let featureAvailable = true;
|
||||
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
|
||||
|
||||
before(async function() {
|
||||
conn = await oracledb.getConnection(dbConfig);
|
||||
|
@ -228,17 +227,6 @@ describe('3. examples.js', function() {
|
|||
sql = "INSERT INTO nodb_purchaseorder (po_document) VALUES (:bv)";
|
||||
const result = await conn.execute(sql, [s]);
|
||||
assert.strictEqual(result.rowsAffected, 1);
|
||||
if (await testsUtil.isJsonMetaDataRunnable()) {
|
||||
oracledb.fetchTypeHandler = function(metaData) {
|
||||
// overwrite default converter for VARCHAR2 type.
|
||||
if (metaData.isJson && metaData.dbType === oracledb.DB_TYPE_VARCHAR) {
|
||||
const myConverter = (v) => {
|
||||
return v;
|
||||
};
|
||||
return {converter: myConverter};
|
||||
}
|
||||
};
|
||||
}
|
||||
}); // before
|
||||
|
||||
after(async function() {
|
||||
|
@ -247,7 +235,6 @@ describe('3. examples.js', function() {
|
|||
await conn.execute("DROP TABLE nodb_purchaseorder PURGE");
|
||||
}
|
||||
await conn.close();
|
||||
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
|
||||
}
|
||||
}); // after
|
||||
|
||||
|
|
|
@ -504,8 +504,8 @@ describe('271. fetchTypeHandler.js', function() {
|
|||
const clobVal = '[-1, 2, 3]';
|
||||
const blobVal = Buffer.from('{ "KeyA": 8, "KeyB": "A String" }');
|
||||
const charVal = '[-2, 2, 3, [34, 23, 24]]';
|
||||
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
|
||||
|
||||
oracledb.future.oldJsonColumnAsObj = true;
|
||||
await connection.execute(plsql);
|
||||
const sql = `INSERT into ${TABLE} VALUES (:1, :2, :3)`;
|
||||
await connection.execute(sql, [clobVal, blobVal, charVal]);
|
||||
|
@ -515,10 +515,12 @@ describe('271. fetchTypeHandler.js', function() {
|
|||
assert.deepStrictEqual(result.rows[0][1], JSON.parse(blobVal));
|
||||
assert.deepStrictEqual(result.rows[0][2], JSON.parse(charVal));
|
||||
|
||||
// User defined handler can overwrite the default behaviour of
|
||||
// converting IS JSON columns to JSON objects.
|
||||
// fetchtype handlers given preference than oldJsonColumnAsObj setting.
|
||||
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
|
||||
|
||||
// register typehandler only for BLOB.
|
||||
// For other columns, they are still returned as JSON object.
|
||||
oracledb.fetchTypeHandler = function(metaData) {
|
||||
// overwrite only for BLOB type to return LOB object.
|
||||
if (metaData.isJson && metaData.dbType === oracledb.DB_TYPE_BLOB) {
|
||||
const myConverter = (v) => {
|
||||
return v;
|
||||
|
@ -526,16 +528,16 @@ describe('271. fetchTypeHandler.js', function() {
|
|||
return { converter: myConverter };
|
||||
}
|
||||
};
|
||||
// mark fetch type for CLOB column as string but it will be
|
||||
// converted to JSON object unless handler for CLOB is written.
|
||||
result = await connection.execute(`select cdata, bdata, chardata from ${TABLE}`, {}, {
|
||||
fetchInfo: { CDATA: { type: oracledb.STRING } }
|
||||
});
|
||||
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
|
||||
result = await connection.execute(`select * from ${TABLE}`);
|
||||
assert.deepStrictEqual(result.rows[0][0], JSON.parse(clobVal));
|
||||
assert(result.rows[0][1] instanceof oracledb.Lob);
|
||||
const blobData = await result.rows[0][1].getData();
|
||||
assert.deepStrictEqual(blobData, blobVal);
|
||||
assert.deepStrictEqual(result.rows[0][2], JSON.parse(charVal));
|
||||
|
||||
// restore the global settings.
|
||||
oracledb.future.oldJsonColumnAsObj = false;
|
||||
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
|
||||
|
||||
await connection.execute(testsUtil.sqlDropTable(TABLE));
|
||||
});
|
||||
|
||||
|
|
|
@ -65,7 +65,6 @@ describe('253. jsonBind1.js', function() {
|
|||
" p_inout := p_inout; \n" +
|
||||
"END;";
|
||||
let skip = false;
|
||||
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
|
||||
before (async function() {
|
||||
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
|
||||
oracledb.extendedMetaData = true;
|
||||
|
@ -74,15 +73,6 @@ describe('253. jsonBind1.js', function() {
|
|||
skip = true;
|
||||
this.skip();
|
||||
}
|
||||
oracledb.fetchTypeHandler = function(metaData) {
|
||||
// overwrite default converter for CLOB, BLOB and VARCHAR type.
|
||||
if (metaData.isJson && metaData.dbType !== oracledb.DB_TYPE_JSON) {
|
||||
const myConverter = (v) => {
|
||||
return v;
|
||||
};
|
||||
return {converter: myConverter};
|
||||
}
|
||||
};
|
||||
await conn.execute(create_table_sql);
|
||||
await conn.commit();
|
||||
});
|
||||
|
@ -95,7 +85,6 @@ describe('253. jsonBind1.js', function() {
|
|||
if (conn) {
|
||||
await conn.close();
|
||||
}
|
||||
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
|
||||
});
|
||||
|
||||
beforeEach(async function() {
|
||||
|
|
|
@ -38,7 +38,6 @@ const testsUtil = require('./testsUtil.js');
|
|||
describe('254. jsonBind2.js', function() {
|
||||
let conn = null;
|
||||
const outFormatBak = oracledb.outFormat;
|
||||
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
|
||||
|
||||
before (async function() {
|
||||
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
|
||||
|
@ -47,23 +46,11 @@ describe('254. jsonBind2.js', function() {
|
|||
if (conn.oracleServerVersion < 1201000200) {
|
||||
this.skip();
|
||||
}
|
||||
if (await testsUtil.isJsonMetaDataRunnable()) {
|
||||
oracledb.fetchTypeHandler = function(metaData) {
|
||||
// overwrite default converter for CLOB, BLOB and VARCHAR type.
|
||||
if (metaData.isJson && metaData.dbType !== oracledb.DB_TYPE_JSON) {
|
||||
const myConverter = (v) => {
|
||||
return v;
|
||||
};
|
||||
return {converter: myConverter};
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
after (async function() {
|
||||
oracledb.outFormat = outFormatBak;
|
||||
await conn.close();
|
||||
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
|
||||
});
|
||||
|
||||
describe('254.1 Map javascript object into BLOB', function() {
|
||||
|
|
Loading…
Reference in New Issue