Skip to content

Commit d75d5e5

Browse files
committed
Support lob.getData() with temp lobs and lobs being written to
1 parent 1a0b960 commit d75d5e5

File tree

5 files changed

+259
-1
lines changed

5 files changed

+259
-1
lines changed

src/njsLob.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,13 @@ static bool njsLob_getDataAsync(njsBaton *baton)
291291
njsLob *lob = (njsLob*) baton->callingInstance;
292292
bool ok = true;
293293

294+
// if the length is marked dirty, acquire it at this time
295+
if (lob->dirtyLength) {
296+
if (dpiLob_getSize(lob->handle, &lob->length) < 0)
297+
return njsBaton_setErrorDPI(baton);
298+
lob->dirtyLength = false;
299+
}
300+
294301
// determine size of buffer that is required
295302
if (lob->dataType == NJS_DATATYPE_BLOB) {
296303
baton->bufferSize = lob->length;
@@ -572,6 +579,7 @@ static bool njsLob_writeAsync(njsBaton *baton)
572579
}
573580
return false;
574581
}
582+
lob->dirtyLength = true;
575583
return true;
576584
}
577585

src/njsModule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ struct njsLob {
547547
uint32_t chunkSize;
548548
uint64_t length;
549549
bool isAutoClose;
550+
bool dirtyLength;
550551
};
551552

552553
// data for keeping track of LOBs in the worker thread

