diff --git a/javascript/node-oracledb/README.md b/javascript/node-oracledb/README.md index d4274066..b106667f 100644 --- a/javascript/node-oracledb/README.md +++ b/javascript/node-oracledb/README.md @@ -1,6 +1,6 @@ # Node-oracledb Examples -This directory contains [node-oracledb 2.3](https://www.npmjs.com/package/oracledb) examples. +This directory contains [node-oracledb 3.0](https://www.npmjs.com/package/oracledb) examples. The node-oracledb add-on for Node.js powers high performance Oracle Database applications. @@ -8,6 +8,11 @@ The node-oracledb add-on for Node.js powers high performance Oracle Database app [Issues and questions](https://github.com/oracle/node-oracledb/issues) +Issues and questions about node-oracledb can be posted on +[GitHub](https://github.com/oracle/node-oracledb/issues) or +[Slack](https://node-oracledb.slack.com/) ([link to join +Slack](https://join.slack.com/t/node-oracledb/shared_invite/enQtNDI4NTUyNjMzMDA5LWRiZWRkZjQ3NjBhNDUwOGJlNDFiZWJhZTIzYTJkMWQ5N2UwNTg5NzNmNmY1YmZjZGYxNmRhOTkyOTlhMmViNjY)). + To run the examples: - [Install node-oracledb](https://oracle.github.io/node-oracledb/INSTALL.html). diff --git a/javascript/node-oracledb/calltimeout.js b/javascript/node-oracledb/calltimeout.js new file mode 100755 index 00000000..0e359312 --- /dev/null +++ b/javascript/node-oracledb/calltimeout.js @@ -0,0 +1,74 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. */ + +/****************************************************************************** + * + * You may not use the identified files except in compliance with the Apache + * License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at + * http://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. + * + * NAME + * calltimeout.js + * + * DESCRIPTION + * Shows how to time out long running database calls. + * See https://oracle.github.io/node-oracledb/doc/api.html#dbcalltimeouts + * Node-oracledb must be using Oracle Client 18c libraries, or greater. + * + * This example uses Async/Await of Node 8. + * + *****************************************************************************/ + +let oracledb = require("oracledb"); +let dbConfig = require('./dbconfig.js'); + +// "Sleep" in the database for a number of seconds. +// This uses an inefficent sleep implementation instead of +// dbms_lock.sleep() which not all users can use. +const sql = ` + DECLARE + t DATE := SYSDATE + (:sleepsec * (1/86400)); + BEGIN + LOOP + EXIT WHEN t <= SYSDATE; + END LOOP; + END;`; + +let dboptime = 4; // seconds the DB operation will take +let timeout = 2; // seconds the application will wait for the DB operation + +async function runTest() { + let connection; + + try { + connection = await oracledb.getConnection(dbConfig); + connection.callTimeout = timeout * 1000; // milliseconds + console.log("Database call timeout set to " + connection.callTimeout / 1000 + " seconds"); + console.log("Executing a " + dboptime + " second DB operation"); + await connection.execute(sql, [dboptime]); + console.log("DB operation successfully completed"); + } catch (err) { + if (err.message.startsWith('DPI-1067:') || err.errorNum === 3114) + console.log('DB operation was stopped after exceeding the call timeout'); + else + console.error(err); + } finally { + if (connection) { + try { + await connection.close(); + } catch (err) { + console.error(err); + } + } + } +} + +runTest(); diff --git a/javascript/node-oracledb/demo.sql b/javascript/node-oracledb/demo.sql index 545de182..2d08ed64 100644 --- a/javascript/node-oracledb/demo.sql +++ b/javascript/node-oracledb/demo.sql @@ -257,4 +257,6 @@ COMMIT; -- The DBA must grant access: -- GRANT CHANGE NOTIFICATION TO myuser; -create table cqntable (k number); +BEGIN EXECUTE IMMEDIATE 'DROP TABLE cqntable'; EXCEPTION WHEN OTHERS THEN IF SQLCODE <> -942 THEN RAISE; END IF; END; +/ +CREATE TABLE cqntable (k NUMBER); diff --git a/javascript/node-oracledb/example.js b/javascript/node-oracledb/example.js new file mode 100644 index 00000000..fc441255 --- /dev/null +++ b/javascript/node-oracledb/example.js @@ -0,0 +1,115 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. */ + +/****************************************************************************** + * + * You may not use the identified files except in compliance with the Apache + * License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at + * http://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. + * + * NAME + * example.js + * + * DESCRIPTION + * A basic node-oracledb example using Node.js 8's async/await syntax. + * + * For a connection pool example see webapp.js + * For a ResultSet example see resultset2.js + * For a query stream example see selectstream.js + * For a Promise example see promises.js + * For a callback example see select1.js + * + *****************************************************************************/ + +var oracledb = require('oracledb'); +var dbConfig = require('./dbconfig.js'); + +async function run() { + let connection; + + try { + + let sql, binds, options, result; + + connection = await oracledb.getConnection( { + user : dbConfig.user, + password : dbConfig.password, + connectString : dbConfig.connectString + }); + + // Create a table + + await connection.execute( + `BEGIN + EXECUTE IMMEDIATE 'DROP TABLE mytab'; + EXCEPTION + WHEN OTHERS THEN + IF SQLCODE NOT IN (-00942) THEN + RAISE; + END IF; + END;`); + + await connection.execute( + `CREATE TABLE mytab (id NUMBER, data VARCHAR2(20))`); + + // Insert some data + + sql = `INSERT INTO mytab VALUES (:1, :2)`; + + binds = [ [101, "Alpha" ], [102, "Beta" ], [103, "Gamma" ] ]; + + // For a complete list of options see the documentation. + options = { + autoCommit: true, + // batchErrors: true, // continue processing even if there are data errors + bindDefs: [ + { type: oracledb.NUMBER }, + { type: oracledb.STRING, maxSize: 20 } + ] + }; + + result = await connection.executeMany(sql, binds, options); + + console.log("Number of rows inserted:", result.rowsAffected); + + // Query the data + + sql = `SELECT * FROM mytab`; + + binds = {}; + + // For a complete list of options see the documentation. + options = { + outFormat: oracledb.OBJECT // query result format + // extendedMetaData: true, // get extra metadata + // fetchArraySize: 100 // internal buffer allocation size for tuning + }; + + result = await connection.execute(sql, binds, options); + + console.log("Column metadata: ", result.metaData); + console.log("Query results: "); + console.log(result.rows); + + } catch (err) { + console.error(err); + } finally { + if (connection) { + try { + await connection.close(); + } catch (err) { + console.error(err); + } + } + } +} + +run(); diff --git a/javascript/node-oracledb/insert1.js b/javascript/node-oracledb/insert1.js index 52f817dc..db4c1d25 100644 --- a/javascript/node-oracledb/insert1.js +++ b/javascript/node-oracledb/insert1.js @@ -21,6 +21,8 @@ * DESCRIPTION * Creates a table and inserts data. Shows DDL and DML * + * (To insert many records at a time see em_insert1.js) + * *****************************************************************************/ var async = require('async'); diff --git a/javascript/node-oracledb/select1.js b/javascript/node-oracledb/select1.js index 369d4a14..a2a13d37 100644 --- a/javascript/node-oracledb/select1.js +++ b/javascript/node-oracledb/select1.js @@ -25,6 +25,7 @@ * Scripts to create the HR schema can be found at: * https://github.com/oracle/db-sample-schemas * + * For an async/await example see selectawait.js * For a connection pool example see webapp.js * For a ResultSet example see resultset2.js * For a query stream example see selectstream.js diff --git a/javascript/node-oracledb/selectjson.js b/javascript/node-oracledb/selectjson.js index 89a03b8f..15672317 100644 --- a/javascript/node-oracledb/selectjson.js +++ b/javascript/node-oracledb/selectjson.js @@ -21,7 +21,7 @@ * DESCRIPTION * Shows some JSON features of Oracle Database 12c. * Requires Oracle Database 12.1.0.2, which has extensive JSON datatype support. - * See http://docs.oracle.com/database/122/ADJSN/toc.htm + * See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN * * Uses Oracle's sample HR schema. * Also run demo.sql to create the required extra table or do: diff --git a/javascript/node-oracledb/selectjsonblob.js b/javascript/node-oracledb/selectjsonblob.js index 3c856096..632f9a17 100644 --- a/javascript/node-oracledb/selectjsonblob.js +++ b/javascript/node-oracledb/selectjsonblob.js @@ -21,7 +21,7 @@ * DESCRIPTION * Executes sample insert and query statements using a JSON column with BLOB storage. * Requires Oracle Database 12.1.0.2, which has extensive JSON datatype support. - * See https://docs.oracle.com/database/122/ADJSN/toc.htm + * See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN * * Use demo.sql to create the required table. * diff --git a/javascript/node-oracledb/soda1.js b/javascript/node-oracledb/soda1.js new file mode 100644 index 00000000..340c7599 --- /dev/null +++ b/javascript/node-oracledb/soda1.js @@ -0,0 +1,133 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. */ + +/****************************************************************************** + * + * You may not use the identified files except in compliance with the Apache + * License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at + * http://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. + * + * NAME + * soda1.js + * + * DESCRIPTION + * Basic Simple Oracle Document Access (SODA) example. + * + * Creates and uses a SODA collection. + * Requires Oracle Database and Client 18.3, or higher. + * The user must have been granted the SODA_APP privilege. + * See https://oracle.github.io/node-oracledb/doc/api.html#sodaoverview + * + * This uses Node 8's async/await syntax but could be rewritten to + * use callbacks. + * + *****************************************************************************/ + +var oracledb = require('oracledb'); +var dbConfig = require('./dbconfig.js'); + +// The general recommendation for simple SODA usage is to enable autocommit +oracledb.autoCommit = true; + +async function run() { + let conn, collection; + + try { + let soda, indexSpec, content, doc, key, documents, res; + + conn = await oracledb.getConnection(dbConfig); + + // Create the parent object for SODA + soda = conn.getSodaDatabase(); + + // Create a new SODA collection and index + // This will open an existing collection, if the name is already in use. + collection = await soda.createCollection("mycollection"); + indexSpec = { "name": "CITY_IDX", + "fields": [ { + "path": "address.city", + "datatype": "string", + "order": "asc" } ] }; + await collection.createIndex(indexSpec); + + // Insert a document. + // A system generated key is created by default. + content = {name: "Matilda", address: {city: "Melbourne"}}; + doc = await collection.insertOneAndGet(content); + key = doc.key; + console.log("The key of the new SODA document is: ", key); + + // Fetch the document back + doc = await collection.find().key(key).getOne(); // A SodaDocument + content = doc.getContent(); // A JavaScript object + console.log('Retrieved SODA document as an object:'); + console.log(content); + content = doc.getContentAsString(); // A JSON string + console.log('Retrieved SODA document as a string:'); + console.log(content); + + // Replace document contents + content = {name: "Matilda", address: {city: "Sydney"}}; + await collection.find().key(key).replaceOne(content); + + // Insert some more documents without caring about their keys + content = {name: "Venkat", address: {city: "Bengaluru"}}; + await collection.insertOne(content); + content = {name: "May", address: {city: "London"}}; + await collection.insertOne(content); + content = {name: "Sally-Ann", address: {city: "San Francisco"}}; + await collection.insertOne(content); + + // Find all documents with city names starting with 'S' + console.log('Cities starting with S'); + documents = await collection.find() + .filter({"address.city": {"$like": "S%"}}) + .getDocuments(); + + for (let i = 0; i < documents.length; i++) { + content = documents[i].getContent(); + console.log(' city is: ', content.address.city); + } + + // Count all documents + res = await collection.find().count(); + console.log('Collection has ' + res.count + ' documents'); + + // Remove documents with cities containing 'o' + console.log('Removing documents'); + res = await collection.find().filter({"address.city": {"$regex": ".*o.*"}}).remove(); + console.log('Dropped ' + res.count + ' documents'); + + // Count all documents + res = await collection.find().count(); + console.log('Collection has ' + res.count + ' documents'); + + } catch (err) { + console.error(err); + } finally { + if (collection) { + // Drop the collection + let res = await collection.drop(); + if (res.dropped) { + console.log('Collection was dropped'); + } + } + if (conn) { + try { + await conn.close(); + } catch (err) { + console.error(err); + } + } + } +} + +run(); diff --git a/javascript/node-oracledb/webapp.js b/javascript/node-oracledb/webapp.js index 41f1b6fc..f06fc188 100644 --- a/javascript/node-oracledb/webapp.js +++ b/javascript/node-oracledb/webapp.js @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. */ +/* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. */ /****************************************************************************** * @@ -27,6 +27,10 @@ * accepts a URL parameter for the department ID, for example: * http://localhost:7000/90 * + * In some networks, pool termination may hang unless you have + * 'disable_oob=on' in sqlnet.ora, see + * https://oracle.github.io/node-oracledb/doc/api.html#tnsadmin + * * Uses Oracle's sample HR schema. Scripts to create the HR schema * can be found at: https://github.com/oracle/db-sample-schemas * @@ -47,15 +51,14 @@ function init() { password: dbConfig.password, connectString: dbConfig.connectString // Default values shown below - // events: false, // whether to handle Oracle Database FAN and RLB events + // events: false, // whether to handle Oracle Database FAN and RLB events or support CQN // externalAuth: false, // whether connections should be established using External Authentication // poolAlias: 'myalias' // set an alias to allow access to the pool via a name // poolIncrement: 1, // only grow the pool by one connection at a time // poolMax: 4, // maximum size of the pool. Increase UV_THREADPOOL_SIZE if you increase poolMax // poolMin: 0, // start with no connections; let the pool shrink completely - // poolPingInterval: 60, // check aliveness of connection if in the pool for 60 seconds + // poolPingInterval: 60, // check aliveness of connection if idle in the pool for 60 seconds // poolTimeout: 60, // terminate connections that are idle in the pool for 60 seconds - // queueRequests: true, // let Node.js queue new getConnection() requests if all pool connections are in use // queueTimeout: 60000, // terminate getConnection() calls in the queue longer than 60000 milliseconds // stmtCacheSize: 30 // number of statements that are cached in the statement cache of each connection }, @@ -202,14 +205,32 @@ function htmlFooter(response) { response.end(); } +function closePoolAndExit() { + console.log("\nTerminating"); + try { + // get the pool from the pool cache and close it when no + // connections are in use, or force it closed after 10 seconds + var pool = oracledb.getPool(); + pool.close(10, function(err) { + if (err) + console.error(err); + else + console.log("Pool closed"); + process.exit(0); + }); + } catch(err) { + // Ignore getPool() error, which may occur if multiple signals + // sent and the pool has already been removed from the cache. + process.exit(0); + } +} + process .on('SIGTERM', function() { - console.log("\nTerminating"); - process.exit(0); + closePoolAndExit(); }) .on('SIGINT', function() { - console.log("\nTerminating"); - process.exit(0); + closePoolAndExit(); }); init(); diff --git a/javascript/node-oracledb/webappawait.js b/javascript/node-oracledb/webappawait.js new file mode 100644 index 00000000..dd76a414 --- /dev/null +++ b/javascript/node-oracledb/webappawait.js @@ -0,0 +1,209 @@ +/* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. */ + +/****************************************************************************** + * + * You may not use the identified files except in compliance with the Apache + * License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at + * http://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. + * + * NAME + * webappawait.js + * + * DESCRIPTION + * Shows a web based query using connections from connection pool. This is + * similar to webapp.js but uses Node 8's async/await syntax. + * + * This displays a table of employees in the specified department. + * + * The script creates an HTTP server listening on port 7000 and + * accepts a URL parameter for the department ID, for example: + * http://localhost:7000/90 + * + * In some networks, pool termination may hang unless you have + * 'disable_oob=on' in sqlnet.ora, see + * https://oracle.github.io/node-oracledb/doc/api.html#tnsadmin + * + * Uses Oracle's sample HR schema. Scripts to create the HR schema + * can be found at: https://github.com/oracle/db-sample-schemas + * + *****************************************************************************/ + +const http = require('http'); +const oracledb = require('oracledb'); +const dbConfig = require('./dbconfig.js'); +const httpPort = 7000; + +// Main entry point. Creates a connection pool and an HTTP server +// that executes a query based on the URL parameter given. +// The pool values shown are the default values. +async function init() { + try { + await oracledb.createPool({ + user: dbConfig.user, + password: dbConfig.password, + connectString: dbConfig.connectString + // Default values shown below + // events: false, // whether to handle Oracle Database FAN and RLB events or support CQN + // externalAuth: false, // whether connections should be established using External Authentication + // poolAlias: 'myalias' // set an alias to allow access to the pool via a name. + // poolIncrement: 1, // only grow the pool by one connection at a time + // poolMax: 4, // maximum size of the pool. Increase UV_THREADPOOL_SIZE if you increase poolMax + // poolMin: 0, // start with no connections; let the pool shrink completely + // poolPingInterval: 60, // check aliveness of connection if idle in the pool for 60 seconds + // poolTimeout: 60, // terminate connections that are idle in the pool for 60 seconds + // queueTimeout: 60000, // terminate getConnection() calls in the queue longer than 60000 milliseconds + // stmtCacheSize: 30 // number of statements that are cached in the statement cache of each connection + }); + + // Create HTTP server and listen on port httpPort + let server = http.createServer(); + server.on('error', (err) => { + console.log('HTTP server problem: ' + err); + }); + server.on('request', (request, response) => { + handleRequest(request, response); + }); + await server.listen(httpPort); + console.log("Server running at http://localhost:" + httpPort); + } catch (err) { + console.error("init() error: " + err.message); + } +} + +async function handleRequest(request, response) { + const urlparts = request.url.split("/"); + const deptid = urlparts[1]; + + htmlHeader( + response, + "Oracle Database Driver for Node.js", + "Example using node-oracledb driver" + ); + + if (deptid == 'favicon.ico') { + htmlFooter(response); + return; + } + + if (deptid != parseInt(deptid)) { + handleError( + response, + 'URL path "' + deptid + '" is not an integer. Try http://localhost:' + httpPort + '/30', + null + ); + + return; + } + + try { + // Checkout a connection from the default pool + let connection = await oracledb.getConnection(); + + let result = await connection.execute( + `SELECT employee_id, first_name, last_name + FROM employees + WHERE department_id = :id`, + [deptid] // bind variable value + ); + + // Release the connection back to the connection pool + await connection.close(); + + displayResults(response, result, deptid); + } catch (err) { + handleError(response, "handleRequest() error", err); + } + + htmlFooter(response); +} + +// Report an error +function handleError(response, text, err) { + if (err) { + text += ": " + err.message; + } + console.error(text); + response.write("
Error: " + text + "
"); +} + +// Display query results +function displayResults(response, result, deptid) { + response.write("| " + result.metaData[col].name + " | "); + } + response.write("
|---|
| " + result.rows[row][col] + " | "); + } + response.write("