Skip to content

Commit 8a2973a

Browse files
committed
Create a flag oldJsonColumnAsObj inside a new future object for ensuring backward compatibility before converting the IS JSON type column values to JSON object
1 parent 0c129d3 commit 8a2973a

File tree

8 files changed

+82
-56
lines changed

8 files changed

+82
-56
lines changed

doc/src/release_notes.rst

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ node-oracledb `v6.3.0 <https://github.com/oracle/node-oracledb/compare/v6.2.0...
1313
Common Changes
1414
++++++++++++++
1515

16+
#) VARCHAR2 and LOB columns which contain JSON, and have the "IS JSON" check
17+
constraint enabled, can now be fetched in the same way as columns of type
18+
JSON. In node-oracledb :ref:`Thick mode <enablingthick>` this requires
19+
Oracle Client 19 or higher. Applications can get this new fetch behaviour
20+
by setting the new oracledb property
21+
:attr:`oracledb.future.oldJsonColumnAsObj` to `true`. The default value
22+
for this property is `false` which retains the existing fetch behaviour.
23+
In a future version, the new fetch behaviour will become default and
24+
setting this property will no longer be needed.
25+
1626
#) Added constant ``oracledb.DB_TYPE_XMLTYPE`` to represent data of type
1727
``SYS.XMLTYPE`` in metadata ``fetchType`` and ``dbType`` attributes.
1828
Previously the constant used was ``oracledb.DB_TYPE_LONG`` in Thick mode.
@@ -21,13 +31,6 @@ Common Changes
2131
Software Development Kits (SDKs) to generate
2232
:ref:`authentication tokens <tokenbasedauthentication>`.
2333

24-
#) VARCHAR2 and LOB columns which contain JSON, and have the "IS JSON" check
25-
constraint enabled, are now fetched in the same way as columns of type
26-
JSON. In node-oracledb :ref:`Thick mode <enablingthick>` this requires
27-
Oracle Client 19 or higher. Applications can get the old behaviour by
28-
using :attr:`oracledb.fetchTypeHandler` to replace the new default
29-
conversion.
30-
3134
#) Added new connection properties :attr:`connection.dbDomain`,
3235
:attr:`connection.dbName`, :attr:`connection.maxOpenCursors`,
3336
:attr:`connection.serviceName` and :attr:`connection.transactionInProgress`

lib/future.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) 2023, Oracle and/or its affiliates.
2+
3+
//-----------------------------------------------------------------------------
4+
//
5+
// This software is dual-licensed to you under the Universal Permissive License
6+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
7+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
8+
// either license.
9+
//
10+
// If you elect to accept the software under the Apache License, Version 2.0,
11+
// the following applies:
12+
//
13+
// Licensed under the Apache License, Version 2.0 (the "License");
14+
// you may not use this file except in compliance with the License.
15+
// You may obtain a copy of the License at
16+
//
17+
// https://www.apache.org/licenses/LICENSE-2.0
18+
//
19+
// Unless required by applicable law or agreed to in writing, software
20+
// distributed under the License is distributed on an "AS IS" BASIS,
21+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22+
// See the License for the specific language governing permissions and
23+
// limitations under the License.
24+
//
25+
//-----------------------------------------------------------------------------
26+
27+
'use strict';
28+
29+
const errors = require('./errors.js');
30+
31+
// future object used for managing backwards incompatible changes.
32+
class Future {
33+
34+
constructor() {
35+
this._featureFlags = {};
36+
this._featureFlags.oldJsonColumnAsObj = false;
37+
}
38+
39+
get oldJsonColumnAsObj() {
40+
return this._featureFlags.oldJsonColumnAsObj;
41+
}
42+
43+
// fetch VARCHAR2 and LOB columns that contain JSON data (and have
44+
// the "IS JSON" constraint enabled) in the same way that columns
45+
// of type JSON (which requires Oracle Database 21 and higher) are fetched.
46+
set oldJsonColumnAsObj(value) {
47+
errors.assertPropValue(typeof value === 'boolean', "oldJsonColumnAsObj");
48+
this._featureFlags.oldJsonColumnAsObj = value;
49+
}
50+
51+
}
52+
53+
module.exports = new Future;

lib/impl/resultset.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const constants = require('../constants.js');
3030
const errors = require('../errors.js');
3131
const nodbUtil = require('../util.js');
3232
const settings = require('../settings.js');
33+
const future = require('../future.js');
3334
const types = require('../types.js');
3435
const Lob = require('../lob.js');
3536

@@ -106,7 +107,7 @@ class ResultSetImpl {
106107

107108
// If IsJson is set convert to JSON objects unless
108109
// user defined output type handler overwrites it.
109-
if (metadata.isJson && metadata.dbType !== types.DB_TYPE_JSON
110+
if (future.oldJsonColumnAsObj && metadata.isJson && metadata.dbType !== types.DB_TYPE_JSON
110111
&& userConverter === undefined) {
111112
const outConverter = async function(val) {
112113
if (!val) {

lib/oracledb.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const AqDeqOptions = require('./aqDeqOptions.js');
4848
const AqEnqOptions = require('./aqEnqOptions.js');
4949
const AqMessage = require('./aqMessage.js');
5050
const AqQueue = require('./aqQueue.js');
51+
const future = require('./future.js');
5152
const BaseDbObject = require('./dbObject.js');
5253
const Connection = require('./connection.js');
5354
const Lob = require('./lob.js');
@@ -1013,6 +1014,9 @@ module.exports = {
10131014
ARRAY: constants.OUT_FORMAT_ARRAY,
10141015
OBJECT: constants.OUT_FORMAT_OBJECT,
10151016

1017+
// Instances
1018+
future,
1019+
10161020
// property getters
10171021
get autoCommit() {
10181022
return settings.autoCommit;

test/examples.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ describe('3. examples.js', function() {
199199
let conn = null;
200200
const testData = { "userId": 1, "userName": "Chris", "location": "Australia" };
201201
let featureAvailable = true;
202-
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
203202

204203
before(async function() {
205204
conn = await oracledb.getConnection(dbConfig);
@@ -228,17 +227,6 @@ describe('3. examples.js', function() {
228227
sql = "INSERT INTO nodb_purchaseorder (po_document) VALUES (:bv)";
229228
const result = await conn.execute(sql, [s]);
230229
assert.strictEqual(result.rowsAffected, 1);
231-
if (await testsUtil.isJsonMetaDataRunnable()) {
232-
oracledb.fetchTypeHandler = function(metaData) {
233-
// overwrite default converter for VARCHAR2 type.
234-
if (metaData.isJson && metaData.dbType === oracledb.DB_TYPE_VARCHAR) {
235-
const myConverter = (v) => {
236-
return v;
237-
};
238-
return {converter: myConverter};
239-
}
240-
};
241-
}
242230
}); // before
243231

244232
after(async function() {
@@ -247,7 +235,6 @@ describe('3. examples.js', function() {
247235
await conn.execute("DROP TABLE nodb_purchaseorder PURGE");
248236
}
249237
await conn.close();
250-
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
251238
}
252239
}); // after
253240

test/fetchTypeHandler.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,8 @@ describe('271. fetchTypeHandler.js', function() {
504504
const clobVal = '[-1, 2, 3]';
505505
const blobVal = Buffer.from('{ "KeyA": 8, "KeyB": "A String" }');
506506
const charVal = '[-2, 2, 3, [34, 23, 24]]';
507-
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
508507

508+
oracledb.future.oldJsonColumnAsObj = true;
509509
await connection.execute(plsql);
510510
const sql = `INSERT into ${TABLE} VALUES (:1, :2, :3)`;
511511
await connection.execute(sql, [clobVal, blobVal, charVal]);
@@ -515,27 +515,29 @@ describe('271. fetchTypeHandler.js', function() {
515515
assert.deepStrictEqual(result.rows[0][1], JSON.parse(blobVal));
516516
assert.deepStrictEqual(result.rows[0][2], JSON.parse(charVal));
517517

518-
// User defined handler can overwrite the default behaviour of
519-
// converting IS JSON columns to JSON objects.
518+
// fetchtype handlers given preference than oldJsonColumnAsObj setting.
519+
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
520+
521+
// register typehandler only for BLOB.
522+
// For other columns, they are still returned as JSON object.
520523
oracledb.fetchTypeHandler = function(metaData) {
521-
// overwrite only for BLOB type to return LOB object.
522524
if (metaData.isJson && metaData.dbType === oracledb.DB_TYPE_BLOB) {
523525
const myConverter = (v) => {
524526
return v;
525527
};
526528
return { converter: myConverter };
527529
}
528530
};
529-
// mark fetch type for CLOB column as string but it will be
530-
// converted to JSON object unless handler for CLOB is written.
531-
result = await connection.execute(`select cdata, bdata, chardata from ${TABLE}`, {}, {
532-
fetchInfo: { CDATA: { type: oracledb.STRING } }
533-
});
534-
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
531+
result = await connection.execute(`select * from ${TABLE}`);
535532
assert.deepStrictEqual(result.rows[0][0], JSON.parse(clobVal));
536-
assert(result.rows[0][1] instanceof oracledb.Lob);
533+
const blobData = await result.rows[0][1].getData();
534+
assert.deepStrictEqual(blobData, blobVal);
537535
assert.deepStrictEqual(result.rows[0][2], JSON.parse(charVal));
538536

537+
// restore the global settings.
538+
oracledb.future.oldJsonColumnAsObj = false;
539+
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
540+
539541
await connection.execute(testsUtil.sqlDropTable(TABLE));
540542
});
541543

test/jsonBind1.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ describe('253. jsonBind1.js', function() {
6565
" p_inout := p_inout; \n" +
6666
"END;";
6767
let skip = false;
68-
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
6968
before (async function() {
7069
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
7170
oracledb.extendedMetaData = true;
@@ -74,15 +73,6 @@ describe('253. jsonBind1.js', function() {
7473
skip = true;
7574
this.skip();
7675
}
77-
oracledb.fetchTypeHandler = function(metaData) {
78-
// overwrite default converter for CLOB, BLOB and VARCHAR type.
79-
if (metaData.isJson && metaData.dbType !== oracledb.DB_TYPE_JSON) {
80-
const myConverter = (v) => {
81-
return v;
82-
};
83-
return {converter: myConverter};
84-
}
85-
};
8676
await conn.execute(create_table_sql);
8777
await conn.commit();
8878
});
@@ -95,7 +85,6 @@ describe('253. jsonBind1.js', function() {
9585
if (conn) {
9686
await conn.close();
9787
}
98-
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
9988
});
10089

10190
beforeEach(async function() {

test/jsonBind2.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ const testsUtil = require('./testsUtil.js');
3838
describe('254. jsonBind2.js', function() {
3939
let conn = null;
4040
const outFormatBak = oracledb.outFormat;
41-
const defaultFetchTypeHandler = oracledb.fetchTypeHandler;
4241

4342
before (async function() {
4443
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
@@ -47,23 +46,11 @@ describe('254. jsonBind2.js', function() {
4746
if (conn.oracleServerVersion < 1201000200) {
4847
this.skip();
4948
}
50-
if (await testsUtil.isJsonMetaDataRunnable()) {
51-
oracledb.fetchTypeHandler = function(metaData) {
52-
// overwrite default converter for CLOB, BLOB and VARCHAR type.
53-
if (metaData.isJson && metaData.dbType !== oracledb.DB_TYPE_JSON) {
54-
const myConverter = (v) => {
55-
return v;
56-
};
57-
return {converter: myConverter};
58-
}
59-
};
60-
}
6149
});
6250

6351
after (async function() {
6452
oracledb.outFormat = outFormatBak;
6553
await conn.close();
66-
oracledb.fetchTypeHandler = defaultFetchTypeHandler;
6754
});
6855

6956
describe('254.1 Map javascript object into BLOB', function() {

0 commit comments

Comments
 (0)