Skip to content

Commit b00437d

Browse files
committed
Add support for DB 21 JSON internal storage type OSON
1 parent c12cd32 commit b00437d

27 files changed

+2858
-182
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ core
77
GPATH
88
GRTAGS
99
GTAGS
10+
oracle_private/specs/*.pdf
1011

1112
# macOS Ignores
1213
.DS_Store

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# Change Log
22

3-
## node-oracledb v5.1.0-dev (DD Mon YYYY)
4-
5-
**This release is under development**
3+
## node-oracledb v5.1.0 (8 Dec 2020)
64

75
- Added
86
[`oracledb.dbObjectAsPojo`](https://oracle.github.io/node-oracledb/doc/api.html#propdbobjpojo)
@@ -13,6 +11,9 @@
1311
objects" or kept as database-backed objects. This option also applies to
1412
output `BIND_OUT` bind variables.
1513

14+
- Enhanced JSON support to work with Oracle Database 21's native JSON storage
15+
format. A new type `oracledb.DB_TYPE_JSON` was added.
16+
1617
- Numeric suffixes are now added to duplicate SELECT column names when using
1718
`oracledb.OUT_FORMAT_OBJECT` mode, allowing all columns to be represented in
1819
the JavaScript object.

binding.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"src/njsConnection.c",
1212
"src/njsDbObject.c",
1313
"src/njsErrors.c",
14+
"src/njsJsonBuffer.c",
1415
"src/njsLob.c",
1516
"src/njsModule.c",
1617
"src/njsOracleDb.c",
@@ -36,6 +37,7 @@
3637
"odpi/src/dpiGlobal.c",
3738
"odpi/src/dpiHandleList.c",
3839
"odpi/src/dpiHandlePool.c",
40+
"odpi/src/dpiJson.c",
3941
"odpi/src/dpiLob.c",
4042
"odpi/src/dpiMsgProps.c",
4143
"odpi/src/dpiObjectAttr.c",

doc/api.md

Lines changed: 326 additions & 38 deletions
Large diffs are not rendered by default.

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ File Name | Description
9797
[`select1.js`](select1.js) | Executes a basic query without using a connection pool or ResultSet
9898
[`select2.js`](select2.js) | Executes queries to show array and object output formats
9999
[`selectgeometry.js`](selectgeometry.js) | Insert and query Oracle Spatial geometries
100-
[`selectjson.js`](selectjson.js) | Shows some JSON features of Oracle Database
100+
[`selectjson.js`](selectjson.js) | Shows some JSON features of Oracle Database 21c
101101
[`selectjsonblob.js`](selectjsonblob.js) | Shows how to use a BLOB as a JSON column store
102102
[`selectobject.js`](selectobject.js) | Insert and query a named Oracle database object
103103
[`selectnestedcursor.js`](selectnestedcursor.js) | Shows selecting from a nested cursor

examples/selectjson.js

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. */
1+
/* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. */
22

33
/******************************************************************************
44
*
@@ -19,91 +19,111 @@
1919
* selectjson.js
2020
*
2121
* DESCRIPTION
22-
* Shows some JSON features of Oracle Database 12c.
23-
*
24-
* Requires at least Oracle Database 12.1.0.2, which has extensive JSON datatype support.
22+
* Shows some JSON features of Oracle Database 21c.
2523
* See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
2624
*
25+
* For JSON with older databases see selectjsonblob.js
26+
*
27+
* This example requires node-oracledb 5.1 or later.
28+
*
2729
* This example uses Node 8's async/await syntax.
2830
*
2931
*****************************************************************************/
3032

3133
const oracledb = require('oracledb');
3234
const dbConfig = require('./dbconfig.js');
3335

