oracledb 7.0.0 → 26.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/build/Release/oracledb-26.0.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-26.0.0-darwin-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-26.0.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-linux-x64.node +0 -0
- package/build/Release/oracledb-26.0.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-win32-x64.node +0 -0
- package/build/Release/oracledb-26.0.0-win32-x64.node-buildinfo.txt +1 -0
- package/lib/aqDeqOptions.js +4 -4
- package/lib/aqEnqOptions.js +2 -2
- package/lib/aqQueue.js +11 -6
- package/lib/connection.js +73 -18
- package/lib/constants.js +4 -0
- package/lib/endUserSecurityContext.js +30 -25
- package/lib/errors.js +92 -5
- package/lib/impl/connection.js +36 -0
- package/lib/impl/datahandlers/buffer.js +91 -18
- package/lib/impl/datahandlers/oson.js +3 -2
- package/lib/impl/parserHelpers.js +6 -2
- package/lib/impl/resultset.js +32 -11
- package/lib/oracledb.js +34 -0
- package/lib/pool.js +6 -2
- package/lib/queryStream.js +13 -5
- package/lib/resultset.js +10 -3
- package/lib/securityContextProvider.js +206 -0
- package/lib/thin/aq.js +2 -3
- package/lib/thin/connection.js +380 -24
- package/lib/thin/dbObject.js +20 -11
- package/lib/thin/pool.js +4 -3
- package/lib/thin/protocol/capabilities.js +37 -4
- package/lib/thin/protocol/constants.js +49 -1
- package/lib/thin/protocol/messages/aqArray.js +2 -2
- package/lib/thin/protocol/messages/aqBase.js +2 -2
- package/lib/thin/protocol/messages/auth.js +17 -6
- package/lib/thin/protocol/messages/base.js +75 -17
- package/lib/thin/protocol/messages/dataType.js +46 -0
- package/lib/thin/protocol/messages/directPathLoadStream.js +3 -2
- package/lib/thin/protocol/messages/execute.js +10 -4
- package/lib/thin/protocol/messages/index.js +4 -0
- package/lib/thin/protocol/messages/notification.js +281 -0
- package/lib/thin/protocol/messages/subscribe.js +181 -0
- package/lib/thin/protocol/messages/withData.js +23 -10
- package/lib/thin/protocol/packet.js +2 -4
- package/lib/thin/protocol/protocol.js +7 -4
- package/lib/thin/resultSet.js +40 -20
- package/lib/thin/sqlnet/ezConnectResolver.js +9 -2
- package/lib/thin/sqlnet/navNodes.js +7 -5
- package/lib/thin/sqlnet/networkSession.js +17 -11
- package/lib/thin/sqlnet/ntTcp.js +24 -8
- package/lib/thin/sqlnet/packet.js +24 -4
- package/lib/thin/sqlnet/sessionAtts.js +2 -2
- package/lib/thin/statement.js +6 -1
- package/lib/thin/statementCache.js +34 -6
- package/lib/thin/subscr.js +363 -0
- package/lib/transformer.js +60 -17
- package/lib/util.js +46 -8
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/plugins/configProviders/gcpstorage/index.js +121 -0
- package/plugins/configProviders/ociobject/index.js +1 -1
- package/plugins/token/cache.js +154 -0
- package/plugins/token/endUserSecurityProvider/index.js +377 -0
- package/plugins/token/extensionAzure/index.js +160 -28
- package/plugins/token/extensionOci/index.js +221 -16
- package/plugins/vectorsdk/OracleVecDB.js +346 -0
- package/plugins/vectorsdk/index.js +49 -0
- package/plugins/vectorsdk/utils.js +370 -0
- package/plugins/vectorsdk/vectorEmbedding.js +223 -0
- package/plugins/vectorsdk/vectorIndex.js +177 -0
- package/plugins/vectorsdk/vectorRecords.js +406 -0
- package/plugins/vectorsdk/vectorSchema.js +408 -0
- package/plugins/vectorsdk/vectorSearch.js +538 -0
- package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
- package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
- package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +0 -1
- package/eslint.config.js +0 -39
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// Copyright (c) 2026, 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
|
+
|
|
29
|
+
const oracledb = require('oracledb');
|
|
30
|
+
|
|
31
|
+
//-----------------------------------------------------------------------------
|
|
32
|
+
// Constants
|
|
33
|
+
//-----------------------------------------------------------------------------
|
|
34
|
+
const DISTANCE_METRICS = new Set([
|
|
35
|
+
'COSINE',
|
|
36
|
+
'EUCLIDEAN',
|
|
37
|
+
'MANHATTAN',
|
|
38
|
+
'HAMMING',
|
|
39
|
+
'JACCARD',
|
|
40
|
+
'DOT',
|
|
41
|
+
'L2_SQUARED',
|
|
42
|
+
'EUCLIDEAN_SQUARED'
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
//-----------------------------------------------------------------------------
|
|
46
|
+
// Distance metric
|
|
47
|
+
//-----------------------------------------------------------------------------
|
|
48
|
+
function normalizeVectorDistanceType(vectorDistanceType) {
|
|
49
|
+
if (vectorDistanceType == null) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const metric = String(vectorDistanceType).trim().toUpperCase();
|
|
53
|
+
if (!DISTANCE_METRICS.has(metric)) {
|
|
54
|
+
throw new TypeError(
|
|
55
|
+
`Distance Metric must be one of: ${[...DISTANCE_METRICS].join(', ')}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return metric;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//-----------------------------------------------------------------------------
|
|
62
|
+
// Assertions
|
|
63
|
+
//-----------------------------------------------------------------------------
|
|
64
|
+
function isPlainObject(value) {
|
|
65
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Accepts only ordinary objects, reject class instances and built-in objects
|
|
70
|
+
// such as Date, Map, and Set
|
|
71
|
+
const prototype = Object.getPrototypeOf(value);
|
|
72
|
+
return prototype === Object.prototype || prototype === null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function assertCondition(condition, message) {
|
|
76
|
+
if (!condition) {
|
|
77
|
+
throw new TypeError(message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function assertPlainObject(value, name) {
|
|
82
|
+
assertCondition(
|
|
83
|
+
isPlainObject(value),
|
|
84
|
+
`${name} must be an object.`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function assertOptionalPlainObject(value, name) {
|
|
89
|
+
if (value != null) {
|
|
90
|
+
assertPlainObject(value, name);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function assertBoolean(value, name) {
|
|
95
|
+
assertCondition(
|
|
96
|
+
typeof value === 'boolean',
|
|
97
|
+
`${name} must be a boolean.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function assertOptionalBoolean(value, name) {
|
|
102
|
+
if (value !== undefined) {
|
|
103
|
+
assertBoolean(value, name);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function assertNonEmptyString(value, name) {
|
|
108
|
+
assertCondition(
|
|
109
|
+
typeof value === 'string' && value.trim() !== '',
|
|
110
|
+
`${name} must be a non-empty string.`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function assertPositiveInteger(value, name) {
|
|
115
|
+
assertCondition(
|
|
116
|
+
Number.isInteger(value) && value > 0,
|
|
117
|
+
`${name} must be a positive integer.`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function assertIntegerInRange(value, name, min, max) {
|
|
122
|
+
assertCondition(
|
|
123
|
+
Number.isInteger(value) && value >= min && value <= max,
|
|
124
|
+
`${name} must be an integer between ${min} and ${max}.`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function assertArray(value, name) {
|
|
129
|
+
assertCondition(
|
|
130
|
+
Array.isArray(value),
|
|
131
|
+
`${name} must be an array.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function assertNonEmptyArray(value, name) {
|
|
136
|
+
assertCondition(
|
|
137
|
+
Array.isArray(value) && value.length > 0,
|
|
138
|
+
`${name} must be a non-empty array.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function assertOptionalArray(value, name) {
|
|
143
|
+
if (value != null) {
|
|
144
|
+
assertArray(value, name);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
//-----------------------------------------------------------------------------
|
|
149
|
+
// Database helpers
|
|
150
|
+
//-----------------------------------------------------------------------------
|
|
151
|
+
async function readLobAsString(value) {
|
|
152
|
+
// CLOB output may already be materialized or may expose the Lob getData API.
|
|
153
|
+
if (value == null || typeof value === 'string') return value;
|
|
154
|
+
if (typeof value.getData !== 'function') {
|
|
155
|
+
throw new Error(
|
|
156
|
+
'Expected a CLOB LOB or string value from Oracle Database.'
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
return await value.getData();
|
|
161
|
+
} finally {
|
|
162
|
+
value.destroy();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function assertDatabaseVersion(connection, label) {
|
|
167
|
+
// oracleServerVersion encodes 23.26.2.0.0 as 2326020000.
|
|
168
|
+
if (connection.oracleServerVersion < 2326020000) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`${label} requires Oracle Database 23.26.2.0.0 or later.`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
//-----------------------------------------------------------------------------
|
|
176
|
+
// Vector preparation
|
|
177
|
+
//-----------------------------------------------------------------------------
|
|
178
|
+
function isVectorValue(value) {
|
|
179
|
+
return (value instanceof Float32Array ||
|
|
180
|
+
value instanceof Float64Array ||
|
|
181
|
+
value instanceof Int8Array || (Object.getPrototypeOf(value)
|
|
182
|
+
=== Uint8Array.prototype) || value instanceof oracledb.SparseVector);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function assertDimensions(vector, definition) {
|
|
186
|
+
const dimensions = vector instanceof oracledb.SparseVector
|
|
187
|
+
? vector.numDimensions
|
|
188
|
+
: vector.length;
|
|
189
|
+
|
|
190
|
+
if (dimensions !== definition.dimensions) {
|
|
191
|
+
throw new TypeError(
|
|
192
|
+
`Vector dimensions must be ${definition.dimensions}; ` +
|
|
193
|
+
`received ${dimensions}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function convertInt8(values) {
|
|
199
|
+
const converted = new Int8Array(values.length);
|
|
200
|
+
|
|
201
|
+
for (let index = 0; index < values.length; index++) {
|
|
202
|
+
const value = values[index];
|
|
203
|
+
if (!Number.isFinite(value)) {
|
|
204
|
+
throw new TypeError(
|
|
205
|
+
`Vector value at index ${index} must be finite.`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const rounded = Math.round(value);
|
|
210
|
+
if (rounded < -128 || rounded > 127) {
|
|
211
|
+
throw new TypeError(
|
|
212
|
+
`INT8 vector value at index ${index} must be within [-128, 127].`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
converted[index] = rounded;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return converted;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function packBinaryVector(values, dimensions) {
|
|
222
|
+
if (values.length !== dimensions) {
|
|
223
|
+
throw new TypeError(
|
|
224
|
+
`Vector dimensions must be ${dimensions}; received ${values.length}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const packed = new Uint8Array(dimensions / 8);
|
|
229
|
+
|
|
230
|
+
for (let index = 0; index < dimensions; index++) {
|
|
231
|
+
const value = values[index];
|
|
232
|
+
|
|
233
|
+
if (!Number.isFinite(value)) {
|
|
234
|
+
throw new TypeError(
|
|
235
|
+
`Vector value at index ${index} must be finite.`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Pack positive dimensions as one, from the most significant bit to the
|
|
240
|
+
// least significant bit within each byte.
|
|
241
|
+
if (value > 0) {
|
|
242
|
+
packed[index >> 3] |= 1 << (7 - (index & 7));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return packed;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function convertValuesForFormat(values, format) {
|
|
250
|
+
switch (format) {
|
|
251
|
+
case 'FLOAT32':
|
|
252
|
+
return values instanceof Float32Array
|
|
253
|
+
? values
|
|
254
|
+
: new Float32Array(values);
|
|
255
|
+
// We use a consistent default for wildcard (*) formats. Otherwise, similarity
|
|
256
|
+
// search fails when the stored and query vector formats do not match.
|
|
257
|
+
case '*':
|
|
258
|
+
case 'FLOAT64':
|
|
259
|
+
return values instanceof Float64Array
|
|
260
|
+
? values
|
|
261
|
+
: new Float64Array(values);
|
|
262
|
+
case 'INT8':
|
|
263
|
+
return values instanceof Int8Array
|
|
264
|
+
? values
|
|
265
|
+
: convertInt8(values);
|
|
266
|
+
default:
|
|
267
|
+
throw new TypeError(
|
|
268
|
+
`Unsupported vector format: ${format}.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function convertDenseVector(values, definition) {
|
|
274
|
+
if (definition.storageFormat === 'BINARY') {
|
|
275
|
+
if (values instanceof Uint8Array) {
|
|
276
|
+
if (values.length !== definition.dimensions / 8) {
|
|
277
|
+
throw new TypeError(
|
|
278
|
+
`BINARY vector must contain ${definition.dimensions / 8} bytes.`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
return values;
|
|
282
|
+
}
|
|
283
|
+
return packBinaryVector(values, definition.dimensions);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
assertDimensions(values, definition);
|
|
287
|
+
return convertValuesForFormat(values, definition.storageFormat);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function convertDenseToSparseVector(vector, format) {
|
|
291
|
+
// Convert before identifying nonzero values. This matters for INT8 because
|
|
292
|
+
// values may become zero after rounding.
|
|
293
|
+
const converted = convertValuesForFormat(vector, format);
|
|
294
|
+
|
|
295
|
+
const indices = [];
|
|
296
|
+
const values = [];
|
|
297
|
+
|
|
298
|
+
for (let i = 0; i < converted.length; i++) {
|
|
299
|
+
if (converted[i] !== 0) {
|
|
300
|
+
indices.push(i);
|
|
301
|
+
values.push(converted[i]);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return new oracledb.SparseVector({
|
|
306
|
+
numDimensions: vector.length,
|
|
307
|
+
indices: new Uint32Array(indices),
|
|
308
|
+
values: new converted.constructor(values),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function prepareVector(vector, vectorDefinition) {
|
|
313
|
+
|
|
314
|
+
if (vector == null) {
|
|
315
|
+
throw new TypeError('Vector must not be null or undefined.');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!Array.isArray(vector) && !isVectorValue(vector)) {
|
|
319
|
+
throw new TypeError('Unsupported vector representation.');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const definition = vectorDefinition;
|
|
323
|
+
|
|
324
|
+
if (definition.storageType === 'SPARSE') {
|
|
325
|
+
if (vector instanceof oracledb.SparseVector) {
|
|
326
|
+
assertDimensions(vector, definition);
|
|
327
|
+
return new oracledb.SparseVector({
|
|
328
|
+
numDimensions: vector.numDimensions,
|
|
329
|
+
indices: vector.indices,
|
|
330
|
+
values: convertValuesForFormat(
|
|
331
|
+
vector.values,
|
|
332
|
+
definition.storageFormat
|
|
333
|
+
)
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
assertDimensions(vector, definition);
|
|
338
|
+
// Convert a dense input when the configured column uses sparse storage.
|
|
339
|
+
return convertDenseToSparseVector(vector, definition.storageFormat);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (vector instanceof oracledb.SparseVector) {
|
|
343
|
+
throw new TypeError(
|
|
344
|
+
'A SparseVector cannot be used with DENSE storage.'
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return convertDenseVector(vector, definition);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
//-----------------------------------------------------------------------------
|
|
352
|
+
// Exports
|
|
353
|
+
//-----------------------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
module.exports = {
|
|
356
|
+
normalizeVectorDistanceType,
|
|
357
|
+
prepareVector,
|
|
358
|
+
assertDatabaseVersion,
|
|
359
|
+
readLobAsString,
|
|
360
|
+
assertBoolean,
|
|
361
|
+
assertIntegerInRange,
|
|
362
|
+
assertNonEmptyArray,
|
|
363
|
+
assertNonEmptyString,
|
|
364
|
+
assertOptionalArray,
|
|
365
|
+
assertOptionalBoolean,
|
|
366
|
+
assertOptionalPlainObject,
|
|
367
|
+
assertPlainObject,
|
|
368
|
+
assertPositiveInteger,
|
|
369
|
+
isPlainObject
|
|
370
|
+
};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// Copyright (c) 2026, 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
|
+
|
|
29
|
+
const oracledb = require('oracledb');
|
|
30
|
+
const { normalizeIdentifier } = require('./vectorSchema.js');
|
|
31
|
+
const {
|
|
32
|
+
assertBoolean,
|
|
33
|
+
assertNonEmptyString,
|
|
34
|
+
assertOptionalPlainObject,
|
|
35
|
+
assertPlainObject,
|
|
36
|
+
readLobAsString,
|
|
37
|
+
assertDatabaseVersion,
|
|
38
|
+
assertNonEmptyArray
|
|
39
|
+
} = require('./utils.js');
|
|
40
|
+
|
|
41
|
+
//-----------------------------------------------------------------------------
|
|
42
|
+
// generateEmbeddings()
|
|
43
|
+
|
|
44
|
+
// Generate a vector embedding for input text using a preloaded model.
|
|
45
|
+
//-----------------------------------------------------------------------------
|
|
46
|
+
async function generateEmbeddings(connection, modelParams, texts) {
|
|
47
|
+
assertNonEmptyArray(texts, 'texts');
|
|
48
|
+
assertPlainObject(modelParams, 'modelParams');
|
|
49
|
+
|
|
50
|
+
const embeddingSql = `
|
|
51
|
+
DECLARE
|
|
52
|
+
generated_embedding VECTOR;
|
|
53
|
+
BEGIN
|
|
54
|
+
generated_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
|
|
55
|
+
:query_text,
|
|
56
|
+
:model_params
|
|
57
|
+
);
|
|
58
|
+
:embedding := generated_embedding;
|
|
59
|
+
END;
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const result = await connection.executeMany(
|
|
63
|
+
embeddingSql,
|
|
64
|
+
texts.map((text, index) => {
|
|
65
|
+
assertNonEmptyString(text, `texts[${index}]`);
|
|
66
|
+
return {
|
|
67
|
+
query_text: text,
|
|
68
|
+
model_params: modelParams
|
|
69
|
+
};
|
|
70
|
+
}),
|
|
71
|
+
{
|
|
72
|
+
bindDefs: {
|
|
73
|
+
query_text: {
|
|
74
|
+
type: oracledb.DB_TYPE_CLOB
|
|
75
|
+
},
|
|
76
|
+
model_params: {
|
|
77
|
+
type: oracledb.DB_TYPE_JSON
|
|
78
|
+
},
|
|
79
|
+
embedding: {
|
|
80
|
+
dir: oracledb.BIND_OUT,
|
|
81
|
+
type: oracledb.DB_TYPE_VECTOR
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return result.outBinds.map((binds) => binds.embedding);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//-----------------------------------------------------------------------------
|
|
91
|
+
// loadModel()
|
|
92
|
+
|
|
93
|
+
// Load an embedding model into Oracle AI Database.
|
|
94
|
+
//-----------------------------------------------------------------------------
|
|
95
|
+
async function loadModel(connection, options) {
|
|
96
|
+
assertPlainObject(options, 'options');
|
|
97
|
+
|
|
98
|
+
const {
|
|
99
|
+
dirName,
|
|
100
|
+
modelFile,
|
|
101
|
+
modelName,
|
|
102
|
+
modelMetadata = null,
|
|
103
|
+
} = options;
|
|
104
|
+
|
|
105
|
+
assertNonEmptyString(modelFile, 'options.modelFile');
|
|
106
|
+
assertOptionalPlainObject(
|
|
107
|
+
modelMetadata,
|
|
108
|
+
'options.modelMetadata'
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const binds = {
|
|
112
|
+
dir_name: normalizeIdentifier(dirName, 'options.dirName'),
|
|
113
|
+
model_file: modelFile,
|
|
114
|
+
model_name: normalizeIdentifier(modelName, 'options.modelName')
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const plsqlParams = [
|
|
118
|
+
'directory => :dir_name',
|
|
119
|
+
'file_name => :model_file',
|
|
120
|
+
'model_name => :model_name'
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
if (modelMetadata != null) {
|
|
124
|
+
binds.model_metadata = {
|
|
125
|
+
val: modelMetadata,
|
|
126
|
+
type: oracledb.DB_TYPE_JSON
|
|
127
|
+
};
|
|
128
|
+
plsqlParams.push('metadata => :model_metadata');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const plsql = `
|
|
132
|
+
BEGIN
|
|
133
|
+
DBMS_VECTOR.LOAD_ONNX_MODEL(
|
|
134
|
+
${plsqlParams.join(', ')}
|
|
135
|
+
);
|
|
136
|
+
END;
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
await connection.execute(plsql, binds);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//-----------------------------------------------------------------------------
|
|
143
|
+
// describeModel()
|
|
144
|
+
|
|
145
|
+
// Return metadata for a loaded model, including its algorithm, mining function,
|
|
146
|
+
// creation date, and attributes.
|
|
147
|
+
//-----------------------------------------------------------------------------
|
|
148
|
+
async function describeModel(connection, modelName) {
|
|
149
|
+
// requires Oracle Database 23.26.2.0.0 or later
|
|
150
|
+
assertDatabaseVersion(connection, 'describeModel()');
|
|
151
|
+
|
|
152
|
+
const binds = {
|
|
153
|
+
model_name: normalizeIdentifier(modelName, 'modelName'),
|
|
154
|
+
result: {
|
|
155
|
+
dir: oracledb.BIND_OUT,
|
|
156
|
+
type: oracledb.STRING,
|
|
157
|
+
maxSize: 1024 * 1024
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const plsql = `
|
|
162
|
+
BEGIN
|
|
163
|
+
:result := DBMS_VECTOR_DATABASE.DESCRIBE_MODEL(MODEL_NAME => :model_name);
|
|
164
|
+
END;`;
|
|
165
|
+
|
|
166
|
+
const res = await connection.execute(plsql, binds);
|
|
167
|
+
return JSON.parse(res.outBinds.result);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//-----------------------------------------------------------------------------
|
|
171
|
+
// listModels()
|
|
172
|
+
|
|
173
|
+
// Return the models available in the current schema.
|
|
174
|
+
//-----------------------------------------------------------------------------
|
|
175
|
+
async function listModels(connection) {
|
|
176
|
+
// requires Oracle Database 23.26.2.0.0 or later
|
|
177
|
+
assertDatabaseVersion(connection, 'listModels()');
|
|
178
|
+
|
|
179
|
+
const binds = {
|
|
180
|
+
result: {
|
|
181
|
+
dir: oracledb.BIND_OUT,
|
|
182
|
+
type: oracledb.DB_TYPE_CLOB
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const plsql = `
|
|
187
|
+
BEGIN
|
|
188
|
+
:result := DBMS_VECTOR_DATABASE.LIST_MODELS();
|
|
189
|
+
END;`;
|
|
190
|
+
|
|
191
|
+
const res = await connection.execute(plsql, binds);
|
|
192
|
+
const output = await readLobAsString(res.outBinds.result);
|
|
193
|
+
return JSON.parse(output);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//-----------------------------------------------------------------------------
|
|
197
|
+
// dropModel()
|
|
198
|
+
|
|
199
|
+
// Remove an embedding model from Oracle AI Database.
|
|
200
|
+
//-----------------------------------------------------------------------------
|
|
201
|
+
async function dropModel(connection, modelName, force = false) {
|
|
202
|
+
assertBoolean(force, 'force');
|
|
203
|
+
|
|
204
|
+
const safeModelName = normalizeIdentifier(modelName, 'modelName');
|
|
205
|
+
const dropModelSql = `
|
|
206
|
+
BEGIN
|
|
207
|
+
DBMS_VECTOR.DROP_ONNX_MODEL(
|
|
208
|
+
model_name => :model_name,
|
|
209
|
+
force => ${force ? 'TRUE' : 'FALSE'}
|
|
210
|
+
);
|
|
211
|
+
END;
|
|
212
|
+
`;
|
|
213
|
+
|
|
214
|
+
await connection.execute(dropModelSql, { model_name: safeModelName });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = {
|
|
218
|
+
generateEmbeddings,
|
|
219
|
+
loadModel,
|
|
220
|
+
describeModel,
|
|
221
|
+
listModels,
|
|
222
|
+
dropModel
|
|
223
|
+
};
|