|
| 1 | +/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */ |
| 2 | + |
| 3 | +/****************************************************************************** |
| 4 | + * |
| 5 | + * You may not use the identified files except in compliance with the Apache |
| 6 | + * License, Version 2.0 (the "License.") |
| 7 | + * |
| 8 | + * You may obtain a copy of the License at |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0. |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 13 | + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * |
| 15 | + * See the License for the specific language governing permissions and |
| 16 | + * limitations under the License. |
| 17 | + * |
| 18 | + * NAME |
| 19 | + * lobbinds.js |
| 20 | + * |
| 21 | + * DESCRIPTION |
| 22 | + * Demonstrates following LOB bind features |
| 23 | + * 1) DML bind for an INSERT |
| 24 | + * 2) PL/SQL bind IN for CLOB as String, and BLOB as Buffer |
| 25 | + * 3) PL/SQL bind OUT for CLOB as String, and BLOB as Buffer |
| 26 | + * 4) Querying a LOB and binding using PL/SQL IN OUT bind |
| 27 | + * 5) PL/SQL OUT bind followed by PL/SQL IN OUT bind |
| 28 | + * |
| 29 | + * Use demo.sql to create the required tables and procedures |
| 30 | + * Run lobinsert1.js to load text before running this example |
| 31 | + * |
| 32 | + * *****************************************************************************/ |
| 33 | + |
| 34 | +var fs = require('fs'); |
| 35 | +var async = require('async'); |
| 36 | +var oracledb = require('oracledb'); |
| 37 | +var dbConfig = require('./dbconfig.js'); |
| 38 | + |
| 39 | +var clobOutFileName1 = 'lobbindsout1.txt'; |
| 40 | +var clobOutFileName2 = 'lobbindsout2.txt'; |
| 41 | + |
| 42 | +oracledb.autoCommit = true; // for ease of demonstration |
| 43 | + |
| 44 | +var doconnect = function(cb) { |
| 45 | + oracledb.getConnection( |
| 46 | + { |
| 47 | + user : dbConfig.user, |
| 48 | + password : dbConfig.password, |
| 49 | + connectString : dbConfig.connectString |
| 50 | + }, |
| 51 | + cb); |
| 52 | +}; |
| 53 | + |
| 54 | +var dorelease = function(conn) { |
| 55 | + conn.close(function (err) { |
| 56 | + if (err) |
| 57 | + console.error(err.message); |
| 58 | + }); |
| 59 | +}; |
| 60 | + |
| 61 | +// Cleanup anything other than lobinsert1.js demonstration data |
| 62 | +var docleanup = function (conn, cb) { |
| 63 | + conn.execute( |
| 64 | + 'DELETE FROM mylobs WHERE id > 2', |
| 65 | + function(err) { |
| 66 | + return cb(err, conn); |
| 67 | + }); |
| 68 | +}; |
| 69 | + |
| 70 | +// 1. SELECTs a CLOB and inserts it back using an IN bind to an INSERT statement |
| 71 | +var query_bind_insert = function (conn, cb) { |
| 72 | + console.log ("1. query_bind_insert(): Inserting a CLOB using a LOB IN bind for INSERT"); |
| 73 | + conn.execute( |
| 74 | + "SELECT c FROM mylobs WHERE id = :id", |
| 75 | + { id: 1 }, |
| 76 | + function(err, result) |
| 77 | + { |
| 78 | + if (err) { |
| 79 | + return cb(err, conn); |
| 80 | + } |
| 81 | + if (result.rows.length === 0) { |
| 82 | + return cb(new Error('query_bind_insert(): No results. Did you run lobinsert1.js?'), conn); |
| 83 | + } |
| 84 | + var clob1 = result.rows[0][0]; |
| 85 | + if (clob1 === null) { |
| 86 | + return cb(new Error('query_bind_insert(): NULL clob1 found'), conn); |
| 87 | + } |
| 88 | + |
| 89 | + // Insert the value back as a new row |
| 90 | + conn.execute( |
| 91 | + "INSERT INTO mylobs (id, c) VALUES (:id, :c)", |
| 92 | + { id: 10, |
| 93 | + c: {val: clob1, type: oracledb.CLOB, dir: oracledb.BIND_IN} }, |
| 94 | + function(err) { |
| 95 | + if (err) { |
| 96 | + return cb(err, conn); |
| 97 | + } |
| 98 | + clob1.close(function(err) { // clob1 wasn't streamed, so close it explicitly |
| 99 | + if (err) { |
| 100 | + return cb(err, conn); |
| 101 | + } |
| 102 | + console.log (" Completed"); |
| 103 | + return cb(null, conn); |
| 104 | + }); |
| 105 | + }); |
| 106 | + }); |
| 107 | +}; |
| 108 | + |
| 109 | +// 2. Show PL/SQL bind IN for CLOB as String and for BLOB as Buffer. |
| 110 | +var plsql_in_as_str_buf = function (conn, cb) { |
| 111 | + console.log("2. plsql_in_as_str_buf(): Binding of String and Buffer for PL/SQL IN binds"); |
| 112 | + |
| 113 | + // Make up some data |
| 114 | + var bigStr, bigBuf; |
| 115 | + if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) >= 4) { |
| 116 | + bigStr = 'A'.repeat(50000); |
| 117 | + bigBuf = Buffer.from(bigStr); |
| 118 | + } else { |
| 119 | + bigStr = "A"; |
| 120 | + for (var i = 0; i < 15; i++) |
| 121 | + bigStr += bigStr; |
| 122 | + bigBuf = new Buffer(bigStr); |
| 123 | + } |
| 124 | + |
| 125 | + conn.execute( |
| 126 | + "BEGIN lobs_in(:id, :c, :b); END;", |
| 127 | + { id: 20, |
| 128 | + c: {val: bigStr, type: oracledb.STRING, dir: oracledb.BIND_IN}, |
| 129 | + b: {val: bigBuf, type: oracledb.BUFFER, dir: oracledb.BIND_IN} }, |
| 130 | + function (err) |
| 131 | + { |
| 132 | + if (err) { |
| 133 | + return cb(err, conn); |
| 134 | + } |
| 135 | + console.log(" Completed"); |
| 136 | + return cb(null, conn); |
| 137 | + }); |
| 138 | +}; |
| 139 | + |
| 140 | +// 3. Gets text and binary strings from database LOBs using PL/SQL OUT binds |
| 141 | +var plsql_out_as_str_buf = function (conn, cb) { |
| 142 | + console.log("3. plsql_out_as_str_buf(): Fetching as String and Buffer using PL/SQL OUT binds"); |
| 143 | + conn.execute( |
| 144 | + "BEGIN lobs_out(:id, :c, :b); END;", |
| 145 | + { id: 20, |
| 146 | + c: {type: oracledb.STRING, dir: oracledb.BIND_OUT, maxSize: 50000}, |
| 147 | + b: {type: oracledb.BUFFER, dir: oracledb.BIND_OUT, maxSize: 50000} }, |
| 148 | + function (err /*, result */) |
| 149 | + { |
| 150 | + if (err) { |
| 151 | + return cb(err, conn); |
| 152 | + } |
| 153 | + |
| 154 | + // In real life do something with the result.outBinds.c String and the result.outBinds.b Buffer here |
| 155 | + |
| 156 | + console.log (" Completed"); |
| 157 | + return cb(null, conn); |
| 158 | + }); |
| 159 | +}; |
| 160 | + |
| 161 | +// 4. Queries a CLOB and passes it to a PL/SQL procedure as an IN OUT bind |
| 162 | +// Persistent LOBs can be bound to PL/SQL calls as IN OUT. (Temporary LOBs cannot). |
| 163 | +var query_plsql_inout = function (conn, cb) { |
| 164 | + console.log ("4. query_plsql_inout(): Querying then inserting a CLOB using a PL/SQL IN OUT LOB bind"); |
| 165 | + conn.execute( |
| 166 | + "SELECT c FROM mylobs WHERE id = :id", |
| 167 | + { id: 1 }, |
| 168 | + function(err, result) |
| 169 | + { |
| 170 | + if (err) { |
| 171 | + return cb(err, conn); |
| 172 | + } |
| 173 | + if (result.rows.length === 0) { |
| 174 | + return cb(new Error('query_plsql_inout(): No results'), conn); |
| 175 | + } |
| 176 | + var clob1 = result.rows[0][0]; |
| 177 | + if (clob1 === null) { |
| 178 | + return cb(new Error('query_plsql_inout(): NULL clob1 found'), conn); |
| 179 | + } |
| 180 | + |
| 181 | + // Note binding clob1 as IN OUT here causes it be autoclosed by execute(). |
| 182 | + // The returned Lob clob2 will be autoclosed because it is streamed to completion. |
| 183 | + conn.execute( |
| 184 | + "BEGIN lob_in_out(:idbv, :ciobv); END;", |
| 185 | + { idbv: 30, |
| 186 | + ciobv: {val: clob1, type: oracledb.CLOB, dir: oracledb.BIND_INOUT} }, |
| 187 | + function(err, result) { |
| 188 | + if (err) { |
| 189 | + return cb(err, conn); |
| 190 | + } |
| 191 | + |
| 192 | + var clob2 = result.outBinds.ciobv; |
| 193 | + if (clob2 === null) { |
| 194 | + return cb(new Error('plsql_out_inout(): NULL clob2 found'), conn); |
| 195 | + } |
| 196 | + |
| 197 | + // Stream the LOB to a file |
| 198 | + console.log(' Writing to ' + clobOutFileName1); |
| 199 | + clob2.setEncoding('utf8'); // set the encoding so we get a 'string' not a 'buffer' |
| 200 | + clob2.on( |
| 201 | + 'error', |
| 202 | + function(err) |
| 203 | + { |
| 204 | + // console.log("clob2.on 'error' event"); |
| 205 | + return cb(err); |
| 206 | + }); |
| 207 | + clob2.on( |
| 208 | + 'end', |
| 209 | + function() |
| 210 | + { |
| 211 | + // console.log("clob2.on 'end' event"); |
| 212 | + }); |
| 213 | + clob2.on( |
| 214 | + 'close', |
| 215 | + function() |
| 216 | + { |
| 217 | + // console.log("clob2.on 'close' event"); |
| 218 | + |
| 219 | + console.log (" Completed"); |
| 220 | + return cb(null, conn); |
| 221 | + }); |
| 222 | + |
| 223 | + var outStream = fs.createWriteStream(clobOutFileName1); |
| 224 | + outStream.on( |
| 225 | + 'error', |
| 226 | + function(err) |
| 227 | + { |
| 228 | + // console.log("outStream.on 'error' event"); |
| 229 | + return cb(err); |
| 230 | + }); |
| 231 | + |
| 232 | + // Switch into flowing mode and push the LOB to the file |
| 233 | + clob2.pipe(outStream); |
| 234 | + }); |
| 235 | + }); |
| 236 | +}; |
| 237 | + |
| 238 | +// 5. Get CLOB as a PL/SQL OUT bind and pass it to another procedure as IN OUT. |
| 239 | +// Persistent LOBs can be bound to PL/SQL calls as IN OUT. (Temporary LOBs cannot). |
| 240 | +var plsql_out_inout = function (conn, cb) { |
| 241 | + console.log ("5. plsql_out_inout(): Getting a LOB using a PL/SQL OUT bind and inserting it using a PL/SQL IN OUT LOB bind"); |
| 242 | + conn.execute( |
| 243 | + "BEGIN lobs_out(:idbv, :cobv, :bobv); END;", |
| 244 | + { idbv: 1, |
| 245 | + cobv: {type: oracledb.CLOB, dir: oracledb.BIND_OUT}, |
| 246 | + bobv: {type: oracledb.BLOB, dir: oracledb.BIND_OUT} }, // not used in this demo; it will be NULL anyway |
| 247 | + function(err, result) |
| 248 | + { |
| 249 | + if (err) { |
| 250 | + return cb(err, conn); |
| 251 | + } |
| 252 | + |
| 253 | + var clob1 = result.outBinds.cobv; |
| 254 | + if (clob1 === null) { |
| 255 | + return cb(new Error('plsql_out_inout(): NULL clob1 found'), conn); |
| 256 | + } |
| 257 | + |
| 258 | + // Note binding clob1 as IN OUT here causes it be autoclosed by execute(). |
| 259 | + // The returned Lob clob2 will be autoclosed because it is streamed to completion. |
| 260 | + conn.execute( |
| 261 | + "BEGIN lob_in_out(:idbv, :ciobv); END;", |
| 262 | + { idbv: 50, |
| 263 | + ciobv: {val: clob1, type: oracledb.CLOB, dir: oracledb.BIND_INOUT} }, |
| 264 | + function(err, result) { |
| 265 | + if (err) { |
| 266 | + return cb(err, conn); |
| 267 | + } |
| 268 | + |
| 269 | + var clob2 = result.outBinds.ciobv; |
| 270 | + if (clob2 === null) { |
| 271 | + return cb(new Error('plsql_out_inout(): NULL clob2 found'), conn); |
| 272 | + } |
| 273 | + |
| 274 | + // Stream the LOB to a file |
| 275 | + console.log(' Writing to ' + clobOutFileName2); |
| 276 | + clob2.setEncoding('utf8'); // set the encoding so we get a 'string' not a 'buffer' |
| 277 | + clob2.on( |
| 278 | + 'error', |
| 279 | + function(err) |
| 280 | + { |
| 281 | + // console.log("clob2.on 'error' event"); |
| 282 | + return cb(err); |
| 283 | + }); |
| 284 | + clob2.on( |
| 285 | + 'end', |
| 286 | + function() |
| 287 | + { |
| 288 | + // console.log("clob2.on 'end' event"); |
| 289 | + }); |
| 290 | + clob2.on( |
| 291 | + 'close', |
| 292 | + function() |
| 293 | + { |
| 294 | + // console.log("clob2.on 'close' event"); |
| 295 | + console.log (" Completed"); |
| 296 | + return cb(null, conn); |
| 297 | + }); |
| 298 | + |
| 299 | + var outStream = fs.createWriteStream(clobOutFileName2); |
| 300 | + outStream.on( |
| 301 | + 'error', |
| 302 | + function(err) |
| 303 | + { |
| 304 | + // console.log("outStream.on 'error' event"); |
| 305 | + return cb(err); |
| 306 | + }); |
| 307 | + |
| 308 | + // Switch into flowing mode and push the LOB to the file |
| 309 | + clob2.pipe(outStream); |
| 310 | + }); |
| 311 | + }); |
| 312 | +}; |
| 313 | + |
| 314 | +/* |
| 315 | +// 6. Show the number of open temporary LOBs |
| 316 | +var doshowvtemplob = function (conn, cb) { |
| 317 | + console.log('6. Query from V$TEMPORARY_LOBS:'); |
| 318 | + conn.execute( |
| 319 | + "SELECT * FROM V$TEMPORARY_LOBS", |
| 320 | + [], { outFormat: oracledb.OBJECT }, |
| 321 | + function (err, result) { |
| 322 | + if (err) { |
| 323 | + return cb(err, conn); |
| 324 | + } else { |
| 325 | + console.log(result.rows[0]); |
| 326 | + return cb(null, conn); |
| 327 | + } |
| 328 | + }); |
| 329 | +}; |
| 330 | +*/ |
| 331 | + |
| 332 | +async.waterfall( |
| 333 | + [ |
| 334 | + doconnect, |
| 335 | + docleanup, |
| 336 | + query_bind_insert, |
| 337 | + plsql_in_as_str_buf, |
| 338 | + plsql_out_as_str_buf, |
| 339 | + query_plsql_inout, |
| 340 | + plsql_out_inout, |
| 341 | + // doshowvtemplob // Show open temporary Lobs, if desired |
| 342 | + ], |
| 343 | + function (err, conn) { |
| 344 | + if (err) { |
| 345 | + console.error("In waterfall error cb: ==>", err, "<=="); |
| 346 | + } |
| 347 | + if (conn) |
| 348 | + dorelease(conn); |
| 349 | + }); |
0 commit comments