@rex0220/kintone-sql-tools 2.16.0 → 3.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 +4 -1
- package/dist-cli/ksql.js +1689 -299
- package/dist-mcp/ksql-mcp.js +1678 -273
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -78,6 +78,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
78
78
|
["BY", "BY" /* BY */],
|
|
79
79
|
["HAVING", "HAVING" /* HAVING */],
|
|
80
80
|
["ORDER", "ORDER" /* ORDER */],
|
|
81
|
+
["KORDER", "KORDER" /* KORDER */],
|
|
81
82
|
["ASC", "ASC" /* ASC */],
|
|
82
83
|
["DESC", "DESC" /* DESC */],
|
|
83
84
|
["LIMIT", "LIMIT" /* LIMIT */],
|
|
@@ -123,6 +124,11 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
123
124
|
["COALESCE", "COALESCE" /* COALESCE */],
|
|
124
125
|
["NULLIF", "NULLIF" /* NULLIF */],
|
|
125
126
|
["ISNULL", "ISNULL" /* ISNULL */],
|
|
127
|
+
["INSTR", "INSTR" /* INSTR */],
|
|
128
|
+
["GREATEST", "GREATEST" /* GREATEST */],
|
|
129
|
+
["LEAST", "LEAST" /* LEAST */],
|
|
130
|
+
["LPAD", "LPAD" /* LPAD */],
|
|
131
|
+
["RPAD", "RPAD" /* RPAD */],
|
|
126
132
|
["CAST", "CAST" /* CAST */],
|
|
127
133
|
["CONVERT", "CONVERT" /* CONVERT */],
|
|
128
134
|
["FORMAT", "FORMAT" /* FORMAT */],
|
|
@@ -130,6 +136,8 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
130
136
|
["FLOOR", "FLOOR" /* FLOOR */],
|
|
131
137
|
["CEIL", "CEIL" /* CEIL */],
|
|
132
138
|
["CEILING", "CEILING" /* CEILING */],
|
|
139
|
+
["TRUNCATE", "TRUNCATE" /* TRUNCATE */],
|
|
140
|
+
["TRUNC", "TRUNC" /* TRUNC */],
|
|
133
141
|
["ABS", "ABS" /* ABS */],
|
|
134
142
|
["MOD", "MOD" /* MOD */],
|
|
135
143
|
["POWER", "POWER" /* POWER */],
|
|
@@ -141,6 +149,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
141
149
|
["DATE_FORMAT", "DATE_FORMAT" /* DATE_FORMAT */],
|
|
142
150
|
["DATEDIFF", "DATEDIFF" /* DATEDIFF */],
|
|
143
151
|
["DATE_ADD", "DATE_ADD" /* DATE_ADD */],
|
|
152
|
+
["LAST_DAY", "LAST_DAY" /* LAST_DAY */],
|
|
144
153
|
["IF", "IF" /* IF */]
|
|
145
154
|
]);
|
|
146
155
|
|
|
@@ -486,6 +495,13 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
486
495
|
"COALESCE" /* COALESCE */,
|
|
487
496
|
"NULLIF" /* NULLIF */,
|
|
488
497
|
"ISNULL" /* ISNULL */,
|
|
498
|
+
"LEFT" /* LEFT */,
|
|
499
|
+
"RIGHT" /* RIGHT */,
|
|
500
|
+
"INSTR" /* INSTR */,
|
|
501
|
+
"GREATEST" /* GREATEST */,
|
|
502
|
+
"LEAST" /* LEAST */,
|
|
503
|
+
"LPAD" /* LPAD */,
|
|
504
|
+
"RPAD" /* RPAD */,
|
|
489
505
|
"CAST" /* CAST */,
|
|
490
506
|
"CONVERT" /* CONVERT */,
|
|
491
507
|
"FORMAT" /* FORMAT */,
|
|
@@ -493,6 +509,8 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
493
509
|
"FLOOR" /* FLOOR */,
|
|
494
510
|
"CEIL" /* CEIL */,
|
|
495
511
|
"CEILING" /* CEILING */,
|
|
512
|
+
"TRUNCATE" /* TRUNCATE */,
|
|
513
|
+
"TRUNC" /* TRUNC */,
|
|
496
514
|
"ABS" /* ABS */,
|
|
497
515
|
"MOD" /* MOD */,
|
|
498
516
|
"POWER" /* POWER */,
|
|
@@ -504,6 +522,7 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
504
522
|
"DATE_FORMAT" /* DATE_FORMAT */,
|
|
505
523
|
"DATEDIFF" /* DATEDIFF */,
|
|
506
524
|
"DATE_ADD" /* DATE_ADD */,
|
|
525
|
+
"LAST_DAY" /* LAST_DAY */,
|
|
507
526
|
"IF" /* IF */
|
|
508
527
|
]);
|
|
509
528
|
function needsSpaceBetween(prev, cur) {
|
|
@@ -585,7 +604,7 @@ var Parser = class {
|
|
|
585
604
|
case "WITH" /* WITH */:
|
|
586
605
|
return this.parseWith();
|
|
587
606
|
case "SELECT" /* SELECT */:
|
|
588
|
-
return this.tryParseUnionChain(this.parseSelect());
|
|
607
|
+
return this.tryParseUnionChain(this.parseSelect(true));
|
|
589
608
|
case "INSERT" /* INSERT */:
|
|
590
609
|
return this.parseInsert();
|
|
591
610
|
case "UPDATE" /* UPDATE */:
|
|
@@ -775,7 +794,7 @@ var Parser = class {
|
|
|
775
794
|
}
|
|
776
795
|
query = w;
|
|
777
796
|
} else if (tok.kind === "SELECT" /* SELECT */) {
|
|
778
|
-
const sel = this.parseSelect();
|
|
797
|
+
const sel = this.parseSelect(true);
|
|
779
798
|
const chained = this.tryParseUnionChain(sel);
|
|
780
799
|
query = chained;
|
|
781
800
|
} else if (tok.kind === "INSERT" /* INSERT */) {
|
|
@@ -968,7 +987,7 @@ var Parser = class {
|
|
|
968
987
|
// ----------------------------------------------------------
|
|
969
988
|
// SELECT
|
|
970
989
|
// ----------------------------------------------------------
|
|
971
|
-
parseSelect() {
|
|
990
|
+
parseSelect(allowKorder = false) {
|
|
972
991
|
this.expect("SELECT" /* SELECT */);
|
|
973
992
|
const distinct = this.consume("DISTINCT" /* DISTINCT */);
|
|
974
993
|
const columns = this.parseSelectColumns();
|
|
@@ -985,7 +1004,19 @@ var Parser = class {
|
|
|
985
1004
|
having = this.parseWhereExpr();
|
|
986
1005
|
}
|
|
987
1006
|
}
|
|
988
|
-
|
|
1007
|
+
let orderMode = "CANONICAL";
|
|
1008
|
+
let orderBy = [];
|
|
1009
|
+
if (this.consume("ORDER" /* ORDER */)) {
|
|
1010
|
+
this.expect("BY" /* BY */);
|
|
1011
|
+
orderBy = this.parseOrderBy();
|
|
1012
|
+
} else if (this.consume("KORDER" /* KORDER */)) {
|
|
1013
|
+
if (!allowKorder) {
|
|
1014
|
+
throw new ParseError("KORDER BY \u306F\u5229\u7528\u8005\u3078\u7D50\u679C\u3092\u8FD4\u3059\u30C8\u30C3\u30D7\u30EC\u30D9\u30EB SELECT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", this.prev());
|
|
1015
|
+
}
|
|
1016
|
+
orderMode = "KINTONE_NATIVE";
|
|
1017
|
+
this.expect("BY" /* BY */);
|
|
1018
|
+
orderBy = this.parseOrderBy();
|
|
1019
|
+
}
|
|
989
1020
|
const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
|
|
990
1021
|
const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
|
|
991
1022
|
const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
|
|
@@ -1002,6 +1033,7 @@ var Parser = class {
|
|
|
1002
1033
|
where,
|
|
1003
1034
|
groupBy,
|
|
1004
1035
|
having,
|
|
1036
|
+
orderMode,
|
|
1005
1037
|
orderBy,
|
|
1006
1038
|
limit,
|
|
1007
1039
|
offset
|
|
@@ -1039,6 +1071,9 @@ var Parser = class {
|
|
|
1039
1071
|
// ----------------------------------------------------------
|
|
1040
1072
|
tryParseUnionChain(left) {
|
|
1041
1073
|
if (this.peek().kind !== "UNION" /* UNION */) return left;
|
|
1074
|
+
if (left.type === "SELECT" && left.orderMode === "KINTONE_NATIVE") {
|
|
1075
|
+
throw new ParseError("KORDER BY \u306F UNION \u5206\u5C90\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
1076
|
+
}
|
|
1042
1077
|
this.advance();
|
|
1043
1078
|
const all = this.consume("ALL" /* ALL */);
|
|
1044
1079
|
const right = this.parseSelect();
|
|
@@ -1376,6 +1411,10 @@ var Parser = class {
|
|
|
1376
1411
|
// arg: 文字列リテラル / 算術式(フィールド参照・数値含む)/ ネスト文字列関数
|
|
1377
1412
|
// ──────────────────────────────────────────────────
|
|
1378
1413
|
tryStringFuncName() {
|
|
1414
|
+
if (this.peekAt(1).kind === "(" /* LPAREN */) {
|
|
1415
|
+
if (this.peek().kind === "LEFT" /* LEFT */) return "LEFT";
|
|
1416
|
+
if (this.peek().kind === "RIGHT" /* RIGHT */) return "RIGHT";
|
|
1417
|
+
}
|
|
1379
1418
|
const map = {
|
|
1380
1419
|
["UPPER" /* UPPER */]: "UPPER",
|
|
1381
1420
|
["LOWER" /* LOWER */]: "LOWER",
|
|
@@ -1390,6 +1429,11 @@ var Parser = class {
|
|
|
1390
1429
|
["COALESCE" /* COALESCE */]: "COALESCE",
|
|
1391
1430
|
["NULLIF" /* NULLIF */]: "NULLIF",
|
|
1392
1431
|
["ISNULL" /* ISNULL */]: "ISNULL",
|
|
1432
|
+
["INSTR" /* INSTR */]: "INSTR",
|
|
1433
|
+
["GREATEST" /* GREATEST */]: "GREATEST",
|
|
1434
|
+
["LEAST" /* LEAST */]: "LEAST",
|
|
1435
|
+
["LPAD" /* LPAD */]: "LPAD",
|
|
1436
|
+
["RPAD" /* RPAD */]: "RPAD",
|
|
1393
1437
|
["CAST" /* CAST */]: "CAST",
|
|
1394
1438
|
["CONVERT" /* CONVERT */]: "CAST",
|
|
1395
1439
|
// CONVERT → CAST に正規化
|
|
@@ -1399,12 +1443,16 @@ var Parser = class {
|
|
|
1399
1443
|
["CEIL" /* CEIL */]: "CEIL",
|
|
1400
1444
|
["CEILING" /* CEILING */]: "CEIL",
|
|
1401
1445
|
// CEILING → CEIL に正規化
|
|
1446
|
+
["TRUNCATE" /* TRUNCATE */]: "TRUNCATE",
|
|
1447
|
+
["TRUNC" /* TRUNC */]: "TRUNCATE",
|
|
1448
|
+
// TRUNC → TRUNCATE に正規化
|
|
1402
1449
|
["YEAR" /* YEAR */]: "YEAR",
|
|
1403
1450
|
["MONTH" /* MONTH */]: "MONTH",
|
|
1404
1451
|
["DAY" /* DAY */]: "DAY",
|
|
1405
1452
|
["DATE_FORMAT" /* DATE_FORMAT */]: "DATE_FORMAT",
|
|
1406
1453
|
["DATEDIFF" /* DATEDIFF */]: "DATEDIFF",
|
|
1407
1454
|
["DATE_ADD" /* DATE_ADD */]: "DATE_ADD",
|
|
1455
|
+
["LAST_DAY" /* LAST_DAY */]: "LAST_DAY",
|
|
1408
1456
|
["ABS" /* ABS */]: "ABS",
|
|
1409
1457
|
["MOD" /* MOD */]: "MOD",
|
|
1410
1458
|
["POWER" /* POWER */]: "POWER",
|
|
@@ -2679,7 +2727,50 @@ function isReadOnlyStatement(stmt) {
|
|
|
2679
2727
|
return !writesKintone(stmt) && (isReadOnlyType(stmt.type) || isDmlType(stmt.type));
|
|
2680
2728
|
}
|
|
2681
2729
|
function requiresCompleteInput(stmt) {
|
|
2682
|
-
|
|
2730
|
+
if (isDmlType(stmt.type)) return true;
|
|
2731
|
+
switch (stmt.type) {
|
|
2732
|
+
case "SELECT":
|
|
2733
|
+
return selectRequiresCompleteInput(stmt);
|
|
2734
|
+
case "UNION":
|
|
2735
|
+
return unionRequiresCompleteInput(stmt);
|
|
2736
|
+
case "WITH":
|
|
2737
|
+
return stmt.ctes.some(
|
|
2738
|
+
(cte) => cte.query.type === "SELECT" && selectRequiresCompleteInput(cte.query) || cte.query.type === "UNION" && unionRequiresCompleteInput(cte.query)
|
|
2739
|
+
) || (stmt.query.type === "SELECT" ? selectRequiresCompleteInput(stmt.query) : unionRequiresCompleteInput(stmt.query));
|
|
2740
|
+
case "CREATE_TEMP_TABLE":
|
|
2741
|
+
return stmt.query.type === "SELECT" ? selectRequiresCompleteInput(stmt.query) : stmt.query.type === "UNION" ? unionRequiresCompleteInput(stmt.query) : requiresCompleteInput(stmt.query);
|
|
2742
|
+
default:
|
|
2743
|
+
return false;
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
function unionRequiresCompleteInput(stmt) {
|
|
2747
|
+
const left = stmt.left.type === "SELECT" ? selectRequiresCompleteInput(stmt.left) : unionRequiresCompleteInput(stmt.left);
|
|
2748
|
+
return left || selectRequiresCompleteInput(stmt.right);
|
|
2749
|
+
}
|
|
2750
|
+
function selectRequiresCompleteInput(stmt) {
|
|
2751
|
+
if (stmt.orderBy.length > 0) return true;
|
|
2752
|
+
if (stmt.columns.some(
|
|
2753
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0 || column.type === "SCALAR_SUBQUERY_COL" && selectRequiresCompleteInput(column.query) || column.type === "CASE_COL" && column.expr.branches.some(
|
|
2754
|
+
(branch) => whereRequiresCompleteInput(branch.condition)
|
|
2755
|
+
)
|
|
2756
|
+
)) return true;
|
|
2757
|
+
return whereRequiresCompleteInput(stmt.where) || whereRequiresCompleteInput(stmt.having);
|
|
2758
|
+
}
|
|
2759
|
+
function whereRequiresCompleteInput(where) {
|
|
2760
|
+
if (where === null) return false;
|
|
2761
|
+
switch (where.type) {
|
|
2762
|
+
case "BINARY":
|
|
2763
|
+
return (where.right.type === "SUBQUERY_IN_LIST" || where.right.type === "SCALAR_SUBQUERY") && selectRequiresCompleteInput(where.right.query);
|
|
2764
|
+
case "LOGICAL":
|
|
2765
|
+
return whereRequiresCompleteInput(where.left) || whereRequiresCompleteInput(where.right);
|
|
2766
|
+
case "NOT":
|
|
2767
|
+
case "GROUP":
|
|
2768
|
+
return whereRequiresCompleteInput(where.expr);
|
|
2769
|
+
case "EXISTS":
|
|
2770
|
+
return selectRequiresCompleteInput(where.query);
|
|
2771
|
+
case "NULL_CHECK":
|
|
2772
|
+
return false;
|
|
2773
|
+
}
|
|
2683
2774
|
}
|
|
2684
2775
|
function hasWhereClause(stmt) {
|
|
2685
2776
|
if (!stmt || typeof stmt !== "object") return false;
|
|
@@ -3494,6 +3585,7 @@ function buildInlinedQuery(stmt) {
|
|
|
3494
3585
|
where,
|
|
3495
3586
|
groupBy: [],
|
|
3496
3587
|
having: null,
|
|
3588
|
+
orderMode: "CANONICAL",
|
|
3497
3589
|
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
3498
3590
|
limit: final.limit ?? cteBody.limit,
|
|
3499
3591
|
offset: final.offset ?? cteBody.offset,
|
|
@@ -4088,6 +4180,72 @@ function analyzeBatch(statements) {
|
|
|
4088
4180
|
};
|
|
4089
4181
|
}
|
|
4090
4182
|
|
|
4183
|
+
// src/core/fieldSemantics.ts
|
|
4184
|
+
var STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
4185
|
+
"SINGLE_LINE_TEXT",
|
|
4186
|
+
"MULTI_LINE_TEXT",
|
|
4187
|
+
"RICH_TEXT",
|
|
4188
|
+
"LINK",
|
|
4189
|
+
"DATE",
|
|
4190
|
+
"TIME",
|
|
4191
|
+
"DATETIME",
|
|
4192
|
+
"CREATED_TIME",
|
|
4193
|
+
"UPDATED_TIME",
|
|
4194
|
+
"CREATOR",
|
|
4195
|
+
"MODIFIER"
|
|
4196
|
+
]);
|
|
4197
|
+
var OPTION_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
4198
|
+
"DROP_DOWN",
|
|
4199
|
+
"RADIO_BUTTON",
|
|
4200
|
+
"CHECK_BOX",
|
|
4201
|
+
"MULTI_SELECT",
|
|
4202
|
+
"STATUS"
|
|
4203
|
+
]);
|
|
4204
|
+
function resolveFieldSemantics(source) {
|
|
4205
|
+
let compareMode;
|
|
4206
|
+
if (source.fieldType === "RECORD_NUMBER" || source.fieldType === "__ID__") {
|
|
4207
|
+
compareMode = "recordNumber";
|
|
4208
|
+
} else if (source.fieldType === "NUMBER") {
|
|
4209
|
+
compareMode = "number";
|
|
4210
|
+
} else if (source.fieldType === "CALC") {
|
|
4211
|
+
compareMode = source.sortKind === "number" ? "number" : "string";
|
|
4212
|
+
} else if (OPTION_FIELD_TYPES.has(source.fieldType)) {
|
|
4213
|
+
compareMode = "option";
|
|
4214
|
+
} else if (STRING_FIELD_TYPES.has(source.fieldType)) {
|
|
4215
|
+
compareMode = "string";
|
|
4216
|
+
} else {
|
|
4217
|
+
compareMode = "unsupported";
|
|
4218
|
+
}
|
|
4219
|
+
const optionOrder = source.optionOrder ? new Map(Object.entries(source.optionOrder)) : void 0;
|
|
4220
|
+
return {
|
|
4221
|
+
fieldType: source.fieldType,
|
|
4222
|
+
compareMode,
|
|
4223
|
+
inSubtable: source.inSubtable === true,
|
|
4224
|
+
requiresCollectionOperators: source.inSubtable === true || source.requiresCollectionOperators === true,
|
|
4225
|
+
...optionOrder && optionOrder.size > 0 ? { optionOrder } : {}
|
|
4226
|
+
};
|
|
4227
|
+
}
|
|
4228
|
+
function syntheticSemantics(compareMode, fieldType = compareMode === "number" ? "KSQL_NUMBER" : "KSQL_STRING") {
|
|
4229
|
+
return { fieldType, compareMode, inSubtable: false, requiresCollectionOperators: false };
|
|
4230
|
+
}
|
|
4231
|
+
function withFieldSemanticSource(semantics, appId, fieldCode) {
|
|
4232
|
+
return { ...semantics, source: { appId, fieldCode } };
|
|
4233
|
+
}
|
|
4234
|
+
function fieldSemanticsEqual(left, right) {
|
|
4235
|
+
if (left === right) return true;
|
|
4236
|
+
if (!left || !right) return false;
|
|
4237
|
+
if (left.fieldType !== right.fieldType || left.compareMode !== right.compareMode || left.inSubtable !== right.inSubtable || left.requiresCollectionOperators !== right.requiresCollectionOperators) return false;
|
|
4238
|
+
if (left.source?.appId !== right.source?.appId || left.source?.fieldCode !== right.source?.fieldCode) return false;
|
|
4239
|
+
const a = left.optionOrder;
|
|
4240
|
+
const b = right.optionOrder;
|
|
4241
|
+
if (a === b) return true;
|
|
4242
|
+
if (!a || !b || a.size !== b.size) return false;
|
|
4243
|
+
for (const [key, value] of a) {
|
|
4244
|
+
if (b.get(key) !== value) return false;
|
|
4245
|
+
}
|
|
4246
|
+
return true;
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4091
4249
|
// src/core/batchVariables.ts
|
|
4092
4250
|
var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
4093
4251
|
function normalizeBatchVariableName(name) {
|
|
@@ -4123,26 +4281,197 @@ function validateDeclaredBatchVariables(statements, input) {
|
|
|
4123
4281
|
}
|
|
4124
4282
|
|
|
4125
4283
|
// src/core/scalarCompare.ts
|
|
4126
|
-
function
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4284
|
+
function compareCodePointStrings(left, right) {
|
|
4285
|
+
const a = left[Symbol.iterator]();
|
|
4286
|
+
const b = right[Symbol.iterator]();
|
|
4287
|
+
while (true) {
|
|
4288
|
+
const av = a.next();
|
|
4289
|
+
const bv = b.next();
|
|
4290
|
+
if (av.done || bv.done) {
|
|
4291
|
+
if (av.done && bv.done) return 0;
|
|
4292
|
+
return av.done ? -1 : 1;
|
|
4293
|
+
}
|
|
4294
|
+
const ac = av.value.codePointAt(0) ?? 0;
|
|
4295
|
+
const bc = bv.value.codePointAt(0) ?? 0;
|
|
4296
|
+
if (ac < bc) return -1;
|
|
4297
|
+
if (ac > bc) return 1;
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
function triCompare(left, right) {
|
|
4301
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
4302
|
+
}
|
|
4303
|
+
function numberKey(value) {
|
|
4304
|
+
if (value === "") return { band: 0 };
|
|
4305
|
+
const numeric = Number(value);
|
|
4306
|
+
if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
|
|
4307
|
+
if (Number.isFinite(numeric)) return { band: 2, value: numeric };
|
|
4308
|
+
if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
|
|
4309
|
+
if (value === "NaN") return { band: 4 };
|
|
4310
|
+
return { band: 5, value };
|
|
4311
|
+
}
|
|
4312
|
+
function compareNumbers(left, right) {
|
|
4313
|
+
const a = numberKey(left);
|
|
4314
|
+
const b = numberKey(right);
|
|
4315
|
+
if (a.band !== b.band) return a.band < b.band ? -1 : 1;
|
|
4316
|
+
if (a.band === 2 && b.band === 2) return triCompare(a.value, b.value);
|
|
4317
|
+
if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
|
|
4318
|
+
return 0;
|
|
4319
|
+
}
|
|
4320
|
+
function recordNumberKey(value, allowPrefix) {
|
|
4321
|
+
if (value === "") return { empty: true, normalizedId: "", display: value };
|
|
4322
|
+
const match = /^\d+$/.test(value) ? value : allowPrefix ? /-(\d+)$/.exec(value)?.[1] : void 0;
|
|
4323
|
+
if (match === void 0) {
|
|
4324
|
+
throw new Error(`ArgumentError: invalid ${allowPrefix ? "RECORD_NUMBER" : "$id"} value: ${value}`);
|
|
4325
|
+
}
|
|
4326
|
+
return {
|
|
4327
|
+
empty: false,
|
|
4328
|
+
normalizedId: match.replace(/^0+(?=\d)/, ""),
|
|
4329
|
+
display: value
|
|
4330
|
+
};
|
|
4331
|
+
}
|
|
4332
|
+
function compareRecordNumbers(left, right, allowPrefix) {
|
|
4333
|
+
const a = recordNumberKey(left, allowPrefix);
|
|
4334
|
+
const b = recordNumberKey(right, allowPrefix);
|
|
4335
|
+
if (a.empty || b.empty) return a.empty === b.empty ? 0 : a.empty ? -1 : 1;
|
|
4336
|
+
if (a.normalizedId.length !== b.normalizedId.length) {
|
|
4337
|
+
return a.normalizedId.length < b.normalizedId.length ? -1 : 1;
|
|
4338
|
+
}
|
|
4339
|
+
const idCmp = compareCodePointStrings(a.normalizedId, b.normalizedId);
|
|
4340
|
+
return idCmp !== 0 ? idCmp : compareCodePointStrings(a.display, b.display);
|
|
4341
|
+
}
|
|
4342
|
+
function parseOptionValues(value, fieldType) {
|
|
4343
|
+
if (value === "") return [];
|
|
4344
|
+
if (fieldType !== "CHECK_BOX" && fieldType !== "MULTI_SELECT") return [value];
|
|
4345
|
+
try {
|
|
4346
|
+
const parsed = JSON.parse(value);
|
|
4347
|
+
return Array.isArray(parsed) ? parsed.map((item) => String(item ?? "")) : [value];
|
|
4348
|
+
} catch {
|
|
4349
|
+
return [value];
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
function optionVector(value, semantics) {
|
|
4353
|
+
const order = semantics.optionOrder ?? /* @__PURE__ */ new Map();
|
|
4354
|
+
const unique = [...new Set(parseOptionValues(value, semantics.fieldType))];
|
|
4355
|
+
const vector = unique.map((label) => {
|
|
4356
|
+
const rank = order.get(label);
|
|
4357
|
+
return rank === void 0 ? { knownBand: 1, rank: 0, label } : { knownBand: 0, rank, label };
|
|
4358
|
+
});
|
|
4359
|
+
vector.sort(compareOptionElement);
|
|
4360
|
+
return vector;
|
|
4361
|
+
}
|
|
4362
|
+
function compareOptionElement(left, right) {
|
|
4363
|
+
if (left.knownBand !== right.knownBand) return left.knownBand < right.knownBand ? -1 : 1;
|
|
4364
|
+
if (left.rank !== right.rank) return left.rank < right.rank ? -1 : 1;
|
|
4365
|
+
return compareCodePointStrings(left.label, right.label);
|
|
4366
|
+
}
|
|
4367
|
+
function compareOptions(left, right, semantics) {
|
|
4368
|
+
const a = optionVector(left, semantics);
|
|
4369
|
+
const b = optionVector(right, semantics);
|
|
4370
|
+
const length = Math.min(a.length, b.length);
|
|
4371
|
+
for (let index = 0; index < length; index++) {
|
|
4372
|
+
const cmp = compareOptionElement(a[index], b[index]);
|
|
4373
|
+
if (cmp !== 0) return cmp;
|
|
4374
|
+
}
|
|
4375
|
+
return a.length < b.length ? -1 : a.length > b.length ? 1 : 0;
|
|
4376
|
+
}
|
|
4377
|
+
function compareCanonicalValues(left, right, semantics) {
|
|
4378
|
+
switch (semantics.compareMode) {
|
|
4379
|
+
case "string":
|
|
4380
|
+
return compareCodePointStrings(left, right);
|
|
4381
|
+
case "number":
|
|
4382
|
+
return compareNumbers(left, right);
|
|
4383
|
+
case "recordNumber":
|
|
4384
|
+
return compareRecordNumbers(left, right, semantics.fieldType === "RECORD_NUMBER");
|
|
4385
|
+
case "option":
|
|
4386
|
+
return compareOptions(left, right, semantics);
|
|
4387
|
+
case "unsupported":
|
|
4388
|
+
throw new Error(`ArgumentError: values of type ${semantics.fieldType} cannot be compared.`);
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
function compareScalarValues(op, left, right, semantics = syntheticSemantics("string")) {
|
|
4392
|
+
const cmp = compareCanonicalValues(left, right, semantics);
|
|
4135
4393
|
switch (op) {
|
|
4394
|
+
case "=":
|
|
4395
|
+
return cmp === 0;
|
|
4396
|
+
case "!=":
|
|
4397
|
+
case "<>":
|
|
4398
|
+
return cmp !== 0;
|
|
4136
4399
|
case ">":
|
|
4137
|
-
return
|
|
4400
|
+
return cmp > 0;
|
|
4138
4401
|
case "<":
|
|
4139
|
-
return
|
|
4402
|
+
return cmp < 0;
|
|
4140
4403
|
case ">=":
|
|
4141
|
-
return
|
|
4404
|
+
return cmp >= 0;
|
|
4142
4405
|
case "<=":
|
|
4143
|
-
return
|
|
4406
|
+
return cmp <= 0;
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
function selectScalarExtreme(values, extreme) {
|
|
4410
|
+
if (extreme === "least" && values.includes("")) return "";
|
|
4411
|
+
const candidates = extreme === "greatest" ? values.filter((value) => value !== "") : [...values];
|
|
4412
|
+
if (candidates.length === 0) return "";
|
|
4413
|
+
const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
|
|
4414
|
+
const compare = (left, right) => {
|
|
4415
|
+
if (numeric) {
|
|
4416
|
+
const numericCmp = triCompare(Number(left), Number(right));
|
|
4417
|
+
if (numericCmp !== 0) return numericCmp;
|
|
4418
|
+
}
|
|
4419
|
+
return compareCodePointStrings(left, right);
|
|
4420
|
+
};
|
|
4421
|
+
return candidates.reduce((best, candidate) => {
|
|
4422
|
+
const cmp = compare(candidate, best);
|
|
4423
|
+
return extreme === "greatest" ? cmp > 0 ? candidate : best : cmp < 0 ? candidate : best;
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
4426
|
+
|
|
4427
|
+
// src/core/explainMetadata.ts
|
|
4428
|
+
function whereNeedsFieldMetadata(where) {
|
|
4429
|
+
if (where === null) return false;
|
|
4430
|
+
switch (where.type) {
|
|
4431
|
+
case "BINARY":
|
|
4432
|
+
return valueNeedsFieldMetadata(where.left);
|
|
4433
|
+
case "NULL_CHECK":
|
|
4434
|
+
return valueNeedsFieldMetadata(where.field);
|
|
4435
|
+
case "LOGICAL":
|
|
4436
|
+
return whereNeedsFieldMetadata(where.left) || whereNeedsFieldMetadata(where.right);
|
|
4437
|
+
case "NOT":
|
|
4438
|
+
case "GROUP":
|
|
4439
|
+
return whereNeedsFieldMetadata(where.expr);
|
|
4440
|
+
case "EXISTS":
|
|
4441
|
+
return false;
|
|
4144
4442
|
}
|
|
4145
4443
|
}
|
|
4444
|
+
function valueNeedsFieldMetadata(value) {
|
|
4445
|
+
if (Array.isArray(value)) return value.some(valueNeedsFieldMetadata);
|
|
4446
|
+
if (value === null || typeof value !== "object") return false;
|
|
4447
|
+
const item = value;
|
|
4448
|
+
if (item["type"] === "FIELD") return item["field"] !== "$id";
|
|
4449
|
+
if (item["type"] === "SELECT") return false;
|
|
4450
|
+
return Object.values(item).some(valueNeedsFieldMetadata);
|
|
4451
|
+
}
|
|
4452
|
+
function selectNeedsOwnMetadata(statement) {
|
|
4453
|
+
return whereNeedsFieldMetadata(statement.where) || statement.orderBy.length > 0 || statement.columns.some(
|
|
4454
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
4455
|
+
);
|
|
4456
|
+
}
|
|
4457
|
+
function explainNeedsAppMetadata(statement) {
|
|
4458
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4459
|
+
const visit = (node) => {
|
|
4460
|
+
if (node === null || typeof node !== "object") return false;
|
|
4461
|
+
if (seen.has(node)) return false;
|
|
4462
|
+
seen.add(node);
|
|
4463
|
+
if (Array.isArray(node)) return node.some(visit);
|
|
4464
|
+
const item = node;
|
|
4465
|
+
if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
|
|
4466
|
+
return true;
|
|
4467
|
+
}
|
|
4468
|
+
if ((item["type"] === "UPDATE" || item["type"] === "DELETE") && whereNeedsFieldMetadata(node.where)) {
|
|
4469
|
+
return true;
|
|
4470
|
+
}
|
|
4471
|
+
return Object.values(item).some(visit);
|
|
4472
|
+
};
|
|
4473
|
+
return visit(statement);
|
|
4474
|
+
}
|
|
4146
4475
|
|
|
4147
4476
|
// src/engine/evalFunc.ts
|
|
4148
4477
|
function evalArithExpr(expr, row) {
|
|
@@ -4191,6 +4520,36 @@ function evalStringFunc(expr, row) {
|
|
|
4191
4520
|
const len = args[2] !== void 0 ? Number(args[2]) : void 0;
|
|
4192
4521
|
return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
|
|
4193
4522
|
}
|
|
4523
|
+
case "LEFT": {
|
|
4524
|
+
assertArity("LEFT", args, 2, 2);
|
|
4525
|
+
const str = args[0];
|
|
4526
|
+
const n = Math.trunc(Number(args[1]));
|
|
4527
|
+
return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
|
|
4528
|
+
}
|
|
4529
|
+
case "RIGHT": {
|
|
4530
|
+
assertArity("RIGHT", args, 2, 2);
|
|
4531
|
+
const str = args[0];
|
|
4532
|
+
const n = Math.trunc(Number(args[1]));
|
|
4533
|
+
return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
|
|
4534
|
+
}
|
|
4535
|
+
case "INSTR":
|
|
4536
|
+
assertArity("INSTR", args, 2, 2);
|
|
4537
|
+
return String(args[0].indexOf(args[1]) + 1);
|
|
4538
|
+
case "LPAD":
|
|
4539
|
+
case "RPAD": {
|
|
4540
|
+
assertArity(expr.func, args, 2, 3);
|
|
4541
|
+
const str = args[0];
|
|
4542
|
+
const n = Math.trunc(Number(args[1]));
|
|
4543
|
+
if (Number.isNaN(n) || n <= 0) return "";
|
|
4544
|
+
if (str.length >= n) return str.slice(0, n);
|
|
4545
|
+
const pad = args[2] ?? " ";
|
|
4546
|
+
if (pad === "") return str;
|
|
4547
|
+
return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
|
|
4548
|
+
}
|
|
4549
|
+
case "GREATEST":
|
|
4550
|
+
case "LEAST":
|
|
4551
|
+
assertArity(expr.func, args, 2);
|
|
4552
|
+
return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
|
|
4194
4553
|
case "CONCAT":
|
|
4195
4554
|
return args.join("");
|
|
4196
4555
|
case "REPLACE": {
|
|
@@ -4211,6 +4570,9 @@ function evalStringFunc(expr, row) {
|
|
|
4211
4570
|
return applyRoundOp("floor", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
|
|
4212
4571
|
case "CEIL":
|
|
4213
4572
|
return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
|
|
4573
|
+
case "TRUNCATE":
|
|
4574
|
+
assertArity("TRUNCATE", args, 1, 2);
|
|
4575
|
+
return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
|
|
4214
4576
|
case "CAST": {
|
|
4215
4577
|
const val = args[0] ?? "";
|
|
4216
4578
|
const castType = args[1] ?? "TEXT";
|
|
@@ -4243,6 +4605,9 @@ function evalStringFunc(expr, row) {
|
|
|
4243
4605
|
return applyDateDiff(args[0] ?? "", args[1] ?? "");
|
|
4244
4606
|
case "DATE_ADD":
|
|
4245
4607
|
return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
|
|
4608
|
+
case "LAST_DAY":
|
|
4609
|
+
assertArity("LAST_DAY", args, 1, 1);
|
|
4610
|
+
return applyLastDay(args[0]);
|
|
4246
4611
|
case "ABS":
|
|
4247
4612
|
return String(Math.abs(Number(args[0] ?? "0")));
|
|
4248
4613
|
case "MOD": {
|
|
@@ -4265,6 +4630,11 @@ function evalStringFunc(expr, row) {
|
|
|
4265
4630
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4266
4631
|
}
|
|
4267
4632
|
}
|
|
4633
|
+
function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
|
|
4634
|
+
if (args.length >= min && args.length <= max) return;
|
|
4635
|
+
const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
|
|
4636
|
+
throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
|
|
4637
|
+
}
|
|
4268
4638
|
function parseDateParts(s) {
|
|
4269
4639
|
return {
|
|
4270
4640
|
y: s.slice(0, 4) || "0000",
|
|
@@ -4289,6 +4659,9 @@ function applyDateDiff(date1, date2) {
|
|
|
4289
4659
|
return String(Math.round((d1 - d2) / 864e5));
|
|
4290
4660
|
}
|
|
4291
4661
|
function applyDateAdd(dateStr, n, unit) {
|
|
4662
|
+
if (unit !== "YEAR" && unit !== "MONTH" && unit !== "DAY") {
|
|
4663
|
+
throw new Error("ArgumentError: DATE_ADD unit must be YEAR, MONTH, or DAY.");
|
|
4664
|
+
}
|
|
4292
4665
|
if (!dateStr || dateStr.length < 10) return dateStr;
|
|
4293
4666
|
const { y, mo, d } = parseDateParts(dateStr);
|
|
4294
4667
|
const dt = new Date(Date.UTC(+y, +mo - 1, +d));
|
|
@@ -4299,7 +4672,7 @@ function applyDateAdd(dateStr, n, unit) {
|
|
|
4299
4672
|
case "MONTH":
|
|
4300
4673
|
dt.setUTCMonth(dt.getUTCMonth() + n);
|
|
4301
4674
|
break;
|
|
4302
|
-
|
|
4675
|
+
case "DAY":
|
|
4303
4676
|
dt.setUTCDate(dt.getUTCDate() + n);
|
|
4304
4677
|
break;
|
|
4305
4678
|
}
|
|
@@ -4308,6 +4681,15 @@ function applyDateAdd(dateStr, n, unit) {
|
|
|
4308
4681
|
const rd = String(dt.getUTCDate()).padStart(2, "0");
|
|
4309
4682
|
return `${ry}-${rmo}-${rd}`;
|
|
4310
4683
|
}
|
|
4684
|
+
function applyLastDay(dateStr) {
|
|
4685
|
+
if (!dateStr || dateStr.length < 10) return dateStr;
|
|
4686
|
+
const { y, mo } = parseDateParts(dateStr);
|
|
4687
|
+
const dt = new Date(Date.UTC(+y, +mo, 0));
|
|
4688
|
+
const ry = String(dt.getUTCFullYear()).padStart(4, "0");
|
|
4689
|
+
const rmo = String(dt.getUTCMonth() + 1).padStart(2, "0");
|
|
4690
|
+
const rd = String(dt.getUTCDate()).padStart(2, "0");
|
|
4691
|
+
return `${ry}-${rmo}-${rd}`;
|
|
4692
|
+
}
|
|
4311
4693
|
function applyFormat(num, pattern) {
|
|
4312
4694
|
if (/^-?\d+$/.test(pattern.trim())) {
|
|
4313
4695
|
return formatWithComma(num, Math.max(0, Number(pattern)));
|
|
@@ -4355,34 +4737,35 @@ function resolveFieldRef(row, field) {
|
|
|
4355
4737
|
}
|
|
4356
4738
|
|
|
4357
4739
|
// src/engine/evalWhere.ts
|
|
4358
|
-
function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
|
|
4740
|
+
function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
4359
4741
|
switch (expr.type) {
|
|
4360
4742
|
case "BINARY":
|
|
4361
|
-
return evalBinary(expr, row, resolveFieldType, appliedKlikes);
|
|
4743
|
+
return evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4362
4744
|
case "NULL_CHECK":
|
|
4363
4745
|
return evalNullCheck(expr, row);
|
|
4364
4746
|
case "LOGICAL":
|
|
4365
|
-
return evalLogical(expr, row, resolveFieldType, appliedKlikes);
|
|
4747
|
+
return evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4366
4748
|
case "NOT":
|
|
4367
|
-
return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
4749
|
+
return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4368
4750
|
case "GROUP":
|
|
4369
|
-
return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
4751
|
+
return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4370
4752
|
case "EXISTS": {
|
|
4371
4753
|
const exists = expr.resolved;
|
|
4372
4754
|
return expr.not ? !exists : exists;
|
|
4373
4755
|
}
|
|
4374
4756
|
}
|
|
4375
4757
|
}
|
|
4376
|
-
function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
|
|
4758
|
+
function evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
4377
4759
|
if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
|
|
4378
4760
|
if (appliedKlikes?.has(expr)) return true;
|
|
4379
4761
|
throw new Error("KLIKE / NOT KLIKE \u306F\u62BC\u3057\u4E0B\u3052\u6E08\u307F\u96C6\u5408\u306B\u542B\u307E\u308C\u306A\u3044\u305F\u3081 JavaScript \u5074\u3067\u306F\u8A55\u4FA1\u3067\u304D\u307E\u305B\u3093");
|
|
4380
4762
|
}
|
|
4381
|
-
const left = resolveField(expr.left, row, resolveFieldType);
|
|
4763
|
+
const left = resolveField(expr.left, row, resolveFieldType, resolveFieldSemantics2);
|
|
4382
4764
|
const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
|
|
4383
|
-
|
|
4765
|
+
const semantics = semanticsForLeft(expr.left, fieldType, resolveFieldSemantics2);
|
|
4766
|
+
return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType, semantics, resolveFieldSemantics2);
|
|
4384
4767
|
}
|
|
4385
|
-
function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
|
|
4768
|
+
function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics = syntheticSemantics("string"), resolveFieldSemantics2) {
|
|
4386
4769
|
if (op === "IN" || op === "NOT_IN") {
|
|
4387
4770
|
let values = null;
|
|
4388
4771
|
if (right.type === "IN_LIST") {
|
|
@@ -4407,8 +4790,53 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
|
|
|
4407
4790
|
if (op === "KLIKE" || op === "NOT_KLIKE") {
|
|
4408
4791
|
throw new Error("KLIKE / NOT KLIKE \u306F JavaScript \u5074\u3067\u306F\u8A55\u4FA1\u3067\u304D\u307E\u305B\u3093\uFF08SIMPLE SELECT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\uFF09");
|
|
4409
4792
|
}
|
|
4410
|
-
const rightStr = resolveValue(right, row, resolveFieldType);
|
|
4411
|
-
return compareScalarValues(op, leftStr, rightStr);
|
|
4793
|
+
const rightStr = resolveValue(right, row, resolveFieldType, resolveFieldSemantics2);
|
|
4794
|
+
return compareScalarValues(op, leftStr, rightStr, semantics);
|
|
4795
|
+
}
|
|
4796
|
+
var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
4797
|
+
"LENGTH",
|
|
4798
|
+
"INSTR",
|
|
4799
|
+
"ROUND",
|
|
4800
|
+
"FLOOR",
|
|
4801
|
+
"CEIL",
|
|
4802
|
+
"TRUNCATE",
|
|
4803
|
+
"YEAR",
|
|
4804
|
+
"MONTH",
|
|
4805
|
+
"DAY",
|
|
4806
|
+
"DATEDIFF",
|
|
4807
|
+
"ABS",
|
|
4808
|
+
"MOD",
|
|
4809
|
+
"POWER",
|
|
4810
|
+
"SQRT"
|
|
4811
|
+
]);
|
|
4812
|
+
function semanticsForLeft(left, fieldType, resolveSemantics) {
|
|
4813
|
+
if (left.type === "FIELD") {
|
|
4814
|
+
return resolveSemantics?.(left) ?? (fieldType ? resolveFieldSemantics({ fieldType }) : syntheticSemantics("string"));
|
|
4815
|
+
}
|
|
4816
|
+
if (left.type === "ARITH_FIELD") return syntheticSemantics("number");
|
|
4817
|
+
if (left.type === "FUNC_FIELD") {
|
|
4818
|
+
return syntheticSemantics(NUMERIC_STRING_FUNCTIONS.has(left.expr.func) ? "number" : "string");
|
|
4819
|
+
}
|
|
4820
|
+
if (left.type === "CASE_FIELD") {
|
|
4821
|
+
const results = [
|
|
4822
|
+
...left.expr.branches.map((branch) => branch.result),
|
|
4823
|
+
...left.expr.elseResult ? [left.expr.elseResult] : []
|
|
4824
|
+
];
|
|
4825
|
+
const modes = results.map((result) => {
|
|
4826
|
+
if (result.type === "NUMBER" || result.type === "ARITH") return syntheticSemantics("number");
|
|
4827
|
+
if (result.type === "STRING_FUNC") {
|
|
4828
|
+
return syntheticSemantics(NUMERIC_STRING_FUNCTIONS.has(result.func) ? "number" : "string");
|
|
4829
|
+
}
|
|
4830
|
+
if (result.type === "FIELD_REF") {
|
|
4831
|
+
const dot = result.field.indexOf(".");
|
|
4832
|
+
const ref = dot > 0 ? { type: "FIELD", tableAlias: result.field.slice(0, dot), field: result.field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: result.field };
|
|
4833
|
+
return resolveSemantics?.(ref) ?? syntheticSemantics("string");
|
|
4834
|
+
}
|
|
4835
|
+
return syntheticSemantics("string");
|
|
4836
|
+
});
|
|
4837
|
+
if (modes.length > 0 && modes.every((mode) => mode.compareMode === modes[0].compareMode)) return modes[0];
|
|
4838
|
+
}
|
|
4839
|
+
return syntheticSemantics("string");
|
|
4412
4840
|
}
|
|
4413
4841
|
var STRING_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
|
|
4414
4842
|
var OBJECT_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -4459,20 +4887,20 @@ function evalNullCheck(expr, row) {
|
|
|
4459
4887
|
const val = resolveField(expr.field, row);
|
|
4460
4888
|
return expr.not ? val !== "" : val === "";
|
|
4461
4889
|
}
|
|
4462
|
-
function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
|
|
4890
|
+
function evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
4463
4891
|
if (expr.op === "AND") {
|
|
4464
|
-
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
4892
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4465
4893
|
}
|
|
4466
|
-
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
4894
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4467
4895
|
}
|
|
4468
|
-
function resolveField(field, row, resolveFieldType) {
|
|
4896
|
+
function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
|
|
4469
4897
|
if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
|
|
4470
4898
|
if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
|
|
4471
|
-
if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType);
|
|
4899
|
+
if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
4472
4900
|
const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
4473
4901
|
return resolveFieldRef(row, key);
|
|
4474
4902
|
}
|
|
4475
|
-
function resolveValue(value, row, resolveFieldType) {
|
|
4903
|
+
function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
|
|
4476
4904
|
switch (value.type) {
|
|
4477
4905
|
case "VARIABLE":
|
|
4478
4906
|
throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
|
|
@@ -4495,14 +4923,14 @@ function resolveValue(value, row, resolveFieldType) {
|
|
|
4495
4923
|
if (value.expr.type === "STRING_FUNC") return evalStringFunc(value.expr, row);
|
|
4496
4924
|
return String(evalArithExpr(value.expr, row));
|
|
4497
4925
|
case "CASE_VALUE":
|
|
4498
|
-
return evalCaseWhen(value.expr, row, resolveFieldType);
|
|
4926
|
+
return evalCaseWhen(value.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
4499
4927
|
case "ARRAY":
|
|
4500
4928
|
return value.elements.map((e) => e.value).join(",");
|
|
4501
4929
|
}
|
|
4502
4930
|
}
|
|
4503
|
-
function evalCaseWhen(expr, row, resolveFieldType) {
|
|
4931
|
+
function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
4504
4932
|
for (const branch of expr.branches) {
|
|
4505
|
-
if (evalWhere(branch.condition, row, resolveFieldType)) {
|
|
4933
|
+
if (evalWhere(branch.condition, row, resolveFieldType, void 0, resolveFieldSemantics2)) {
|
|
4506
4934
|
return evalCaseResult(branch.result, row);
|
|
4507
4935
|
}
|
|
4508
4936
|
}
|
|
@@ -5133,6 +5561,141 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
5133
5561
|
};
|
|
5134
5562
|
}
|
|
5135
5563
|
|
|
5564
|
+
// src/core/optimization/canonicalOrderPlanner.ts
|
|
5565
|
+
var REST_OFFSET_MAX = 1e4;
|
|
5566
|
+
var REST_LIMIT_MAX = 500;
|
|
5567
|
+
function fieldSemantics(item, semantics) {
|
|
5568
|
+
return item.key.type === "FIELD_NAME" ? semantics.get(item.key.name) : void 0;
|
|
5569
|
+
}
|
|
5570
|
+
function planCanonicalOrder(input) {
|
|
5571
|
+
const { stmt } = input;
|
|
5572
|
+
const reasons = [];
|
|
5573
|
+
const windowOrderBy = stmt.columns.flatMap(
|
|
5574
|
+
(column) => column.type === "WINDOW_COL" ? column.orderBy : []
|
|
5575
|
+
);
|
|
5576
|
+
const allOrderBy = [...stmt.orderBy, ...windowOrderBy];
|
|
5577
|
+
for (const item of allOrderBy) {
|
|
5578
|
+
if (item.key.type !== "FIELD_NAME") continue;
|
|
5579
|
+
const semantics = fieldSemantics(item, input.orderSemantics);
|
|
5580
|
+
if (!semantics) {
|
|
5581
|
+
reasons.push("ORDER_KEY_UNRESOLVED");
|
|
5582
|
+
continue;
|
|
5583
|
+
}
|
|
5584
|
+
if (semantics.fieldType === "KSQL_AMBIGUOUS") reasons.push("ORDER_KEY_AMBIGUOUS");
|
|
5585
|
+
else if (semantics.compareMode === "unsupported") reasons.push("ORDER_KEY_UNSUPPORTED");
|
|
5586
|
+
}
|
|
5587
|
+
if (reasons.includes("ORDER_KEY_AMBIGUOUS")) {
|
|
5588
|
+
throw new Error(
|
|
5589
|
+
"ArgumentError: ORDER BY key is an ambiguous column reference (reason=ORDER_KEY_AMBIGUOUS). Qualify the key with its table alias."
|
|
5590
|
+
);
|
|
5591
|
+
}
|
|
5592
|
+
if (reasons.includes("ORDER_KEY_UNSUPPORTED") || reasons.includes("ORDER_KEY_UNRESOLVED")) {
|
|
5593
|
+
const reason = reasons.includes("ORDER_KEY_UNSUPPORTED") ? "ORDER_KEY_UNSUPPORTED" : "ORDER_KEY_UNRESOLVED";
|
|
5594
|
+
throw new Error(`ArgumentError: ORDER BY key has no canonical comparison contract (reason=${reason}).`);
|
|
5595
|
+
}
|
|
5596
|
+
const allRestEquivalent = stmt.orderBy.length > 0 && windowOrderBy.length === 0 && stmt.orderBy.every(
|
|
5597
|
+
(item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
|
|
5598
|
+
);
|
|
5599
|
+
if (!allRestEquivalent) reasons.push("ORDER_KEY_NOT_REST_EQUIVALENT");
|
|
5600
|
+
if (input.whereCapability !== "EXACT_PUSHDOWN") reasons.push("WHERE_NOT_EXACT");
|
|
5601
|
+
if (input.staticMode !== "SIMPLE") reasons.push("QUERY_SHAPE_LOCAL");
|
|
5602
|
+
if (stmt.limit === null || stmt.limit < 0 || stmt.limit > REST_LIMIT_MAX) {
|
|
5603
|
+
reasons.push("LIMIT_NOT_REST_WINDOW");
|
|
5604
|
+
}
|
|
5605
|
+
if ((stmt.offset ?? 0) < 0 || (stmt.offset ?? 0) > REST_OFFSET_MAX) {
|
|
5606
|
+
reasons.push("OFFSET_NOT_REST_WINDOW");
|
|
5607
|
+
}
|
|
5608
|
+
if (stmt.limit !== null && stmt.limit > input.maxRecords) reasons.push("MAX_RECORDS_WINDOW");
|
|
5609
|
+
if (input.hasKlike) reasons.push("KLIKE_NOT_REST_WINDOW");
|
|
5610
|
+
if (reasons.length === 0) {
|
|
5611
|
+
return {
|
|
5612
|
+
kind: "CANONICAL_REST_TOP_N",
|
|
5613
|
+
requiresCompleteInput: false,
|
|
5614
|
+
localOrderBy: false,
|
|
5615
|
+
applyLocalOffsetLimit: false,
|
|
5616
|
+
reasonCodes: []
|
|
5617
|
+
};
|
|
5618
|
+
}
|
|
5619
|
+
return {
|
|
5620
|
+
kind: "CANONICAL_LOCAL",
|
|
5621
|
+
requiresCompleteInput: allOrderBy.length > 0,
|
|
5622
|
+
localOrderBy: stmt.orderBy.length > 0,
|
|
5623
|
+
applyLocalOffsetLimit: stmt.orderBy.length > 0,
|
|
5624
|
+
reasonCodes: [...new Set(reasons)]
|
|
5625
|
+
};
|
|
5626
|
+
}
|
|
5627
|
+
|
|
5628
|
+
// src/core/optimization/korderPlanner.ts
|
|
5629
|
+
var KORDER_NATIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
5630
|
+
"RECORD_NUMBER",
|
|
5631
|
+
"SINGLE_LINE_TEXT",
|
|
5632
|
+
"NUMBER",
|
|
5633
|
+
"CALC",
|
|
5634
|
+
"DATE",
|
|
5635
|
+
"DATETIME",
|
|
5636
|
+
"TIME",
|
|
5637
|
+
"CREATED_TIME",
|
|
5638
|
+
"UPDATED_TIME",
|
|
5639
|
+
"DROP_DOWN",
|
|
5640
|
+
"RADIO_BUTTON",
|
|
5641
|
+
"STATUS",
|
|
5642
|
+
"LINK",
|
|
5643
|
+
"CREATOR",
|
|
5644
|
+
"MODIFIER"
|
|
5645
|
+
]);
|
|
5646
|
+
function planKorderNative(input) {
|
|
5647
|
+
const { stmt } = input;
|
|
5648
|
+
const reasons = [];
|
|
5649
|
+
if (stmt.orderMode !== "KINTONE_NATIVE") reasons.push("KORDER_MODE_REQUIRED");
|
|
5650
|
+
if (stmt.from.cteName !== null || stmt.from.subtableCode || input.staticMode !== "SIMPLE") {
|
|
5651
|
+
reasons.push("KORDER_QUERY_SHAPE_UNSUPPORTED");
|
|
5652
|
+
}
|
|
5653
|
+
if (input.whereCapability !== "EXACT_PUSHDOWN") reasons.push("KORDER_WHERE_NOT_EXACT");
|
|
5654
|
+
if (input.hasKlike) reasons.push("KORDER_KLIKE_UNSUPPORTED");
|
|
5655
|
+
if (stmt.orderBy.length === 0) reasons.push("KORDER_KEY_REQUIRED");
|
|
5656
|
+
for (const item of stmt.orderBy) {
|
|
5657
|
+
if (item.key.type !== "FIELD_NAME") {
|
|
5658
|
+
reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(key=${item.key.type})`);
|
|
5659
|
+
continue;
|
|
5660
|
+
}
|
|
5661
|
+
const name = item.key.name;
|
|
5662
|
+
const semantics = input.orderSemantics.get(name);
|
|
5663
|
+
if (!semantics) {
|
|
5664
|
+
reasons.push(`KORDER_KEY_UNRESOLVED(field=${name})`);
|
|
5665
|
+
continue;
|
|
5666
|
+
}
|
|
5667
|
+
if (name === "$id") continue;
|
|
5668
|
+
if (!semantics.source || semantics.source.fieldCode !== name) {
|
|
5669
|
+
reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(field=${name})`);
|
|
5670
|
+
continue;
|
|
5671
|
+
}
|
|
5672
|
+
if (!KORDER_NATIVE_FIELD_TYPES.has(semantics.fieldType)) {
|
|
5673
|
+
reasons.push(`KORDER_TYPE_UNSUPPORTED(field=${name}, type=${semantics.fieldType})`);
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
if (stmt.limit === null || stmt.limit < 0 || stmt.limit > 500) {
|
|
5677
|
+
reasons.push(`KORDER_LIMIT_INVALID(limit=${String(stmt.limit)})`);
|
|
5678
|
+
}
|
|
5679
|
+
if (stmt.limit !== null && stmt.limit > input.maxRecords) {
|
|
5680
|
+
reasons.push(`KORDER_LIMIT_EXCEEDS_MAX_RECORDS(limit=${stmt.limit}, maxRecords=${input.maxRecords})`);
|
|
5681
|
+
}
|
|
5682
|
+
const offset = stmt.offset ?? 0;
|
|
5683
|
+
if (offset < 0 || offset > 1e4) reasons.push(`KORDER_OFFSET_INVALID(offset=${offset})`);
|
|
5684
|
+
const unique = [...new Set(reasons)];
|
|
5685
|
+
if (unique.length > 0) {
|
|
5686
|
+
throw new Error(
|
|
5687
|
+
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
|
|
5688
|
+
);
|
|
5689
|
+
}
|
|
5690
|
+
return {
|
|
5691
|
+
kind: "KORDER_NATIVE",
|
|
5692
|
+
requiresCompleteInput: false,
|
|
5693
|
+
localOrderBy: false,
|
|
5694
|
+
applyLocalOffsetLimit: false,
|
|
5695
|
+
reasonCodes: []
|
|
5696
|
+
};
|
|
5697
|
+
}
|
|
5698
|
+
|
|
5136
5699
|
// src/engine/process.ts
|
|
5137
5700
|
function flatten(record, alias) {
|
|
5138
5701
|
const row = {};
|
|
@@ -5197,9 +5760,9 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
5197
5760
|
}
|
|
5198
5761
|
return result;
|
|
5199
5762
|
}
|
|
5200
|
-
function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
|
|
5763
|
+
function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
5201
5764
|
if (where === null) return rows;
|
|
5202
|
-
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
|
|
5765
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
|
|
5203
5766
|
}
|
|
5204
5767
|
function hasAggregateColumns(columns) {
|
|
5205
5768
|
return columns.some(
|
|
@@ -5260,7 +5823,7 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5260
5823
|
let strVal;
|
|
5261
5824
|
if (arg.type === "FIELD_REF") {
|
|
5262
5825
|
const raw = row[arg.field];
|
|
5263
|
-
if (raw === void 0 || raw === "") continue;
|
|
5826
|
+
if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
|
|
5264
5827
|
strVal = raw;
|
|
5265
5828
|
} else {
|
|
5266
5829
|
const n = evalArithExpr(arg, row);
|
|
@@ -5272,10 +5835,16 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5272
5835
|
const eff = distinct ? [...new Set(strValues)] : strValues;
|
|
5273
5836
|
if (func === "COUNT") return eff.length;
|
|
5274
5837
|
if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
|
|
5275
|
-
const
|
|
5276
|
-
if (
|
|
5277
|
-
if (eff.length === 0) return
|
|
5278
|
-
|
|
5838
|
+
const comparison = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
|
|
5839
|
+
if (func === "MIN" || func === "MAX") {
|
|
5840
|
+
if (eff.length === 0) return 0;
|
|
5841
|
+
const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" ? syntheticSemantics("string") : syntheticSemantics("number"));
|
|
5842
|
+
let result = eff[0];
|
|
5843
|
+
for (const candidate of eff.slice(1)) {
|
|
5844
|
+
const cmp = compareCanonicalValues(candidate, result, semantics);
|
|
5845
|
+
if (func === "MAX" && cmp > 0 || func === "MIN" && cmp < 0) result = candidate;
|
|
5846
|
+
}
|
|
5847
|
+
return result;
|
|
5279
5848
|
}
|
|
5280
5849
|
const nums = eff.map(Number);
|
|
5281
5850
|
switch (func) {
|
|
@@ -5283,37 +5852,12 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5283
5852
|
return nums.reduce((a, b) => a + b, 0);
|
|
5284
5853
|
case "AVG":
|
|
5285
5854
|
return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
5286
|
-
// Math.max(...nums) は要素数が多いと RangeError になるためループで求める
|
|
5287
|
-
case "MAX":
|
|
5288
|
-
return nums.length === 0 ? 0 : maxOf(nums);
|
|
5289
|
-
case "MIN":
|
|
5290
|
-
return nums.length === 0 ? 0 : minOf(nums);
|
|
5291
5855
|
}
|
|
5292
5856
|
}
|
|
5293
5857
|
function toAggregateFieldRef(field) {
|
|
5294
5858
|
const dot = field.indexOf(".");
|
|
5295
5859
|
return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
|
|
5296
5860
|
}
|
|
5297
|
-
function maxStringOf(values) {
|
|
5298
|
-
let value = values[0];
|
|
5299
|
-
for (const candidate of values) if (candidate > value) value = candidate;
|
|
5300
|
-
return value;
|
|
5301
|
-
}
|
|
5302
|
-
function minStringOf(values) {
|
|
5303
|
-
let value = values[0];
|
|
5304
|
-
for (const candidate of values) if (candidate < value) value = candidate;
|
|
5305
|
-
return value;
|
|
5306
|
-
}
|
|
5307
|
-
function maxOf(nums) {
|
|
5308
|
-
let m = nums[0];
|
|
5309
|
-
for (const n of nums) if (n > m) m = n;
|
|
5310
|
-
return m;
|
|
5311
|
-
}
|
|
5312
|
-
function minOf(nums) {
|
|
5313
|
-
let m = nums[0];
|
|
5314
|
-
for (const n of nums) if (n < m) m = n;
|
|
5315
|
-
return m;
|
|
5316
|
-
}
|
|
5317
5861
|
function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
5318
5862
|
if (node.type === "NUMBER") return node.value;
|
|
5319
5863
|
if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
|
|
@@ -5345,9 +5889,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
|
|
|
5345
5889
|
const argStr = aggregateArgLabel(arg);
|
|
5346
5890
|
return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
|
|
5347
5891
|
}
|
|
5348
|
-
function applyHaving(rows, having, resolveFieldType) {
|
|
5892
|
+
function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
|
|
5349
5893
|
if (having === null) return rows;
|
|
5350
|
-
return rows.filter((row) => evalWhere(having, row, resolveFieldType));
|
|
5894
|
+
return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
|
|
5351
5895
|
}
|
|
5352
5896
|
function applyDistinct(rows, columns) {
|
|
5353
5897
|
if (rows.length === 0) return rows;
|
|
@@ -5399,27 +5943,37 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
5399
5943
|
return JSON.stringify(values);
|
|
5400
5944
|
};
|
|
5401
5945
|
}
|
|
5402
|
-
function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
5946
|
+
function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
|
|
5403
5947
|
if (orderBy.length === 0) return rows;
|
|
5404
|
-
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
|
|
5405
|
-
}
|
|
5406
|
-
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
|
|
5407
|
-
const keyMeta = orderBy.map(({ key }) =>
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5948
|
+
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2).rows.map((item) => item.row);
|
|
5949
|
+
}
|
|
5950
|
+
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
|
|
5951
|
+
const keyMeta = orderBy.map(({ key }) => {
|
|
5952
|
+
if (key.type === "ARITH_KEY") return { semantics: syntheticSemantics("number") };
|
|
5953
|
+
if (key.type === "FUNC_KEY") {
|
|
5954
|
+
return { semantics: syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(key.expr.func) ? "number" : "string") };
|
|
5955
|
+
}
|
|
5956
|
+
const semantics = fieldSemantics2?.get(key.name);
|
|
5957
|
+
if (semantics) return { semantics };
|
|
5958
|
+
const orderMap = optionOrders?.get(key.name);
|
|
5959
|
+
if (orderMap) {
|
|
5960
|
+
return {
|
|
5961
|
+
semantics: {
|
|
5962
|
+
fieldType: "MULTI_SELECT",
|
|
5963
|
+
compareMode: "option",
|
|
5964
|
+
inSubtable: false,
|
|
5965
|
+
requiresCollectionOperators: false,
|
|
5966
|
+
optionOrder: orderMap
|
|
5967
|
+
}
|
|
5968
|
+
};
|
|
5969
|
+
}
|
|
5970
|
+
return { semantics: syntheticSemantics(sortKinds?.get(key.name) ?? "string") };
|
|
5971
|
+
});
|
|
5411
5972
|
const decorated = rows.map((row) => ({
|
|
5412
5973
|
row,
|
|
5413
5974
|
keys: orderBy.map(({ key }, i) => {
|
|
5414
5975
|
const s = evalOrderKey(key, row);
|
|
5415
|
-
|
|
5416
|
-
const orderMap = keyMeta[i].orderMap;
|
|
5417
|
-
return {
|
|
5418
|
-
s,
|
|
5419
|
-
n,
|
|
5420
|
-
isNum: !Number.isNaN(n),
|
|
5421
|
-
rank: orderMap ? minChoiceIndex(parseChoiceValues(s), orderMap) : 0
|
|
5422
|
-
};
|
|
5976
|
+
return { s };
|
|
5423
5977
|
})
|
|
5424
5978
|
}));
|
|
5425
5979
|
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
@@ -5434,15 +5988,24 @@ function compareDecoratedRows(a, b, orderBy, keyMeta) {
|
|
|
5434
5988
|
return 0;
|
|
5435
5989
|
}
|
|
5436
5990
|
function compareSortKeys(a, b, meta) {
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5991
|
+
return compareCanonicalValues(a.s, b.s, meta.semantics);
|
|
5992
|
+
}
|
|
5993
|
+
var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
5994
|
+
"LENGTH",
|
|
5995
|
+
"INSTR",
|
|
5996
|
+
"ROUND",
|
|
5997
|
+
"FLOOR",
|
|
5998
|
+
"CEIL",
|
|
5999
|
+
"TRUNCATE",
|
|
6000
|
+
"YEAR",
|
|
6001
|
+
"MONTH",
|
|
6002
|
+
"DAY",
|
|
6003
|
+
"DATEDIFF",
|
|
6004
|
+
"ABS",
|
|
6005
|
+
"MOD",
|
|
6006
|
+
"POWER",
|
|
6007
|
+
"SQRT"
|
|
6008
|
+
]);
|
|
5446
6009
|
function evalOrderKey(key, row) {
|
|
5447
6010
|
switch (key.type) {
|
|
5448
6011
|
case "FIELD_NAME":
|
|
@@ -5453,30 +6016,7 @@ function evalOrderKey(key, row) {
|
|
|
5453
6016
|
return evalStringFunc(key.expr, row);
|
|
5454
6017
|
}
|
|
5455
6018
|
}
|
|
5456
|
-
function
|
|
5457
|
-
const trimmed = raw.trim();
|
|
5458
|
-
if (trimmed === "") return [""];
|
|
5459
|
-
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
5460
|
-
try {
|
|
5461
|
-
const arr = JSON.parse(trimmed);
|
|
5462
|
-
if (Array.isArray(arr)) {
|
|
5463
|
-
return arr.map((v) => String(v ?? ""));
|
|
5464
|
-
}
|
|
5465
|
-
} catch {
|
|
5466
|
-
}
|
|
5467
|
-
}
|
|
5468
|
-
return [trimmed];
|
|
5469
|
-
}
|
|
5470
|
-
function minChoiceIndex(values, orderMap) {
|
|
5471
|
-
let min = Number.MAX_SAFE_INTEGER;
|
|
5472
|
-
for (const value of values) {
|
|
5473
|
-
const idx = orderMap.get(value);
|
|
5474
|
-
const rank = idx ?? Number.MAX_SAFE_INTEGER;
|
|
5475
|
-
if (rank < min) min = rank;
|
|
5476
|
-
}
|
|
5477
|
-
return min;
|
|
5478
|
-
}
|
|
5479
|
-
function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
6019
|
+
function applyWindow(rows, columns, optionOrders, sortKinds, fieldSemantics2) {
|
|
5480
6020
|
const windows = columns.filter((column) => column.type === "WINDOW_COL");
|
|
5481
6021
|
if (rows.length === 0 || windows.length === 0) return rows;
|
|
5482
6022
|
for (const window of windows) {
|
|
@@ -5488,7 +6028,7 @@ function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
|
5488
6028
|
else partitions.set(key, [row]);
|
|
5489
6029
|
}
|
|
5490
6030
|
for (const partition of partitions.values()) {
|
|
5491
|
-
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
|
|
6031
|
+
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
|
|
5492
6032
|
const sorted = sortedResult.rows;
|
|
5493
6033
|
let rank = 1;
|
|
5494
6034
|
let denseRank = 1;
|
|
@@ -5513,7 +6053,7 @@ function applyLimit(rows, limit, offset) {
|
|
|
5513
6053
|
if (limit === null) return rows.slice(start);
|
|
5514
6054
|
return rows.slice(start, start + limit);
|
|
5515
6055
|
}
|
|
5516
|
-
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
6056
|
+
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2) {
|
|
5517
6057
|
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
5518
6058
|
const projected2 = rows.map((row) => stripParentShortcutColumns(row));
|
|
5519
6059
|
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
|
|
@@ -5577,7 +6117,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
|
5577
6117
|
}
|
|
5578
6118
|
case "CASE_COL": {
|
|
5579
6119
|
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
5580
|
-
out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
|
|
6120
|
+
out[key] = evalCaseWhen(col.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
5581
6121
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
5582
6122
|
break;
|
|
5583
6123
|
}
|
|
@@ -5732,6 +6272,26 @@ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
|
5732
6272
|
args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
|
|
5733
6273
|
};
|
|
5734
6274
|
}
|
|
6275
|
+
function deriveOutputOrderSemantics(columns) {
|
|
6276
|
+
const result = /* @__PURE__ */ new Map();
|
|
6277
|
+
for (const column of columns) {
|
|
6278
|
+
if (!("alias" in column) || !column.alias) continue;
|
|
6279
|
+
if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
6280
|
+
result.set(column.alias, syntheticSemantics("number"));
|
|
6281
|
+
} else if (column.type === "AGGREGATE") {
|
|
6282
|
+
if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
|
|
6283
|
+
result.set(column.alias, syntheticSemantics("number"));
|
|
6284
|
+
} else if (column.func === "GROUP_CONCAT") {
|
|
6285
|
+
result.set(column.alias, syntheticSemantics("string"));
|
|
6286
|
+
}
|
|
6287
|
+
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
|
|
6288
|
+
result.set(column.alias, syntheticSemantics("string"));
|
|
6289
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
6290
|
+
result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
|
|
6291
|
+
}
|
|
6292
|
+
}
|
|
6293
|
+
return result;
|
|
6294
|
+
}
|
|
5735
6295
|
function runFullScan(input) {
|
|
5736
6296
|
const {
|
|
5737
6297
|
stmt,
|
|
@@ -5739,12 +6299,17 @@ function runFullScan(input) {
|
|
|
5739
6299
|
scalarCache,
|
|
5740
6300
|
optionOrders,
|
|
5741
6301
|
sortKinds,
|
|
6302
|
+
orderSemantics,
|
|
5742
6303
|
fieldTypeResolver,
|
|
6304
|
+
fieldSemanticsResolver,
|
|
5743
6305
|
havingFieldTypeResolver,
|
|
6306
|
+
havingFieldSemanticsResolver,
|
|
5744
6307
|
aggregateSortKindResolver,
|
|
5745
6308
|
appliedKlikes,
|
|
5746
6309
|
sourceColumns
|
|
5747
6310
|
} = input;
|
|
6311
|
+
const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
|
|
6312
|
+
for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
|
|
5748
6313
|
let rows = [];
|
|
5749
6314
|
const mainAlias = stmt.from.alias;
|
|
5750
6315
|
const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
|
|
@@ -5755,18 +6320,18 @@ function runFullScan(input) {
|
|
|
5755
6320
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
5756
6321
|
rows = applyJoin(rows, rightRows, join2);
|
|
5757
6322
|
}
|
|
5758
|
-
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
|
|
6323
|
+
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
|
|
5759
6324
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
5760
6325
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
|
|
5761
6326
|
}
|
|
5762
|
-
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
|
|
5763
|
-
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
|
|
6327
|
+
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
|
|
6328
|
+
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
5764
6329
|
if (stmt.distinct) {
|
|
5765
6330
|
rows = applyDistinct(rows, stmt.columns);
|
|
5766
6331
|
}
|
|
5767
|
-
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
|
|
6332
|
+
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
5768
6333
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
5769
|
-
return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
|
|
6334
|
+
return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns, fieldSemanticsResolver);
|
|
5770
6335
|
}
|
|
5771
6336
|
|
|
5772
6337
|
// src/converter/subtableAdapter.ts
|
|
@@ -6030,8 +6595,194 @@ function renderValidationValue(value) {
|
|
|
6030
6595
|
if (sql.type === "STRING" || sql.type === "NUMBER") return String(sql.value ?? "");
|
|
6031
6596
|
if (sql.type === "ARRAY") return JSON.stringify(sql.elements?.map((e) => e.value) ?? []);
|
|
6032
6597
|
}
|
|
6033
|
-
if (Array.isArray(value)) return JSON.stringify(value);
|
|
6034
|
-
return String(value);
|
|
6598
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
6599
|
+
return String(value);
|
|
6600
|
+
}
|
|
6601
|
+
|
|
6602
|
+
// src/core/optimization/whereCapability.ts
|
|
6603
|
+
var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
|
|
6604
|
+
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
6605
|
+
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
6606
|
+
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6607
|
+
["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6608
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6609
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6610
|
+
["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
6611
|
+
["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
6612
|
+
["DATE", new Set(RANGE_AND_EQUALITY)],
|
|
6613
|
+
["TIME", new Set(RANGE_AND_EQUALITY)],
|
|
6614
|
+
["DATETIME", new Set(RANGE_AND_EQUALITY)],
|
|
6615
|
+
["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
6616
|
+
["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
6617
|
+
["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6618
|
+
["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6619
|
+
["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
6620
|
+
["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
6621
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6622
|
+
["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6623
|
+
["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6624
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6625
|
+
["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
6626
|
+
["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6627
|
+
["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6628
|
+
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6629
|
+
["STATUS", new Set(EQUALITY_IN)]
|
|
6630
|
+
]);
|
|
6631
|
+
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
6632
|
+
"RECORD_NUMBER",
|
|
6633
|
+
"__ID__",
|
|
6634
|
+
"CREATOR",
|
|
6635
|
+
"MODIFIER",
|
|
6636
|
+
"CREATED_TIME",
|
|
6637
|
+
"UPDATED_TIME",
|
|
6638
|
+
"DATE",
|
|
6639
|
+
"TIME",
|
|
6640
|
+
"DATETIME",
|
|
6641
|
+
"SINGLE_LINE_TEXT",
|
|
6642
|
+
"LINK",
|
|
6643
|
+
"NUMBER",
|
|
6644
|
+
"CALC",
|
|
6645
|
+
"MULTI_LINE_TEXT",
|
|
6646
|
+
"RICH_TEXT",
|
|
6647
|
+
"RADIO_BUTTON",
|
|
6648
|
+
"DROP_DOWN",
|
|
6649
|
+
"STATUS",
|
|
6650
|
+
// 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
|
|
6651
|
+
"KSQL_STRING",
|
|
6652
|
+
"KSQL_NUMBER",
|
|
6653
|
+
"KSQL_BOOLEAN"
|
|
6654
|
+
]);
|
|
6655
|
+
var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
6656
|
+
"CHECK_BOX",
|
|
6657
|
+
"MULTI_SELECT",
|
|
6658
|
+
"FILE",
|
|
6659
|
+
"USER_SELECT",
|
|
6660
|
+
"ORGANIZATION_SELECT",
|
|
6661
|
+
"GROUP_SELECT",
|
|
6662
|
+
"STATUS_ASSIGNEE",
|
|
6663
|
+
"CATEGORY"
|
|
6664
|
+
]);
|
|
6665
|
+
function nativeWhereOperatorsForType(fieldType) {
|
|
6666
|
+
return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
|
|
6667
|
+
}
|
|
6668
|
+
function classifyWhereCapability(where, resolveField2) {
|
|
6669
|
+
if (where === null) {
|
|
6670
|
+
return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
|
|
6671
|
+
}
|
|
6672
|
+
return classifyNode(where, resolveField2);
|
|
6673
|
+
}
|
|
6674
|
+
function classifyNode(where, resolveField2) {
|
|
6675
|
+
switch (where.type) {
|
|
6676
|
+
case "BINARY":
|
|
6677
|
+
return classifyBinary(where.op, where.left, where.right.type, resolveField2);
|
|
6678
|
+
case "NULL_CHECK":
|
|
6679
|
+
if (where.field.type !== "FIELD") return localExpression();
|
|
6680
|
+
return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
|
|
6681
|
+
case "EXISTS":
|
|
6682
|
+
return localExpression();
|
|
6683
|
+
case "GROUP":
|
|
6684
|
+
return classifyNode(where.expr, resolveField2);
|
|
6685
|
+
case "NOT": {
|
|
6686
|
+
const inner = classifyNode(where.expr, resolveField2);
|
|
6687
|
+
return inner.capability === "SUPERSET_PREFILTER" ? { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] } : inner;
|
|
6688
|
+
}
|
|
6689
|
+
case "LOGICAL": {
|
|
6690
|
+
const left = classifyNode(where.left, resolveField2);
|
|
6691
|
+
const right = classifyNode(where.right, resolveField2);
|
|
6692
|
+
return combineLogical(where.op, left, right);
|
|
6693
|
+
}
|
|
6694
|
+
}
|
|
6695
|
+
}
|
|
6696
|
+
function classifyBinary(op, left, rightType, resolveField2) {
|
|
6697
|
+
if (left.type !== "FIELD") return localExpression();
|
|
6698
|
+
const semantics = resolveField2(left);
|
|
6699
|
+
if (!semantics) {
|
|
6700
|
+
return unsupported("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
|
|
6701
|
+
}
|
|
6702
|
+
if (!hasLocalContract(semantics.fieldType, op)) {
|
|
6703
|
+
return unsupported("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, normalizeOperator(op));
|
|
6704
|
+
}
|
|
6705
|
+
const nativeOp = normalizeOperator(op);
|
|
6706
|
+
const native = nativeWhereOperatorsForType(semantics.fieldType);
|
|
6707
|
+
const rightCanPush = rightType === "STRING" || rightType === "NUMBER" || rightType === "IN_LIST" || rightType === "KINTONE_FUNC";
|
|
6708
|
+
const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
|
|
6709
|
+
const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
|
|
6710
|
+
if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
|
|
6711
|
+
return {
|
|
6712
|
+
capability: "EXACT_PUSHDOWN",
|
|
6713
|
+
reasons: [{
|
|
6714
|
+
code: "WHERE_EXACT",
|
|
6715
|
+
field: left.field,
|
|
6716
|
+
fieldType: semantics.fieldType,
|
|
6717
|
+
operator: nativeOp
|
|
6718
|
+
}]
|
|
6719
|
+
};
|
|
6720
|
+
}
|
|
6721
|
+
return {
|
|
6722
|
+
capability: "LOCAL_ONLY",
|
|
6723
|
+
reasons: [{
|
|
6724
|
+
code: "WHERE_RESIDUAL",
|
|
6725
|
+
field: left.field,
|
|
6726
|
+
fieldType: semantics.fieldType,
|
|
6727
|
+
operator: nativeOp
|
|
6728
|
+
}]
|
|
6729
|
+
};
|
|
6730
|
+
}
|
|
6731
|
+
function classifyLocalOnlyField(field, operator, resolveField2) {
|
|
6732
|
+
const semantics = resolveField2(field);
|
|
6733
|
+
if (!semantics) return unsupported("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
|
|
6734
|
+
if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
|
|
6735
|
+
return unsupported("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
|
|
6736
|
+
}
|
|
6737
|
+
return {
|
|
6738
|
+
capability: "LOCAL_ONLY",
|
|
6739
|
+
reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
|
|
6740
|
+
};
|
|
6741
|
+
}
|
|
6742
|
+
function hasLocalContract(fieldType, op) {
|
|
6743
|
+
if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
|
|
6744
|
+
if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
|
|
6745
|
+
return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
|
|
6746
|
+
}
|
|
6747
|
+
function normalizeOperator(op) {
|
|
6748
|
+
switch (op) {
|
|
6749
|
+
case "<>":
|
|
6750
|
+
return "!=";
|
|
6751
|
+
case "IN":
|
|
6752
|
+
return "in";
|
|
6753
|
+
case "NOT_IN":
|
|
6754
|
+
return "not in";
|
|
6755
|
+
case "LIKE":
|
|
6756
|
+
case "KLIKE":
|
|
6757
|
+
return "like";
|
|
6758
|
+
case "NOT_LIKE":
|
|
6759
|
+
case "NOT_KLIKE":
|
|
6760
|
+
return "not like";
|
|
6761
|
+
default:
|
|
6762
|
+
return op;
|
|
6763
|
+
}
|
|
6764
|
+
}
|
|
6765
|
+
function combineLogical(op, left, right) {
|
|
6766
|
+
const reasons = [...left.reasons, ...right.reasons];
|
|
6767
|
+
if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
|
|
6768
|
+
return { capability: "UNSUPPORTED", reasons };
|
|
6769
|
+
}
|
|
6770
|
+
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
6771
|
+
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
6772
|
+
}
|
|
6773
|
+
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
6774
|
+
return {
|
|
6775
|
+
capability: "SUPERSET_PREFILTER",
|
|
6776
|
+
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
6777
|
+
};
|
|
6778
|
+
}
|
|
6779
|
+
return { capability: "LOCAL_ONLY", reasons };
|
|
6780
|
+
}
|
|
6781
|
+
function localExpression() {
|
|
6782
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
6783
|
+
}
|
|
6784
|
+
function unsupported(code, field, fieldType, operator) {
|
|
6785
|
+
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
6035
6786
|
}
|
|
6036
6787
|
|
|
6037
6788
|
// src/execute.ts
|
|
@@ -6044,8 +6795,20 @@ var SearchAbortedError = class extends Error {
|
|
|
6044
6795
|
};
|
|
6045
6796
|
var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
|
|
6046
6797
|
var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
|
|
6798
|
+
var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
|
|
6799
|
+
var nextDefaultCacheContextId = 1;
|
|
6800
|
+
function resolveCacheContext(client, explicit) {
|
|
6801
|
+
if (explicit) return explicit;
|
|
6802
|
+
let context = defaultCacheContextByClient.get(client);
|
|
6803
|
+
if (!context) {
|
|
6804
|
+
context = `client:${nextDefaultCacheContextId++}`;
|
|
6805
|
+
defaultCacheContextByClient.set(client, context);
|
|
6806
|
+
}
|
|
6807
|
+
return context;
|
|
6808
|
+
}
|
|
6047
6809
|
async function execute(sql, client, options = {}) {
|
|
6048
6810
|
const startedAt = Date.now();
|
|
6811
|
+
const cacheContext = resolveCacheContext(client, options.cacheContext);
|
|
6049
6812
|
const stmt = parseSql(sql);
|
|
6050
6813
|
const metrics = createEmptyMetrics();
|
|
6051
6814
|
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
@@ -6059,7 +6822,7 @@ async function execute(sql, client, options = {}) {
|
|
|
6059
6822
|
stmt,
|
|
6060
6823
|
guardedClient,
|
|
6061
6824
|
options,
|
|
6062
|
-
|
|
6825
|
+
cacheContext
|
|
6063
6826
|
);
|
|
6064
6827
|
metrics.elapsedMs = Date.now() - startedAt;
|
|
6065
6828
|
return { ...attachSearchAbortWarning(result, collector), metrics };
|
|
@@ -6174,7 +6937,7 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
6174
6937
|
case "DESCRIBE":
|
|
6175
6938
|
return executeDescribe(stmt, client, cacheContext);
|
|
6176
6939
|
case "EXPLAIN":
|
|
6177
|
-
return executeExplain(stmt);
|
|
6940
|
+
return executeExplain(stmt, client, cacheContext, options.maxRecords ?? 1e4);
|
|
6178
6941
|
// 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
|
|
6179
6942
|
case "CREATE_TEMP_TABLE":
|
|
6180
6943
|
throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
@@ -6205,7 +6968,7 @@ function materializedColumnMetaEqual(left, right) {
|
|
|
6205
6968
|
if (!left || !right || left.size !== right.size) return false;
|
|
6206
6969
|
for (const [column, meta] of left) {
|
|
6207
6970
|
const candidate = right.get(column);
|
|
6208
|
-
if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType) return false;
|
|
6971
|
+
if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType || !fieldSemanticsEqual(candidate.semantics, meta.semantics)) return false;
|
|
6209
6972
|
}
|
|
6210
6973
|
return true;
|
|
6211
6974
|
}
|
|
@@ -6236,7 +6999,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
6236
6999
|
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
6237
7000
|
const startedAt = Date.now();
|
|
6238
7001
|
const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
|
|
6239
|
-
const cacheContext = options.cacheContext
|
|
7002
|
+
const cacheContext = resolveCacheContext(client, options.cacheContext);
|
|
6240
7003
|
const tempTables = /* @__PURE__ */ new Map();
|
|
6241
7004
|
const variables = /* @__PURE__ */ new Map();
|
|
6242
7005
|
const results = [];
|
|
@@ -6330,7 +7093,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
6330
7093
|
cacheContext,
|
|
6331
7094
|
tempTables
|
|
6332
7095
|
);
|
|
6333
|
-
|
|
7096
|
+
const first = resolvedStmt2.expr.query.columns[0];
|
|
7097
|
+
const numeric = first?.type === "ARITH_COL" || first?.type === "ARITH_AGG_COL" || first?.type === "WINDOW_COL" || first?.type === "AGGREGATE" && (first.func === "COUNT" || first.func === "SUM" || first.func === "AVG");
|
|
7098
|
+
const numberValue = numeric ? Number(value) : Number.NaN;
|
|
7099
|
+
variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
|
|
6334
7100
|
} catch (e) {
|
|
6335
7101
|
if (e instanceof ScalarSubqueryError) {
|
|
6336
7102
|
throw new Error(`ArgumentError: ${e.message}`);
|
|
@@ -6561,13 +7327,14 @@ var ScalarSubqueryError = class extends Error {
|
|
|
6561
7327
|
};
|
|
6562
7328
|
async function executeAssert(stmt, client, options, cacheContext, tempTables) {
|
|
6563
7329
|
const left = await evalAssertOperand(stmt.left, client, options, cacheContext, tempTables);
|
|
7330
|
+
const semantics = stmt.left.type === "NUMBER" || stmt.left.type === "ARITH" ? syntheticSemantics("number") : syntheticSemantics("string");
|
|
6564
7331
|
if (stmt.op === "BETWEEN") {
|
|
6565
7332
|
if (stmt.low === null || stmt.high === null) {
|
|
6566
7333
|
throw new Error("ArgumentError: malformed ASSERT statement.");
|
|
6567
7334
|
}
|
|
6568
7335
|
const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
|
|
6569
7336
|
const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
|
|
6570
|
-
if (!compareScalarValues(">=", left, low) || !compareScalarValues("<=", left, high)) {
|
|
7337
|
+
if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
|
|
6571
7338
|
throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
|
|
6572
7339
|
}
|
|
6573
7340
|
return { type: "ASSERT", condition: stmt.text };
|
|
@@ -6576,7 +7343,7 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
|
|
|
6576
7343
|
throw new Error("ArgumentError: malformed ASSERT statement.");
|
|
6577
7344
|
}
|
|
6578
7345
|
const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
|
|
6579
|
-
if (!compareScalarValues(stmt.op, left, right)) {
|
|
7346
|
+
if (!compareScalarValues(stmt.op, left, right, semantics)) {
|
|
6580
7347
|
throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
|
|
6581
7348
|
}
|
|
6582
7349
|
return { type: "ASSERT", condition: stmt.text };
|
|
@@ -6649,6 +7416,149 @@ function evalAssertArith(node) {
|
|
|
6649
7416
|
}
|
|
6650
7417
|
throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
|
|
6651
7418
|
}
|
|
7419
|
+
async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables, forcePhysicalMetadata = false) {
|
|
7420
|
+
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
7421
|
+
const physicalAppIds = forcePhysicalMetadata || whereNeedsFieldMetadata(stmt.where) ? [...new Set(tables.filter((table) => table.cteName === null).map((table) => table.appId))] : [];
|
|
7422
|
+
const infosByApp = new Map(
|
|
7423
|
+
await Promise.all(physicalAppIds.map(async (appId) => {
|
|
7424
|
+
const infos = await getFieldsCached(appId, client, cacheContext);
|
|
7425
|
+
return [appId, new Map(infos.map((info) => [info.code, info]))];
|
|
7426
|
+
}))
|
|
7427
|
+
);
|
|
7428
|
+
const orderedFields = /* @__PURE__ */ new Set();
|
|
7429
|
+
const collectOrderedFields = (node) => {
|
|
7430
|
+
if (Array.isArray(node)) {
|
|
7431
|
+
node.forEach(collectOrderedFields);
|
|
7432
|
+
return;
|
|
7433
|
+
}
|
|
7434
|
+
if (node === null || typeof node !== "object") return;
|
|
7435
|
+
const value = node;
|
|
7436
|
+
if (value["type"] === "SELECT") return;
|
|
7437
|
+
if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
|
|
7438
|
+
const left = value["left"];
|
|
7439
|
+
if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
|
|
7440
|
+
orderedFields.add(left["field"]);
|
|
7441
|
+
}
|
|
7442
|
+
}
|
|
7443
|
+
Object.values(value).forEach(collectOrderedFields);
|
|
7444
|
+
};
|
|
7445
|
+
collectOrderedFields(stmt.where);
|
|
7446
|
+
collectOrderedFields(stmt.having);
|
|
7447
|
+
for (const column of stmt.columns) {
|
|
7448
|
+
if (column.type === "CASE_COL") collectOrderedFields(column.expr);
|
|
7449
|
+
}
|
|
7450
|
+
const statusOrdersByApp = /* @__PURE__ */ new Map();
|
|
7451
|
+
await Promise.all([...infosByApp].map(async ([appId, infos]) => {
|
|
7452
|
+
const needsStatus = [...orderedFields].some((field) => infos.get(field)?.fieldType === "STATUS");
|
|
7453
|
+
if (!needsStatus) return;
|
|
7454
|
+
const order = await loadProcessStatusOrder(appId, client, cacheContext);
|
|
7455
|
+
if (order) statusOrdersByApp.set(appId, order);
|
|
7456
|
+
}));
|
|
7457
|
+
const fromPhysical = (table, field) => {
|
|
7458
|
+
if (field === "$id") return withFieldSemanticSource(
|
|
7459
|
+
resolveFieldSemantics({ fieldType: "__ID__" }),
|
|
7460
|
+
table.appId,
|
|
7461
|
+
"$id"
|
|
7462
|
+
);
|
|
7463
|
+
const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field));
|
|
7464
|
+
if (!info) return void 0;
|
|
7465
|
+
const base = info.semantics ?? resolveFieldSemantics(info);
|
|
7466
|
+
const semantics = info.fieldType === "STATUS" && statusOrdersByApp.has(table.appId) ? { ...base, optionOrder: statusOrdersByApp.get(table.appId) } : base;
|
|
7467
|
+
return withFieldSemanticSource(
|
|
7468
|
+
semantics,
|
|
7469
|
+
table.appId,
|
|
7470
|
+
info.code
|
|
7471
|
+
);
|
|
7472
|
+
};
|
|
7473
|
+
return (field) => {
|
|
7474
|
+
if (field.tableAlias !== null) {
|
|
7475
|
+
if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
7476
|
+
return fromPhysical(stmt.from, field.field);
|
|
7477
|
+
}
|
|
7478
|
+
const table = tables.find((candidate) => candidate.alias === field.tableAlias);
|
|
7479
|
+
if (!table) return void 0;
|
|
7480
|
+
if (table.cteName !== null) {
|
|
7481
|
+
return materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
|
|
7482
|
+
}
|
|
7483
|
+
return fromPhysical(table, field.field);
|
|
7484
|
+
}
|
|
7485
|
+
if (stmt.joins.length === 0) {
|
|
7486
|
+
if (stmt.from.cteName !== null) {
|
|
7487
|
+
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
|
|
7488
|
+
}
|
|
7489
|
+
return fromPhysical(stmt.from, field.field);
|
|
7490
|
+
}
|
|
7491
|
+
const matches = tables.flatMap((table) => {
|
|
7492
|
+
const semantics = table.cteName !== null ? materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics : fromPhysical(table, field.field);
|
|
7493
|
+
return semantics ? [semantics] : [];
|
|
7494
|
+
});
|
|
7495
|
+
if (matches.length === 1) return matches[0];
|
|
7496
|
+
return matches.length > 1 ? syntheticSemantics("string") : void 0;
|
|
7497
|
+
};
|
|
7498
|
+
}
|
|
7499
|
+
function selectCaseConditionsNeedFieldMetadata(stmt) {
|
|
7500
|
+
return stmt.columns.some((column) => column.type === "CASE_COL" && column.expr.branches.some((branch) => whereNeedsFieldMetadata(branch.condition)));
|
|
7501
|
+
}
|
|
7502
|
+
function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
|
|
7503
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
7504
|
+
for (const column of stmt.columns) {
|
|
7505
|
+
if (!("alias" in column) || !column.alias) continue;
|
|
7506
|
+
let semantics;
|
|
7507
|
+
if (column.type === "FIELD") semantics = rowResolver(aggregateFieldRef(column.field));
|
|
7508
|
+
else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
7509
|
+
semantics = syntheticSemantics("number");
|
|
7510
|
+
} else if (column.type === "AGGREGATE") {
|
|
7511
|
+
if (column.func === "MIN" || column.func === "MAX") {
|
|
7512
|
+
semantics = column.arg.type === "FIELD_REF" ? rowResolver(aggregateFieldRef(column.arg.field)) : syntheticSemantics("number");
|
|
7513
|
+
} else {
|
|
7514
|
+
semantics = column.func === "GROUP_CONCAT" ? syntheticSemantics("string") : syntheticSemantics("number");
|
|
7515
|
+
}
|
|
7516
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
7517
|
+
semantics = stringFunctionColumnMeta(column.expr).semantics;
|
|
7518
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
|
|
7519
|
+
semantics = syntheticSemantics("string");
|
|
7520
|
+
}
|
|
7521
|
+
if (semantics) aliases.set(column.alias, semantics);
|
|
7522
|
+
}
|
|
7523
|
+
return (field) => field.tableAlias === null && aliases.has(field.field) ? aliases.get(field.field) : rowResolver(field);
|
|
7524
|
+
}
|
|
7525
|
+
async function resolveSelectWhereCapability(stmt, client, cacheContext, materializedTables) {
|
|
7526
|
+
if (stmt.where === null) return classifyWhereCapability(null, () => void 0);
|
|
7527
|
+
const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables);
|
|
7528
|
+
return classifyWhereCapability(stmt.where, resolver);
|
|
7529
|
+
}
|
|
7530
|
+
function formatWhereCapabilityFailure(result) {
|
|
7531
|
+
const reason = result.reasons.find(
|
|
7532
|
+
(candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED"
|
|
7533
|
+
) ?? result.reasons[0];
|
|
7534
|
+
const details = [
|
|
7535
|
+
reason?.field ? `field=${reason.field}` : null,
|
|
7536
|
+
reason?.fieldType ? `type=${reason.fieldType}` : null,
|
|
7537
|
+
reason?.operator ? `operator=${reason.operator}` : null,
|
|
7538
|
+
reason?.code ? `reason=${reason.code}` : null
|
|
7539
|
+
].filter((value) => value !== null).join(", ");
|
|
7540
|
+
return details || "reason=WHERE_UNSUPPORTED";
|
|
7541
|
+
}
|
|
7542
|
+
function hasCanonicalOrder(stmt) {
|
|
7543
|
+
return stmt.orderBy.length > 0 || stmt.columns.some(
|
|
7544
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
7545
|
+
);
|
|
7546
|
+
}
|
|
7547
|
+
async function assertDmlWhereCapability(stmt, client, cacheContext) {
|
|
7548
|
+
if (stmt.subtableCode || stmt.type === "UPDATE" && stmt.from != null) return;
|
|
7549
|
+
const fields = whereNeedsFieldMetadata(stmt.where) ? await getFieldsCached(stmt.appId, client, cacheContext) : [];
|
|
7550
|
+
const byCode = new Map(fields.map((field) => [field.code, field]));
|
|
7551
|
+
const result = classifyWhereCapability(stmt.where, (field) => {
|
|
7552
|
+
if (field.field === "$id") return resolveFieldSemantics({ fieldType: "__ID__" });
|
|
7553
|
+
const info = byCode.get(field.field);
|
|
7554
|
+
return info?.semantics ?? (info ? resolveFieldSemantics(info) : void 0);
|
|
7555
|
+
});
|
|
7556
|
+
if (result.capability !== "EXACT_PUSHDOWN") {
|
|
7557
|
+
throw new DmlConvertError(
|
|
7558
|
+
`WHERE predicate cannot be represented by kintone REST (${formatWhereCapabilityFailure(result)})`
|
|
7559
|
+
);
|
|
7560
|
+
}
|
|
7561
|
+
}
|
|
6652
7562
|
async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
|
|
6653
7563
|
let result;
|
|
6654
7564
|
if (isNoFromSelect(stmt)) {
|
|
@@ -6659,12 +7569,51 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
6659
7569
|
return result;
|
|
6660
7570
|
}
|
|
6661
7571
|
await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
|
|
6662
|
-
const
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
7572
|
+
const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
|
|
7573
|
+
if (whereCapability.capability === "UNSUPPORTED") {
|
|
7574
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
7575
|
+
}
|
|
7576
|
+
const staticMode = resolveSelectMode(stmt);
|
|
7577
|
+
const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
|
|
7578
|
+
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
7579
|
+
const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
|
|
7580
|
+
stmt,
|
|
7581
|
+
staticMode: mode,
|
|
7582
|
+
whereCapability: whereCapability.capability,
|
|
7583
|
+
orderSemantics: orderMeta.semantics,
|
|
7584
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
7585
|
+
hasKlike: whereHasKlike(stmt.where)
|
|
7586
|
+
}) : null;
|
|
7587
|
+
await validateSelectFieldCodes(
|
|
7588
|
+
stmt,
|
|
7589
|
+
orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : mode,
|
|
7590
|
+
client,
|
|
7591
|
+
cacheContext
|
|
7592
|
+
);
|
|
7593
|
+
const completeInputRequired = orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" ? requiresCompleteInput({ ...stmt, orderBy: [] }) : requiresCompleteInput(stmt);
|
|
7594
|
+
const truncateWasDisabled = completeInputRequired && options.onLimitReached === "truncate";
|
|
7595
|
+
const effectiveOptions = truncateWasDisabled ? { ...options, onLimitReached: "error" } : options;
|
|
7596
|
+
try {
|
|
7597
|
+
if (mode === "SIMPLE") {
|
|
7598
|
+
result = await executeSimpleSelect(stmt, client, effectiveOptions, cacheContext, orderPlan, orderMeta);
|
|
7599
|
+
} else {
|
|
7600
|
+
result = await executeFullScanSelect(
|
|
7601
|
+
stmt,
|
|
7602
|
+
client,
|
|
7603
|
+
effectiveOptions,
|
|
7604
|
+
cacheContext,
|
|
7605
|
+
cteCache,
|
|
7606
|
+
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
7607
|
+
orderMeta
|
|
7608
|
+
);
|
|
7609
|
+
}
|
|
7610
|
+
} catch (error) {
|
|
7611
|
+
if (completeInputRequired && error instanceof FetchAllLimitError) {
|
|
7612
|
+
throw new FetchAllLimitError(
|
|
7613
|
+
"ORDER BY\u306E\u6B63\u3057\u3044\u7D50\u679C\u306B\u306F\u5B8C\u5168\u306A\u5019\u88DC\u96C6\u5408\u304C\u5FC5\u8981\u3067\u3059\u3002" + (truncateWasDisabled ? "onLimit=truncate\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002" : "") + error.message
|
|
7614
|
+
);
|
|
7615
|
+
}
|
|
7616
|
+
throw error;
|
|
6668
7617
|
}
|
|
6669
7618
|
if (captureColumnMeta) {
|
|
6670
7619
|
materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
|
|
@@ -6725,19 +7674,30 @@ function executeNoFromSelect(stmt) {
|
|
|
6725
7674
|
const rows = applyLimit(projected, stmt.limit, stmt.offset);
|
|
6726
7675
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
|
|
6727
7676
|
}
|
|
6728
|
-
async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
6729
|
-
const
|
|
7677
|
+
async function executeSimpleSelect(stmt, client, options, cacheContext, orderPlan, orderMeta) {
|
|
7678
|
+
const restStmt = orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt;
|
|
7679
|
+
const params = selectToKintoneParams(restStmt);
|
|
7680
|
+
const fetchFields = orderPlan?.kind === "CANONICAL_LOCAL" ? selectToFetchAllFields(stmt, stmt.from) : params.fields;
|
|
6730
7681
|
const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
|
|
6731
7682
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
7683
|
+
const projectionSemanticsResolver = stmt.columns.some((column) => column.type === "CASE_COL") ? await buildWhereFieldSemanticsResolver(
|
|
7684
|
+
stmt,
|
|
7685
|
+
client,
|
|
7686
|
+
cacheContext,
|
|
7687
|
+
void 0,
|
|
7688
|
+
selectCaseConditionsNeedFieldMetadata(stmt)
|
|
7689
|
+
) : void 0;
|
|
6732
7690
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
6733
7691
|
const warnings = /* @__PURE__ */ new Set();
|
|
6734
7692
|
const onLimit = options.onLimitReached ?? "error";
|
|
6735
7693
|
const parallel = options.fetchParallel ?? 1;
|
|
6736
|
-
const
|
|
7694
|
+
const useRestWindow = stmt.orderBy.length > 0 ? orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" : stmt.limit !== null && stmt.limit <= 500;
|
|
6737
7695
|
const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
|
|
6738
7696
|
const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords && !whereHasKlike(stmt.where) ? needed : void 0;
|
|
6739
7697
|
let records;
|
|
6740
|
-
if (
|
|
7698
|
+
if (orderPlan?.kind === "KORDER_NATIVE" && stmt.limit === 0) {
|
|
7699
|
+
records = [];
|
|
7700
|
+
} else if (useRestWindow) {
|
|
6741
7701
|
const res = await client.getRecords({
|
|
6742
7702
|
app: params.app,
|
|
6743
7703
|
query: params.query,
|
|
@@ -6750,7 +7710,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
6750
7710
|
client.getRecords,
|
|
6751
7711
|
params.app,
|
|
6752
7712
|
baseQuery,
|
|
6753
|
-
|
|
7713
|
+
fetchFields,
|
|
6754
7714
|
{
|
|
6755
7715
|
parallel,
|
|
6756
7716
|
maxRecords,
|
|
@@ -6763,16 +7723,23 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
6763
7723
|
);
|
|
6764
7724
|
}
|
|
6765
7725
|
let rows = records.map((r) => flatten(r, null));
|
|
6766
|
-
if (!
|
|
6767
|
-
|
|
6768
|
-
|
|
7726
|
+
if (!useRestWindow) {
|
|
7727
|
+
rows = applyOrderBy(
|
|
7728
|
+
rows,
|
|
7729
|
+
stmt.orderBy,
|
|
7730
|
+
orderMeta.optionOrders,
|
|
7731
|
+
orderMeta.sortKinds,
|
|
7732
|
+
orderMeta.semantics
|
|
7733
|
+
);
|
|
6769
7734
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
6770
7735
|
}
|
|
6771
7736
|
const { rows: projected, columns } = project(
|
|
6772
7737
|
rows,
|
|
6773
7738
|
stmt.columns,
|
|
6774
7739
|
void 0,
|
|
6775
|
-
fieldTypeResolvers.row
|
|
7740
|
+
fieldTypeResolvers.row,
|
|
7741
|
+
void 0,
|
|
7742
|
+
projectionSemanticsResolver
|
|
6776
7743
|
);
|
|
6777
7744
|
return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
|
|
6778
7745
|
}
|
|
@@ -6845,8 +7812,8 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
|
|
|
6845
7812
|
const statusFields = collectCandidateFieldCodes(candidates).filter((fieldCode) => fieldTypes.get(fieldCode) === "STATUS");
|
|
6846
7813
|
if (statusFields.length > 0) {
|
|
6847
7814
|
const process2 = await getProcessStatusesCached(appId, client, cacheContext);
|
|
6848
|
-
if (process2.enable && process2.states.length > 0) {
|
|
6849
|
-
const states = new Set(process2.states);
|
|
7815
|
+
if (process2.enable && process2.states && process2.states.length > 0) {
|
|
7816
|
+
const states = new Set(process2.states.map((state) => state.name));
|
|
6850
7817
|
for (const fieldCode of statusFields) fieldOptions.set(fieldCode, states);
|
|
6851
7818
|
}
|
|
6852
7819
|
}
|
|
@@ -7024,7 +7991,18 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
7024
7991
|
return [appId, new Map(infos.map((info) => [info.code, info]))];
|
|
7025
7992
|
}))
|
|
7026
7993
|
);
|
|
7994
|
+
const statusOrdersByApp = /* @__PURE__ */ new Map();
|
|
7995
|
+
const aggregateFieldNames = new Set(refs.map((ref) => ref.field));
|
|
7996
|
+
await Promise.all([...fieldInfosByApp].map(async ([appId, infos]) => {
|
|
7997
|
+
if (![...aggregateFieldNames].some((field) => infos.get(field)?.fieldType === "STATUS")) return;
|
|
7998
|
+
const order = await loadProcessStatusOrder(appId, client, cacheContext);
|
|
7999
|
+
if (order) statusOrdersByApp.set(appId, order);
|
|
8000
|
+
}));
|
|
7027
8001
|
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
8002
|
+
const semanticsForInfo = (info, appId) => {
|
|
8003
|
+
const base = info.semantics ?? resolveFieldSemantics(info);
|
|
8004
|
+
return info.fieldType === "STATUS" && statusOrdersByApp.has(appId) ? { ...base, optionOrder: statusOrdersByApp.get(appId) } : base;
|
|
8005
|
+
};
|
|
7028
8006
|
return (ref) => {
|
|
7029
8007
|
let info;
|
|
7030
8008
|
if (ref.tableAlias !== null) {
|
|
@@ -7034,40 +8012,130 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
7034
8012
|
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
7035
8013
|
if (!table) return void 0;
|
|
7036
8014
|
if (table.cteName !== null) {
|
|
7037
|
-
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.
|
|
8015
|
+
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
|
|
7038
8016
|
}
|
|
7039
8017
|
info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7040
8018
|
}
|
|
7041
8019
|
} else if (stmt.joins.length === 0) {
|
|
7042
8020
|
if (stmt.from.cteName !== null) {
|
|
7043
|
-
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.
|
|
8021
|
+
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
|
|
7044
8022
|
}
|
|
7045
8023
|
info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
7046
8024
|
} else {
|
|
7047
8025
|
const matches = tables.flatMap((table) => {
|
|
7048
8026
|
if (table.cteName !== null) {
|
|
7049
8027
|
const materialized = materializedTables?.get(table.cteName);
|
|
7050
|
-
return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.
|
|
8028
|
+
return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string")] : [];
|
|
7051
8029
|
}
|
|
7052
8030
|
const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7053
|
-
return candidate ? [
|
|
8031
|
+
return candidate ? [semanticsForInfo(candidate, table.appId)] : [];
|
|
7054
8032
|
});
|
|
7055
8033
|
if (matches.length !== 1) return void 0;
|
|
7056
8034
|
return matches[0];
|
|
7057
8035
|
}
|
|
7058
|
-
|
|
8036
|
+
if (!info) return void 0;
|
|
8037
|
+
const sourceTable = ref.tableAlias !== null ? tables.find((table) => table.alias === ref.tableAlias) : stmt.joins.length === 0 ? stmt.from : void 0;
|
|
8038
|
+
return semanticsForInfo(info, sourceTable?.appId ?? stmt.from.appId);
|
|
7059
8039
|
};
|
|
7060
8040
|
}
|
|
7061
8041
|
function fieldCodeForTypeLookup(table, field) {
|
|
7062
8042
|
if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
|
|
7063
8043
|
return field;
|
|
7064
8044
|
}
|
|
7065
|
-
function materializedMetaFromFieldInfo(info) {
|
|
7066
|
-
|
|
8045
|
+
function materializedMetaFromFieldInfo(info, sourceAppId) {
|
|
8046
|
+
const semantics = info.semantics ?? resolveFieldSemantics(info);
|
|
8047
|
+
return {
|
|
8048
|
+
sortKind: aggregateSortKind(info),
|
|
8049
|
+
fieldType: info.fieldType,
|
|
8050
|
+
semantics: sourceAppId === void 0 ? semantics : withFieldSemanticSource(semantics, sourceAppId, info.code)
|
|
8051
|
+
};
|
|
8052
|
+
}
|
|
8053
|
+
function withCanonicalRestTie(stmt) {
|
|
8054
|
+
const hasId = stmt.orderBy.some(
|
|
8055
|
+
(item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
|
|
8056
|
+
);
|
|
8057
|
+
return hasId ? stmt : {
|
|
8058
|
+
...stmt,
|
|
8059
|
+
orderBy: [...stmt.orderBy, { key: { type: "FIELD_NAME", name: "$id" }, direction: "ASC" }]
|
|
8060
|
+
};
|
|
8061
|
+
}
|
|
8062
|
+
function syntheticColumnMeta(compareMode) {
|
|
8063
|
+
return { sortKind: compareMode, semantics: syntheticSemantics(compareMode) };
|
|
8064
|
+
}
|
|
8065
|
+
function unknownStringColumnMeta() {
|
|
8066
|
+
return { semantics: syntheticSemantics("string", "KSQL_UNKNOWN") };
|
|
8067
|
+
}
|
|
8068
|
+
function unsupportedColumnMeta(fieldType = "KSQL_ARRAY") {
|
|
8069
|
+
return {
|
|
8070
|
+
semantics: { fieldType, compareMode: "unsupported", inSubtable: false, requiresCollectionOperators: false }
|
|
8071
|
+
};
|
|
8072
|
+
}
|
|
8073
|
+
function systemColumnMeta(field) {
|
|
8074
|
+
if (field === "$id" || field === "_rid" || field === "_pid") {
|
|
8075
|
+
return {
|
|
8076
|
+
sortKind: "number",
|
|
8077
|
+
fieldType: "__ID__",
|
|
8078
|
+
semantics: resolveFieldSemantics({ fieldType: "__ID__" })
|
|
8079
|
+
};
|
|
8080
|
+
}
|
|
8081
|
+
if (field === "$revision") return syntheticColumnMeta("number");
|
|
8082
|
+
return void 0;
|
|
8083
|
+
}
|
|
8084
|
+
var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
8085
|
+
"LENGTH",
|
|
8086
|
+
"INSTR",
|
|
8087
|
+
"ROUND",
|
|
8088
|
+
"FLOOR",
|
|
8089
|
+
"CEIL",
|
|
8090
|
+
"TRUNCATE",
|
|
8091
|
+
"YEAR",
|
|
8092
|
+
"MONTH",
|
|
8093
|
+
"DAY",
|
|
8094
|
+
"DATEDIFF",
|
|
8095
|
+
"ABS",
|
|
8096
|
+
"MOD",
|
|
8097
|
+
"POWER",
|
|
8098
|
+
"SQRT"
|
|
8099
|
+
]);
|
|
8100
|
+
function stringFunctionColumnMeta(expr) {
|
|
8101
|
+
if (expr.func === "CAST") {
|
|
8102
|
+
const target = expr.args[1];
|
|
8103
|
+
return target?.type === "STRING" && target.value === "NUMBER" ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
|
|
8104
|
+
}
|
|
8105
|
+
return NUMBER_RETURNING_STRING_FUNCTIONS.has(expr.func) ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
|
|
8106
|
+
}
|
|
8107
|
+
function caseResultColumnMeta(result, resolveField2) {
|
|
8108
|
+
if (result.type === "STRING") return syntheticColumnMeta("string");
|
|
8109
|
+
if (result.type === "ARRAY") return unsupportedColumnMeta();
|
|
8110
|
+
if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
|
|
8111
|
+
if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
|
|
8112
|
+
const source = resolveField2(aggregateFieldRef(result.field));
|
|
8113
|
+
return source ?? unknownStringColumnMeta();
|
|
8114
|
+
}
|
|
8115
|
+
function mergeExpressionColumnMeta(candidates) {
|
|
8116
|
+
if (candidates.length === 0) return unknownStringColumnMeta();
|
|
8117
|
+
const first = candidates[0];
|
|
8118
|
+
const withoutSource = (semantics) => {
|
|
8119
|
+
if (!semantics) return void 0;
|
|
8120
|
+
const { source: _source, ...rest } = semantics;
|
|
8121
|
+
return rest;
|
|
8122
|
+
};
|
|
8123
|
+
if (candidates.every(
|
|
8124
|
+
(candidate) => candidate.sortKind === first.sortKind && candidate.fieldType === first.fieldType && fieldSemanticsEqual(withoutSource(candidate.semantics), withoutSource(first.semantics))
|
|
8125
|
+
)) {
|
|
8126
|
+
const sameSource = candidates.every(
|
|
8127
|
+
(candidate) => fieldSemanticsEqual(candidate.semantics, first.semantics)
|
|
8128
|
+
);
|
|
8129
|
+
return sameSource ? first : { ...first, semantics: withoutSource(first.semantics) };
|
|
8130
|
+
}
|
|
8131
|
+
if (candidates.some((candidate) => candidate.semantics?.compareMode === "unsupported")) {
|
|
8132
|
+
return unsupportedColumnMeta("KSQL_MIXED_UNSUPPORTED");
|
|
8133
|
+
}
|
|
8134
|
+
return unknownStringColumnMeta();
|
|
7067
8135
|
}
|
|
7068
8136
|
function selectNeedsSourceColumnMeta(stmt) {
|
|
7069
8137
|
return stmt.columns.some(
|
|
7070
|
-
(column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF"
|
|
8138
|
+
(column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "CASE_COL" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF"
|
|
7071
8139
|
);
|
|
7072
8140
|
}
|
|
7073
8141
|
async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
|
|
@@ -7084,18 +8152,18 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7084
8152
|
if (ref.tableAlias !== null) {
|
|
7085
8153
|
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
7086
8154
|
const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
|
|
7087
|
-
return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
|
|
8155
|
+
return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
|
|
7088
8156
|
}
|
|
7089
8157
|
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
7090
8158
|
if (!table) return void 0;
|
|
7091
8159
|
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
7092
8160
|
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7093
|
-
return info ? materializedMetaFromFieldInfo(info) :
|
|
8161
|
+
return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
7094
8162
|
}
|
|
7095
8163
|
if (stmt.joins.length === 0) {
|
|
7096
8164
|
if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
|
|
7097
8165
|
const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
7098
|
-
return info ? materializedMetaFromFieldInfo(info) :
|
|
8166
|
+
return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
|
|
7099
8167
|
}
|
|
7100
8168
|
const matches = tables.flatMap((table) => {
|
|
7101
8169
|
if (table.cteName !== null) {
|
|
@@ -7104,7 +8172,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7104
8172
|
return [materialized.columnMeta?.get(ref.field)];
|
|
7105
8173
|
}
|
|
7106
8174
|
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7107
|
-
|
|
8175
|
+
const meta = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
8176
|
+
return meta ? [meta] : [];
|
|
7108
8177
|
});
|
|
7109
8178
|
return matches.length === 1 ? matches[0] : void 0;
|
|
7110
8179
|
};
|
|
@@ -7134,19 +8203,27 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7134
8203
|
meta = resolveField2(aggregateFieldRef(column.field));
|
|
7135
8204
|
} else if (column.type === "AGGREGATE") {
|
|
7136
8205
|
if (column.func === "GROUP_CONCAT") {
|
|
7137
|
-
meta =
|
|
8206
|
+
meta = syntheticColumnMeta("string");
|
|
7138
8207
|
} else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
|
|
7139
|
-
meta =
|
|
8208
|
+
meta = syntheticColumnMeta("number");
|
|
7140
8209
|
} else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
|
|
7141
8210
|
const source = resolveField2(aggregateFieldRef(column.arg.field));
|
|
7142
|
-
if (source
|
|
8211
|
+
if (source) meta = source;
|
|
7143
8212
|
}
|
|
7144
8213
|
} else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
|
|
7145
|
-
meta =
|
|
8214
|
+
meta = syntheticColumnMeta("number");
|
|
7146
8215
|
} else if (column.type === "LITERAL_COL") {
|
|
7147
|
-
meta =
|
|
8216
|
+
meta = syntheticColumnMeta("string");
|
|
8217
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
8218
|
+
meta = stringFunctionColumnMeta(column.expr);
|
|
7148
8219
|
} else if (column.type === "WINDOW_COL") {
|
|
7149
|
-
meta =
|
|
8220
|
+
meta = syntheticColumnMeta("number");
|
|
8221
|
+
} else if (column.type === "CASE_COL") {
|
|
8222
|
+
const results = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
|
|
8223
|
+
if (column.expr.elseResult) results.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
|
|
8224
|
+
meta = mergeExpressionColumnMeta(results);
|
|
8225
|
+
} else if (column.type === "SCALAR_SUBQUERY_COL") {
|
|
8226
|
+
meta = unknownStringColumnMeta();
|
|
7150
8227
|
}
|
|
7151
8228
|
if (meta) inferred.set(output, meta);
|
|
7152
8229
|
});
|
|
@@ -7160,7 +8237,8 @@ function mergeUnionColumnMeta(left, right) {
|
|
|
7160
8237
|
const a = leftMeta?.get(column);
|
|
7161
8238
|
const rightColumn = right.columns[index];
|
|
7162
8239
|
const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
|
|
7163
|
-
if (a && b
|
|
8240
|
+
if (a && b) merged.set(column, mergeExpressionColumnMeta([a, b]));
|
|
8241
|
+
else if (a || b) merged.set(column, unknownStringColumnMeta());
|
|
7164
8242
|
});
|
|
7165
8243
|
return merged;
|
|
7166
8244
|
}
|
|
@@ -7197,7 +8275,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
7197
8275
|
};
|
|
7198
8276
|
return { row, having };
|
|
7199
8277
|
}
|
|
7200
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
|
|
8278
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
|
|
7201
8279
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
7202
8280
|
const warnings = /* @__PURE__ */ new Set();
|
|
7203
8281
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -7211,6 +8289,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7211
8289
|
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
7212
8290
|
]);
|
|
7213
8291
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
8292
|
+
const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
|
|
8293
|
+
stmt,
|
|
8294
|
+
client,
|
|
8295
|
+
cacheContext,
|
|
8296
|
+
cteCache,
|
|
8297
|
+
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
8298
|
+
);
|
|
8299
|
+
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
7214
8300
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
7215
8301
|
validateKlikePushdownPlan(pushdownPlan);
|
|
7216
8302
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
@@ -7224,7 +8310,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7224
8310
|
true,
|
|
7225
8311
|
options.onLimitReached ?? "error",
|
|
7226
8312
|
warnings,
|
|
7227
|
-
mainPushDown
|
|
8313
|
+
mainPushDown,
|
|
8314
|
+
allowOriginalWherePushdown
|
|
7228
8315
|
);
|
|
7229
8316
|
const parallelJoins = [];
|
|
7230
8317
|
const onOptJoins = [];
|
|
@@ -7250,7 +8337,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7250
8337
|
}
|
|
7251
8338
|
}
|
|
7252
8339
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
7253
|
-
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
8340
|
+
const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
7254
8341
|
scalarCachePromise.catch(() => {
|
|
7255
8342
|
});
|
|
7256
8343
|
orderByMetaPromise.catch(() => {
|
|
@@ -7287,15 +8374,18 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7287
8374
|
tables.set(join2.table.alias, joinRecords);
|
|
7288
8375
|
}));
|
|
7289
8376
|
const scalarCache = await scalarCachePromise;
|
|
7290
|
-
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
8377
|
+
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
7291
8378
|
const { rows, columns } = runFullScan({
|
|
7292
8379
|
tables,
|
|
7293
8380
|
stmt,
|
|
7294
8381
|
scalarCache,
|
|
7295
8382
|
optionOrders,
|
|
7296
8383
|
sortKinds,
|
|
8384
|
+
orderSemantics: semantics,
|
|
7297
8385
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
8386
|
+
fieldSemanticsResolver,
|
|
7298
8387
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
8388
|
+
havingFieldSemanticsResolver,
|
|
7299
8389
|
aggregateSortKindResolver,
|
|
7300
8390
|
appliedKlikes: pushdownPlan.appliedKlikes
|
|
7301
8391
|
});
|
|
@@ -7392,16 +8482,39 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7392
8482
|
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
|
|
7393
8483
|
resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
|
|
7394
8484
|
]);
|
|
8485
|
+
const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
|
|
8486
|
+
if (whereCapability.capability === "UNSUPPORTED") {
|
|
8487
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
8488
|
+
}
|
|
8489
|
+
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
8490
|
+
if (hasCanonicalOrder(stmt)) {
|
|
8491
|
+
(stmt.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
|
|
8492
|
+
stmt,
|
|
8493
|
+
staticMode: "FULL_SCAN",
|
|
8494
|
+
whereCapability: whereCapability.capability,
|
|
8495
|
+
orderSemantics: orderMeta.semantics,
|
|
8496
|
+
maxRecords,
|
|
8497
|
+
hasKlike: whereHasKlike(stmt.where)
|
|
8498
|
+
});
|
|
8499
|
+
}
|
|
7395
8500
|
const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
|
|
7396
8501
|
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
7397
8502
|
loadTypedInFieldTypes(stmt, client, cacheContext),
|
|
7398
8503
|
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
7399
8504
|
]);
|
|
7400
8505
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
8506
|
+
const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
|
|
8507
|
+
stmt,
|
|
8508
|
+
client,
|
|
8509
|
+
cacheContext,
|
|
8510
|
+
cteCache,
|
|
8511
|
+
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
8512
|
+
);
|
|
8513
|
+
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
7401
8514
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
7402
8515
|
validateKlikePushdownPlan(pushdownPlan);
|
|
7403
8516
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
7404
|
-
const orderByMetaPromise =
|
|
8517
|
+
const orderByMetaPromise = Promise.resolve(orderMeta);
|
|
7405
8518
|
scalarCachePromise.catch(() => {
|
|
7406
8519
|
});
|
|
7407
8520
|
orderByMetaPromise.catch(() => {
|
|
@@ -7420,7 +8533,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7420
8533
|
true,
|
|
7421
8534
|
options.onLimitReached ?? "error",
|
|
7422
8535
|
warnings,
|
|
7423
|
-
pushdownPlan.mainCondition
|
|
8536
|
+
pushdownPlan.mainCondition,
|
|
8537
|
+
whereCapability.capability === "EXACT_PUSHDOWN"
|
|
7424
8538
|
);
|
|
7425
8539
|
tables.set(stmt.from.alias, mainRecords);
|
|
7426
8540
|
}
|
|
@@ -7457,7 +8571,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7457
8571
|
});
|
|
7458
8572
|
await Promise.all(joinFetches);
|
|
7459
8573
|
const scalarCache = await scalarCachePromise;
|
|
7460
|
-
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
8574
|
+
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
7461
8575
|
const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
|
|
7462
8576
|
const { rows, columns } = runFullScan({
|
|
7463
8577
|
tables,
|
|
@@ -7465,8 +8579,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7465
8579
|
scalarCache,
|
|
7466
8580
|
optionOrders,
|
|
7467
8581
|
sortKinds,
|
|
8582
|
+
orderSemantics: semantics,
|
|
7468
8583
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
8584
|
+
fieldSemanticsResolver,
|
|
7469
8585
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
8586
|
+
havingFieldSemanticsResolver,
|
|
7470
8587
|
aggregateSortKindResolver,
|
|
7471
8588
|
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
7472
8589
|
sourceColumns
|
|
@@ -7478,13 +8595,13 @@ function processRowToKintoneRecord(row) {
|
|
|
7478
8595
|
Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
|
|
7479
8596
|
);
|
|
7480
8597
|
}
|
|
7481
|
-
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null) {
|
|
8598
|
+
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true) {
|
|
7482
8599
|
const fields = selectToFetchAllFields(stmt, table);
|
|
7483
8600
|
const onTruncate = (max) => {
|
|
7484
8601
|
warnings.add(`\u53D6\u5F97\u4E0A\u9650\uFF08${max} \u4EF6\uFF09\u306B\u9054\u3057\u305F\u305F\u3081\u3001${max} \u4EF6\u3067\u6253\u3061\u5207\u3063\u3066\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002`);
|
|
7485
8602
|
};
|
|
7486
8603
|
if (!table.subtableCode) {
|
|
7487
|
-
const baseQuery = isMainTable ? selectToFetchAllParams(stmt, table.appId).query : "";
|
|
8604
|
+
const baseQuery = isMainTable && allowOriginalWherePushdown ? selectToFetchAllParams(stmt, table.appId).query : "";
|
|
7488
8605
|
const pushQuery = pushDownCond !== null ? whereToKintone(pushDownCond) : "";
|
|
7489
8606
|
const query = baseQuery && pushQuery ? `(${baseQuery}) and (${pushQuery})` : baseQuery || pushQuery;
|
|
7490
8607
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, table.appId, query, fields, {
|
|
@@ -7688,6 +8805,10 @@ async function getProcessStatusesCached(appId, client, cacheContext) {
|
|
|
7688
8805
|
setScopedCacheValue(processStatusCache, cacheContext, appId, loading);
|
|
7689
8806
|
return loading;
|
|
7690
8807
|
}
|
|
8808
|
+
async function loadProcessStatusOrder(appId, client, cacheContext) {
|
|
8809
|
+
const process2 = await getProcessStatusesCached(appId, client, cacheContext);
|
|
8810
|
+
return process2.enable && process2.states !== null ? new Map(process2.states.map((state) => [state.name, state.index])) : void 0;
|
|
8811
|
+
}
|
|
7691
8812
|
async function getFieldTypeMap(appId, client, cacheContext) {
|
|
7692
8813
|
const cached = getScopedCacheValue(fieldTypeCache, cacheContext, appId);
|
|
7693
8814
|
if (cached) return cached;
|
|
@@ -7731,18 +8852,118 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
|
7731
8852
|
setScopedCacheValue(sortKindCache, cacheContext, appId, map);
|
|
7732
8853
|
return map;
|
|
7733
8854
|
}
|
|
7734
|
-
|
|
8855
|
+
function orderByFieldNames(stmt) {
|
|
8856
|
+
const items = [
|
|
8857
|
+
...stmt.orderBy,
|
|
8858
|
+
...stmt.columns.flatMap((column) => column.type === "WINDOW_COL" ? column.orderBy : [])
|
|
8859
|
+
];
|
|
8860
|
+
return [...new Set(items.flatMap(
|
|
8861
|
+
(item) => item.key.type === "FIELD_NAME" ? [item.key.name] : []
|
|
8862
|
+
))];
|
|
8863
|
+
}
|
|
8864
|
+
async function buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables) {
|
|
8865
|
+
const names = orderByFieldNames(stmt);
|
|
8866
|
+
if (names.length === 0) return /* @__PURE__ */ new Map();
|
|
8867
|
+
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
8868
|
+
const ambiguousFields = /* @__PURE__ */ new Set();
|
|
8869
|
+
const infosByApp = new Map(
|
|
8870
|
+
await Promise.all([...new Set(
|
|
8871
|
+
tables.filter((table) => table.cteName === null).map((table) => table.appId)
|
|
8872
|
+
)].map(async (appId) => {
|
|
8873
|
+
const infos = await getFieldsCached(appId, client, cacheContext);
|
|
8874
|
+
return [appId, new Map(infos.map((info) => [info.code, info]))];
|
|
8875
|
+
}))
|
|
8876
|
+
);
|
|
8877
|
+
const resolveField2 = (ref) => {
|
|
8878
|
+
if (ref.tableAlias !== null) {
|
|
8879
|
+
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
8880
|
+
const info2 = infosByApp.get(stmt.from.appId)?.get(ref.field);
|
|
8881
|
+
return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
|
|
8882
|
+
}
|
|
8883
|
+
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
8884
|
+
if (!table) return void 0;
|
|
8885
|
+
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
8886
|
+
const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
8887
|
+
return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
8888
|
+
}
|
|
8889
|
+
if (stmt.joins.length === 0) {
|
|
8890
|
+
if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
|
|
8891
|
+
const info = infosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
8892
|
+
return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
|
|
8893
|
+
}
|
|
8894
|
+
const matches = tables.flatMap((table) => {
|
|
8895
|
+
if (table.cteName !== null) {
|
|
8896
|
+
const materialized = materializedTables?.get(table.cteName);
|
|
8897
|
+
const meta2 = materialized?.columns.includes(ref.field) ? materialized.columnMeta?.get(ref.field) : void 0;
|
|
8898
|
+
return meta2 ? [meta2] : [];
|
|
8899
|
+
}
|
|
8900
|
+
const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
8901
|
+
const meta = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
8902
|
+
return meta ? [meta] : [];
|
|
8903
|
+
});
|
|
8904
|
+
if (matches.length > 1) ambiguousFields.add(ref.field);
|
|
8905
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
8906
|
+
};
|
|
8907
|
+
const aliasSemantics = /* @__PURE__ */ new Map();
|
|
8908
|
+
for (const column of stmt.columns) {
|
|
8909
|
+
if (!("alias" in column) || !column.alias) continue;
|
|
8910
|
+
let meta;
|
|
8911
|
+
if (column.type === "FIELD") meta = resolveField2(aggregateFieldRef(column.field));
|
|
8912
|
+
else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
8913
|
+
meta = syntheticColumnMeta("number");
|
|
8914
|
+
} else if (column.type === "LITERAL_COL") meta = syntheticColumnMeta("string");
|
|
8915
|
+
else if (column.type === "STRFUNC_COL") meta = stringFunctionColumnMeta(column.expr);
|
|
8916
|
+
else if (column.type === "SCALAR_SUBQUERY_COL") meta = unknownStringColumnMeta();
|
|
8917
|
+
else if (column.type === "CASE_COL") {
|
|
8918
|
+
const candidates = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
|
|
8919
|
+
if (column.expr.elseResult) candidates.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
|
|
8920
|
+
meta = mergeExpressionColumnMeta(candidates);
|
|
8921
|
+
} else if (column.type === "AGGREGATE") {
|
|
8922
|
+
if (column.func === "MIN" || column.func === "MAX") {
|
|
8923
|
+
meta = column.arg.type === "FIELD_REF" ? resolveField2(aggregateFieldRef(column.arg.field)) : syntheticColumnMeta("number");
|
|
8924
|
+
} else {
|
|
8925
|
+
meta = column.func === "GROUP_CONCAT" ? syntheticColumnMeta("string") : syntheticColumnMeta("number");
|
|
8926
|
+
}
|
|
8927
|
+
}
|
|
8928
|
+
if (meta?.semantics) aliasSemantics.set(column.alias, meta.semantics);
|
|
8929
|
+
}
|
|
8930
|
+
const result = /* @__PURE__ */ new Map();
|
|
8931
|
+
for (const name of names) {
|
|
8932
|
+
const base = aliasSemantics.get(name) ?? resolveField2(aggregateFieldRef(name))?.semantics;
|
|
8933
|
+
if (!base) {
|
|
8934
|
+
const ref = aggregateFieldRef(name);
|
|
8935
|
+
if (ref.tableAlias === null && ambiguousFields.has(ref.field)) {
|
|
8936
|
+
result.set(name, resolveFieldSemantics({ fieldType: "KSQL_AMBIGUOUS" }));
|
|
8937
|
+
}
|
|
8938
|
+
continue;
|
|
8939
|
+
}
|
|
8940
|
+
let semantics = base;
|
|
8941
|
+
if (base.fieldType === "STATUS" && base.source && stmt.orderMode !== "KINTONE_NATIVE") {
|
|
8942
|
+
const process2 = await getProcessStatusesCached(base.source.appId, client, cacheContext);
|
|
8943
|
+
if (process2.enable && process2.states !== null) {
|
|
8944
|
+
semantics = {
|
|
8945
|
+
...base,
|
|
8946
|
+
optionOrder: new Map(process2.states.map((state) => [state.name, state.index]))
|
|
8947
|
+
};
|
|
8948
|
+
}
|
|
8949
|
+
}
|
|
8950
|
+
result.set(name, semantics);
|
|
8951
|
+
}
|
|
8952
|
+
return result;
|
|
8953
|
+
}
|
|
8954
|
+
async function buildOrderByMetaForSelect(stmt, client, cacheContext, materializedTables) {
|
|
7735
8955
|
const hasWindowOrderBy = stmt.columns.some(
|
|
7736
8956
|
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
7737
8957
|
);
|
|
7738
8958
|
if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
|
|
7739
|
-
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
|
|
8959
|
+
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map(), semantics: /* @__PURE__ */ new Map() };
|
|
7740
8960
|
}
|
|
7741
|
-
const [optionOrders, sortKinds] = await Promise.all([
|
|
8961
|
+
const [optionOrders, sortKinds, semantics] = await Promise.all([
|
|
7742
8962
|
buildOptionOrdersForSelect(stmt, client, cacheContext),
|
|
7743
|
-
buildSortKindsForSelect(stmt, client, cacheContext)
|
|
8963
|
+
buildSortKindsForSelect(stmt, client, cacheContext),
|
|
8964
|
+
buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables)
|
|
7744
8965
|
]);
|
|
7745
|
-
return { optionOrders, sortKinds };
|
|
8966
|
+
return { optionOrders, sortKinds, semantics };
|
|
7746
8967
|
}
|
|
7747
8968
|
async function buildOptionOrdersForSelect(stmt, client, cacheContext) {
|
|
7748
8969
|
const optionOrders = /* @__PURE__ */ new Map();
|
|
@@ -7840,6 +9061,9 @@ var RejectLimitExceededError = class extends Error {
|
|
|
7840
9061
|
}
|
|
7841
9062
|
};
|
|
7842
9063
|
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
9064
|
+
if (stmt.type === "UPDATE") {
|
|
9065
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9066
|
+
}
|
|
7843
9067
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
7844
9068
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
7845
9069
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
@@ -7879,18 +9103,22 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
7879
9103
|
const columnMeta = /* @__PURE__ */ new Map();
|
|
7880
9104
|
for (const column of payloadFields) {
|
|
7881
9105
|
if (column === "$id") {
|
|
7882
|
-
columnMeta.set(column, {
|
|
9106
|
+
columnMeta.set(column, {
|
|
9107
|
+
sortKind: "number",
|
|
9108
|
+
fieldType: "RECORD_NUMBER",
|
|
9109
|
+
semantics: resolveFieldSemantics({ fieldType: "RECORD_NUMBER" })
|
|
9110
|
+
});
|
|
7883
9111
|
continue;
|
|
7884
9112
|
}
|
|
7885
9113
|
const info = infoByCode.get(column);
|
|
7886
|
-
if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
|
|
7887
|
-
}
|
|
7888
|
-
columnMeta.set("$err_statement",
|
|
7889
|
-
columnMeta.set("$err_operation",
|
|
7890
|
-
columnMeta.set("$err_row",
|
|
7891
|
-
columnMeta.set("$err_field",
|
|
7892
|
-
columnMeta.set("$err_code",
|
|
7893
|
-
columnMeta.set("$err_message",
|
|
9114
|
+
if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info, stmt.appId));
|
|
9115
|
+
}
|
|
9116
|
+
columnMeta.set("$err_statement", syntheticColumnMeta("number"));
|
|
9117
|
+
columnMeta.set("$err_operation", syntheticColumnMeta("string"));
|
|
9118
|
+
columnMeta.set("$err_row", syntheticColumnMeta("number"));
|
|
9119
|
+
columnMeta.set("$err_field", syntheticColumnMeta("string"));
|
|
9120
|
+
columnMeta.set("$err_code", syntheticColumnMeta("string"));
|
|
9121
|
+
columnMeta.set("$err_message", syntheticColumnMeta("string"));
|
|
7894
9122
|
materializedMetaByValidationResult.set(result, columnMeta);
|
|
7895
9123
|
return { result, candidates, invalidRowNumbers, columnMeta };
|
|
7896
9124
|
}
|
|
@@ -8276,6 +9504,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
8276
9504
|
};
|
|
8277
9505
|
}
|
|
8278
9506
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
9507
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
8279
9508
|
if (stmt.subtableCode) {
|
|
8280
9509
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
8281
9510
|
}
|
|
@@ -8353,6 +9582,7 @@ function collectUpdateFromTargetFields(stmt) {
|
|
|
8353
9582
|
return [...fields];
|
|
8354
9583
|
}
|
|
8355
9584
|
async function executeDelete(stmt, client, options, cacheContext) {
|
|
9585
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
8356
9586
|
if (stmt.subtableCode) {
|
|
8357
9587
|
return executeDeleteSubtable(stmt, client, options, cacheContext);
|
|
8358
9588
|
}
|
|
@@ -8725,6 +9955,18 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8725
9955
|
client,
|
|
8726
9956
|
cacheContext
|
|
8727
9957
|
);
|
|
9958
|
+
const reorderFields = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
9959
|
+
const reorderSemanticsByCode = new Map(reorderFields.map((field) => [
|
|
9960
|
+
field.code,
|
|
9961
|
+
field.semantics ?? resolveFieldSemantics(field)
|
|
9962
|
+
]));
|
|
9963
|
+
const resolveReorderSemantics = (field) => {
|
|
9964
|
+
if (field.field === "_idx" || field.field === "_pid" || field.field === "_rid") {
|
|
9965
|
+
return syntheticSemantics("number");
|
|
9966
|
+
}
|
|
9967
|
+
const code = field.field.startsWith("_p.") ? field.field.slice(3) : field.field;
|
|
9968
|
+
return reorderSemanticsByCode.get(code) ?? syntheticSemantics("string");
|
|
9969
|
+
};
|
|
8728
9970
|
const parents = await fetchAll(
|
|
8729
9971
|
client.getRecords,
|
|
8730
9972
|
stmt.appId,
|
|
@@ -8733,7 +9975,13 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8733
9975
|
{ maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
|
|
8734
9976
|
);
|
|
8735
9977
|
const expanded = expandRowsForSubtableDml(parents, stmt.subtableCode);
|
|
8736
|
-
const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(
|
|
9978
|
+
const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(
|
|
9979
|
+
stmt.where,
|
|
9980
|
+
r.flat,
|
|
9981
|
+
resolveFieldType,
|
|
9982
|
+
void 0,
|
|
9983
|
+
resolveReorderSemantics
|
|
9984
|
+
)).map((r) => r.parentId));
|
|
8737
9985
|
if (options.confirm) {
|
|
8738
9986
|
const ok = await options.confirm(targetParentIds.size, "UPDATE");
|
|
8739
9987
|
if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
|
|
@@ -8744,7 +9992,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8744
9992
|
if (!parent) continue;
|
|
8745
9993
|
const rows = getMutableTableRows(parent, stmt.subtableCode);
|
|
8746
9994
|
const sortable = rows.map((row, i) => ({ row, i, flat: buildFlatRowForSort(parent, stmt.subtableCode, row, i) }));
|
|
8747
|
-
sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by));
|
|
9995
|
+
sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by, resolveReorderSemantics));
|
|
8748
9996
|
const orderedRowIds = sortable.map((x) => x.row.id ?? "");
|
|
8749
9997
|
await client.putRecords(buildSubtableReorderPutParams(stmt.appId, pid, getRevision(parent), stmt.subtableCode, orderedRowIds));
|
|
8750
9998
|
}
|
|
@@ -8765,14 +10013,12 @@ function buildFlatRowForSort(parent, subtableCode, row, idx) {
|
|
|
8765
10013
|
}
|
|
8766
10014
|
return flat;
|
|
8767
10015
|
}
|
|
8768
|
-
function compareByOrder(a, b, orderBy) {
|
|
10016
|
+
function compareByOrder(a, b, orderBy, resolveSemantics) {
|
|
8769
10017
|
for (const item of orderBy) {
|
|
8770
10018
|
const av = evalOrderKeyForRow(item.key, a);
|
|
8771
10019
|
const bv = evalOrderKeyForRow(item.key, b);
|
|
8772
|
-
const
|
|
8773
|
-
const
|
|
8774
|
-
const numeric = !Number.isNaN(an) && !Number.isNaN(bn);
|
|
8775
|
-
const cmp = numeric ? an - bn : av.localeCompare(bv, "ja");
|
|
10020
|
+
const semantics = item.key.type === "FIELD_NAME" ? resolveSemantics(aggregateFieldRef(item.key.name)) : item.key.type === "ARITH_KEY" ? syntheticSemantics("number") : stringFunctionColumnMeta(item.key.expr).semantics ?? syntheticSemantics("string");
|
|
10021
|
+
const cmp = compareCanonicalValues(av, bv, semantics ?? syntheticSemantics("string"));
|
|
8776
10022
|
if (cmp !== 0) return item.direction === "ASC" ? cmp : -cmp;
|
|
8777
10023
|
}
|
|
8778
10024
|
return 0;
|
|
@@ -8969,35 +10215,140 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
8969
10215
|
pending.forEach(([i], idx) => cache.set(i, values[idx]));
|
|
8970
10216
|
return cache;
|
|
8971
10217
|
}
|
|
8972
|
-
function
|
|
10218
|
+
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4) {
|
|
10219
|
+
const fieldApps = /* @__PURE__ */ new Set();
|
|
10220
|
+
const processStatusApps = /* @__PURE__ */ new Set();
|
|
10221
|
+
const tracedClient = {
|
|
10222
|
+
...client,
|
|
10223
|
+
getFields: async (appId) => {
|
|
10224
|
+
fieldApps.add(appId);
|
|
10225
|
+
return client.getFields(appId);
|
|
10226
|
+
},
|
|
10227
|
+
getProcessStatuses: async (appId) => {
|
|
10228
|
+
processStatusApps.add(appId);
|
|
10229
|
+
return client.getProcessStatuses(appId);
|
|
10230
|
+
}
|
|
10231
|
+
};
|
|
10232
|
+
const capabilities = /* @__PURE__ */ new Map();
|
|
10233
|
+
const orderPlans = /* @__PURE__ */ new Map();
|
|
10234
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10235
|
+
const visit = async (node) => {
|
|
10236
|
+
if (node === null || typeof node !== "object") return;
|
|
10237
|
+
if (seen.has(node)) return;
|
|
10238
|
+
seen.add(node);
|
|
10239
|
+
if (Array.isArray(node)) {
|
|
10240
|
+
await Promise.all(node.map(visit));
|
|
10241
|
+
return;
|
|
10242
|
+
}
|
|
10243
|
+
const typed = node;
|
|
10244
|
+
if (typed["type"] === "SELECT") {
|
|
10245
|
+
const select = node;
|
|
10246
|
+
const physicalApps = [select.from, ...select.joins.map((join2) => join2.table)].filter((table) => table.cteName === null).map((table) => table.appId);
|
|
10247
|
+
const needsWhereSchema = whereNeedsFieldMetadata(select.where);
|
|
10248
|
+
if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
10249
|
+
physicalApps.forEach((appId) => fieldApps.add(appId));
|
|
10250
|
+
}
|
|
10251
|
+
const capability = await resolveSelectWhereCapability(select, tracedClient, cacheContext);
|
|
10252
|
+
if (capability.capability === "UNSUPPORTED") {
|
|
10253
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
10254
|
+
}
|
|
10255
|
+
capabilities.set(select, capability);
|
|
10256
|
+
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
10257
|
+
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
10258
|
+
if (select.orderMode !== "KINTONE_NATIVE") {
|
|
10259
|
+
for (const semantics of meta.semantics.values()) {
|
|
10260
|
+
if (semantics.fieldType === "STATUS" && semantics.source) {
|
|
10261
|
+
processStatusApps.add(semantics.source.appId);
|
|
10262
|
+
}
|
|
10263
|
+
}
|
|
10264
|
+
}
|
|
10265
|
+
const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
|
|
10266
|
+
if (hasCanonicalOrder(select) && !hasUnmaterializedSource) {
|
|
10267
|
+
const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
|
|
10268
|
+
orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
|
|
10269
|
+
stmt: select,
|
|
10270
|
+
staticMode: mode,
|
|
10271
|
+
whereCapability: capability.capability,
|
|
10272
|
+
orderSemantics: meta.semantics,
|
|
10273
|
+
maxRecords,
|
|
10274
|
+
hasKlike: whereHasKlike(select.where)
|
|
10275
|
+
}));
|
|
10276
|
+
}
|
|
10277
|
+
}
|
|
10278
|
+
} else if (typed["type"] === "UPDATE" || typed["type"] === "DELETE") {
|
|
10279
|
+
fieldApps.add(node.appId);
|
|
10280
|
+
await assertDmlWhereCapability(
|
|
10281
|
+
node,
|
|
10282
|
+
tracedClient,
|
|
10283
|
+
cacheContext
|
|
10284
|
+
);
|
|
10285
|
+
}
|
|
10286
|
+
await Promise.all(Object.values(typed).map(visit));
|
|
10287
|
+
};
|
|
10288
|
+
await visit(query);
|
|
10289
|
+
if (typeof query === "object" && query !== null && query.type === "WITH" && canInlineSingleCte(query)) {
|
|
10290
|
+
const inlined = buildInlinedQuery(query);
|
|
10291
|
+
const capability = await resolveSelectWhereCapability(inlined, tracedClient, cacheContext);
|
|
10292
|
+
if (capability.capability === "UNSUPPORTED") {
|
|
10293
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
10294
|
+
}
|
|
10295
|
+
capabilities.set(inlined, capability);
|
|
10296
|
+
if (hasCanonicalOrder(inlined)) {
|
|
10297
|
+
const meta = await buildOrderByMetaForSelect(inlined, tracedClient, cacheContext);
|
|
10298
|
+
orderPlans.set(inlined, (inlined.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
|
|
10299
|
+
stmt: inlined,
|
|
10300
|
+
staticMode: capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(inlined) : "FULL_SCAN",
|
|
10301
|
+
whereCapability: capability.capability,
|
|
10302
|
+
orderSemantics: meta.semantics,
|
|
10303
|
+
maxRecords,
|
|
10304
|
+
hasKlike: whereHasKlike(inlined.where)
|
|
10305
|
+
}));
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10308
|
+
return { capabilities, orderPlans, fieldApps, processStatusApps };
|
|
10309
|
+
}
|
|
10310
|
+
function explainMetadataLines(analysis) {
|
|
10311
|
+
return [
|
|
10312
|
+
...[...analysis.fieldApps].sort((a, b) => a - b).map((appId) => ` metadata API: form definition APP${appId}`),
|
|
10313
|
+
...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`)
|
|
10314
|
+
];
|
|
10315
|
+
}
|
|
10316
|
+
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4) {
|
|
8973
10317
|
const statements = parseSqlBatch(sql);
|
|
8974
10318
|
const analysis = analyzeBatch(statements);
|
|
8975
10319
|
validateDeclaredBatchVariables(statements, injectedVariables);
|
|
8976
10320
|
const variables = /* @__PURE__ */ new Map();
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
10321
|
+
const plans = [];
|
|
10322
|
+
for (let i = 0; i < statements.length; i++) {
|
|
10323
|
+
const stmt = statements[i];
|
|
10324
|
+
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
|
|
10325
|
+
validateKlikeStatement(planStmt);
|
|
10326
|
+
const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords);
|
|
10327
|
+
const statementPlan = buildBatchStatementPlan(
|
|
10328
|
+
planStmt,
|
|
10329
|
+
analysis.statements[i],
|
|
10330
|
+
whereAnalysis.capabilities,
|
|
10331
|
+
whereAnalysis.orderPlans
|
|
10332
|
+
);
|
|
10333
|
+
const metadataPlan = explainMetadataLines(whereAnalysis);
|
|
10334
|
+
plans.push({
|
|
10335
|
+
index: i,
|
|
10336
|
+
type: analysis.statements[i].statementType,
|
|
10337
|
+
plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
|
|
10338
|
+
});
|
|
10339
|
+
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
10340
|
+
variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
|
|
10341
|
+
}
|
|
10342
|
+
}
|
|
10343
|
+
return { statementCount: statements.length, statements: plans };
|
|
8993
10344
|
}
|
|
8994
|
-
function buildBatchStatementPlan(stmt, info) {
|
|
10345
|
+
function buildBatchStatementPlan(stmt, info, capabilities, orderPlans) {
|
|
8995
10346
|
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
8996
10347
|
return [
|
|
8997
10348
|
`CREATE TEMP TABLE ${stmt.name}`,
|
|
8998
10349
|
` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
|
|
8999
10350
|
` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u65E2\u5B9A\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001tempTableMaxRows \u3067\u5909\u66F4\u53EF\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
|
|
9000
|
-
...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
|
|
10351
|
+
...buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans).map((l) => ` ${l}`)
|
|
9001
10352
|
];
|
|
9002
10353
|
}
|
|
9003
10354
|
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
@@ -9013,7 +10364,7 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9013
10364
|
`SET @${stmt.name} = (SELECT ...)`,
|
|
9014
10365
|
" value: \u30B5\u30D6\u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u6642\u306B1\u56DE\u8A55\u4FA1\uFF081\u884C1\u5217\u30FB\u30D0\u30C3\u30C1\u5185\u5B9A\u6570\u30FB\u7D50\u679C\u30E1\u30BF\u30C7\u30FC\u30BF\u306B\u306F\u975E\u516C\u958B\uFF09",
|
|
9015
10366
|
" subquery:",
|
|
9016
|
-
...buildPlanForBatchQuery(stmt.expr.query, subInfo).map((l) => ` ${l}`)
|
|
10367
|
+
...buildPlanForBatchQuery(stmt.expr.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`)
|
|
9017
10368
|
];
|
|
9018
10369
|
}
|
|
9019
10370
|
return [
|
|
@@ -9029,7 +10380,7 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9029
10380
|
}
|
|
9030
10381
|
if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
|
|
9031
10382
|
if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
|
|
9032
|
-
if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
|
|
10383
|
+
if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans);
|
|
9033
10384
|
if (stmt.type === "ASSERT") {
|
|
9034
10385
|
const lines = [
|
|
9035
10386
|
`ASSERT ${stmt.text}`,
|
|
@@ -9041,11 +10392,11 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9041
10392
|
subqueries.forEach((sq, i) => {
|
|
9042
10393
|
lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
|
|
9043
10394
|
const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
|
|
9044
|
-
lines.push(...buildPlanForBatchQuery(sq.query, subInfo).map((l) => ` ${l}`));
|
|
10395
|
+
lines.push(...buildPlanForBatchQuery(sq.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`));
|
|
9045
10396
|
});
|
|
9046
10397
|
return lines;
|
|
9047
10398
|
}
|
|
9048
|
-
return buildPlanForBatchQuery(stmt, info);
|
|
10399
|
+
return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
|
|
9049
10400
|
}
|
|
9050
10401
|
function hasTempTableRef(node) {
|
|
9051
10402
|
if (Array.isArray(node)) return node.some(hasTempTableRef);
|
|
@@ -9057,9 +10408,9 @@ function hasTempTableRef(node) {
|
|
|
9057
10408
|
}
|
|
9058
10409
|
return false;
|
|
9059
10410
|
}
|
|
9060
|
-
function buildPlanForBatchQuery(query, info) {
|
|
10411
|
+
function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
|
|
9061
10412
|
if (info.tempTablesReferenced.length === 0) {
|
|
9062
|
-
return buildExplainPlan(query);
|
|
10413
|
+
return buildExplainPlan(query, void 0, capabilities, orderPlans);
|
|
9063
10414
|
}
|
|
9064
10415
|
const lines = [];
|
|
9065
10416
|
if (query.type === "INSERT_SELECT") {
|
|
@@ -9084,8 +10435,12 @@ function buildPlanForBatchQuery(query, info) {
|
|
|
9084
10435
|
lines.push(" note: \u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3078\u306E WHERE \u30D7\u30C3\u30B7\u30E5\u30C0\u30A6\u30F3\u306F\u884C\u308F\u308C\u306A\u3044");
|
|
9085
10436
|
return lines;
|
|
9086
10437
|
}
|
|
9087
|
-
function executeExplain(stmt) {
|
|
9088
|
-
const
|
|
10438
|
+
async function executeExplain(stmt, client, cacheContext, maxRecords) {
|
|
10439
|
+
const analysis = await buildExplainWhereAnalysis(stmt.query, client, cacheContext, maxRecords);
|
|
10440
|
+
const lines = [
|
|
10441
|
+
...explainMetadataLines(analysis),
|
|
10442
|
+
...buildExplainPlan(stmt.query, void 0, analysis.capabilities, analysis.orderPlans)
|
|
10443
|
+
];
|
|
9089
10444
|
return {
|
|
9090
10445
|
type: "SELECT",
|
|
9091
10446
|
columns: ["plan"],
|
|
@@ -9093,29 +10448,45 @@ function executeExplain(stmt) {
|
|
|
9093
10448
|
rowCount: lines.length
|
|
9094
10449
|
};
|
|
9095
10450
|
}
|
|
9096
|
-
function buildExplainPlan(query, label) {
|
|
9097
|
-
if (query.type === "UNION") return buildUnionPlan(query);
|
|
9098
|
-
if (query.type === "WITH") return buildWithPlan(query);
|
|
10451
|
+
function buildExplainPlan(query, label, capabilities, orderPlans) {
|
|
10452
|
+
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
|
|
10453
|
+
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
|
|
9099
10454
|
if (query.type === "INSERT") return buildInsertPlan(query, label);
|
|
9100
|
-
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label);
|
|
10455
|
+
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
|
|
9101
10456
|
if (query.type === "UPSERT") return buildUpsertPlan(query, label);
|
|
9102
|
-
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label);
|
|
9103
|
-
if (query.type === "UPDATE") return buildUpdatePlan(query, label);
|
|
10457
|
+
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans);
|
|
10458
|
+
if (query.type === "UPDATE") return buildUpdatePlan(query, label, capabilities, orderPlans);
|
|
9104
10459
|
if (query.type === "DELETE") return buildDeletePlan(query, label);
|
|
9105
10460
|
if (query.type === "REORDER") return buildReorderPlan(query, label);
|
|
9106
|
-
return buildSelectPlan(query, label);
|
|
10461
|
+
return buildSelectPlan(query, label, capabilities, orderPlans);
|
|
9107
10462
|
}
|
|
9108
|
-
function buildSelectPlan(stmt, label) {
|
|
9109
|
-
const
|
|
10463
|
+
function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
10464
|
+
const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
10465
|
+
const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
10466
|
+
const mode = orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN" ? "FULL_SCAN" : resolveSelectMode(stmt);
|
|
9110
10467
|
const reasons = collectFullScanReasons(stmt);
|
|
10468
|
+
if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
|
|
10469
|
+
reasons.push(...whereCapability.reasons.map((reason) => reason.code));
|
|
10470
|
+
}
|
|
9111
10471
|
const lines = [];
|
|
9112
10472
|
if (label) lines.push(label);
|
|
9113
10473
|
lines.push(` mode: ${mode}`);
|
|
10474
|
+
if (orderPlan) {
|
|
10475
|
+
lines.push(` order plan: ${orderPlan.kind}`);
|
|
10476
|
+
if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
|
|
10477
|
+
if (orderPlan.kind === "KORDER_NATIVE") {
|
|
10478
|
+
lines.push(" order semantics: kintone native (not kSQL canonical)");
|
|
10479
|
+
lines.push(" REST execution: single GET");
|
|
10480
|
+
}
|
|
10481
|
+
}
|
|
10482
|
+
if (orderPlan?.requiresCompleteInput ?? requiresCompleteInput(stmt)) {
|
|
10483
|
+
lines.push(" complete input: required (ORDER BY / window ORDER BY; onLimit=truncate disabled)");
|
|
10484
|
+
}
|
|
9114
10485
|
if (mode === "FULL_SCAN" && reasons.length > 0) {
|
|
9115
10486
|
lines.push(` reason: ${reasons.join(", ")}`);
|
|
9116
10487
|
}
|
|
9117
10488
|
if (mode === "SIMPLE") {
|
|
9118
|
-
const params = selectToKintoneParams(stmt);
|
|
10489
|
+
const params = selectToKintoneParams(orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt);
|
|
9119
10490
|
lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
|
|
9120
10491
|
lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
|
|
9121
10492
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
@@ -9125,7 +10496,8 @@ function buildSelectPlan(stmt, label) {
|
|
|
9125
10496
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
9126
10497
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
9127
10498
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
9128
|
-
const
|
|
10499
|
+
const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
|
|
10500
|
+
const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)";
|
|
9129
10501
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
9130
10502
|
lines.push(` kintone query: ${mainQ}`);
|
|
9131
10503
|
if (mainCandidate !== null) {
|
|
@@ -9147,10 +10519,10 @@ function buildSelectPlan(stmt, label) {
|
|
|
9147
10519
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|
|
9148
10520
|
}
|
|
9149
10521
|
}
|
|
9150
|
-
lines.push(...collectSubqueryPlans(stmt));
|
|
10522
|
+
lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
|
|
9151
10523
|
return lines;
|
|
9152
10524
|
}
|
|
9153
|
-
function buildUnionPlan(stmt) {
|
|
10525
|
+
function buildUnionPlan(stmt, capabilities, orderPlans) {
|
|
9154
10526
|
const selects = [];
|
|
9155
10527
|
const collect = (u) => {
|
|
9156
10528
|
if (u.type === "SELECT") {
|
|
@@ -9164,24 +10536,25 @@ function buildUnionPlan(stmt) {
|
|
|
9164
10536
|
const lines = [];
|
|
9165
10537
|
selects.forEach((sel, i) => {
|
|
9166
10538
|
if (i > 0) lines.push("");
|
|
9167
|
-
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]
|
|
10539
|
+
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
|
|
9168
10540
|
});
|
|
9169
10541
|
return lines;
|
|
9170
10542
|
}
|
|
9171
|
-
function buildWithPlan(stmt) {
|
|
10543
|
+
function buildWithPlan(stmt, capabilities, orderPlans) {
|
|
9172
10544
|
const lines = [];
|
|
9173
10545
|
for (const cte of stmt.ctes) {
|
|
9174
10546
|
if (cte.query.type === "SELECT") {
|
|
9175
|
-
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]
|
|
10547
|
+
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
|
|
9176
10548
|
lines.push("");
|
|
9177
10549
|
}
|
|
9178
10550
|
}
|
|
9179
10551
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
9180
|
-
lines.push(...buildExplainPlan(stmt.query, "[main]"));
|
|
10552
|
+
lines.push(...buildExplainPlan(stmt.query, "[main]", capabilities, orderPlans));
|
|
9181
10553
|
}
|
|
9182
10554
|
if (canInlineSingleCte(stmt)) {
|
|
9183
10555
|
lines.push("");
|
|
9184
|
-
|
|
10556
|
+
const inlined = buildInlinedQuery(stmt);
|
|
10557
|
+
lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
|
|
9185
10558
|
}
|
|
9186
10559
|
return lines;
|
|
9187
10560
|
}
|
|
@@ -9209,7 +10582,7 @@ function collectFullScanReasons(stmt) {
|
|
|
9209
10582
|
r.push("ORDER BY \u306B\u5F0F");
|
|
9210
10583
|
return r;
|
|
9211
10584
|
}
|
|
9212
|
-
function collectSubqueryPlans(stmt) {
|
|
10585
|
+
function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
9213
10586
|
const lines = [];
|
|
9214
10587
|
let idx = 1;
|
|
9215
10588
|
const visitWhere = (w) => {
|
|
@@ -9218,16 +10591,16 @@ function collectSubqueryPlans(stmt) {
|
|
|
9218
10591
|
case "BINARY":
|
|
9219
10592
|
if (w.right.type === "SCALAR_SUBQUERY") {
|
|
9220
10593
|
lines.push("");
|
|
9221
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]
|
|
10594
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9222
10595
|
}
|
|
9223
10596
|
if (w.right.type === "SUBQUERY_IN_LIST") {
|
|
9224
10597
|
lines.push("");
|
|
9225
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]
|
|
10598
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9226
10599
|
}
|
|
9227
10600
|
break;
|
|
9228
10601
|
case "EXISTS":
|
|
9229
10602
|
lines.push("");
|
|
9230
|
-
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]
|
|
10603
|
+
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9231
10604
|
break;
|
|
9232
10605
|
case "LOGICAL":
|
|
9233
10606
|
visitWhere(w.left);
|
|
@@ -9245,7 +10618,7 @@ function collectSubqueryPlans(stmt) {
|
|
|
9245
10618
|
for (const col of stmt.columns) {
|
|
9246
10619
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
9247
10620
|
lines.push("");
|
|
9248
|
-
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]
|
|
10621
|
+
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9249
10622
|
}
|
|
9250
10623
|
}
|
|
9251
10624
|
if (stmt.having) visitWhere(stmt.having);
|
|
@@ -9263,7 +10636,7 @@ function buildInsertPlan(stmt, label) {
|
|
|
9263
10636
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
9264
10637
|
return lines;
|
|
9265
10638
|
}
|
|
9266
|
-
function buildInsertSelectPlan(stmt, label) {
|
|
10639
|
+
function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
9267
10640
|
const lines = [];
|
|
9268
10641
|
if (label) lines.push(label);
|
|
9269
10642
|
lines.push(` [INSERT SELECT]`);
|
|
@@ -9271,10 +10644,10 @@ function buildInsertSelectPlan(stmt, label) {
|
|
|
9271
10644
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
9272
10645
|
lines.push(` api: POST /k/v1/records.json\uFF08\u4EF6\u6570\u306F SELECT \u7D50\u679C\u306B\u4F9D\u5B58\u3001100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`);
|
|
9273
10646
|
lines.push("");
|
|
9274
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
|
|
10647
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
9275
10648
|
return lines;
|
|
9276
10649
|
}
|
|
9277
|
-
function buildUpdatePlan(stmt, label) {
|
|
10650
|
+
function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
9278
10651
|
const isArith = hasArithAssignment(stmt);
|
|
9279
10652
|
const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
|
|
9280
10653
|
const lines = [];
|
|
@@ -9308,7 +10681,7 @@ function buildUpdatePlan(stmt, label) {
|
|
|
9308
10681
|
for (const a of stmt.assignments) {
|
|
9309
10682
|
if (a.value.type === "SCALAR_SUBQUERY") {
|
|
9310
10683
|
lines.push("");
|
|
9311
|
-
lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]
|
|
10684
|
+
lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans));
|
|
9312
10685
|
}
|
|
9313
10686
|
}
|
|
9314
10687
|
return lines;
|
|
@@ -9335,7 +10708,7 @@ function buildUpsertPlan(stmt, label) {
|
|
|
9335
10708
|
` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json \xD7 ${batchCount}`
|
|
9336
10709
|
];
|
|
9337
10710
|
}
|
|
9338
|
-
function buildUpsertSelectPlan(stmt, label) {
|
|
10711
|
+
function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
9339
10712
|
const lines = [
|
|
9340
10713
|
...label ? [label] : [],
|
|
9341
10714
|
` [UPSERT SELECT]`,
|
|
@@ -9345,7 +10718,7 @@ function buildUpsertSelectPlan(stmt, label) {
|
|
|
9345
10718
|
` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json\uFF08100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`,
|
|
9346
10719
|
``
|
|
9347
10720
|
];
|
|
9348
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
|
|
10721
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
9349
10722
|
return lines;
|
|
9350
10723
|
}
|
|
9351
10724
|
function buildReorderPlan(stmt, label) {
|
|
@@ -9887,12 +11260,14 @@ function flattenFormFieldProperties(properties) {
|
|
|
9887
11260
|
function flattenFields(properties, lookupCopyFields, inSubtable = false) {
|
|
9888
11261
|
const out = [];
|
|
9889
11262
|
for (const field of Object.values(properties)) {
|
|
9890
|
-
|
|
11263
|
+
const optionOrder = toOptionOrderMap(field.options);
|
|
11264
|
+
const sortKind = detectSortKind(field.type, field.format);
|
|
11265
|
+
const info = {
|
|
9891
11266
|
code: field.code,
|
|
9892
11267
|
label: field.label,
|
|
9893
11268
|
fieldType: field.type,
|
|
9894
|
-
optionOrder
|
|
9895
|
-
sortKind
|
|
11269
|
+
optionOrder,
|
|
11270
|
+
sortKind,
|
|
9896
11271
|
required: field.required,
|
|
9897
11272
|
minValue: normalizeConstraintValue(field.minValue),
|
|
9898
11273
|
maxValue: normalizeConstraintValue(field.maxValue),
|
|
@@ -9901,7 +11276,9 @@ function flattenFields(properties, lookupCopyFields, inSubtable = false) {
|
|
|
9901
11276
|
defaultValue: field.defaultValue,
|
|
9902
11277
|
inSubtable,
|
|
9903
11278
|
writable: !lookupCopyFields.has(field.code) && !NON_WRITABLE_FIELD_TYPES2.has(field.type)
|
|
9904
|
-
}
|
|
11279
|
+
};
|
|
11280
|
+
info.semantics = resolveFieldSemantics(info);
|
|
11281
|
+
out.push(info);
|
|
9905
11282
|
if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true));
|
|
9906
11283
|
}
|
|
9907
11284
|
return out;
|
|
@@ -9956,6 +11333,18 @@ function detectSortKind(fieldType, calcFormat) {
|
|
|
9956
11333
|
return void 0;
|
|
9957
11334
|
}
|
|
9958
11335
|
|
|
11336
|
+
// src/core/processStatus.ts
|
|
11337
|
+
function normalizeProcessStatusStates(states) {
|
|
11338
|
+
if (states === null) return null;
|
|
11339
|
+
return Object.values(states).map((state) => {
|
|
11340
|
+
const index = Number(state.index);
|
|
11341
|
+
if (!Number.isSafeInteger(index) || index < 0) {
|
|
11342
|
+
throw new Error(`ArgumentError: invalid process status index: ${String(state.index)}`);
|
|
11343
|
+
}
|
|
11344
|
+
return { name: state.name, index };
|
|
11345
|
+
});
|
|
11346
|
+
}
|
|
11347
|
+
|
|
9959
11348
|
// src/cli/nodeKintoneClient.ts
|
|
9960
11349
|
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
9961
11350
|
function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
@@ -10153,7 +11542,7 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
10153
11542
|
const res = await requestJson(`${apiBasePath}/app/status.json?${qs.toString()}`, { method: "GET" }, appId);
|
|
10154
11543
|
return {
|
|
10155
11544
|
enable: res.enable,
|
|
10156
|
-
states:
|
|
11545
|
+
states: normalizeProcessStatusStates(res.states)
|
|
10157
11546
|
};
|
|
10158
11547
|
}
|
|
10159
11548
|
};
|
|
@@ -10623,7 +12012,7 @@ Options:
|
|
|
10623
12012
|
(batch + json: prints one JSON envelope for the whole batch)
|
|
10624
12013
|
--max-records <n> Max records to fetch (default: 500)
|
|
10625
12014
|
--fetch-parallel <n> Parallel page fetches per query: 1-10 (default: 3)
|
|
10626
|
-
--on-limit <mode> On record limit: error | truncate
|
|
12015
|
+
--on-limit <mode> On record limit: error | truncate (local ORDER BY needs complete input)
|
|
10627
12016
|
--temp-table-max-rows <n> Max rows per temp table (default: 10000, always errors on overflow)
|
|
10628
12017
|
--timeout <ms> Request timeout in milliseconds (default: 30000)
|
|
10629
12018
|
--max-concurrent <n> Max concurrent kintone requests: 1-50 (default: 10)
|
|
@@ -11907,7 +13296,7 @@ async function run() {
|
|
|
11907
13296
|
let isBatchSql = false;
|
|
11908
13297
|
let batchContainsDml = false;
|
|
11909
13298
|
let batchAnalysis = null;
|
|
11910
|
-
let
|
|
13299
|
+
let dryRunNeedsMetadata = false;
|
|
11911
13300
|
if (args.diagRecordId === null) {
|
|
11912
13301
|
sql = args.executeSql;
|
|
11913
13302
|
if (!sql && args.filePath) sql = (0, import_fs2.readFileSync)(args.filePath, "utf-8");
|
|
@@ -11934,17 +13323,16 @@ async function run() {
|
|
|
11934
13323
|
}
|
|
11935
13324
|
try {
|
|
11936
13325
|
const statements = parseSqlStatements(sql);
|
|
13326
|
+
dryRunNeedsMetadata = statements.some(explainNeedsAppMetadata);
|
|
11937
13327
|
if (statements.length > 1) {
|
|
11938
13328
|
batchAnalysis = analyzeBatch(statements);
|
|
11939
13329
|
isBatchSql = true;
|
|
11940
13330
|
batchContainsDml = batchAnalysis.containsDml;
|
|
11941
|
-
needsCompleteInput = batchAnalysis.requiresCompleteInput;
|
|
11942
13331
|
} else {
|
|
11943
13332
|
const stmt = parseSqlStatement(sql);
|
|
11944
13333
|
parsedStmt = stmt;
|
|
11945
13334
|
stmtType = getStatementType(stmt);
|
|
11946
13335
|
isDmlStatement = writesKintone(stmt);
|
|
11947
|
-
needsCompleteInput = requiresCompleteInput(stmt);
|
|
11948
13336
|
hasWhere = hasWhereClause(stmt);
|
|
11949
13337
|
insertValuesCount = getInsertValuesCount(stmt);
|
|
11950
13338
|
const supported = stmtType === "SELECT" || stmtType === "UNION" || stmtType === "WITH" || stmtType === "EXPLAIN" || stmtType === "SHOW_APPS" || stmtType === "DESCRIBE" || stmtType === "ASSERT" || isDmlType(stmtType);
|
|
@@ -11991,10 +13379,13 @@ async function run() {
|
|
|
11991
13379
|
const yes = args.yes || envBool("KSQL_YES") === true || Boolean(profile.dml?.yes);
|
|
11992
13380
|
const allowWithoutWhere = args.allowWithoutWhere || envBool("KSQL_ALLOW_WITHOUT_WHERE") === true || Boolean(profile.dml?.allowWithoutWhere);
|
|
11993
13381
|
const dmlMaxRows = args.dmlMaxRows ?? envInt2("KSQL_DML_MAX_ROWS") ?? profile.dml?.maxRows ?? 100;
|
|
11994
|
-
const
|
|
11995
|
-
const
|
|
11996
|
-
|
|
11997
|
-
|
|
13382
|
+
const isValidationOnly = batchAnalysis?.containsValidationOnly === true || parsedStmt !== null && typeof parsedStmt === "object" && "validateOnly" in parsedStmt && parsedStmt.validateOnly === true;
|
|
13383
|
+
const surfaceForcesOnLimitError = isDmlStatement || batchContainsDml || isValidationOnly;
|
|
13384
|
+
const effectiveOnLimit = surfaceForcesOnLimitError ? "error" : onLimit;
|
|
13385
|
+
if (surfaceForcesOnLimitError && onLimit === "truncate" && !quiet && !args.dryRun) {
|
|
13386
|
+
const reason = isDmlStatement || batchContainsDml ? "DML" : "VALIDATE ONLY";
|
|
13387
|
+
process.stderr.write(`note: onLimit=truncate is ignored for ${reason} (forced to error)
|
|
13388
|
+
`);
|
|
11998
13389
|
}
|
|
11999
13390
|
if (format === "markdown" && noHeader) {
|
|
12000
13391
|
process.stderr.write("ArgumentError: --no-header cannot be used with --format markdown|md.\n");
|
|
@@ -12025,30 +13416,6 @@ async function run() {
|
|
|
12025
13416
|
process.stderr.write("ArgumentError: DML is disabled. Use --allow-dml to enable UPDATE/DELETE/INSERT/UPSERT/REORDER.\n");
|
|
12026
13417
|
return 2;
|
|
12027
13418
|
}
|
|
12028
|
-
if (args.dryRun) {
|
|
12029
|
-
let plans;
|
|
12030
|
-
try {
|
|
12031
|
-
plans = buildBatchExplainPlans(sql, args.variables);
|
|
12032
|
-
} catch (err) {
|
|
12033
|
-
const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
|
|
12034
|
-
bindings: sqlDiagnosticContext.appBindingByMappedApp,
|
|
12035
|
-
rewriteSegments: sqlDiagnosticContext.rewriteSegments
|
|
12036
|
-
}) : err;
|
|
12037
|
-
process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
|
|
12038
|
-
`);
|
|
12039
|
-
return toExitCodeFromError(restored);
|
|
12040
|
-
}
|
|
12041
|
-
const out = [];
|
|
12042
|
-
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|
|
12043
|
-
restoredStatements.forEach((p) => {
|
|
12044
|
-
if (p.index > 0) out.push("");
|
|
12045
|
-
out.push(`[${p.index + 1}] ${p.type}`);
|
|
12046
|
-
out.push(...p.plan);
|
|
12047
|
-
});
|
|
12048
|
-
process.stdout.write(`${out.join("\n")}
|
|
12049
|
-
`);
|
|
12050
|
-
return 0;
|
|
12051
|
-
}
|
|
12052
13419
|
}
|
|
12053
13420
|
if (isDmlStatement) {
|
|
12054
13421
|
if (hasProfileSyntax && stmtType === "DELETE") {
|
|
@@ -12075,7 +13442,7 @@ async function run() {
|
|
|
12075
13442
|
appProfileByApp.set(appId, appBindingByMappedApp.get(appId)?.profile ?? profileName.toLowerCase());
|
|
12076
13443
|
}
|
|
12077
13444
|
const cacheContext = buildCacheContext(profileName, appBindingByMappedApp);
|
|
12078
|
-
if (args.dryRun) {
|
|
13445
|
+
if (args.dryRun && !dryRunNeedsMetadata) {
|
|
12079
13446
|
client = createDryRunClient();
|
|
12080
13447
|
} else {
|
|
12081
13448
|
for (const explicitProfile of appProfileByApp.values()) {
|
|
@@ -12309,6 +13676,29 @@ async function run() {
|
|
|
12309
13676
|
maxDelayMs: args.retryMaxDelay ?? profile.query?.retryMaxDelayMs
|
|
12310
13677
|
})));
|
|
12311
13678
|
}
|
|
13679
|
+
if (isBatchSql && args.dryRun) {
|
|
13680
|
+
try {
|
|
13681
|
+
const plans = await buildBatchExplainPlans(sql, client, args.variables, cacheContext, maxRecords);
|
|
13682
|
+
const out = [];
|
|
13683
|
+
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|
|
13684
|
+
restoredStatements.forEach((p) => {
|
|
13685
|
+
if (p.index > 0) out.push("");
|
|
13686
|
+
out.push(`[${p.index + 1}] ${p.type}`);
|
|
13687
|
+
out.push(...p.plan);
|
|
13688
|
+
});
|
|
13689
|
+
process.stdout.write(`${out.join("\n")}
|
|
13690
|
+
`);
|
|
13691
|
+
return 0;
|
|
13692
|
+
} catch (err) {
|
|
13693
|
+
const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
|
|
13694
|
+
bindings: sqlDiagnosticContext.appBindingByMappedApp,
|
|
13695
|
+
rewriteSegments: sqlDiagnosticContext.rewriteSegments
|
|
13696
|
+
}) : err;
|
|
13697
|
+
process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
|
|
13698
|
+
`);
|
|
13699
|
+
return toExitCodeFromError(restored);
|
|
13700
|
+
}
|
|
13701
|
+
}
|
|
12312
13702
|
try {
|
|
12313
13703
|
if (isDmlStatement && !args.dryRun) {
|
|
12314
13704
|
const stmtAppId = parsedStmt && typeof parsedStmt === "object" && typeof parsedStmt.appId === "number" ? parsedStmt.appId : appIds[0];
|