36+
oracledb.extendedMetaData = true;
37+
3438
async function run() {
3539

3640
let connection;
3741

3842
try {
43+
3944
connection = await oracledb.getConnection(dbConfig);
4045

41-
if (connection.oracleServerVersion < 1201000200) {
42-
throw new Error('This example only works with Oracle Database 12.1.0.2 or greater');
46+
if (connection.oracleServerVersion < 2100000000) {
47+
throw new Error('This example requires Oracle Database 21.1 or later. Try selectjsonblob.js.');
4348
}
4449

45-
const stmts = [
46-
`DROP TABLE no_purchaseorder`,
50+
console.log('1. Creating Table');
4751

48-
// Note if your applications always insert valid JSON, you may delete
49-
// the IS JSON check to remove its additional validation overhead.
50-
`CREATE TABLE no_purchaseorder (po_document VARCHAR2(4000) CHECK (po_document IS JSON))`
51-
];
52+
try {
53+
await connection.execute(`DROP TABLE no_purchaseorder`);
54+
} catch(e) {
55+
if (e.errorNum != 942)
56+
console.error(e);
57+
}
5258

53-
for (const s of stmts) {
54-
try {
55-
await connection.execute(s);
56-
} catch(e) {
57-
if (e.errorNum != 942)
58-
console.error(e);
59-
}
59+
connection.execute(`CREATE TABLE no_purchaseorder (po_document JSON)`);
60+
61+
console.log('2. Inserting Data');
62+
63+
const inssql = `INSERT INTO no_purchaseorder (po_document) VALUES (:bv)`;
64+
const data = { "userId": 1, "userName": "Anna", "location": "Australia" };
65+
if (oracledb.oracleClientVersion >= 2100000000) {
66+
await connection.execute(inssql, { bv: { val: data, type: oracledb.DB_TYPE_JSON } });
67+
} else {
68+
// With older client versions, insert as a JSON string
69+
const s = JSON.stringify(data);
70+
const b = Buffer.from(s, 'utf8');
71+
await connection.execute(inssql, { bv: { val: b } });
6072
}
6173

62-
let result;
74+
let result, j;
6375

64-
console.log('Inserting Data');
65-
const data = { "userId": 1, "userName": "Chris", "location": "Australia" };
66-
const s = JSON.stringify(data);
67-
await connection.execute(
68-
`INSERT INTO no_purchaseorder (po_document) VALUES (:bv)`,
69-
[s], // bind the JSON string for inserting into the JSON column.
70-
{ autoCommit: true }
71-
);
76+
console.log('3. Selecting JSON stored in a column');
7277

73-
console.log('1. Selecting JSON stored in a VARCHAR2 column');
7478
result = await connection.execute(
7579
`SELECT po_document
7680
FROM no_purchaseorder
77-
WHERE JSON_EXISTS (po_document, '$.location')`
81+
WHERE JSON_EXISTS (po_document, '$.location')
82+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`
7883
);
79-
const js = JSON.parse(result.rows[0][0]); // just show first record
80-
console.log('Query results: ', js);
84+
if (result.metaData[0].fetchType == oracledb.DB_TYPE_JSON) {
85+
j = result.rows[0][0];
86+
} else {
87+
// Oracle Client libraries < 21 will return a BLOB
88+
const d = await result.rows[0][0].getData();
89+
j = await JSON.parse(d);
90+
}
91+
console.log('Query results: ', j);
92+
93+
console.log('4. Using JSON_VALUE to extract a value from a JSON column');
8194

82-
console.log('2. Using JSON_VALUE to extract a value from a JSON column');
8395
result = await connection.execute(
8496
`SELECT JSON_VALUE(po_document, '$.location')
85-
FROM no_purchaseorder`
97+
FROM no_purchaseorder
98+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`
8699
);
87100
console.log('Query results: ', result.rows[0][0]); // just show first record
88101

89-
if (connection.oracleServerVersion < 1202000000) {
90-
throw new Error('These examples only work with Oracle Database 12.2 or greater');
91-
}
102+
console.log('5. Using dot-notation to extract a value from a JSON column');
92103

93-
console.log('3. Using dot-notation to extract a value from a JSON column');
94104
result = await connection.execute(
95105
`SELECT po.po_document.location
96-
FROM no_purchaseorder po`
106+
FROM no_purchaseorder po
107+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`
97108
);
98-
console.log('Query results: ', result.rows[0][0]); // just show first record
109+
if (result.metaData[0].fetchType == oracledb.DB_TYPE_JSON) {
110+
j = result.rows[0][0];
111+
} else {
112+
// Oracle Client libraries < 21 will return a BLOB
113+
const d = await result.rows[0][0].getData();
114+
j = await JSON.parse(d);
115+
}
116+
console.log('Query results: ', j);
117+
118+
console.log('6. Using JSON_OBJECT to extract relational data as JSON');
99119

100-
console.log('4. Using JSON_OBJECT to extract relational data as JSON');
101120
result = await connection.execute(
102121
`SELECT JSON_OBJECT('key' IS d.dummy) dummy
103122
FROM dual d`
104123
);
105-
for (let row of result.rows)
106-
console.log(row[0]);
124+
for (let row of result.rows) {
125+
console.log('Query results: ', row[0]);
126+
}
107127

108128
} catch (err) {
109129
console.error(err);

examples/selectjsonblob.js

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. */
1+
/* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. */
22

33
/******************************************************************************
44
*
@@ -19,12 +19,13 @@
1919
* selectjsonblob.js
2020
*
2121
* DESCRIPTION
22-
* Executes sample insert and query statements using a JSON column with BLOB storage.
22+
* Shows how to use a BLOB as a JSON column store.
2323
*
24-
* Requires Oracle Database 12.1.0.2, which has extensive JSON datatype support.
25-
* See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
24+
* Note: with Oracle Database 21c using the new JSON type is recommended
25+
* instead, see selectjson.js
2626
*
27-
* This example requires node-oracledb 1.13 or later.
27+
* Requires Oracle Database 12.1.0.2 or later.
28+
* See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
2829
*
2930
* This example uses Node 8's async/await syntax.
3031
*
@@ -33,6 +34,8 @@
3334
const oracledb = require('oracledb');
3435
const dbConfig = require('./dbconfig.js');
3536

37+
oracledb.extendedMetaData = true;
38+
3639
async function run() {
3740

3841
let connection;
@@ -42,58 +45,81 @@ async function run() {
4245
connection = await oracledb.getConnection(dbConfig);
4346

4447
if (connection.oracleServerVersion < 1201000200) {
45-
throw new Error('This example only works with Oracle Database 12.1.0.2 or greater');
48+
throw new Error('Using JSON requires Oracle Database 12.1.0.2 or later');
4649
}
4750

48-
const stmts = [
49-
`DROP TABLE no_purchaseorder_b`,
51+
console.log('1. Creating Table');
5052

51-
`CREATE TABLE no_purchaseorder_b (po_document BLOB CHECK (po_document IS JSON)) LOB (po_document) STORE AS (CACHE)`
52-
];
53+
try {
54+
await connection.execute(`DROP TABLE no_purchaseorder_b`);
55+
} catch(e) {
56+
if (e.errorNum != 942)
57+
console.error(e);
58+
}
5359

54-
for (const s of stmts) {
55-
try {
56-
await connection.execute(s);
57-
} catch(e) {
58-
if (e.errorNum != 942)
59-
console.error(e);
60-
}
60+
await connection.execute(
61+
`CREATE TABLE no_purchaseorder_b
62+
(po_document BLOB CHECK (po_document IS JSON)) LOB (po_document) STORE AS (CACHE)`);
63+
64+
console.log('2. Inserting Data');
65+
66+
const inssql = `INSERT INTO no_purchaseorder_b (po_document) VALUES (:bv)`;
67+
const data = { "userId": 1, "userName": "Anna", "location": "Australia" };
68+
69+
if (oracledb.oracleClientVersion >= 2100000000 && connection.oracleServerVersion >= 2100000000 ) {
70+
// Take advantage of direct binding of JavaScript objects
71+
await connection.execute(inssql, { bv: { val: data, type: oracledb.DB_TYPE_JSON } });
72+
} else {
73+
// Insert the data as a JSON string
74+
const s = JSON.stringify(data);
75+
const b = Buffer.from(s, 'utf8');
76+
await connection.execute(inssql, { bv: { val: b } });
6177
}
6278

63-
let result;
79+
console.log('3. Selecting JSON stored in a BLOB column');
6480

65-
console.log('Inserting Data');
66-
const data = { "userId": 2, "userName": "Bob", "location": "USA" };
67-
const s = JSON.stringify(data);
68-
const b = Buffer.from(s, 'utf8');
69-
await connection.execute(
70-
`INSERT INTO no_purchaseorder_b (po_document) VALUES (:lobbv)`,
71-
{ lobbv: b }
72-
);
81+
let result, j;
7382

74-
console.log('Selecting JSON stored in a BLOB column:');
7583
result = await connection.execute(
7684
`SELECT po_document
7785
FROM no_purchaseorder_b
78-
WHERE JSON_EXISTS (po_document, '$.location')`,
79-
[],
80-
{ fetchInfo: { "PO_DOCUMENT": { type: oracledb.BUFFER } } } // Fetch as a Buffer instead of a Stream
86+
WHERE JSON_EXISTS (po_document, '$.location')
87+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`
8188
);
82-
if (result.rows.length === 0)
83-
throw new Error('No results');
84-
console.log(result.rows[0][0].toString('utf8'));
89+
const d = await result.rows[0][0].getData();
90+
j = await JSON.parse(d);
91+
console.log('Query results: ', j);
92+
93+
console.log('4. Using JSON_VALUE to extract a value from a JSON column');
8594

86-
console.log('Selecting a JSON value using "dotted" notation:');
87-
if (connection.oracleServerVersion < 1202000000) {
88-
throw new Error('This example only works with Oracle Database 12.2 or greater');
89-
}
9095
result = await connection.execute(
91-
`SELECT pob.po_document.location
92-
FROM no_purchaseorder_b pob`
96+
`SELECT JSON_VALUE(po_document, '$.location')
97+
FROM no_purchaseorder_b
98+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`
9399
);
94-
if (result.rows.length === 0)
95-
throw new Error('No results');
96-
console.log(result.rows[0][0]);
100+
console.log('Query results: ', result.rows[0][0]); // just show first record
101+
102+
if (connection.oracleServerVersion >= 1202000000) {
103+
104+
console.log('5. Using dot-notation to extract a value from a JSON column');
105+
106+
result = await connection.execute(
107+
`SELECT po.po_document.location
108+
FROM no_purchaseorder_b po
109+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`
110+
);
111+
console.log('Query results: ', result.rows[0][0]);
112+
113+
console.log('6. Using JSON_OBJECT to extract relational data as JSON');
114+
115+
result = await connection.execute(
116+
`SELECT JSON_OBJECT('key' IS d.dummy) dummy
117+
FROM dual d`
118+
);
119+
for (let row of result.rows) {
120+
console.log('Query results: ', row[0]);
121+
}
122+
}
97123

98124
} catch (err) {
99125
console.error(err);

src/njsConnection.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2351,7 +2351,8 @@ static bool njsConnection_scanExecuteBinds(njsBaton *baton, napi_env env,
23512351
// specified by the application; for OUT binds, the value from the
23522352
// application must be accepted as is as there is no way to
23532353
// validate it
2354-
if (var->varTypeNum != DPI_ORACLE_TYPE_OBJECT) {
2354+
if (var->varTypeNum != DPI_ORACLE_TYPE_OBJECT &&
2355+
var->varTypeNum != DPI_ORACLE_TYPE_JSON) {
23552356

23562357
NJS_CHECK_NAPI(env, napi_is_array(env, bindValue, &check))
23572358
if (check) {

src/njsErrors.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ static const char *njsErrorMessages[] = {
8484
"NJS-075: only one of connectString and connectionString can be used", // errDblConnectionString
8585
"NJS-076: connection request rejected. Pool queue length queueMax %d reached", // errQueueMax
8686
"NJS-077: Oracle Client library has already been initialized", // errClientLibAlreadyInitialized
87+
"NJS-078: unsupported data type %u in JSON value", // errUnsupportedDataTypeInJson
88+
"NJS-079: cannot convert from JavaScript value to JSON value", // errConvertToJsonValue
8789
};
8890

8991

0 commit comments

Comments
 (0)