@rex0220/kintone-sql-tools 2.17.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 +1588 -301
- package/dist-mcp/ksql-mcp.js +1579 -277
- 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 */],
|
|
@@ -603,7 +604,7 @@ var Parser = class {
|
|
|
603
604
|
case "WITH" /* WITH */:
|
|
604
605
|
return this.parseWith();
|
|
605
606
|
case "SELECT" /* SELECT */:
|
|
606
|
-
return this.tryParseUnionChain(this.parseSelect());
|
|
607
|
+
return this.tryParseUnionChain(this.parseSelect(true));
|
|
607
608
|
case "INSERT" /* INSERT */:
|
|
608
609
|
return this.parseInsert();
|
|
609
610
|
case "UPDATE" /* UPDATE */:
|
|
@@ -793,7 +794,7 @@ var Parser = class {
|
|
|
793
794
|
}
|
|
794
795
|
query = w;
|
|
795
796
|
} else if (tok.kind === "SELECT" /* SELECT */) {
|
|
796
|
-
const sel = this.parseSelect();
|
|
797
|
+
const sel = this.parseSelect(true);
|
|
797
798
|
const chained = this.tryParseUnionChain(sel);
|
|
798
799
|
query = chained;
|
|
799
800
|
} else if (tok.kind === "INSERT" /* INSERT */) {
|
|
@@ -986,7 +987,7 @@ var Parser = class {
|
|
|
986
987
|
// ----------------------------------------------------------
|
|
987
988
|
// SELECT
|
|
988
989
|
// ----------------------------------------------------------
|
|
989
|
-
parseSelect() {
|
|
990
|
+
parseSelect(allowKorder = false) {
|
|
990
991
|
this.expect("SELECT" /* SELECT */);
|
|
991
992
|
const distinct = this.consume("DISTINCT" /* DISTINCT */);
|
|
992
993
|
const columns = this.parseSelectColumns();
|
|
@@ -1003,7 +1004,19 @@ var Parser = class {
|
|
|
1003
1004
|
having = this.parseWhereExpr();
|
|
1004
1005
|
}
|
|
1005
1006
|
}
|
|
1006
|
-
|
|
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
|
+
}
|
|
1007
1020
|
const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
|
|
1008
1021
|
const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
|
|
1009
1022
|
const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
|
|
@@ -1020,6 +1033,7 @@ var Parser = class {
|
|
|
1020
1033
|
where,
|
|
1021
1034
|
groupBy,
|
|
1022
1035
|
having,
|
|
1036
|
+
orderMode,
|
|
1023
1037
|
orderBy,
|
|
1024
1038
|
limit,
|
|
1025
1039
|
offset
|
|
@@ -1057,6 +1071,9 @@ var Parser = class {
|
|
|
1057
1071
|
// ----------------------------------------------------------
|
|
1058
1072
|
tryParseUnionChain(left) {
|
|
1059
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
|
+
}
|
|
1060
1077
|
this.advance();
|
|
1061
1078
|
const all = this.consume("ALL" /* ALL */);
|
|
1062
1079
|
const right = this.parseSelect();
|
|
@@ -2710,7 +2727,50 @@ function isReadOnlyStatement(stmt) {
|
|
|
2710
2727
|
return !writesKintone(stmt) && (isReadOnlyType(stmt.type) || isDmlType(stmt.type));
|
|
2711
2728
|
}
|
|
2712
2729
|
function requiresCompleteInput(stmt) {
|
|
2713
|
-
|
|
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
|
+
}
|
|
2714
2774
|
}
|
|
2715
2775
|
function hasWhereClause(stmt) {
|
|
2716
2776
|
if (!stmt || typeof stmt !== "object") return false;
|
|
@@ -3525,6 +3585,7 @@ function buildInlinedQuery(stmt) {
|
|
|
3525
3585
|
where,
|
|
3526
3586
|
groupBy: [],
|
|
3527
3587
|
having: null,
|
|
3588
|
+
orderMode: "CANONICAL",
|
|
3528
3589
|
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
3529
3590
|
limit: final.limit ?? cteBody.limit,
|
|
3530
3591
|
offset: final.offset ?? cteBody.offset,
|
|
@@ -4119,6 +4180,72 @@ function analyzeBatch(statements) {
|
|
|
4119
4180
|
};
|
|
4120
4181
|
}
|
|
4121
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
|
+
|
|
4122
4249
|
// src/core/batchVariables.ts
|
|
4123
4250
|
var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
4124
4251
|
function normalizeBatchVariableName(name) {
|
|
@@ -4154,24 +4281,129 @@ function validateDeclaredBatchVariables(statements, input) {
|
|
|
4154
4281
|
}
|
|
4155
4282
|
|
|
4156
4283
|
// src/core/scalarCompare.ts
|
|
4157
|
-
function
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
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);
|
|
4166
4393
|
switch (op) {
|
|
4394
|
+
case "=":
|
|
4395
|
+
return cmp === 0;
|
|
4396
|
+
case "!=":
|
|
4397
|
+
case "<>":
|
|
4398
|
+
return cmp !== 0;
|
|
4167
4399
|
case ">":
|
|
4168
|
-
return
|
|
4400
|
+
return cmp > 0;
|
|
4169
4401
|
case "<":
|
|
4170
|
-
return
|
|
4402
|
+
return cmp < 0;
|
|
4171
4403
|
case ">=":
|
|
4172
|
-
return
|
|
4404
|
+
return cmp >= 0;
|
|
4173
4405
|
case "<=":
|
|
4174
|
-
return
|
|
4406
|
+
return cmp <= 0;
|
|
4175
4407
|
}
|
|
4176
4408
|
}
|
|
4177
4409
|
function selectScalarExtreme(values, extreme) {
|
|
@@ -4181,12 +4413,10 @@ function selectScalarExtreme(values, extreme) {
|
|
|
4181
4413
|
const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
|
|
4182
4414
|
const compare = (left, right) => {
|
|
4183
4415
|
if (numeric) {
|
|
4184
|
-
const
|
|
4185
|
-
|
|
4186
|
-
if (leftNum < rightNum) return -1;
|
|
4187
|
-
if (leftNum > rightNum) return 1;
|
|
4416
|
+
const numericCmp = triCompare(Number(left), Number(right));
|
|
4417
|
+
if (numericCmp !== 0) return numericCmp;
|
|
4188
4418
|
}
|
|
4189
|
-
return left
|
|
4419
|
+
return compareCodePointStrings(left, right);
|
|
4190
4420
|
};
|
|
4191
4421
|
return candidates.reduce((best, candidate) => {
|
|
4192
4422
|
const cmp = compare(candidate, best);
|
|
@@ -4194,6 +4424,55 @@ function selectScalarExtreme(values, extreme) {
|
|
|
4194
4424
|
});
|
|
4195
4425
|
}
|
|
4196
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;
|
|
4442
|
+
}
|
|
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
|
+
}
|
|
4475
|
+
|
|
4197
4476
|
// src/engine/evalFunc.ts
|
|
4198
4477
|
function evalArithExpr(expr, row) {
|
|
4199
4478
|
if (expr.type === "NUMBER") return expr.value;
|
|
@@ -4458,34 +4737,35 @@ function resolveFieldRef(row, field) {
|
|
|
4458
4737
|
}
|
|
4459
4738
|
|
|
4460
4739
|
// src/engine/evalWhere.ts
|
|
4461
|
-
function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
|
|
4740
|
+
function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
4462
4741
|
switch (expr.type) {
|
|
4463
4742
|
case "BINARY":
|
|
4464
|
-
return evalBinary(expr, row, resolveFieldType, appliedKlikes);
|
|
4743
|
+
return evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4465
4744
|
case "NULL_CHECK":
|
|
4466
4745
|
return evalNullCheck(expr, row);
|
|
4467
4746
|
case "LOGICAL":
|
|
4468
|
-
return evalLogical(expr, row, resolveFieldType, appliedKlikes);
|
|
4747
|
+
return evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4469
4748
|
case "NOT":
|
|
4470
|
-
return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
4749
|
+
return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4471
4750
|
case "GROUP":
|
|
4472
|
-
return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
4751
|
+
return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
4473
4752
|
case "EXISTS": {
|
|
4474
4753
|
const exists = expr.resolved;
|
|
4475
4754
|
return expr.not ? !exists : exists;
|
|
4476
4755
|
}
|
|
4477
4756
|
}
|
|
4478
4757
|
}
|
|
4479
|
-
function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
|
|
4758
|
+
function evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
4480
4759
|
if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
|
|
4481
4760
|
if (appliedKlikes?.has(expr)) return true;
|
|
4482
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");
|
|
4483
4762
|
}
|
|
4484
|
-
const left = resolveField(expr.left, row, resolveFieldType);
|
|
4763
|
+
const left = resolveField(expr.left, row, resolveFieldType, resolveFieldSemantics2);
|
|
4485
4764
|
const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
|
|
4486
|
-
|
|
4765
|
+
const semantics = semanticsForLeft(expr.left, fieldType, resolveFieldSemantics2);
|
|
4766
|
+
return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType, semantics, resolveFieldSemantics2);
|
|
4487
4767
|
}
|
|
4488
|
-
function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
|
|
4768
|
+
function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics = syntheticSemantics("string"), resolveFieldSemantics2) {
|
|
4489
4769
|
if (op === "IN" || op === "NOT_IN") {
|
|
4490
4770
|
let values = null;
|
|
4491
4771
|
if (right.type === "IN_LIST") {
|
|
@@ -4510,8 +4790,53 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
|
|
|
4510
4790
|
if (op === "KLIKE" || op === "NOT_KLIKE") {
|
|
4511
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");
|
|
4512
4792
|
}
|
|
4513
|
-
const rightStr = resolveValue(right, row, resolveFieldType);
|
|
4514
|
-
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");
|
|
4515
4840
|
}
|
|
4516
4841
|
var STRING_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
|
|
4517
4842
|
var OBJECT_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -4562,20 +4887,20 @@ function evalNullCheck(expr, row) {
|
|
|
4562
4887
|
const val = resolveField(expr.field, row);
|
|
4563
4888
|
return expr.not ? val !== "" : val === "";
|
|
4564
4889
|
}
|
|
4565
|
-
function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
|
|
4890
|
+
function evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
4566
4891
|
if (expr.op === "AND") {
|
|
4567
|
-
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);
|
|
4568
4893
|
}
|
|
4569
|
-
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);
|
|
4570
4895
|
}
|
|
4571
|
-
function resolveField(field, row, resolveFieldType) {
|
|
4896
|
+
function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
|
|
4572
4897
|
if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
|
|
4573
4898
|
if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
|
|
4574
|
-
if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType);
|
|
4899
|
+
if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
4575
4900
|
const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
4576
4901
|
return resolveFieldRef(row, key);
|
|
4577
4902
|
}
|
|
4578
|
-
function resolveValue(value, row, resolveFieldType) {
|
|
4903
|
+
function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
|
|
4579
4904
|
switch (value.type) {
|
|
4580
4905
|
case "VARIABLE":
|
|
4581
4906
|
throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
|
|
@@ -4598,14 +4923,14 @@ function resolveValue(value, row, resolveFieldType) {
|
|
|
4598
4923
|
if (value.expr.type === "STRING_FUNC") return evalStringFunc(value.expr, row);
|
|
4599
4924
|
return String(evalArithExpr(value.expr, row));
|
|
4600
4925
|
case "CASE_VALUE":
|
|
4601
|
-
return evalCaseWhen(value.expr, row, resolveFieldType);
|
|
4926
|
+
return evalCaseWhen(value.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
4602
4927
|
case "ARRAY":
|
|
4603
4928
|
return value.elements.map((e) => e.value).join(",");
|
|
4604
4929
|
}
|
|
4605
4930
|
}
|
|
4606
|
-
function evalCaseWhen(expr, row, resolveFieldType) {
|
|
4931
|
+
function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
4607
4932
|
for (const branch of expr.branches) {
|
|
4608
|
-
if (evalWhere(branch.condition, row, resolveFieldType)) {
|
|
4933
|
+
if (evalWhere(branch.condition, row, resolveFieldType, void 0, resolveFieldSemantics2)) {
|
|
4609
4934
|
return evalCaseResult(branch.result, row);
|
|
4610
4935
|
}
|
|
4611
4936
|
}
|
|
@@ -5236,6 +5561,141 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
5236
5561
|
};
|
|
5237
5562
|
}
|
|
5238
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
|
+
|
|
5239
5699
|
// src/engine/process.ts
|
|
5240
5700
|
function flatten(record, alias) {
|
|
5241
5701
|
const row = {};
|
|
@@ -5300,9 +5760,9 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
5300
5760
|
}
|
|
5301
5761
|
return result;
|
|
5302
5762
|
}
|
|
5303
|
-
function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
|
|
5763
|
+
function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
5304
5764
|
if (where === null) return rows;
|
|
5305
|
-
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
|
|
5765
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
|
|
5306
5766
|
}
|
|
5307
5767
|
function hasAggregateColumns(columns) {
|
|
5308
5768
|
return columns.some(
|
|
@@ -5363,7 +5823,7 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5363
5823
|
let strVal;
|
|
5364
5824
|
if (arg.type === "FIELD_REF") {
|
|
5365
5825
|
const raw = row[arg.field];
|
|
5366
|
-
if (raw === void 0 || raw === "") continue;
|
|
5826
|
+
if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
|
|
5367
5827
|
strVal = raw;
|
|
5368
5828
|
} else {
|
|
5369
5829
|
const n = evalArithExpr(arg, row);
|
|
@@ -5375,10 +5835,16 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5375
5835
|
const eff = distinct ? [...new Set(strValues)] : strValues;
|
|
5376
5836
|
if (func === "COUNT") return eff.length;
|
|
5377
5837
|
if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
|
|
5378
|
-
const
|
|
5379
|
-
if (
|
|
5380
|
-
if (eff.length === 0) return
|
|
5381
|
-
|
|
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;
|
|
5382
5848
|
}
|
|
5383
5849
|
const nums = eff.map(Number);
|
|
5384
5850
|
switch (func) {
|
|
@@ -5386,37 +5852,12 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5386
5852
|
return nums.reduce((a, b) => a + b, 0);
|
|
5387
5853
|
case "AVG":
|
|
5388
5854
|
return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
5389
|
-
// Math.max(...nums) は要素数が多いと RangeError になるためループで求める
|
|
5390
|
-
case "MAX":
|
|
5391
|
-
return nums.length === 0 ? 0 : maxOf(nums);
|
|
5392
|
-
case "MIN":
|
|
5393
|
-
return nums.length === 0 ? 0 : minOf(nums);
|
|
5394
5855
|
}
|
|
5395
5856
|
}
|
|
5396
5857
|
function toAggregateFieldRef(field) {
|
|
5397
5858
|
const dot = field.indexOf(".");
|
|
5398
5859
|
return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
|
|
5399
5860
|
}
|
|
5400
|
-
function maxStringOf(values) {
|
|
5401
|
-
let value = values[0];
|
|
5402
|
-
for (const candidate of values) if (candidate > value) value = candidate;
|
|
5403
|
-
return value;
|
|
5404
|
-
}
|
|
5405
|
-
function minStringOf(values) {
|
|
5406
|
-
let value = values[0];
|
|
5407
|
-
for (const candidate of values) if (candidate < value) value = candidate;
|
|
5408
|
-
return value;
|
|
5409
|
-
}
|
|
5410
|
-
function maxOf(nums) {
|
|
5411
|
-
let m = nums[0];
|
|
5412
|
-
for (const n of nums) if (n > m) m = n;
|
|
5413
|
-
return m;
|
|
5414
|
-
}
|
|
5415
|
-
function minOf(nums) {
|
|
5416
|
-
let m = nums[0];
|
|
5417
|
-
for (const n of nums) if (n < m) m = n;
|
|
5418
|
-
return m;
|
|
5419
|
-
}
|
|
5420
5861
|
function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
5421
5862
|
if (node.type === "NUMBER") return node.value;
|
|
5422
5863
|
if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
|
|
@@ -5448,9 +5889,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
|
|
|
5448
5889
|
const argStr = aggregateArgLabel(arg);
|
|
5449
5890
|
return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
|
|
5450
5891
|
}
|
|
5451
|
-
function applyHaving(rows, having, resolveFieldType) {
|
|
5892
|
+
function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
|
|
5452
5893
|
if (having === null) return rows;
|
|
5453
|
-
return rows.filter((row) => evalWhere(having, row, resolveFieldType));
|
|
5894
|
+
return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
|
|
5454
5895
|
}
|
|
5455
5896
|
function applyDistinct(rows, columns) {
|
|
5456
5897
|
if (rows.length === 0) return rows;
|
|
@@ -5502,27 +5943,37 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
5502
5943
|
return JSON.stringify(values);
|
|
5503
5944
|
};
|
|
5504
5945
|
}
|
|
5505
|
-
function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
5946
|
+
function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
|
|
5506
5947
|
if (orderBy.length === 0) return rows;
|
|
5507
|
-
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
|
|
5508
|
-
}
|
|
5509
|
-
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
|
|
5510
|
-
const keyMeta = orderBy.map(({ key }) =>
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
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
|
+
});
|
|
5514
5972
|
const decorated = rows.map((row) => ({
|
|
5515
5973
|
row,
|
|
5516
5974
|
keys: orderBy.map(({ key }, i) => {
|
|
5517
5975
|
const s = evalOrderKey(key, row);
|
|
5518
|
-
|
|
5519
|
-
const orderMap = keyMeta[i].orderMap;
|
|
5520
|
-
return {
|
|
5521
|
-
s,
|
|
5522
|
-
n,
|
|
5523
|
-
isNum: !Number.isNaN(n),
|
|
5524
|
-
rank: orderMap ? minChoiceIndex(parseChoiceValues(s), orderMap) : 0
|
|
5525
|
-
};
|
|
5976
|
+
return { s };
|
|
5526
5977
|
})
|
|
5527
5978
|
}));
|
|
5528
5979
|
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
@@ -5537,15 +5988,24 @@ function compareDecoratedRows(a, b, orderBy, keyMeta) {
|
|
|
5537
5988
|
return 0;
|
|
5538
5989
|
}
|
|
5539
5990
|
function compareSortKeys(a, b, meta) {
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
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
|
+
]);
|
|
5549
6009
|
function evalOrderKey(key, row) {
|
|
5550
6010
|
switch (key.type) {
|
|
5551
6011
|
case "FIELD_NAME":
|
|
@@ -5556,30 +6016,7 @@ function evalOrderKey(key, row) {
|
|
|
5556
6016
|
return evalStringFunc(key.expr, row);
|
|
5557
6017
|
}
|
|
5558
6018
|
}
|
|
5559
|
-
function
|
|
5560
|
-
const trimmed = raw.trim();
|
|
5561
|
-
if (trimmed === "") return [""];
|
|
5562
|
-
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
5563
|
-
try {
|
|
5564
|
-
const arr = JSON.parse(trimmed);
|
|
5565
|
-
if (Array.isArray(arr)) {
|
|
5566
|
-
return arr.map((v) => String(v ?? ""));
|
|
5567
|
-
}
|
|
5568
|
-
} catch {
|
|
5569
|
-
}
|
|
5570
|
-
}
|
|
5571
|
-
return [trimmed];
|
|
5572
|
-
}
|
|
5573
|
-
function minChoiceIndex(values, orderMap) {
|
|
5574
|
-
let min = Number.MAX_SAFE_INTEGER;
|
|
5575
|
-
for (const value of values) {
|
|
5576
|
-
const idx = orderMap.get(value);
|
|
5577
|
-
const rank = idx ?? Number.MAX_SAFE_INTEGER;
|
|
5578
|
-
if (rank < min) min = rank;
|
|
5579
|
-
}
|
|
5580
|
-
return min;
|
|
5581
|
-
}
|
|
5582
|
-
function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
6019
|
+
function applyWindow(rows, columns, optionOrders, sortKinds, fieldSemantics2) {
|
|
5583
6020
|
const windows = columns.filter((column) => column.type === "WINDOW_COL");
|
|
5584
6021
|
if (rows.length === 0 || windows.length === 0) return rows;
|
|
5585
6022
|
for (const window of windows) {
|
|
@@ -5591,7 +6028,7 @@ function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
|
5591
6028
|
else partitions.set(key, [row]);
|
|
5592
6029
|
}
|
|
5593
6030
|
for (const partition of partitions.values()) {
|
|
5594
|
-
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
|
|
6031
|
+
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
|
|
5595
6032
|
const sorted = sortedResult.rows;
|
|
5596
6033
|
let rank = 1;
|
|
5597
6034
|
let denseRank = 1;
|
|
@@ -5616,7 +6053,7 @@ function applyLimit(rows, limit, offset) {
|
|
|
5616
6053
|
if (limit === null) return rows.slice(start);
|
|
5617
6054
|
return rows.slice(start, start + limit);
|
|
5618
6055
|
}
|
|
5619
|
-
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
6056
|
+
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2) {
|
|
5620
6057
|
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
5621
6058
|
const projected2 = rows.map((row) => stripParentShortcutColumns(row));
|
|
5622
6059
|
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
|
|
@@ -5680,7 +6117,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
|
5680
6117
|
}
|
|
5681
6118
|
case "CASE_COL": {
|
|
5682
6119
|
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
5683
|
-
out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
|
|
6120
|
+
out[key] = evalCaseWhen(col.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
5684
6121
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
5685
6122
|
break;
|
|
5686
6123
|
}
|
|
@@ -5835,6 +6272,26 @@ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
|
5835
6272
|
args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
|
|
5836
6273
|
};
|
|
5837
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
|
+
}
|
|
5838
6295
|
function runFullScan(input) {
|
|
5839
6296
|
const {
|
|
5840
6297
|
stmt,
|
|
@@ -5842,12 +6299,17 @@ function runFullScan(input) {
|
|
|
5842
6299
|
scalarCache,
|
|
5843
6300
|
optionOrders,
|
|
5844
6301
|
sortKinds,
|
|
6302
|
+
orderSemantics,
|
|
5845
6303
|
fieldTypeResolver,
|
|
6304
|
+
fieldSemanticsResolver,
|
|
5846
6305
|
havingFieldTypeResolver,
|
|
6306
|
+
havingFieldSemanticsResolver,
|
|
5847
6307
|
aggregateSortKindResolver,
|
|
5848
6308
|
appliedKlikes,
|
|
5849
6309
|
sourceColumns
|
|
5850
6310
|
} = input;
|
|
6311
|
+
const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
|
|
6312
|
+
for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
|
|
5851
6313
|
let rows = [];
|
|
5852
6314
|
const mainAlias = stmt.from.alias;
|
|
5853
6315
|
const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
|
|
@@ -5858,18 +6320,18 @@ function runFullScan(input) {
|
|
|
5858
6320
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
5859
6321
|
rows = applyJoin(rows, rightRows, join2);
|
|
5860
6322
|
}
|
|
5861
|
-
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
|
|
6323
|
+
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
|
|
5862
6324
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
5863
6325
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
|
|
5864
6326
|
}
|
|
5865
|
-
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
|
|
5866
|
-
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
|
|
6327
|
+
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
|
|
6328
|
+
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
5867
6329
|
if (stmt.distinct) {
|
|
5868
6330
|
rows = applyDistinct(rows, stmt.columns);
|
|
5869
6331
|
}
|
|
5870
|
-
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
|
|
6332
|
+
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
5871
6333
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
5872
|
-
return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
|
|
6334
|
+
return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns, fieldSemanticsResolver);
|
|
5873
6335
|
}
|
|
5874
6336
|
|
|
5875
6337
|
// src/converter/subtableAdapter.ts
|
|
@@ -6137,6 +6599,192 @@ function renderValidationValue(value) {
|
|
|
6137
6599
|
return String(value);
|
|
6138
6600
|
}
|
|
6139
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 }] };
|
|
6786
|
+
}
|
|
6787
|
+
|
|
6140
6788
|
// src/execute.ts
|
|
6141
6789
|
var SEARCH_ABORTED_WARNING = "\u691C\u7D22\u304C 10 \u4E07\u4EF6\u3067\u6253\u3061\u5207\u3089\u308C\u3001\u7D50\u679C\u304C\u6B20\u843D\u3057\u305F\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002";
|
|
6142
6790
|
var SearchAbortedError = class extends Error {
|
|
@@ -6147,8 +6795,20 @@ var SearchAbortedError = class extends Error {
|
|
|
6147
6795
|
};
|
|
6148
6796
|
var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
|
|
6149
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
|
+
}
|
|
6150
6809
|
async function execute(sql, client, options = {}) {
|
|
6151
6810
|
const startedAt = Date.now();
|
|
6811
|
+
const cacheContext = resolveCacheContext(client, options.cacheContext);
|
|
6152
6812
|
const stmt = parseSql(sql);
|
|
6153
6813
|
const metrics = createEmptyMetrics();
|
|
6154
6814
|
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
@@ -6162,7 +6822,7 @@ async function execute(sql, client, options = {}) {
|
|
|
6162
6822
|
stmt,
|
|
6163
6823
|
guardedClient,
|
|
6164
6824
|
options,
|
|
6165
|
-
|
|
6825
|
+
cacheContext
|
|
6166
6826
|
);
|
|
6167
6827
|
metrics.elapsedMs = Date.now() - startedAt;
|
|
6168
6828
|
return { ...attachSearchAbortWarning(result, collector), metrics };
|
|
@@ -6277,7 +6937,7 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
6277
6937
|
case "DESCRIBE":
|
|
6278
6938
|
return executeDescribe(stmt, client, cacheContext);
|
|
6279
6939
|
case "EXPLAIN":
|
|
6280
|
-
return executeExplain(stmt);
|
|
6940
|
+
return executeExplain(stmt, client, cacheContext, options.maxRecords ?? 1e4);
|
|
6281
6941
|
// 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
|
|
6282
6942
|
case "CREATE_TEMP_TABLE":
|
|
6283
6943
|
throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
@@ -6308,7 +6968,7 @@ function materializedColumnMetaEqual(left, right) {
|
|
|
6308
6968
|
if (!left || !right || left.size !== right.size) return false;
|
|
6309
6969
|
for (const [column, meta] of left) {
|
|
6310
6970
|
const candidate = right.get(column);
|
|
6311
|
-
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;
|
|
6312
6972
|
}
|
|
6313
6973
|
return true;
|
|
6314
6974
|
}
|
|
@@ -6339,7 +6999,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
6339
6999
|
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
6340
7000
|
const startedAt = Date.now();
|
|
6341
7001
|
const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
|
|
6342
|
-
const cacheContext = options.cacheContext
|
|
7002
|
+
const cacheContext = resolveCacheContext(client, options.cacheContext);
|
|
6343
7003
|
const tempTables = /* @__PURE__ */ new Map();
|
|
6344
7004
|
const variables = /* @__PURE__ */ new Map();
|
|
6345
7005
|
const results = [];
|
|
@@ -6433,7 +7093,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
6433
7093
|
cacheContext,
|
|
6434
7094
|
tempTables
|
|
6435
7095
|
);
|
|
6436
|
-
|
|
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 });
|
|
6437
7100
|
} catch (e) {
|
|
6438
7101
|
if (e instanceof ScalarSubqueryError) {
|
|
6439
7102
|
throw new Error(`ArgumentError: ${e.message}`);
|
|
@@ -6664,13 +7327,14 @@ var ScalarSubqueryError = class extends Error {
|
|
|
6664
7327
|
};
|
|
6665
7328
|
async function executeAssert(stmt, client, options, cacheContext, tempTables) {
|
|
6666
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");
|
|
6667
7331
|
if (stmt.op === "BETWEEN") {
|
|
6668
7332
|
if (stmt.low === null || stmt.high === null) {
|
|
6669
7333
|
throw new Error("ArgumentError: malformed ASSERT statement.");
|
|
6670
7334
|
}
|
|
6671
7335
|
const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
|
|
6672
7336
|
const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
|
|
6673
|
-
if (!compareScalarValues(">=", left, low) || !compareScalarValues("<=", left, high)) {
|
|
7337
|
+
if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
|
|
6674
7338
|
throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
|
|
6675
7339
|
}
|
|
6676
7340
|
return { type: "ASSERT", condition: stmt.text };
|
|
@@ -6679,7 +7343,7 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
|
|
|
6679
7343
|
throw new Error("ArgumentError: malformed ASSERT statement.");
|
|
6680
7344
|
}
|
|
6681
7345
|
const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
|
|
6682
|
-
if (!compareScalarValues(stmt.op, left, right)) {
|
|
7346
|
+
if (!compareScalarValues(stmt.op, left, right, semantics)) {
|
|
6683
7347
|
throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
|
|
6684
7348
|
}
|
|
6685
7349
|
return { type: "ASSERT", condition: stmt.text };
|
|
@@ -6752,6 +7416,149 @@ function evalAssertArith(node) {
|
|
|
6752
7416
|
}
|
|
6753
7417
|
throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
|
|
6754
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
|
+
}
|
|
6755
7562
|
async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
|
|
6756
7563
|
let result;
|
|
6757
7564
|
if (isNoFromSelect(stmt)) {
|
|
@@ -6762,12 +7569,51 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
6762
7569
|
return result;
|
|
6763
7570
|
}
|
|
6764
7571
|
await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
|
|
6765
|
-
const
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
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;
|
|
6771
7617
|
}
|
|
6772
7618
|
if (captureColumnMeta) {
|
|
6773
7619
|
materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
|
|
@@ -6828,19 +7674,30 @@ function executeNoFromSelect(stmt) {
|
|
|
6828
7674
|
const rows = applyLimit(projected, stmt.limit, stmt.offset);
|
|
6829
7675
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
|
|
6830
7676
|
}
|
|
6831
|
-
async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
6832
|
-
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;
|
|
6833
7681
|
const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
|
|
6834
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;
|
|
6835
7690
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
6836
7691
|
const warnings = /* @__PURE__ */ new Set();
|
|
6837
7692
|
const onLimit = options.onLimitReached ?? "error";
|
|
6838
7693
|
const parallel = options.fetchParallel ?? 1;
|
|
6839
|
-
const
|
|
7694
|
+
const useRestWindow = stmt.orderBy.length > 0 ? orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" : stmt.limit !== null && stmt.limit <= 500;
|
|
6840
7695
|
const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
|
|
6841
7696
|
const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords && !whereHasKlike(stmt.where) ? needed : void 0;
|
|
6842
7697
|
let records;
|
|
6843
|
-
if (
|
|
7698
|
+
if (orderPlan?.kind === "KORDER_NATIVE" && stmt.limit === 0) {
|
|
7699
|
+
records = [];
|
|
7700
|
+
} else if (useRestWindow) {
|
|
6844
7701
|
const res = await client.getRecords({
|
|
6845
7702
|
app: params.app,
|
|
6846
7703
|
query: params.query,
|
|
@@ -6853,7 +7710,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
6853
7710
|
client.getRecords,
|
|
6854
7711
|
params.app,
|
|
6855
7712
|
baseQuery,
|
|
6856
|
-
|
|
7713
|
+
fetchFields,
|
|
6857
7714
|
{
|
|
6858
7715
|
parallel,
|
|
6859
7716
|
maxRecords,
|
|
@@ -6866,16 +7723,23 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
6866
7723
|
);
|
|
6867
7724
|
}
|
|
6868
7725
|
let rows = records.map((r) => flatten(r, null));
|
|
6869
|
-
if (!
|
|
6870
|
-
|
|
6871
|
-
|
|
7726
|
+
if (!useRestWindow) {
|
|
7727
|
+
rows = applyOrderBy(
|
|
7728
|
+
rows,
|
|
7729
|
+
stmt.orderBy,
|
|
7730
|
+
orderMeta.optionOrders,
|
|
7731
|
+
orderMeta.sortKinds,
|
|
7732
|
+
orderMeta.semantics
|
|
7733
|
+
);
|
|
6872
7734
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
6873
7735
|
}
|
|
6874
7736
|
const { rows: projected, columns } = project(
|
|
6875
7737
|
rows,
|
|
6876
7738
|
stmt.columns,
|
|
6877
7739
|
void 0,
|
|
6878
|
-
fieldTypeResolvers.row
|
|
7740
|
+
fieldTypeResolvers.row,
|
|
7741
|
+
void 0,
|
|
7742
|
+
projectionSemanticsResolver
|
|
6879
7743
|
);
|
|
6880
7744
|
return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
|
|
6881
7745
|
}
|
|
@@ -6948,8 +7812,8 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
|
|
|
6948
7812
|
const statusFields = collectCandidateFieldCodes(candidates).filter((fieldCode) => fieldTypes.get(fieldCode) === "STATUS");
|
|
6949
7813
|
if (statusFields.length > 0) {
|
|
6950
7814
|
const process2 = await getProcessStatusesCached(appId, client, cacheContext);
|
|
6951
|
-
if (process2.enable && process2.states.length > 0) {
|
|
6952
|
-
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));
|
|
6953
7817
|
for (const fieldCode of statusFields) fieldOptions.set(fieldCode, states);
|
|
6954
7818
|
}
|
|
6955
7819
|
}
|
|
@@ -7127,7 +7991,18 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
7127
7991
|
return [appId, new Map(infos.map((info) => [info.code, info]))];
|
|
7128
7992
|
}))
|
|
7129
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
|
+
}));
|
|
7130
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
|
+
};
|
|
7131
8006
|
return (ref) => {
|
|
7132
8007
|
let info;
|
|
7133
8008
|
if (ref.tableAlias !== null) {
|
|
@@ -7137,40 +8012,130 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
7137
8012
|
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
7138
8013
|
if (!table) return void 0;
|
|
7139
8014
|
if (table.cteName !== null) {
|
|
7140
|
-
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.
|
|
8015
|
+
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
|
|
7141
8016
|
}
|
|
7142
8017
|
info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7143
8018
|
}
|
|
7144
8019
|
} else if (stmt.joins.length === 0) {
|
|
7145
8020
|
if (stmt.from.cteName !== null) {
|
|
7146
|
-
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.
|
|
8021
|
+
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
|
|
7147
8022
|
}
|
|
7148
8023
|
info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
7149
8024
|
} else {
|
|
7150
8025
|
const matches = tables.flatMap((table) => {
|
|
7151
8026
|
if (table.cteName !== null) {
|
|
7152
8027
|
const materialized = materializedTables?.get(table.cteName);
|
|
7153
|
-
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")] : [];
|
|
7154
8029
|
}
|
|
7155
8030
|
const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7156
|
-
return candidate ? [
|
|
8031
|
+
return candidate ? [semanticsForInfo(candidate, table.appId)] : [];
|
|
7157
8032
|
});
|
|
7158
8033
|
if (matches.length !== 1) return void 0;
|
|
7159
8034
|
return matches[0];
|
|
7160
8035
|
}
|
|
7161
|
-
|
|
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);
|
|
7162
8039
|
};
|
|
7163
8040
|
}
|
|
7164
8041
|
function fieldCodeForTypeLookup(table, field) {
|
|
7165
8042
|
if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
|
|
7166
8043
|
return field;
|
|
7167
8044
|
}
|
|
7168
|
-
function materializedMetaFromFieldInfo(info) {
|
|
7169
|
-
|
|
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();
|
|
7170
8135
|
}
|
|
7171
8136
|
function selectNeedsSourceColumnMeta(stmt) {
|
|
7172
8137
|
return stmt.columns.some(
|
|
7173
|
-
(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"
|
|
7174
8139
|
);
|
|
7175
8140
|
}
|
|
7176
8141
|
async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
|
|
@@ -7187,18 +8152,18 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7187
8152
|
if (ref.tableAlias !== null) {
|
|
7188
8153
|
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
7189
8154
|
const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
|
|
7190
|
-
return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
|
|
8155
|
+
return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
|
|
7191
8156
|
}
|
|
7192
8157
|
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
7193
8158
|
if (!table) return void 0;
|
|
7194
8159
|
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
7195
8160
|
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7196
|
-
return info ? materializedMetaFromFieldInfo(info) :
|
|
8161
|
+
return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
7197
8162
|
}
|
|
7198
8163
|
if (stmt.joins.length === 0) {
|
|
7199
8164
|
if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
|
|
7200
8165
|
const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
7201
|
-
return info ? materializedMetaFromFieldInfo(info) :
|
|
8166
|
+
return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
|
|
7202
8167
|
}
|
|
7203
8168
|
const matches = tables.flatMap((table) => {
|
|
7204
8169
|
if (table.cteName !== null) {
|
|
@@ -7207,7 +8172,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7207
8172
|
return [materialized.columnMeta?.get(ref.field)];
|
|
7208
8173
|
}
|
|
7209
8174
|
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7210
|
-
|
|
8175
|
+
const meta = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
8176
|
+
return meta ? [meta] : [];
|
|
7211
8177
|
});
|
|
7212
8178
|
return matches.length === 1 ? matches[0] : void 0;
|
|
7213
8179
|
};
|
|
@@ -7237,19 +8203,27 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7237
8203
|
meta = resolveField2(aggregateFieldRef(column.field));
|
|
7238
8204
|
} else if (column.type === "AGGREGATE") {
|
|
7239
8205
|
if (column.func === "GROUP_CONCAT") {
|
|
7240
|
-
meta =
|
|
8206
|
+
meta = syntheticColumnMeta("string");
|
|
7241
8207
|
} else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
|
|
7242
|
-
meta =
|
|
8208
|
+
meta = syntheticColumnMeta("number");
|
|
7243
8209
|
} else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
|
|
7244
8210
|
const source = resolveField2(aggregateFieldRef(column.arg.field));
|
|
7245
|
-
if (source
|
|
8211
|
+
if (source) meta = source;
|
|
7246
8212
|
}
|
|
7247
8213
|
} else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
|
|
7248
|
-
meta =
|
|
8214
|
+
meta = syntheticColumnMeta("number");
|
|
7249
8215
|
} else if (column.type === "LITERAL_COL") {
|
|
7250
|
-
meta =
|
|
8216
|
+
meta = syntheticColumnMeta("string");
|
|
8217
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
8218
|
+
meta = stringFunctionColumnMeta(column.expr);
|
|
7251
8219
|
} else if (column.type === "WINDOW_COL") {
|
|
7252
|
-
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();
|
|
7253
8227
|
}
|
|
7254
8228
|
if (meta) inferred.set(output, meta);
|
|
7255
8229
|
});
|
|
@@ -7263,7 +8237,8 @@ function mergeUnionColumnMeta(left, right) {
|
|
|
7263
8237
|
const a = leftMeta?.get(column);
|
|
7264
8238
|
const rightColumn = right.columns[index];
|
|
7265
8239
|
const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
|
|
7266
|
-
if (a && b
|
|
8240
|
+
if (a && b) merged.set(column, mergeExpressionColumnMeta([a, b]));
|
|
8241
|
+
else if (a || b) merged.set(column, unknownStringColumnMeta());
|
|
7267
8242
|
});
|
|
7268
8243
|
return merged;
|
|
7269
8244
|
}
|
|
@@ -7300,7 +8275,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
7300
8275
|
};
|
|
7301
8276
|
return { row, having };
|
|
7302
8277
|
}
|
|
7303
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
|
|
8278
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
|
|
7304
8279
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
7305
8280
|
const warnings = /* @__PURE__ */ new Set();
|
|
7306
8281
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -7314,6 +8289,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7314
8289
|
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
7315
8290
|
]);
|
|
7316
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);
|
|
7317
8300
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
7318
8301
|
validateKlikePushdownPlan(pushdownPlan);
|
|
7319
8302
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
@@ -7327,7 +8310,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7327
8310
|
true,
|
|
7328
8311
|
options.onLimitReached ?? "error",
|
|
7329
8312
|
warnings,
|
|
7330
|
-
mainPushDown
|
|
8313
|
+
mainPushDown,
|
|
8314
|
+
allowOriginalWherePushdown
|
|
7331
8315
|
);
|
|
7332
8316
|
const parallelJoins = [];
|
|
7333
8317
|
const onOptJoins = [];
|
|
@@ -7353,7 +8337,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7353
8337
|
}
|
|
7354
8338
|
}
|
|
7355
8339
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
7356
|
-
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
8340
|
+
const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
7357
8341
|
scalarCachePromise.catch(() => {
|
|
7358
8342
|
});
|
|
7359
8343
|
orderByMetaPromise.catch(() => {
|
|
@@ -7390,15 +8374,18 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7390
8374
|
tables.set(join2.table.alias, joinRecords);
|
|
7391
8375
|
}));
|
|
7392
8376
|
const scalarCache = await scalarCachePromise;
|
|
7393
|
-
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
8377
|
+
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
7394
8378
|
const { rows, columns } = runFullScan({
|
|
7395
8379
|
tables,
|
|
7396
8380
|
stmt,
|
|
7397
8381
|
scalarCache,
|
|
7398
8382
|
optionOrders,
|
|
7399
8383
|
sortKinds,
|
|
8384
|
+
orderSemantics: semantics,
|
|
7400
8385
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
8386
|
+
fieldSemanticsResolver,
|
|
7401
8387
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
8388
|
+
havingFieldSemanticsResolver,
|
|
7402
8389
|
aggregateSortKindResolver,
|
|
7403
8390
|
appliedKlikes: pushdownPlan.appliedKlikes
|
|
7404
8391
|
});
|
|
@@ -7495,16 +8482,39 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7495
8482
|
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
|
|
7496
8483
|
resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
|
|
7497
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
|
+
}
|
|
7498
8500
|
const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
|
|
7499
8501
|
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
7500
8502
|
loadTypedInFieldTypes(stmt, client, cacheContext),
|
|
7501
8503
|
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
7502
8504
|
]);
|
|
7503
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);
|
|
7504
8514
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
7505
8515
|
validateKlikePushdownPlan(pushdownPlan);
|
|
7506
8516
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
7507
|
-
const orderByMetaPromise =
|
|
8517
|
+
const orderByMetaPromise = Promise.resolve(orderMeta);
|
|
7508
8518
|
scalarCachePromise.catch(() => {
|
|
7509
8519
|
});
|
|
7510
8520
|
orderByMetaPromise.catch(() => {
|
|
@@ -7523,7 +8533,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7523
8533
|
true,
|
|
7524
8534
|
options.onLimitReached ?? "error",
|
|
7525
8535
|
warnings,
|
|
7526
|
-
pushdownPlan.mainCondition
|
|
8536
|
+
pushdownPlan.mainCondition,
|
|
8537
|
+
whereCapability.capability === "EXACT_PUSHDOWN"
|
|
7527
8538
|
);
|
|
7528
8539
|
tables.set(stmt.from.alias, mainRecords);
|
|
7529
8540
|
}
|
|
@@ -7560,7 +8571,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7560
8571
|
});
|
|
7561
8572
|
await Promise.all(joinFetches);
|
|
7562
8573
|
const scalarCache = await scalarCachePromise;
|
|
7563
|
-
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
8574
|
+
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
7564
8575
|
const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
|
|
7565
8576
|
const { rows, columns } = runFullScan({
|
|
7566
8577
|
tables,
|
|
@@ -7568,8 +8579,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7568
8579
|
scalarCache,
|
|
7569
8580
|
optionOrders,
|
|
7570
8581
|
sortKinds,
|
|
8582
|
+
orderSemantics: semantics,
|
|
7571
8583
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
8584
|
+
fieldSemanticsResolver,
|
|
7572
8585
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
8586
|
+
havingFieldSemanticsResolver,
|
|
7573
8587
|
aggregateSortKindResolver,
|
|
7574
8588
|
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
7575
8589
|
sourceColumns
|
|
@@ -7581,13 +8595,13 @@ function processRowToKintoneRecord(row) {
|
|
|
7581
8595
|
Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
|
|
7582
8596
|
);
|
|
7583
8597
|
}
|
|
7584
|
-
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) {
|
|
7585
8599
|
const fields = selectToFetchAllFields(stmt, table);
|
|
7586
8600
|
const onTruncate = (max) => {
|
|
7587
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`);
|
|
7588
8602
|
};
|
|
7589
8603
|
if (!table.subtableCode) {
|
|
7590
|
-
const baseQuery = isMainTable ? selectToFetchAllParams(stmt, table.appId).query : "";
|
|
8604
|
+
const baseQuery = isMainTable && allowOriginalWherePushdown ? selectToFetchAllParams(stmt, table.appId).query : "";
|
|
7591
8605
|
const pushQuery = pushDownCond !== null ? whereToKintone(pushDownCond) : "";
|
|
7592
8606
|
const query = baseQuery && pushQuery ? `(${baseQuery}) and (${pushQuery})` : baseQuery || pushQuery;
|
|
7593
8607
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, table.appId, query, fields, {
|
|
@@ -7791,6 +8805,10 @@ async function getProcessStatusesCached(appId, client, cacheContext) {
|
|
|
7791
8805
|
setScopedCacheValue(processStatusCache, cacheContext, appId, loading);
|
|
7792
8806
|
return loading;
|
|
7793
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
|
+
}
|
|
7794
8812
|
async function getFieldTypeMap(appId, client, cacheContext) {
|
|
7795
8813
|
const cached = getScopedCacheValue(fieldTypeCache, cacheContext, appId);
|
|
7796
8814
|
if (cached) return cached;
|
|
@@ -7834,18 +8852,118 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
|
7834
8852
|
setScopedCacheValue(sortKindCache, cacheContext, appId, map);
|
|
7835
8853
|
return map;
|
|
7836
8854
|
}
|
|
7837
|
-
|
|
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) {
|
|
7838
8955
|
const hasWindowOrderBy = stmt.columns.some(
|
|
7839
8956
|
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
7840
8957
|
);
|
|
7841
8958
|
if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
|
|
7842
|
-
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
|
|
8959
|
+
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map(), semantics: /* @__PURE__ */ new Map() };
|
|
7843
8960
|
}
|
|
7844
|
-
const [optionOrders, sortKinds] = await Promise.all([
|
|
8961
|
+
const [optionOrders, sortKinds, semantics] = await Promise.all([
|
|
7845
8962
|
buildOptionOrdersForSelect(stmt, client, cacheContext),
|
|
7846
|
-
buildSortKindsForSelect(stmt, client, cacheContext)
|
|
8963
|
+
buildSortKindsForSelect(stmt, client, cacheContext),
|
|
8964
|
+
buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables)
|
|
7847
8965
|
]);
|
|
7848
|
-
return { optionOrders, sortKinds };
|
|
8966
|
+
return { optionOrders, sortKinds, semantics };
|
|
7849
8967
|
}
|
|
7850
8968
|
async function buildOptionOrdersForSelect(stmt, client, cacheContext) {
|
|
7851
8969
|
const optionOrders = /* @__PURE__ */ new Map();
|
|
@@ -7943,6 +9061,9 @@ var RejectLimitExceededError = class extends Error {
|
|
|
7943
9061
|
}
|
|
7944
9062
|
};
|
|
7945
9063
|
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
9064
|
+
if (stmt.type === "UPDATE") {
|
|
9065
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9066
|
+
}
|
|
7946
9067
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
7947
9068
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
7948
9069
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
@@ -7982,18 +9103,22 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
7982
9103
|
const columnMeta = /* @__PURE__ */ new Map();
|
|
7983
9104
|
for (const column of payloadFields) {
|
|
7984
9105
|
if (column === "$id") {
|
|
7985
|
-
columnMeta.set(column, {
|
|
9106
|
+
columnMeta.set(column, {
|
|
9107
|
+
sortKind: "number",
|
|
9108
|
+
fieldType: "RECORD_NUMBER",
|
|
9109
|
+
semantics: resolveFieldSemantics({ fieldType: "RECORD_NUMBER" })
|
|
9110
|
+
});
|
|
7986
9111
|
continue;
|
|
7987
9112
|
}
|
|
7988
9113
|
const info = infoByCode.get(column);
|
|
7989
|
-
if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
|
|
7990
|
-
}
|
|
7991
|
-
columnMeta.set("$err_statement",
|
|
7992
|
-
columnMeta.set("$err_operation",
|
|
7993
|
-
columnMeta.set("$err_row",
|
|
7994
|
-
columnMeta.set("$err_field",
|
|
7995
|
-
columnMeta.set("$err_code",
|
|
7996
|
-
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"));
|
|
7997
9122
|
materializedMetaByValidationResult.set(result, columnMeta);
|
|
7998
9123
|
return { result, candidates, invalidRowNumbers, columnMeta };
|
|
7999
9124
|
}
|
|
@@ -8379,6 +9504,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
8379
9504
|
};
|
|
8380
9505
|
}
|
|
8381
9506
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
9507
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
8382
9508
|
if (stmt.subtableCode) {
|
|
8383
9509
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
8384
9510
|
}
|
|
@@ -8456,6 +9582,7 @@ function collectUpdateFromTargetFields(stmt) {
|
|
|
8456
9582
|
return [...fields];
|
|
8457
9583
|
}
|
|
8458
9584
|
async function executeDelete(stmt, client, options, cacheContext) {
|
|
9585
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
8459
9586
|
if (stmt.subtableCode) {
|
|
8460
9587
|
return executeDeleteSubtable(stmt, client, options, cacheContext);
|
|
8461
9588
|
}
|
|
@@ -8828,6 +9955,18 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8828
9955
|
client,
|
|
8829
9956
|
cacheContext
|
|
8830
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
|
+
};
|
|
8831
9970
|
const parents = await fetchAll(
|
|
8832
9971
|
client.getRecords,
|
|
8833
9972
|
stmt.appId,
|
|
@@ -8836,7 +9975,13 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8836
9975
|
{ maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
|
|
8837
9976
|
);
|
|
8838
9977
|
const expanded = expandRowsForSubtableDml(parents, stmt.subtableCode);
|
|
8839
|
-
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));
|
|
8840
9985
|
if (options.confirm) {
|
|
8841
9986
|
const ok = await options.confirm(targetParentIds.size, "UPDATE");
|
|
8842
9987
|
if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
|
|
@@ -8847,7 +9992,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8847
9992
|
if (!parent) continue;
|
|
8848
9993
|
const rows = getMutableTableRows(parent, stmt.subtableCode);
|
|
8849
9994
|
const sortable = rows.map((row, i) => ({ row, i, flat: buildFlatRowForSort(parent, stmt.subtableCode, row, i) }));
|
|
8850
|
-
sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by));
|
|
9995
|
+
sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by, resolveReorderSemantics));
|
|
8851
9996
|
const orderedRowIds = sortable.map((x) => x.row.id ?? "");
|
|
8852
9997
|
await client.putRecords(buildSubtableReorderPutParams(stmt.appId, pid, getRevision(parent), stmt.subtableCode, orderedRowIds));
|
|
8853
9998
|
}
|
|
@@ -8868,14 +10013,12 @@ function buildFlatRowForSort(parent, subtableCode, row, idx) {
|
|
|
8868
10013
|
}
|
|
8869
10014
|
return flat;
|
|
8870
10015
|
}
|
|
8871
|
-
function compareByOrder(a, b, orderBy) {
|
|
10016
|
+
function compareByOrder(a, b, orderBy, resolveSemantics) {
|
|
8872
10017
|
for (const item of orderBy) {
|
|
8873
10018
|
const av = evalOrderKeyForRow(item.key, a);
|
|
8874
10019
|
const bv = evalOrderKeyForRow(item.key, b);
|
|
8875
|
-
const
|
|
8876
|
-
const
|
|
8877
|
-
const numeric = !Number.isNaN(an) && !Number.isNaN(bn);
|
|
8878
|
-
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"));
|
|
8879
10022
|
if (cmp !== 0) return item.direction === "ASC" ? cmp : -cmp;
|
|
8880
10023
|
}
|
|
8881
10024
|
return 0;
|
|
@@ -9072,35 +10215,140 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
9072
10215
|
pending.forEach(([i], idx) => cache.set(i, values[idx]));
|
|
9073
10216
|
return cache;
|
|
9074
10217
|
}
|
|
9075
|
-
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) {
|
|
9076
10317
|
const statements = parseSqlBatch(sql);
|
|
9077
10318
|
const analysis = analyzeBatch(statements);
|
|
9078
10319
|
validateDeclaredBatchVariables(statements, injectedVariables);
|
|
9079
10320
|
const variables = /* @__PURE__ */ new Map();
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
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 };
|
|
9096
10344
|
}
|
|
9097
|
-
function buildBatchStatementPlan(stmt, info) {
|
|
10345
|
+
function buildBatchStatementPlan(stmt, info, capabilities, orderPlans) {
|
|
9098
10346
|
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
9099
10347
|
return [
|
|
9100
10348
|
`CREATE TEMP TABLE ${stmt.name}`,
|
|
9101
10349
|
` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
|
|
9102
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`,
|
|
9103
|
-
...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
|
|
10351
|
+
...buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans).map((l) => ` ${l}`)
|
|
9104
10352
|
];
|
|
9105
10353
|
}
|
|
9106
10354
|
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
@@ -9116,7 +10364,7 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9116
10364
|
`SET @${stmt.name} = (SELECT ...)`,
|
|
9117
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",
|
|
9118
10366
|
" subquery:",
|
|
9119
|
-
...buildPlanForBatchQuery(stmt.expr.query, subInfo).map((l) => ` ${l}`)
|
|
10367
|
+
...buildPlanForBatchQuery(stmt.expr.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`)
|
|
9120
10368
|
];
|
|
9121
10369
|
}
|
|
9122
10370
|
return [
|
|
@@ -9132,7 +10380,7 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9132
10380
|
}
|
|
9133
10381
|
if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
|
|
9134
10382
|
if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
|
|
9135
|
-
if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
|
|
10383
|
+
if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans);
|
|
9136
10384
|
if (stmt.type === "ASSERT") {
|
|
9137
10385
|
const lines = [
|
|
9138
10386
|
`ASSERT ${stmt.text}`,
|
|
@@ -9144,11 +10392,11 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9144
10392
|
subqueries.forEach((sq, i) => {
|
|
9145
10393
|
lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
|
|
9146
10394
|
const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
|
|
9147
|
-
lines.push(...buildPlanForBatchQuery(sq.query, subInfo).map((l) => ` ${l}`));
|
|
10395
|
+
lines.push(...buildPlanForBatchQuery(sq.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`));
|
|
9148
10396
|
});
|
|
9149
10397
|
return lines;
|
|
9150
10398
|
}
|
|
9151
|
-
return buildPlanForBatchQuery(stmt, info);
|
|
10399
|
+
return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
|
|
9152
10400
|
}
|
|
9153
10401
|
function hasTempTableRef(node) {
|
|
9154
10402
|
if (Array.isArray(node)) return node.some(hasTempTableRef);
|
|
@@ -9160,9 +10408,9 @@ function hasTempTableRef(node) {
|
|
|
9160
10408
|
}
|
|
9161
10409
|
return false;
|
|
9162
10410
|
}
|
|
9163
|
-
function buildPlanForBatchQuery(query, info) {
|
|
10411
|
+
function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
|
|
9164
10412
|
if (info.tempTablesReferenced.length === 0) {
|
|
9165
|
-
return buildExplainPlan(query);
|
|
10413
|
+
return buildExplainPlan(query, void 0, capabilities, orderPlans);
|
|
9166
10414
|
}
|
|
9167
10415
|
const lines = [];
|
|
9168
10416
|
if (query.type === "INSERT_SELECT") {
|
|
@@ -9187,8 +10435,12 @@ function buildPlanForBatchQuery(query, info) {
|
|
|
9187
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");
|
|
9188
10436
|
return lines;
|
|
9189
10437
|
}
|
|
9190
|
-
function executeExplain(stmt) {
|
|
9191
|
-
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
|
+
];
|
|
9192
10444
|
return {
|
|
9193
10445
|
type: "SELECT",
|
|
9194
10446
|
columns: ["plan"],
|
|
@@ -9196,29 +10448,45 @@ function executeExplain(stmt) {
|
|
|
9196
10448
|
rowCount: lines.length
|
|
9197
10449
|
};
|
|
9198
10450
|
}
|
|
9199
|
-
function buildExplainPlan(query, label) {
|
|
9200
|
-
if (query.type === "UNION") return buildUnionPlan(query);
|
|
9201
|
-
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);
|
|
9202
10454
|
if (query.type === "INSERT") return buildInsertPlan(query, label);
|
|
9203
|
-
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label);
|
|
10455
|
+
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
|
|
9204
10456
|
if (query.type === "UPSERT") return buildUpsertPlan(query, label);
|
|
9205
|
-
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label);
|
|
9206
|
-
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);
|
|
9207
10459
|
if (query.type === "DELETE") return buildDeletePlan(query, label);
|
|
9208
10460
|
if (query.type === "REORDER") return buildReorderPlan(query, label);
|
|
9209
|
-
return buildSelectPlan(query, label);
|
|
10461
|
+
return buildSelectPlan(query, label, capabilities, orderPlans);
|
|
9210
10462
|
}
|
|
9211
|
-
function buildSelectPlan(stmt, label) {
|
|
9212
|
-
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);
|
|
9213
10467
|
const reasons = collectFullScanReasons(stmt);
|
|
10468
|
+
if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
|
|
10469
|
+
reasons.push(...whereCapability.reasons.map((reason) => reason.code));
|
|
10470
|
+
}
|
|
9214
10471
|
const lines = [];
|
|
9215
10472
|
if (label) lines.push(label);
|
|
9216
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
|
+
}
|
|
9217
10485
|
if (mode === "FULL_SCAN" && reasons.length > 0) {
|
|
9218
10486
|
lines.push(` reason: ${reasons.join(", ")}`);
|
|
9219
10487
|
}
|
|
9220
10488
|
if (mode === "SIMPLE") {
|
|
9221
|
-
const params = selectToKintoneParams(stmt);
|
|
10489
|
+
const params = selectToKintoneParams(orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt);
|
|
9222
10490
|
lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
|
|
9223
10491
|
lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
|
|
9224
10492
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
@@ -9228,7 +10496,8 @@ function buildSelectPlan(stmt, label) {
|
|
|
9228
10496
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
9229
10497
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
9230
10498
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
9231
|
-
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)";
|
|
9232
10501
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
9233
10502
|
lines.push(` kintone query: ${mainQ}`);
|
|
9234
10503
|
if (mainCandidate !== null) {
|
|
@@ -9250,10 +10519,10 @@ function buildSelectPlan(stmt, label) {
|
|
|
9250
10519
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|
|
9251
10520
|
}
|
|
9252
10521
|
}
|
|
9253
|
-
lines.push(...collectSubqueryPlans(stmt));
|
|
10522
|
+
lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
|
|
9254
10523
|
return lines;
|
|
9255
10524
|
}
|
|
9256
|
-
function buildUnionPlan(stmt) {
|
|
10525
|
+
function buildUnionPlan(stmt, capabilities, orderPlans) {
|
|
9257
10526
|
const selects = [];
|
|
9258
10527
|
const collect = (u) => {
|
|
9259
10528
|
if (u.type === "SELECT") {
|
|
@@ -9267,24 +10536,25 @@ function buildUnionPlan(stmt) {
|
|
|
9267
10536
|
const lines = [];
|
|
9268
10537
|
selects.forEach((sel, i) => {
|
|
9269
10538
|
if (i > 0) lines.push("");
|
|
9270
|
-
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]
|
|
10539
|
+
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
|
|
9271
10540
|
});
|
|
9272
10541
|
return lines;
|
|
9273
10542
|
}
|
|
9274
|
-
function buildWithPlan(stmt) {
|
|
10543
|
+
function buildWithPlan(stmt, capabilities, orderPlans) {
|
|
9275
10544
|
const lines = [];
|
|
9276
10545
|
for (const cte of stmt.ctes) {
|
|
9277
10546
|
if (cte.query.type === "SELECT") {
|
|
9278
|
-
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]
|
|
10547
|
+
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
|
|
9279
10548
|
lines.push("");
|
|
9280
10549
|
}
|
|
9281
10550
|
}
|
|
9282
10551
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
9283
|
-
lines.push(...buildExplainPlan(stmt.query, "[main]"));
|
|
10552
|
+
lines.push(...buildExplainPlan(stmt.query, "[main]", capabilities, orderPlans));
|
|
9284
10553
|
}
|
|
9285
10554
|
if (canInlineSingleCte(stmt)) {
|
|
9286
10555
|
lines.push("");
|
|
9287
|
-
|
|
10556
|
+
const inlined = buildInlinedQuery(stmt);
|
|
10557
|
+
lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
|
|
9288
10558
|
}
|
|
9289
10559
|
return lines;
|
|
9290
10560
|
}
|
|
@@ -9312,7 +10582,7 @@ function collectFullScanReasons(stmt) {
|
|
|
9312
10582
|
r.push("ORDER BY \u306B\u5F0F");
|
|
9313
10583
|
return r;
|
|
9314
10584
|
}
|
|
9315
|
-
function collectSubqueryPlans(stmt) {
|
|
10585
|
+
function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
9316
10586
|
const lines = [];
|
|
9317
10587
|
let idx = 1;
|
|
9318
10588
|
const visitWhere = (w) => {
|
|
@@ -9321,16 +10591,16 @@ function collectSubqueryPlans(stmt) {
|
|
|
9321
10591
|
case "BINARY":
|
|
9322
10592
|
if (w.right.type === "SCALAR_SUBQUERY") {
|
|
9323
10593
|
lines.push("");
|
|
9324
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]
|
|
10594
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9325
10595
|
}
|
|
9326
10596
|
if (w.right.type === "SUBQUERY_IN_LIST") {
|
|
9327
10597
|
lines.push("");
|
|
9328
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]
|
|
10598
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9329
10599
|
}
|
|
9330
10600
|
break;
|
|
9331
10601
|
case "EXISTS":
|
|
9332
10602
|
lines.push("");
|
|
9333
|
-
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]
|
|
10603
|
+
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9334
10604
|
break;
|
|
9335
10605
|
case "LOGICAL":
|
|
9336
10606
|
visitWhere(w.left);
|
|
@@ -9348,7 +10618,7 @@ function collectSubqueryPlans(stmt) {
|
|
|
9348
10618
|
for (const col of stmt.columns) {
|
|
9349
10619
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
9350
10620
|
lines.push("");
|
|
9351
|
-
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]
|
|
10621
|
+
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9352
10622
|
}
|
|
9353
10623
|
}
|
|
9354
10624
|
if (stmt.having) visitWhere(stmt.having);
|
|
@@ -9366,7 +10636,7 @@ function buildInsertPlan(stmt, label) {
|
|
|
9366
10636
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
9367
10637
|
return lines;
|
|
9368
10638
|
}
|
|
9369
|
-
function buildInsertSelectPlan(stmt, label) {
|
|
10639
|
+
function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
9370
10640
|
const lines = [];
|
|
9371
10641
|
if (label) lines.push(label);
|
|
9372
10642
|
lines.push(` [INSERT SELECT]`);
|
|
@@ -9374,10 +10644,10 @@ function buildInsertSelectPlan(stmt, label) {
|
|
|
9374
10644
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
9375
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`);
|
|
9376
10646
|
lines.push("");
|
|
9377
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
|
|
10647
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
9378
10648
|
return lines;
|
|
9379
10649
|
}
|
|
9380
|
-
function buildUpdatePlan(stmt, label) {
|
|
10650
|
+
function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
9381
10651
|
const isArith = hasArithAssignment(stmt);
|
|
9382
10652
|
const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
|
|
9383
10653
|
const lines = [];
|
|
@@ -9411,7 +10681,7 @@ function buildUpdatePlan(stmt, label) {
|
|
|
9411
10681
|
for (const a of stmt.assignments) {
|
|
9412
10682
|
if (a.value.type === "SCALAR_SUBQUERY") {
|
|
9413
10683
|
lines.push("");
|
|
9414
|
-
lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]
|
|
10684
|
+
lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans));
|
|
9415
10685
|
}
|
|
9416
10686
|
}
|
|
9417
10687
|
return lines;
|
|
@@ -9438,7 +10708,7 @@ function buildUpsertPlan(stmt, label) {
|
|
|
9438
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}`
|
|
9439
10709
|
];
|
|
9440
10710
|
}
|
|
9441
|
-
function buildUpsertSelectPlan(stmt, label) {
|
|
10711
|
+
function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
9442
10712
|
const lines = [
|
|
9443
10713
|
...label ? [label] : [],
|
|
9444
10714
|
` [UPSERT SELECT]`,
|
|
@@ -9448,7 +10718,7 @@ function buildUpsertSelectPlan(stmt, label) {
|
|
|
9448
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`,
|
|
9449
10719
|
``
|
|
9450
10720
|
];
|
|
9451
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
|
|
10721
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
9452
10722
|
return lines;
|
|
9453
10723
|
}
|
|
9454
10724
|
function buildReorderPlan(stmt, label) {
|
|
@@ -9990,12 +11260,14 @@ function flattenFormFieldProperties(properties) {
|
|
|
9990
11260
|
function flattenFields(properties, lookupCopyFields, inSubtable = false) {
|
|
9991
11261
|
const out = [];
|
|
9992
11262
|
for (const field of Object.values(properties)) {
|
|
9993
|
-
|
|
11263
|
+
const optionOrder = toOptionOrderMap(field.options);
|
|
11264
|
+
const sortKind = detectSortKind(field.type, field.format);
|
|
11265
|
+
const info = {
|
|
9994
11266
|
code: field.code,
|
|
9995
11267
|
label: field.label,
|
|
9996
11268
|
fieldType: field.type,
|
|
9997
|
-
optionOrder
|
|
9998
|
-
sortKind
|
|
11269
|
+
optionOrder,
|
|
11270
|
+
sortKind,
|
|
9999
11271
|
required: field.required,
|
|
10000
11272
|
minValue: normalizeConstraintValue(field.minValue),
|
|
10001
11273
|
maxValue: normalizeConstraintValue(field.maxValue),
|
|
@@ -10004,7 +11276,9 @@ function flattenFields(properties, lookupCopyFields, inSubtable = false) {
|
|
|
10004
11276
|
defaultValue: field.defaultValue,
|
|
10005
11277
|
inSubtable,
|
|
10006
11278
|
writable: !lookupCopyFields.has(field.code) && !NON_WRITABLE_FIELD_TYPES2.has(field.type)
|
|
10007
|
-
}
|
|
11279
|
+
};
|
|
11280
|
+
info.semantics = resolveFieldSemantics(info);
|
|
11281
|
+
out.push(info);
|
|
10008
11282
|
if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true));
|
|
10009
11283
|
}
|
|
10010
11284
|
return out;
|
|
@@ -10059,6 +11333,18 @@ function detectSortKind(fieldType, calcFormat) {
|
|
|
10059
11333
|
return void 0;
|
|
10060
11334
|
}
|
|
10061
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
|
+
|
|
10062
11348
|
// src/cli/nodeKintoneClient.ts
|
|
10063
11349
|
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
10064
11350
|
function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
@@ -10256,7 +11542,7 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
10256
11542
|
const res = await requestJson(`${apiBasePath}/app/status.json?${qs.toString()}`, { method: "GET" }, appId);
|
|
10257
11543
|
return {
|
|
10258
11544
|
enable: res.enable,
|
|
10259
|
-
states:
|
|
11545
|
+
states: normalizeProcessStatusStates(res.states)
|
|
10260
11546
|
};
|
|
10261
11547
|
}
|
|
10262
11548
|
};
|
|
@@ -10726,7 +12012,7 @@ Options:
|
|
|
10726
12012
|
(batch + json: prints one JSON envelope for the whole batch)
|
|
10727
12013
|
--max-records <n> Max records to fetch (default: 500)
|
|
10728
12014
|
--fetch-parallel <n> Parallel page fetches per query: 1-10 (default: 3)
|
|
10729
|
-
--on-limit <mode> On record limit: error | truncate
|
|
12015
|
+
--on-limit <mode> On record limit: error | truncate (local ORDER BY needs complete input)
|
|
10730
12016
|
--temp-table-max-rows <n> Max rows per temp table (default: 10000, always errors on overflow)
|
|
10731
12017
|
--timeout <ms> Request timeout in milliseconds (default: 30000)
|
|
10732
12018
|
--max-concurrent <n> Max concurrent kintone requests: 1-50 (default: 10)
|
|
@@ -12010,7 +13296,7 @@ async function run() {
|
|
|
12010
13296
|
let isBatchSql = false;
|
|
12011
13297
|
let batchContainsDml = false;
|
|
12012
13298
|
let batchAnalysis = null;
|
|
12013
|
-
let
|
|
13299
|
+
let dryRunNeedsMetadata = false;
|
|
12014
13300
|
if (args.diagRecordId === null) {
|
|
12015
13301
|
sql = args.executeSql;
|
|
12016
13302
|
if (!sql && args.filePath) sql = (0, import_fs2.readFileSync)(args.filePath, "utf-8");
|
|
@@ -12037,17 +13323,16 @@ async function run() {
|
|
|
12037
13323
|
}
|
|
12038
13324
|
try {
|
|
12039
13325
|
const statements = parseSqlStatements(sql);
|
|
13326
|
+
dryRunNeedsMetadata = statements.some(explainNeedsAppMetadata);
|
|
12040
13327
|
if (statements.length > 1) {
|
|
12041
13328
|
batchAnalysis = analyzeBatch(statements);
|
|
12042
13329
|
isBatchSql = true;
|
|
12043
13330
|
batchContainsDml = batchAnalysis.containsDml;
|
|
12044
|
-
needsCompleteInput = batchAnalysis.requiresCompleteInput;
|
|
12045
13331
|
} else {
|
|
12046
13332
|
const stmt = parseSqlStatement(sql);
|
|
12047
13333
|
parsedStmt = stmt;
|
|
12048
13334
|
stmtType = getStatementType(stmt);
|
|
12049
13335
|
isDmlStatement = writesKintone(stmt);
|
|
12050
|
-
needsCompleteInput = requiresCompleteInput(stmt);
|
|
12051
13336
|
hasWhere = hasWhereClause(stmt);
|
|
12052
13337
|
insertValuesCount = getInsertValuesCount(stmt);
|
|
12053
13338
|
const supported = stmtType === "SELECT" || stmtType === "UNION" || stmtType === "WITH" || stmtType === "EXPLAIN" || stmtType === "SHOW_APPS" || stmtType === "DESCRIBE" || stmtType === "ASSERT" || isDmlType(stmtType);
|
|
@@ -12094,10 +13379,13 @@ async function run() {
|
|
|
12094
13379
|
const yes = args.yes || envBool("KSQL_YES") === true || Boolean(profile.dml?.yes);
|
|
12095
13380
|
const allowWithoutWhere = args.allowWithoutWhere || envBool("KSQL_ALLOW_WITHOUT_WHERE") === true || Boolean(profile.dml?.allowWithoutWhere);
|
|
12096
13381
|
const dmlMaxRows = args.dmlMaxRows ?? envInt2("KSQL_DML_MAX_ROWS") ?? profile.dml?.maxRows ?? 100;
|
|
12097
|
-
const
|
|
12098
|
-
const
|
|
12099
|
-
|
|
12100
|
-
|
|
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
|
+
`);
|
|
12101
13389
|
}
|
|
12102
13390
|
if (format === "markdown" && noHeader) {
|
|
12103
13391
|
process.stderr.write("ArgumentError: --no-header cannot be used with --format markdown|md.\n");
|
|
@@ -12128,30 +13416,6 @@ async function run() {
|
|
|
12128
13416
|
process.stderr.write("ArgumentError: DML is disabled. Use --allow-dml to enable UPDATE/DELETE/INSERT/UPSERT/REORDER.\n");
|
|
12129
13417
|
return 2;
|
|
12130
13418
|
}
|
|
12131
|
-
if (args.dryRun) {
|
|
12132
|
-
let plans;
|
|
12133
|
-
try {
|
|
12134
|
-
plans = buildBatchExplainPlans(sql, args.variables);
|
|
12135
|
-
} catch (err) {
|
|
12136
|
-
const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
|
|
12137
|
-
bindings: sqlDiagnosticContext.appBindingByMappedApp,
|
|
12138
|
-
rewriteSegments: sqlDiagnosticContext.rewriteSegments
|
|
12139
|
-
}) : err;
|
|
12140
|
-
process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
|
|
12141
|
-
`);
|
|
12142
|
-
return toExitCodeFromError(restored);
|
|
12143
|
-
}
|
|
12144
|
-
const out = [];
|
|
12145
|
-
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|
|
12146
|
-
restoredStatements.forEach((p) => {
|
|
12147
|
-
if (p.index > 0) out.push("");
|
|
12148
|
-
out.push(`[${p.index + 1}] ${p.type}`);
|
|
12149
|
-
out.push(...p.plan);
|
|
12150
|
-
});
|
|
12151
|
-
process.stdout.write(`${out.join("\n")}
|
|
12152
|
-
`);
|
|
12153
|
-
return 0;
|
|
12154
|
-
}
|
|
12155
13419
|
}
|
|
12156
13420
|
if (isDmlStatement) {
|
|
12157
13421
|
if (hasProfileSyntax && stmtType === "DELETE") {
|
|
@@ -12178,7 +13442,7 @@ async function run() {
|
|
|
12178
13442
|
appProfileByApp.set(appId, appBindingByMappedApp.get(appId)?.profile ?? profileName.toLowerCase());
|
|
12179
13443
|
}
|
|
12180
13444
|
const cacheContext = buildCacheContext(profileName, appBindingByMappedApp);
|
|
12181
|
-
if (args.dryRun) {
|
|
13445
|
+
if (args.dryRun && !dryRunNeedsMetadata) {
|
|
12182
13446
|
client = createDryRunClient();
|
|
12183
13447
|
} else {
|
|
12184
13448
|
for (const explicitProfile of appProfileByApp.values()) {
|
|
@@ -12412,6 +13676,29 @@ async function run() {
|
|
|
12412
13676
|
maxDelayMs: args.retryMaxDelay ?? profile.query?.retryMaxDelayMs
|
|
12413
13677
|
})));
|
|
12414
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
|
+
}
|
|
12415
13702
|
try {
|
|
12416
13703
|
if (isDmlStatement && !args.dryRun) {
|
|
12417
13704
|
const stmtAppId = parsedStmt && typeof parsedStmt === "object" && typeof parsedStmt.appId === "number" ? parsedStmt.appId : appIds[0];
|