UNPKG

8.21 kBJavaScriptView Raw
1// Copyright (c) 2016, 2024, 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
29const { Buffer } = require('buffer');
30const { Duplex } = require('stream');
31const constants = require('./constants.js');
32const errors = require('./errors.js');
33const nodbUtil = require('./util.js');
34const types = require('./types.js');
35
36class Lob extends Duplex {
37
38 constructor() {
39 super({ decodeStrings: false });
40 this.offset = 1;
41 this._isActive = false;
42 this.once('finish', function() {
43 if (this._autoCloseLob) {
44 this.destroy();
45 }
46 });
47 }
48
49 // called by stream.destroy() and ensures that the LOB is closed if it has
50 // not already been closed (never called directly)
51 async _destroy(err, cb) {
52 // if LOB was already closed, nothing to do!
53 if (err && err.message.startsWith("NJS-003:"))
54 delete this._impl;
55 if (this._impl) {
56 const lobImpl = this._impl;
57 delete this._impl;
58 try {
59 await lobImpl.close();
60 } catch (closeErr) {
61 cb(closeErr);
62 return;
63 }
64 }
65 cb(err);
66 }
67
68 // implementation of streaming read; if LOB is set to auto-close, the lob is
69 // automatically closed when an error occurs or when there are no more bytes
70 // to transfer; all that needs to be done here is to destroy the streaming
71 // LOB
72 async _read() {
73 try {
74 const data = await this._serializedRead(this.offset);
75 if (data) {
76 this.offset += data.length;
77 this.push(data);
78 } else {
79 this.push(null);
80 if (this._autoCloseLob) {
81 this.destroy();
82 }
83 }
84 } catch (err) {
85 if (this._autoCloseLob)
86 this.destroy(err);
87 throw err;
88 }
89 }
90
91 // simple wrapper so that serialization can take place on a JavaScript fn
92 async _readData(offset) {
93 errors.assert(this._impl, errors.ERR_INVALID_LOB);
94 try {
95 return await this._impl.read(offset);
96 } catch (err) {
97 throw errors.transformErr(err, this._readData);
98 }
99 }
100
101 // called to associate a LOB implementation with this user facing object
102 _setup(lobImpl, autoCloseLob) {
103 this._impl = lobImpl;
104 this._chunkSize = lobImpl.getChunkSize();
105 this._pieceSize = lobImpl.getPieceSize();
106 this._length = lobImpl.getLength();
107 this._type = lobImpl.getType();
108 if (typeof this._type === 'number') {
109 this._type = types.getTypeByNum(this._type);
110 }
111 this._autoCloseLob = autoCloseLob;
112 }
113
114 // implementation of streaming write; if LOB is set to auto-close, the lob is
115 // automatically closed in the "finish" event; all that needs to be done here
116 // is to destroy the streaming LOB
117 async _write(data, encoding, cb) {
118
119 // convert data if needed
120 if (this.type == constants.DB_TYPE_BLOB && !Buffer.isBuffer(data)) {
121 data = Buffer.from(data);
122 } else if (this.type == constants.DB_TYPE_CLOB &&
123 Buffer.isBuffer(data)) {
124 data = data.toString();
125 }
126
127 try {
128 await this._serializedWrite(this.offset, data);
129 } catch (err) {
130 if (this._autoCloseLob)
131 this.destroy(err);
132 cb(err);
133 return;
134 }
135 this.offset += data.length;
136 cb(null);
137
138 }
139
140 // simple wrapper so that serialization can take place on a JavaScript fn
141 async _writeData(offset, data) {
142 errors.assert(this._impl, errors.ERR_INVALID_LOB);
143 try {
144 await this._impl.write(offset, data);
145 } catch (err) {
146 throw errors.transformErr(err, this._writeData);
147 }
148 }
149
150 //---------------------------------------------------------------------------
151 // chunkSize
152 //
153 // Property for the chunk size of the LOB.
154 //---------------------------------------------------------------------------
155 get chunkSize() {
156 return this._chunkSize;
157 }
158
159 //---------------------------------------------------------------------------
160 // close()
161 //
162 // Close the LOB and make it unusable for further operations. If the LOB is
163 // already closed, nothing is done in order to support multiple close()
164 // calls.
165 //
166 // This method is deprecated and will be removed in a future version of the
167 // node-oracledb driver. Use lob.destroy() instead. NOTE: this method will
168 // emit a duplicate "close" event in order to be compatible with previous
169 // versions of node-oracledb.
170 //---------------------------------------------------------------------------
171 async close() {
172 errors.assertArgCount(arguments, 0, 0);
173 if (this._impl) {
174 const lobImpl = this._impl;
175 delete this._impl;
176 try {
177 await lobImpl.close();
178 this.emit('close');
179 } catch (err) {
180 this.destroy(err);
181 }
182 }
183 }
184
185 //---------------------------------------------------------------------------
186 // getData()
187 //
188 // Return a portion (or all) of the data in the LOB. Note that the amount
189 // and offset are in bytes for BLOB and BFILE type LOBs and in UCS - 2 code
190 // points for CLOB and NCLOB type LOBs.UCS-2 code points are equivalent
191 // to characters for all but supplemental characters.If supplemental
192 // characters are in the LOB, the offset and amount will have to be chosen
193 // carefully to avoid splitting a character.
194 // Returns data in the LOB as a single string or buffer.
195 //---------------------------------------------------------------------------
196 async getData(offset, amount) {
197 errors.assertArgCount(arguments, 0, 2);
198 if (offset === undefined) {
199 offset = 1;
200 } else {
201 errors.assertParamValue(Number.isInteger(offset) && offset > 0, 1);
202 }
203 if (amount === undefined) {
204 amount = 0;
205 } else {
206 errors.assertParamValue(Number.isInteger(amount) && amount > 0, 2);
207 }
208 errors.assert(this._impl, errors.ERR_INVALID_LOB);
209 return await this._impl.getData(offset, amount);
210 }
211
212 //---------------------------------------------------------------------------
213 // length
214 //
215 // Property for the length of the LOB.
216 //---------------------------------------------------------------------------
217 get length() {
218 return this._length;
219 }
220
221 //---------------------------------------------------------------------------
222 // pieceSize
223 //
224 // Property for the size to use for each piece that is transferred when
225 // reading from the LOB.
226 //---------------------------------------------------------------------------
227 get pieceSize() {
228 return this._pieceSize;
229 }
230
231 set pieceSize(value) {
232 errors.assertPropValue(Number.isInteger(value) && value >= 0, "pieceSize");
233 errors.assert(this._impl, errors.ERR_INVALID_LOB);
234 this._impl.setPieceSize(value);
235 this._pieceSize = value;
236 }
237
238 //---------------------------------------------------------------------------
239 // type
240 //
241 // Property for the type of the LOB.
242 //---------------------------------------------------------------------------
243 get type() {
244 return this._type;
245 }
246
247}
248
249nodbUtil.wrapFns(Lob.prototype, errors.ERR_BUSY_LOB,
250 "close",
251 "getData");
252Lob.prototype._serializedRead = nodbUtil.serialize(Lob.prototype._readData);
253Lob.prototype._serializedWrite = nodbUtil.serialize(Lob.prototype._writeData);
254
255module.exports = Lob;