pg-mvc-service 2.0.4 → 2.0.6
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/dist/models/SqlUtils/ValidateValueUtil.js +46 -0
- package/dist/models/SqlUtils/WhereExpression.js +173 -32
- package/index.d.ts +0 -1
- package/package.json +1 -1
- package/src/models/SqlUtils/ValidateValueUtil.ts +52 -0
- package/src/models/SqlUtils/WhereExpression.ts +175 -33
- package/src/models/Type.ts +1 -1
|
@@ -198,6 +198,52 @@ class ValidateValueUtil {
|
|
|
198
198
|
}
|
|
199
199
|
return true;
|
|
200
200
|
}
|
|
201
|
+
static isErrorInteger(value) {
|
|
202
|
+
let numberValue;
|
|
203
|
+
if (typeof value === 'string') {
|
|
204
|
+
if (value.trim() === "" || isNaN(Number(value))) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
numberValue = Number(value);
|
|
208
|
+
}
|
|
209
|
+
else if (typeof value === 'number') {
|
|
210
|
+
numberValue = value;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
if (Number.isInteger(numberValue) === false) {
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
if (numberValue < -2147483648 || numberValue > 2147483647) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
static isErrorReal(value) {
|
|
224
|
+
let numberValue;
|
|
225
|
+
if (typeof value === 'string') {
|
|
226
|
+
if (value.trim() === "" || isNaN(Number(value))) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
numberValue = Number(value);
|
|
230
|
+
}
|
|
231
|
+
else if (typeof value === 'number') {
|
|
232
|
+
numberValue = value;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
// 特殊値チェック
|
|
238
|
+
if (isFinite(numberValue) === false) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
// 範囲チェック(real型の範囲)
|
|
242
|
+
if (numberValue < -3.4e38 || numberValue > 3.4e38) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
201
247
|
static isErrorNumber(value) {
|
|
202
248
|
if (typeof value === 'string') {
|
|
203
249
|
if (value.trim() === "" || isNaN(Number(value))) {
|
|
@@ -83,21 +83,21 @@ class WhereExpression {
|
|
|
83
83
|
// Are the operators correct?
|
|
84
84
|
const useableOperator = {
|
|
85
85
|
integer: ["=", "!=", ">", ">=", "<", "<=", "in", "not in"],
|
|
86
|
-
'integer[]': [],
|
|
86
|
+
'integer[]': ["=", "any", "@>", "&&"],
|
|
87
87
|
real: ["=", "!=", ">", ">=", "<", "<="],
|
|
88
|
-
'real[]': [],
|
|
88
|
+
'real[]': ["=", "any", "@>", "&&"],
|
|
89
89
|
string: ["=", "!=", "like", "ilike", "h2f_like", "h2f_ilike", "in", "not in"],
|
|
90
|
-
'string[]': [],
|
|
90
|
+
'string[]': ["=", "any", "@>", "&&"],
|
|
91
91
|
uuid: ["=", "!=", "in", "not in"],
|
|
92
|
-
'uuid[]': [],
|
|
92
|
+
'uuid[]': ["=", "any", "@>", "&&"],
|
|
93
93
|
bool: ["=", "!=", "in", "not in"],
|
|
94
|
-
'bool[]': [],
|
|
94
|
+
'bool[]': ["=", "any", "@>", "&&"],
|
|
95
95
|
date: ["=", "!=", ">", ">=", "<", "<="],
|
|
96
|
-
'date[]': [],
|
|
96
|
+
'date[]': ["=", "any", "@>", "&&"],
|
|
97
97
|
time: ["=", "!=", ">", ">=", "<", "<="],
|
|
98
|
-
'time[]': [],
|
|
98
|
+
'time[]': ["=", "any", "@>", "&&"],
|
|
99
99
|
timestamp: ["=", "!=", ">", ">=", "<", "<="],
|
|
100
|
-
'timestamp[]': [],
|
|
100
|
+
'timestamp[]': ["=", "any", "@>", "&&"],
|
|
101
101
|
json: [],
|
|
102
102
|
'json[]': [],
|
|
103
103
|
jsonb: [],
|
|
@@ -124,8 +124,15 @@ class WhereExpression {
|
|
|
124
124
|
throw new Error(`When comparing with null, operators other than =, != cannot be used. (${operator})`);
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
const isColumnRight = right !== null && typeof right === 'object' && 'model' in right && 'name' in right;
|
|
128
|
+
const isArrayColumnLeft = leftColumn.type.endsWith("[]");
|
|
129
|
+
if (isArrayColumnLeft) {
|
|
130
|
+
if (isColumnRight) {
|
|
131
|
+
return this.createExpressionArrayColumn(leftColumn, operator, right, varLength);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
return this.createExpressionArrayValue(leftColumn, operator, right, varLength);
|
|
135
|
+
}
|
|
129
136
|
}
|
|
130
137
|
else {
|
|
131
138
|
return this.createExpression(leftColumn, operator, right, varLength);
|
|
@@ -156,8 +163,9 @@ class WhereExpression {
|
|
|
156
163
|
// If the right side value is a column specification
|
|
157
164
|
if (right !== null && typeof right === 'object' && 'model' in right && 'name' in right) {
|
|
158
165
|
const rightColumn = right.model.getColumn(right.name);
|
|
166
|
+
// 型の不一致エラーメッセージを改善
|
|
159
167
|
if (leftColumn.type !== rightColumn.type) {
|
|
160
|
-
throw new Error(`
|
|
168
|
+
throw new Error(`Type mismatch: column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
|
|
161
169
|
}
|
|
162
170
|
// LIKE operators are different, so handle separately
|
|
163
171
|
switch (operator) {
|
|
@@ -198,34 +206,167 @@ class WhereExpression {
|
|
|
198
206
|
};
|
|
199
207
|
}
|
|
200
208
|
static createExpressionArrayValue(leftColumn, operator, right, varLength) {
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
209
|
+
// バリデーションチェック
|
|
210
|
+
switch (operator) {
|
|
211
|
+
case 'any':
|
|
212
|
+
switch (leftColumn.type) {
|
|
213
|
+
case 'integer[]':
|
|
214
|
+
if (ValidateValueUtil_1.default.isErrorInteger(right)) {
|
|
215
|
+
throw new Error(`Expected integer value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case 'real[]':
|
|
219
|
+
if (ValidateValueUtil_1.default.isErrorReal(right)) {
|
|
220
|
+
throw new Error(`Expected numeric value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
case 'string[]':
|
|
224
|
+
if (ValidateValueUtil_1.default.isErrorString(right)) {
|
|
225
|
+
throw new Error(`Expected string value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
case 'uuid[]':
|
|
229
|
+
if (ValidateValueUtil_1.default.isErrorUUID(right)) {
|
|
230
|
+
throw new Error(`Expected UUID value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
case 'bool[]':
|
|
234
|
+
if (ValidateValueUtil_1.default.isErrorBool(right)) {
|
|
235
|
+
throw new Error(`Expected boolean value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case 'date[]':
|
|
239
|
+
if (ValidateValueUtil_1.default.isErrorDate(right)) {
|
|
240
|
+
throw new Error(`Expected date value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
case 'time[]':
|
|
244
|
+
if (ValidateValueUtil_1.default.isErrorTime(right)) {
|
|
245
|
+
throw new Error(`Expected time value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
case 'timestamp[]':
|
|
249
|
+
if (ValidateValueUtil_1.default.isErrorTimestamp(right)) {
|
|
250
|
+
throw new Error(`Expected timestamp value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
case '=':
|
|
256
|
+
case '@>':
|
|
257
|
+
case '&&':
|
|
258
|
+
if (Array.isArray(right) === false) {
|
|
259
|
+
throw new Error(`Expected array format for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
260
|
+
}
|
|
261
|
+
for (const value of right) {
|
|
262
|
+
switch (leftColumn.type) {
|
|
263
|
+
case 'integer[]':
|
|
264
|
+
if (ValidateValueUtil_1.default.isErrorInteger(value)) {
|
|
265
|
+
throw new Error(`Expected integer value in array element, but received: ${JSON.stringify(value)}`);
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
case 'real[]':
|
|
269
|
+
if (ValidateValueUtil_1.default.isErrorReal(value)) {
|
|
270
|
+
throw new Error(`Expected numeric value in array element, but received: ${JSON.stringify(value)}`);
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
case 'string[]':
|
|
274
|
+
if (ValidateValueUtil_1.default.isErrorString(value)) {
|
|
275
|
+
throw new Error(`Expected string value in array element, but received: ${JSON.stringify(value)}`);
|
|
276
|
+
}
|
|
277
|
+
break;
|
|
278
|
+
case 'uuid[]':
|
|
279
|
+
if (ValidateValueUtil_1.default.isErrorUUID(value)) {
|
|
280
|
+
throw new Error(`Expected UUID value in array element, but received: ${JSON.stringify(value)}`);
|
|
281
|
+
}
|
|
282
|
+
break;
|
|
283
|
+
case 'bool[]':
|
|
284
|
+
if (ValidateValueUtil_1.default.isErrorBool(value)) {
|
|
285
|
+
throw new Error(`Expected boolean value in array element, but received: ${JSON.stringify(value)}`);
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
case 'date[]':
|
|
289
|
+
if (ValidateValueUtil_1.default.isErrorDate(value)) {
|
|
290
|
+
throw new Error(`Expected date value in array element, but received: ${JSON.stringify(value)}`);
|
|
291
|
+
}
|
|
292
|
+
break;
|
|
293
|
+
case 'time[]':
|
|
294
|
+
if (ValidateValueUtil_1.default.isErrorTime(value)) {
|
|
295
|
+
throw new Error(`Expected time value in array element, but received: ${JSON.stringify(value)}`);
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
298
|
+
case 'timestamp[]':
|
|
299
|
+
if (ValidateValueUtil_1.default.isErrorTimestamp(value)) {
|
|
300
|
+
throw new Error(`Expected timestamp value in array element, but received: ${JSON.stringify(value)}`);
|
|
301
|
+
}
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
break;
|
|
306
|
+
default:
|
|
307
|
+
throw new Error(`Unsupported operator '${operator}' for array column operations.`);
|
|
308
|
+
}
|
|
217
309
|
switch (operator) {
|
|
218
310
|
case '=':
|
|
219
|
-
//
|
|
311
|
+
// =で検索すると順番まで一致させないといけないので、順番不一致でも中身があってれば一致とするようにする
|
|
312
|
+
return {
|
|
313
|
+
expression: `(${leftColumn.expression} @> $${varLength} AND $${varLength} @> $${leftColumn.expression})`,
|
|
314
|
+
vars: [right]
|
|
315
|
+
};
|
|
316
|
+
case '@>':
|
|
317
|
+
case '&&':
|
|
220
318
|
return {
|
|
221
319
|
expression: `${leftColumn.expression} ${operator} $${varLength}`,
|
|
222
320
|
vars: [right]
|
|
223
321
|
};
|
|
322
|
+
case 'any':
|
|
323
|
+
return {
|
|
324
|
+
expression: `$${varLength} = ANY(${leftColumn.expression})`,
|
|
325
|
+
vars: [right]
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
static createExpressionArrayColumn(leftColumn, operator, right, varLength) {
|
|
330
|
+
const rightColumn = right.model.getColumn(right.name);
|
|
331
|
+
// バリデーションチェック
|
|
332
|
+
switch (operator) {
|
|
333
|
+
case 'any':
|
|
334
|
+
// any演算子の場合
|
|
335
|
+
if (leftColumn.type !== rightColumn.type.replace('[]', '')) {
|
|
336
|
+
throw new Error(`Type mismatch: array column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and scalar column [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) are incompatible for ANY operation.`);
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
case '=':
|
|
340
|
+
case '@>':
|
|
341
|
+
case '&&':
|
|
342
|
+
// 配列演算子の場合
|
|
343
|
+
if (leftColumn.type !== rightColumn.type) {
|
|
344
|
+
throw new Error(`Type mismatch: array columns [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
|
|
345
|
+
}
|
|
346
|
+
break;
|
|
347
|
+
default:
|
|
348
|
+
// サポートされていない演算子
|
|
349
|
+
throw new Error(`Operator '${operator}' is not supported for array column operations. Supported operators: =, @>, &&, ANY`);
|
|
350
|
+
}
|
|
351
|
+
switch (operator) {
|
|
352
|
+
case '=':
|
|
353
|
+
// =で検索すると順番まで一致させないといけないので、順番不一致でも中身があってれば一致とするようにする
|
|
354
|
+
return {
|
|
355
|
+
expression: `(${leftColumn.expression} @> $${rightColumn.expression} AND ${rightColumn.expression} @> $${leftColumn.expression})`,
|
|
356
|
+
vars: []
|
|
357
|
+
};
|
|
358
|
+
case '@>':
|
|
359
|
+
case '&&':
|
|
360
|
+
return {
|
|
361
|
+
expression: `${leftColumn.expression} ${operator} $${rightColumn.expression}`,
|
|
362
|
+
vars: []
|
|
363
|
+
};
|
|
364
|
+
case 'any':
|
|
365
|
+
return {
|
|
366
|
+
expression: `$${rightColumn.expression} = ANY(${leftColumn.expression})`,
|
|
367
|
+
vars: []
|
|
368
|
+
};
|
|
224
369
|
}
|
|
225
|
-
return {
|
|
226
|
-
expression: `${leftColumn.expression} ${operator} $${varLength}`,
|
|
227
|
-
vars: [right]
|
|
228
|
-
};
|
|
229
370
|
}
|
|
230
371
|
/**
|
|
231
372
|
* SQL statement to convert half-width characters to full-width
|
package/index.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ import ValidateClient from './src/models/ValidateClient';
|
|
|
20
20
|
import { TableModel } from "./src/models/TableModel";
|
|
21
21
|
export { TableModel } from "./src/models/TableModel";
|
|
22
22
|
|
|
23
|
-
import { TColumnAttribute, TColumnType, TColumnArrayType, TColumn, TColumnDetail, TOperator, TColumnInfo, TQuery, TSelectExpression, TAggregateFuncType, TCondition, TNestedCondition, TSortKeyword, TKeyFormat } from './src/models/Type';
|
|
24
23
|
export { TColumnAttribute, TColumnType, TColumnArrayType, TColumn, TColumnDetail, TOperator, TColumnInfo, TQuery, TSelectExpression, TAggregateFuncType, TCondition, TNestedCondition, TSortKeyword, TKeyFormat } from './src/models/Type';
|
|
25
24
|
|
|
26
25
|
declare module 'pg-mvc-service' {
|
package/package.json
CHANGED
|
@@ -216,6 +216,58 @@ export default class ValidateValueUtil {
|
|
|
216
216
|
return true;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
static isErrorInteger(value: any): boolean {
|
|
220
|
+
let numberValue: number;
|
|
221
|
+
if (typeof value === 'string') {
|
|
222
|
+
if (value.trim() === "" || isNaN(Number(value))) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
numberValue = Number(value);
|
|
227
|
+
} else if (typeof value === 'number') {
|
|
228
|
+
numberValue = value;
|
|
229
|
+
} else {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (Number.isInteger(numberValue) === false) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (numberValue < -2147483648 || numberValue > 2147483647) {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
static isErrorReal(value: any): boolean {
|
|
245
|
+
let numberValue: number;
|
|
246
|
+
if (typeof value === 'string') {
|
|
247
|
+
if (value.trim() === "" || isNaN(Number(value))) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
numberValue = Number(value);
|
|
252
|
+
} else if (typeof value === 'number') {
|
|
253
|
+
numberValue = value;
|
|
254
|
+
} else {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 特殊値チェック
|
|
259
|
+
if (isFinite(numberValue) === false) {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 範囲チェック(real型の範囲)
|
|
264
|
+
if (numberValue < -3.4e38 || numberValue > 3.4e38) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
|
|
219
271
|
static isErrorNumber(value: any): boolean {
|
|
220
272
|
if (typeof value === 'string') {
|
|
221
273
|
if (value.trim() === "" || isNaN(Number(value))) {
|
|
@@ -96,23 +96,23 @@ export default class WhereExpression {
|
|
|
96
96
|
const leftColumn = left.model.getColumn(left.name);
|
|
97
97
|
|
|
98
98
|
// Are the operators correct?
|
|
99
|
-
const useableOperator: { [key in TColumnType | TColumnArrayType]:
|
|
99
|
+
const useableOperator: { [key in TColumnType | TColumnArrayType]: TOperator[] } = {
|
|
100
100
|
integer: ["=", "!=", ">", ">=", "<", "<=", "in", "not in"],
|
|
101
|
-
'integer[]': [],
|
|
101
|
+
'integer[]': ["=", "any", "@>", "&&"],
|
|
102
102
|
real: ["=", "!=", ">", ">=", "<", "<="],
|
|
103
|
-
'real[]': [],
|
|
103
|
+
'real[]': ["=", "any", "@>", "&&"],
|
|
104
104
|
string: ["=", "!=", "like", "ilike", "h2f_like", "h2f_ilike", "in", "not in"],
|
|
105
|
-
'string[]': [],
|
|
105
|
+
'string[]': ["=", "any", "@>", "&&"],
|
|
106
106
|
uuid: ["=", "!=", "in", "not in"],
|
|
107
|
-
'uuid[]': [],
|
|
107
|
+
'uuid[]': ["=", "any", "@>", "&&"],
|
|
108
108
|
bool: ["=", "!=", "in", "not in"],
|
|
109
|
-
'bool[]': [],
|
|
109
|
+
'bool[]': ["=", "any", "@>", "&&"],
|
|
110
110
|
date: ["=", "!=", ">", ">=", "<", "<="],
|
|
111
|
-
'date[]': [],
|
|
111
|
+
'date[]': ["=", "any", "@>", "&&"],
|
|
112
112
|
time: ["=", "!=", ">", ">=", "<", "<="],
|
|
113
|
-
'time[]': [],
|
|
113
|
+
'time[]': ["=", "any", "@>", "&&"],
|
|
114
114
|
timestamp: ["=", "!=", ">", ">=", "<", "<="],
|
|
115
|
-
'timestamp[]': [],
|
|
115
|
+
'timestamp[]': ["=", "any", "@>", "&&"],
|
|
116
116
|
json: [],
|
|
117
117
|
'json[]': [],
|
|
118
118
|
jsonb: [],
|
|
@@ -141,8 +141,14 @@ export default class WhereExpression {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
const isColumnRight = right !== null && typeof right === 'object' && 'model' in right && 'name' in right;
|
|
145
|
+
const isArrayColumnLeft = leftColumn.type.endsWith("[]");
|
|
146
|
+
if (isArrayColumnLeft) {
|
|
147
|
+
if (isColumnRight) {
|
|
148
|
+
return this.createExpressionArrayColumn(leftColumn, operator, right, varLength);
|
|
149
|
+
} else {
|
|
150
|
+
return this.createExpressionArrayValue(leftColumn, operator, right, varLength);
|
|
151
|
+
}
|
|
146
152
|
} else {
|
|
147
153
|
return this.createExpression(leftColumn, operator, right, varLength);
|
|
148
154
|
}
|
|
@@ -176,8 +182,9 @@ export default class WhereExpression {
|
|
|
176
182
|
if (right !== null && typeof right === 'object' && 'model' in right && 'name' in right) {
|
|
177
183
|
const rightColumn = right.model.getColumn(right.name);
|
|
178
184
|
|
|
185
|
+
// 型の不一致エラーメッセージを改善
|
|
179
186
|
if (leftColumn.type !== rightColumn.type) {
|
|
180
|
-
throw new Error(`
|
|
187
|
+
throw new Error(`Type mismatch: column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
|
|
181
188
|
}
|
|
182
189
|
|
|
183
190
|
// LIKE operators are different, so handle separately
|
|
@@ -224,37 +231,172 @@ export default class WhereExpression {
|
|
|
224
231
|
|
|
225
232
|
private static createExpressionArrayValue(leftColumn: TColumnDetail, operator: TOperator, right: any, varLength: number) : TQuery {
|
|
226
233
|
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
234
|
+
// バリデーションチェック
|
|
235
|
+
switch (operator) {
|
|
236
|
+
case 'any':
|
|
237
|
+
switch (leftColumn.type) {
|
|
238
|
+
case 'integer[]':
|
|
239
|
+
if (ValidateValueUtil.isErrorInteger(right)) {
|
|
240
|
+
throw new Error(`Expected integer value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
case 'real[]':
|
|
244
|
+
if (ValidateValueUtil.isErrorReal(right)) {
|
|
245
|
+
throw new Error(`Expected numeric value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
case 'string[]':
|
|
249
|
+
if (ValidateValueUtil.isErrorString(right)) {
|
|
250
|
+
throw new Error(`Expected string value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
case 'uuid[]':
|
|
254
|
+
if (ValidateValueUtil.isErrorUUID(right)) {
|
|
255
|
+
throw new Error(`Expected UUID value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
case 'bool[]':
|
|
259
|
+
if (ValidateValueUtil.isErrorBool(right)) {
|
|
260
|
+
throw new Error(`Expected boolean value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
case 'date[]':
|
|
264
|
+
if (ValidateValueUtil.isErrorDate(right)) {
|
|
265
|
+
throw new Error(`Expected date value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
case 'time[]':
|
|
269
|
+
if (ValidateValueUtil.isErrorTime(right)) {
|
|
270
|
+
throw new Error(`Expected time value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
case 'timestamp[]':
|
|
274
|
+
if (ValidateValueUtil.isErrorTimestamp(right)) {
|
|
275
|
+
throw new Error(`Expected timestamp value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
276
|
+
}
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
280
|
+
case '=':
|
|
281
|
+
case '@>':
|
|
282
|
+
case '&&':
|
|
283
|
+
if (Array.isArray(right) === false) {
|
|
284
|
+
throw new Error(`Expected array format for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
for (const value of right) {
|
|
288
|
+
switch (leftColumn.type) {
|
|
289
|
+
case 'integer[]':
|
|
290
|
+
if (ValidateValueUtil.isErrorInteger(value)) {
|
|
291
|
+
throw new Error(`Expected integer value in array element, but received: ${JSON.stringify(value)}`);
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
case 'real[]':
|
|
295
|
+
if (ValidateValueUtil.isErrorReal(value)) {
|
|
296
|
+
throw new Error(`Expected numeric value in array element, but received: ${JSON.stringify(value)}`);
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
case 'string[]':
|
|
300
|
+
if (ValidateValueUtil.isErrorString(value)) {
|
|
301
|
+
throw new Error(`Expected string value in array element, but received: ${JSON.stringify(value)}`);
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
case 'uuid[]':
|
|
305
|
+
if (ValidateValueUtil.isErrorUUID(value)) {
|
|
306
|
+
throw new Error(`Expected UUID value in array element, but received: ${JSON.stringify(value)}`);
|
|
307
|
+
}
|
|
308
|
+
break;
|
|
309
|
+
case 'bool[]':
|
|
310
|
+
if (ValidateValueUtil.isErrorBool(value)) {
|
|
311
|
+
throw new Error(`Expected boolean value in array element, but received: ${JSON.stringify(value)}`);
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
case 'date[]':
|
|
315
|
+
if (ValidateValueUtil.isErrorDate(value)) {
|
|
316
|
+
throw new Error(`Expected date value in array element, but received: ${JSON.stringify(value)}`);
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
case 'time[]':
|
|
320
|
+
if (ValidateValueUtil.isErrorTime(value)) {
|
|
321
|
+
throw new Error(`Expected time value in array element, but received: ${JSON.stringify(value)}`);
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
case 'timestamp[]':
|
|
325
|
+
if (ValidateValueUtil.isErrorTimestamp(value)) {
|
|
326
|
+
throw new Error(`Expected timestamp value in array element, but received: ${JSON.stringify(value)}`);
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
default:
|
|
333
|
+
throw new Error(`Unsupported operator '${operator}' for array column operations.`);
|
|
334
|
+
}
|
|
243
335
|
|
|
244
336
|
switch (operator) {
|
|
245
337
|
case '=':
|
|
246
|
-
//
|
|
247
|
-
|
|
338
|
+
// =で検索すると順番まで一致させないといけないので、順番不一致でも中身があってれば一致とするようにする
|
|
339
|
+
return {
|
|
340
|
+
expression: `(${leftColumn.expression} @> $${varLength} AND $${varLength} @> $${leftColumn.expression})`,
|
|
341
|
+
vars: [right]
|
|
342
|
+
}
|
|
343
|
+
case '@>':
|
|
344
|
+
case '&&':
|
|
248
345
|
return {
|
|
249
346
|
expression: `${leftColumn.expression} ${operator} $${varLength}`,
|
|
250
347
|
vars: [right]
|
|
251
348
|
}
|
|
349
|
+
case 'any':
|
|
350
|
+
return {
|
|
351
|
+
expression: `$${varLength} = ANY(${leftColumn.expression})`,
|
|
352
|
+
vars: [right]
|
|
353
|
+
}
|
|
252
354
|
}
|
|
355
|
+
}
|
|
253
356
|
|
|
357
|
+
private static createExpressionArrayColumn(leftColumn: TColumnDetail, operator: TOperator, right: TColumnInfo, varLength: number) : TQuery {
|
|
254
358
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
359
|
+
const rightColumn = right.model.getColumn(right.name);
|
|
360
|
+
|
|
361
|
+
// バリデーションチェック
|
|
362
|
+
switch (operator) {
|
|
363
|
+
case 'any':
|
|
364
|
+
// any演算子の場合
|
|
365
|
+
if (leftColumn.type !== rightColumn.type.replace('[]', '')) {
|
|
366
|
+
throw new Error(`Type mismatch: array column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and scalar column [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) are incompatible for ANY operation.`);
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
case '=':
|
|
370
|
+
case '@>':
|
|
371
|
+
case '&&':
|
|
372
|
+
// 配列演算子の場合
|
|
373
|
+
if (leftColumn.type !== rightColumn.type) {
|
|
374
|
+
throw new Error(`Type mismatch: array columns [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
default:
|
|
378
|
+
// サポートされていない演算子
|
|
379
|
+
throw new Error(`Operator '${operator}' is not supported for array column operations. Supported operators: =, @>, &&, ANY`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
switch (operator) {
|
|
383
|
+
case '=':
|
|
384
|
+
// =で検索すると順番まで一致させないといけないので、順番不一致でも中身があってれば一致とするようにする
|
|
385
|
+
return {
|
|
386
|
+
expression: `(${leftColumn.expression} @> $${rightColumn.expression} AND ${rightColumn.expression} @> $${leftColumn.expression})`,
|
|
387
|
+
vars: []
|
|
388
|
+
}
|
|
389
|
+
case '@>':
|
|
390
|
+
case '&&':
|
|
391
|
+
return {
|
|
392
|
+
expression: `${leftColumn.expression} ${operator} $${rightColumn.expression}`,
|
|
393
|
+
vars: []
|
|
394
|
+
}
|
|
395
|
+
case 'any':
|
|
396
|
+
return {
|
|
397
|
+
expression: `$${rightColumn.expression} = ANY(${leftColumn.expression})`,
|
|
398
|
+
vars: []
|
|
399
|
+
}
|
|
258
400
|
}
|
|
259
401
|
}
|
|
260
402
|
|
package/src/models/Type.ts
CHANGED
|
@@ -47,7 +47,7 @@ export type TColumnDetail = TColumn & {
|
|
|
47
47
|
expression: string
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export type TOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "like" | "ilike" | "h2f_like" | "h2f_ilike" | "in" | "not in";
|
|
50
|
+
export type TOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "like" | "ilike" | "h2f_like" | "h2f_ilike" | "in" | "not in" | "any" | "@>" | "&&";
|
|
51
51
|
export type TColumnInfo = { model: TableModel, name: string }
|
|
52
52
|
export type TQuery = {expression: string, vars?: Array<any>};
|
|
53
53
|
export type TSelectExpression = { expression: string, alias: string }
|