UNPKG

9.05 kBJavaScriptView Raw
1// Copyright (c) 2023, 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 BaseDbObject = require('./dbObject.js');
30const { Buffer } = require('buffer');
31const Lob = require('./lob.js');
32const ResultSet = require('./resultset.js');
33const constants = require('./constants.js');
34const errors = require('./errors.js');
35const util = require('util');
36const types = require('./types.js');
37const nodbUtil = require('./util.js');
38
39//-----------------------------------------------------------------------------
40// checkType()
41//
42// Checks that the type of the data matches one of the given types. If the type
43// has not been specified yet, the first type is assumed to be the correct one.
44//
45// A failure to match results in an exception being thrown. The data in the
46// info parameter is used to determine which error should be thrown.
47//-----------------------------------------------------------------------------
48function checkType(info, options) {
49 if (info.type === undefined && arguments.length > 2) {
50 info.type = arguments[2];
51 } else {
52 let matches = false;
53 for (let i = 2; i < arguments.length; i++) {
54 if (info.type === arguments[i]) {
55 matches = true;
56 break;
57 }
58 }
59 if (!matches) {
60 if (info.attrName) {
61 errors.throwErr(errors.ERR_WRONG_VALUE_FOR_DBOBJECT_ATTR,
62 info.attrName, info.fqn);
63 } else if (info.fqn) {
64 errors.throwErr(errors.ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM, info.fqn);
65 } else if (info.isArray && info.name) {
66 errors.throwErr(errors.ERR_INCOMPATIBLE_TYPE_ARRAY_BIND, options.pos,
67 info.name);
68 } else if (info.isArray) {
69 errors.throwErr(errors.ERR_INCOMPATIBLE_TYPE_ARRAY_INDEX_BIND,
70 options.pos, info.pos);
71 } else {
72 errors.throwErr(errors.ERR_BIND_VALUE_AND_TYPE_MISMATCH);
73 }
74 }
75 }
76}
77
78//-----------------------------------------------------------------------------
79// transformJsonValue()
80//
81// Returns a normalized JSON value. Scalar values are returned unchanged.
82// Arrays are returned as a new array with transformed JSON values. Objects are
83// returned as new objects with keys "fields" and "values", both of which
84// are arrays (with the value transformed to JSON values).
85//-----------------------------------------------------------------------------
86function transformJsonValue(value) {
87
88 // handle simple scalars
89 if (value === undefined || value === null ||
90 typeof value === 'number' || typeof value === 'string' ||
91 typeof value === 'boolean' || Buffer.isBuffer(value) ||
92 util.isDate(value) || nodbUtil.isVectorValue(value))
93 return value;
94
95 // arrays are transformed to a new array with processed values
96 if (Array.isArray(value)) {
97 const outValue = new Array(value.length);
98 for (let i = 0; i < value.length; i++) {
99 outValue[i] = transformJsonValue(value[i]);
100 }
101 return outValue;
102 }
103
104 // database objects are treated as empty objects
105 if (value instanceof BaseDbObject)
106 return {fields: [], values: []};
107
108 // all other objects are transformed to an object with two arrays (fields
109 // and values)
110 const outValue = {};
111 outValue.fields = Object.getOwnPropertyNames(value);
112 outValue.values = new Array(outValue.fields.length);
113 for (let i = 0; i < outValue.fields.length; i++) {
114 outValue.values[i] = transformJsonValue(value[outValue.fields[i]]);
115 }
116 return outValue;
117
118}
119
120//-----------------------------------------------------------------------------
121// transformValueIn()
122//
123// Processes the value supplied by the caller and returns a normalized value,
124// if necessary, for use by the implementation. All checks are performed on the
125// value to ensure it is suitable for the type information supplied. If no type
126// information is supplied, however, the value defines it instead!
127//-----------------------------------------------------------------------------
128function transformValueIn(info, value, options) {
129
130 // null and undefined can always be set so nothing needs to be done
131 if (value === undefined || value === null)
132 return undefined;
133
134 // handle setting plain JS values to database objects
135 if (info.type === types.DB_TYPE_OBJECT) {
136 let obj = value;
137 if (!(value instanceof BaseDbObject)) {
138 obj = new info.typeClass(value);
139 }
140 return obj._impl;
141
142 // handle setting plain JS values to JSON
143 } else if (info.type === types.DB_TYPE_JSON) {
144 return transformJsonValue(value);
145
146 // handle strings
147 } else if (typeof value === 'string') {
148 checkType(info, options,
149 types.DB_TYPE_VARCHAR,
150 types.DB_TYPE_NVARCHAR,
151 types.DB_TYPE_CHAR,
152 types.DB_TYPE_NCHAR,
153 types.DB_TYPE_CLOB,
154 types.DB_TYPE_NCLOB);
155 if (info.type !== types.DB_TYPE_CLOB &&
156 info.type !== types.DB_TYPE_NCLOB) {
157 const valueLen = Buffer.byteLength(value);
158 if (info.maxSize === undefined || valueLen > info.maxSize) {
159 if (info.checkSize) {
160 errors.throwErr(errors.ERR_MAX_SIZE_TOO_SMALL, info.maxSize,
161 valueLen, options.pos);
162 }
163 info.maxSize = valueLen;
164 }
165 }
166 return value;
167
168 // handle numbers
169 } else if (typeof value === 'number') {
170 checkType(info, options,
171 types.DB_TYPE_NUMBER,
172 types.DB_TYPE_BINARY_INTEGER,
173 types.DB_TYPE_BINARY_FLOAT,
174 types.DB_TYPE_BINARY_DOUBLE);
175 if (Number.isNaN(value) && info.type === types.DB_TYPE_NUMBER) {
176 errors.throwErr(errors.ERR_NAN_VALUE);
177 }
178 return value;
179
180 // handle booleans
181 } else if (typeof value === 'boolean') {
182 checkType(info, options, types.DB_TYPE_BOOLEAN);
183 return value;
184
185 // handle dates
186 } else if (util.isDate(value)) {
187 checkType(info, options,
188 types.DB_TYPE_TIMESTAMP,
189 types.DB_TYPE_TIMESTAMP_TZ,
190 types.DB_TYPE_TIMESTAMP_LTZ,
191 types.DB_TYPE_DATE);
192 return value;
193
194 // handle binding buffers
195 } else if (Buffer.isBuffer(value)) {
196 checkType(info, options,
197 types.DB_TYPE_RAW,
198 types.DB_TYPE_BLOB);
199 if (info.type === types.DB_TYPE_RAW &&
200 (info.maxSize === undefined || value.length > info.maxSize)) {
201 if (info.checkSize) {
202 errors.throwErr(errors.ERR_MAX_SIZE_TOO_SMALL, info.maxSize,
203 value.length, options.pos);
204 }
205 info.maxSize = value.length;
206 }
207 return value;
208
209 // handle result sets
210 } else if (value instanceof ResultSet) {
211 checkType(info, options, types.DB_TYPE_CURSOR);
212 return value._impl;
213
214 // handle binding LOBs
215 } else if (value instanceof Lob) {
216 checkType(info, options, value.type);
217 return value._impl;
218
219 // handle database objects
220 } else if (value instanceof BaseDbObject) {
221 checkType(info, options, types.DB_TYPE_OBJECT);
222 return value._impl;
223
224 // handle vectors
225 } else if (value instanceof Float32Array || value instanceof Float64Array ||
226 value instanceof Int8Array) {
227 checkType(info, options, types.DB_TYPE_VECTOR);
228 return value;
229 } else if (info.type === types.DB_TYPE_VECTOR && Array.isArray(value)) {
230 return new Float64Array(value);
231
232 // handle arrays
233 } else if (options.allowArray && Array.isArray(value)) {
234 info.isArray = true;
235 if (info.dir === constants.BIND_IN) {
236 info.maxArraySize = value.length || 1;
237 } else if (info.maxArraySize === undefined) {
238 errors.throwErr(errors.ERR_REQUIRED_MAX_ARRAY_SIZE);
239 } else if (value.length > info.maxArraySize) {
240 errors.throwErr(errors.ERR_INVALID_ARRAY_SIZE);
241 }
242 options.allowArray = false;
243 const transformed = new Array(value.length);
244 for (let i = 0; i < value.length; i++) {
245 options.pos = i;
246 transformed[i] = transformValueIn(info, value[i], options);
247 }
248 return transformed;
249 }
250
251 // no suitable bind value found
252 if (info.type === undefined)
253 errors.throwErr(errors.ERR_INVALID_BIND_DATA_TYPE, 2);
254 checkType(info, options);
255
256}
257
258// define exports
259module.exports = {
260 transformJsonValue,
261 transformValueIn
262
263};