test/getDataOfLob.js

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
/* Copyright (c) 2019, 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+
* 196. getDataOfLob.js
20+
*
21+
* DESCRIPTION
22+
* Test the asynchronous method getData() on LOB columns.
23+
*
24+
*****************************************************************************/
25+
'use strict';
26+
27+
const oracledb = require('oracledb');
28+
const should = require('should');
29+
const fs = require('fs');
30+
const util = require('util');
31+
const dbconfig = require('./dbconfig.js');
32+
const testsUtil = require('./testsUtil.js');
33+
34+
describe('196. getDataOfLob.js', () => {
35+
36+
let conn;
37+
const tab1 = 'nodb_tab_myclob';
38+
const tab2 = 'nodb_tab_myblob';
39+
40+
before('prepare the table', async () => {
41+
try {
42+
conn = await oracledb.getConnection(dbconfig);
43+
44+
let sql =
45+
`create table ${tab1} (
46+
id number(9) not null,
47+
value clob not null
48+
)`;
49+
let plsql = testsUtil.sqlCreateTable(tab1, sql);
50+
await conn.execute(plsql);
51+
52+
sql =
53+
`create table ${tab2} (
54+
id number(9) not null,
55+
value blob not null
56+
)`;
57+
plsql = testsUtil.sqlCreateTable(tab2, sql);
58+
await conn.execute(plsql);
59+
} catch(err) {
60+
should.not.exist(err);
61+
}
62+
}); // before()
63+
64+
after(async () => {
65+
try {
66+
let sql = `drop table ${tab1} purge`;
67+
await conn.execute(sql);
68+
69+
sql = `drop table ${tab2} purge`;
70+
await conn.execute(sql);
71+
72+
await conn.close();
73+
} catch(err) {
74+
should.not.exist(err);
75+
}
76+
}); // after()
77+
78+
it('196.1 getData() works on CLOB ', async () => {
79+
try {
80+
let content = 'A short string value';
81+
let sql = `insert into ${tab1} values (1, '${content}')`;
82+
await conn.execute(sql);
83+
84+
sql = `select * from ${tab1} where id = 1`;
85+
const result = await conn.execute(sql);
86+
const clob = result.rows[0][1];
87+
const value = await clob.getData();
88+
89+
should.strictEqual(value, content);
90+
91+
} catch(err) {
92+
should.not.exist(err);
93+
}
94+
}); // 196.1
95+
96+
it('196.2 getData() returns CLOB as Strings', async () => {
97+
98+
try {
99+
const txtFile = 'test/clobexample.txt';
100+
const inStream = fs.createReadStream(txtFile);
101+
const num = 2;
102+
let sql = `insert into ${tab1} values (:i, empty_clob())
103+
returning value into :lobbv`;
104+
let binds = { i: num, lobbv: { type: oracledb.CLOB, dir: oracledb.BIND_OUT } };
105+
let opt = { autoCommit: false };
106+
107+
// Insertion with Stream
108+
const result = await conn.execute(sql, binds, opt);
109+
110+
const clob = result.outBinds.lobbv[0];
111+
inStream.pipe(clob);
112+
113+
let insertionComplete = new Promise((resolve, reject) => {
114+
inStream.on('error', (err) => reject(err));
115+
clob.on('error', (err) => reject(err));
116+
clob.on('close', () => resolve(conn.commit()));
117+
});
118+
119+
await insertionComplete;
120+
121+
// Query
122+
sql = `select value from ${tab1} where id = ${num}`;
123+
const outResult = await conn.execute(sql);
124+
const outLob = outResult.rows[0][0];
125+
126+
const queryResult = await outLob.getData();
127+
128+
// Verify
129+
const readFile = util.promisify(fs.readFile);
130+
const content = await readFile(txtFile);
131+
should.strictEqual(queryResult, content.toString());
132+
133+
} catch(err) {
134+
should.not.exist(err);
135+
}
136+
137+
}); // 196.2
138+
139+
it('196.3 getData() on BLOB', async () => {
140+
try {
141+
let content = 'A somewhat longer BLOB value';
142+
let sql = `insert into ${tab2} values ( 1, utl_raw.cast_to_raw('${content}') )`;
143+
await conn.execute(sql);
144+
145+
sql = `select * from ${tab2} where id = 1`;
146+
const result = await conn.execute(sql);
147+
const clob = result.rows[0][1];
148+
const value = await clob.getData();
149+
150+
should.strictEqual(value.toString(), content);
151+
152+
} catch(err) {
153+
should.not.exist(err);
154+
}
155+
}); // 196.3
156+
157+
it('196.4 getData() returns BLOB as Buffer', async () => {
158+
try {
159+
const jpgFile = 'test/tree.jpg';
160+
const inStream = fs.createReadStream(jpgFile);
161+
const num = 2;
162+
let sql = `insert into ${tab2} values (:i, empty_blob())
163+
returning value into :lobbv`;
164+
let binds = { i: num, lobbv: { type: oracledb.BLOB, dir: oracledb.BIND_OUT } };
165+
let opt = { autoCommit: false };
166+
167+
// Insertion with Stream
168+
const result = await conn.execute(sql, binds, opt);
169+
170+
const blob = result.outBinds.lobbv[0];
171+
inStream.pipe(blob);
172+
173+
let insertionComplete = new Promise((resolve, reject) => {
174+
inStream.on('error', (err) => reject(err));
175+
blob.on('error', (err) => reject(err));
176+
blob.on('close', () => resolve(conn.commit()));
177+
});
178+
179+
await insertionComplete;
180+
181+
// Query
182+
sql = `select value from ${tab2} where id = ${num}`;
183+
const outResult = await conn.execute(sql);
184+
const outLob = outResult.rows[0][0];
185+
186+
const queryResult = await outLob.getData();
187+
188+
// Verify
189+
const readFile = util.promisify(fs.readFile);
190+
const content = await readFile(jpgFile);
191+
let isEqual = content.equals(queryResult);
192+
(isEqual).should.be.true();
193+
194+
} catch(err) {
195+
should.not.exist(err);
196+
}
197+
}); // 196.4
198+
199+
it('196.5 getData() on empty LOB returns null', async () => {
200+
try {
201+
const tempLob = await conn.createLob(oracledb.BLOB);
202+
const value = await tempLob.getData();
203+
should.strictEqual(value, null);
204+
await tempLob.close();
205+
} catch(err) {
206+
should.not.exist(err);
207+
}
208+
}); // 196.5
209+
210+
it('196.6 works with temp LOB', async () => {
211+
try {
212+
const inFileName = 'test/clobexample.txt';
213+
const tempLob = await conn.createLob(oracledb.CLOB);
214+
const inStream = fs.createReadStream(inFileName);
215+
216+
inStream.pipe(tempLob);
217+
218+
let insertionComplete = new Promise((resolve, reject) => {
219+
inStream.on('error', (err) => reject(err));
220+
tempLob.on('error', (err) => reject(err));
221+
tempLob.on('finish', resolve);
222+
});
223+
224+
await insertionComplete;
225+
226+
// Query
227+
const queryResult = await tempLob.getData();
228+
229+
// Verify
230+
const readFile = util.promisify(fs.readFile);
231+
const content = await readFile(inFileName);
232+
should.strictEqual(queryResult, content.toString());
233+
234+
await tempLob.close();
235+
} catch(err) {
236+
should.not.exist(err);
237+
}
238+
}); // 196.6
239+
240+
});

test/list.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4475,3 +4475,11 @@ Overview of node-oracledb functional tests
44754475
192.1 implicit results with rows fetched
44764476
192.2 implicit Results with Result Sets
44774477
192.3 multiple options, outFormat is OBJECT
4478+
4479+
196. getDataOfLob.js
4480+
196.1 getData() works on CLOB
4481+
196.2 getData() returns CLOB as Strings
4482+
196.3 getData() on BLOB
4483+
196.4 getData() returns BLOB as Buffer
4484+
196.5 getData() on empty LOB returns null
4485+
196.6 works with temp LOB

test/opts/mocha.opts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,5 @@ test/writableProperties2.js
218218
test/fetchBinaryTypesAsString.js
219219
test/currentSchema.js
220220

221-
test/implicitResults.js
221+
test/implicitResults.js
222+
test/getDataOfLob.js

0 commit comments

Comments
 (0)