@rex0220/kintone-sql-tools 2.17.0 → 3.1.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 +5 -1
- package/dist-cli/ksql.js +2152 -310
- package/dist-mcp/ksql-mcp.js +2169 -319
- 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,232 @@ 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 planKorder(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 || !Number.isSafeInteger(stmt.limit) || stmt.limit < 0) {
|
|
5677
|
+
reasons.push(`KORDER_LIMIT_INVALID(limit=${String(stmt.limit)})`);
|
|
5678
|
+
}
|
|
5679
|
+
const offset = stmt.offset ?? 0;
|
|
5680
|
+
if (!Number.isSafeInteger(offset) || offset < 0) {
|
|
5681
|
+
reasons.push(`KORDER_OFFSET_INVALID(offset=${offset})`);
|
|
5682
|
+
}
|
|
5683
|
+
const scanRows = stmt.limit === null ? Number.NaN : offset + stmt.limit;
|
|
5684
|
+
if (stmt.limit !== null && !Number.isSafeInteger(scanRows)) {
|
|
5685
|
+
reasons.push(`KORDER_SCAN_ROWS_INVALID(offset=${offset}, limit=${stmt.limit})`);
|
|
5686
|
+
}
|
|
5687
|
+
const unique = [...new Set(reasons)];
|
|
5688
|
+
if (unique.length > 0) {
|
|
5689
|
+
throw new Error(
|
|
5690
|
+
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
|
|
5691
|
+
);
|
|
5692
|
+
}
|
|
5693
|
+
const native = stmt.limit <= 500 && offset <= 1e4 && stmt.limit <= input.maxRecords;
|
|
5694
|
+
if (!native && scanRows > input.maxRecords) {
|
|
5695
|
+
throw new Error(
|
|
5696
|
+
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; KORDER_SCAN_ROWS_EXCEEDS_MAX_RECORDS(scanRows=${scanRows}, maxRecords=${input.maxRecords})). Use ORDER BY for canonical local ordering, raise maxRecords, or reduce LIMIT/OFFSET.`
|
|
5697
|
+
);
|
|
5698
|
+
}
|
|
5699
|
+
return {
|
|
5700
|
+
kind: native ? "KORDER_NATIVE" : "KORDER_CURSOR",
|
|
5701
|
+
requiresCompleteInput: false,
|
|
5702
|
+
localOrderBy: false,
|
|
5703
|
+
applyLocalOffsetLimit: false,
|
|
5704
|
+
reasonCodes: [],
|
|
5705
|
+
scanRows
|
|
5706
|
+
};
|
|
5707
|
+
}
|
|
5708
|
+
|
|
5709
|
+
// src/core/errors/cursorErrors.ts
|
|
5710
|
+
var CursorCapacityError = class extends Error {
|
|
5711
|
+
constructor(host, limit, waitMs) {
|
|
5712
|
+
super(`CursorCapacityError: host=${host} \u306E active cursor \u4E0A\u9650 ${limit} \u306B ${waitMs}ms \u4EE5\u5185\u3067\u7A7A\u304D\u304C\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002`);
|
|
5713
|
+
this.name = "CursorCapacityError";
|
|
5714
|
+
}
|
|
5715
|
+
};
|
|
5716
|
+
var CursorCreateOutcomeUnknownError = class extends Error {
|
|
5717
|
+
constructor(cause) {
|
|
5718
|
+
super("CursorCreateOutcomeUnknownError: Create Cursor \u306E\u6210\u5426\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u81EA\u52D5\u518D\u8A66\u884C\u305B\u305A\u3001\u6700\u592710\u5206+\u5B89\u5168\u4F59\u88D5\u306E\u9593\u306F\u67A0\u3092\u9694\u96E2\u3057\u307E\u3059\u3002");
|
|
5719
|
+
this.name = "CursorCreateOutcomeUnknownError";
|
|
5720
|
+
this.cause = cause;
|
|
5721
|
+
}
|
|
5722
|
+
};
|
|
5723
|
+
var CursorCleanupWarning = class extends Error {
|
|
5724
|
+
constructor(cause) {
|
|
5725
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
5726
|
+
super(`CursorCleanupWarning: Cursor \u306E\u89E3\u653E\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u7D50\u679C\u306F\u6709\u52B9\u3067\u3059\u304C\u3001\u6700\u592710\u5206+\u5B89\u5168\u4F59\u88D5\u306E\u9593\u306F\u67A0\u3092\u9694\u96E2\u3057\u307E\u3059\u3002\u8A73\u7D30: ${detail}`);
|
|
5727
|
+
this.name = "CursorCleanupWarning";
|
|
5728
|
+
this.cause = cause;
|
|
5729
|
+
}
|
|
5730
|
+
};
|
|
5731
|
+
|
|
5732
|
+
// src/core/optimization/korderCursorExecutor.ts
|
|
5733
|
+
async function executeKorderCursor(input) {
|
|
5734
|
+
const handle = await input.client.openCursor({
|
|
5735
|
+
app: input.app,
|
|
5736
|
+
fields: input.fields.length > 0 ? input.fields : void 0,
|
|
5737
|
+
query: input.query,
|
|
5738
|
+
size: 500
|
|
5739
|
+
});
|
|
5740
|
+
const records = [];
|
|
5741
|
+
let seen = 0;
|
|
5742
|
+
let primaryError;
|
|
5743
|
+
let cleanupWarning;
|
|
5744
|
+
try {
|
|
5745
|
+
if (handle.totalCount > input.offset) {
|
|
5746
|
+
while (records.length < input.limit) {
|
|
5747
|
+
const page = await handle.nextPage();
|
|
5748
|
+
for (const record of page.records) {
|
|
5749
|
+
if (seen < input.offset) seen += 1;
|
|
5750
|
+
else if (records.length < input.limit) records.push(record);
|
|
5751
|
+
else break;
|
|
5752
|
+
}
|
|
5753
|
+
if (!page.next) break;
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
} catch (error) {
|
|
5757
|
+
primaryError = error;
|
|
5758
|
+
throw error;
|
|
5759
|
+
} finally {
|
|
5760
|
+
try {
|
|
5761
|
+
await handle.close();
|
|
5762
|
+
} catch (cleanupError) {
|
|
5763
|
+
if (primaryError && primaryError instanceof Error) {
|
|
5764
|
+
Object.defineProperty(primaryError, "cursorCleanupError", {
|
|
5765
|
+
value: cleanupError,
|
|
5766
|
+
configurable: true
|
|
5767
|
+
});
|
|
5768
|
+
} else {
|
|
5769
|
+
cleanupWarning = new CursorCleanupWarning(cleanupError).message;
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
return { records, cleanupWarning };
|
|
5774
|
+
}
|
|
5775
|
+
|
|
5776
|
+
// src/converter/korderCursorQuery.ts
|
|
5777
|
+
function buildKorderCursorQuery(stmt) {
|
|
5778
|
+
const parts = [];
|
|
5779
|
+
if (stmt.where) parts.push(whereToKintone(stmt.where));
|
|
5780
|
+
const order = stmt.orderBy.map((item) => {
|
|
5781
|
+
if (item.key.type !== "FIELD_NAME") {
|
|
5782
|
+
throw new Error("ArgumentError: KORDER cursor key must be a direct field.");
|
|
5783
|
+
}
|
|
5784
|
+
return `${item.key.name} ${item.direction === "ASC" ? "asc" : "desc"}`;
|
|
5785
|
+
});
|
|
5786
|
+
parts.push(`order by ${order.join(", ")}`);
|
|
5787
|
+
return parts.join(" ");
|
|
5788
|
+
}
|
|
5789
|
+
|
|
5239
5790
|
// src/engine/process.ts
|
|
5240
5791
|
function flatten(record, alias) {
|
|
5241
5792
|
const row = {};
|
|
@@ -5300,9 +5851,9 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
5300
5851
|
}
|
|
5301
5852
|
return result;
|
|
5302
5853
|
}
|
|
5303
|
-
function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
|
|
5854
|
+
function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
5304
5855
|
if (where === null) return rows;
|
|
5305
|
-
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
|
|
5856
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
|
|
5306
5857
|
}
|
|
5307
5858
|
function hasAggregateColumns(columns) {
|
|
5308
5859
|
return columns.some(
|
|
@@ -5363,7 +5914,7 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5363
5914
|
let strVal;
|
|
5364
5915
|
if (arg.type === "FIELD_REF") {
|
|
5365
5916
|
const raw = row[arg.field];
|
|
5366
|
-
if (raw === void 0 || raw === "") continue;
|
|
5917
|
+
if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
|
|
5367
5918
|
strVal = raw;
|
|
5368
5919
|
} else {
|
|
5369
5920
|
const n = evalArithExpr(arg, row);
|
|
@@ -5375,10 +5926,16 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5375
5926
|
const eff = distinct ? [...new Set(strValues)] : strValues;
|
|
5376
5927
|
if (func === "COUNT") return eff.length;
|
|
5377
5928
|
if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
|
|
5378
|
-
const
|
|
5379
|
-
if (
|
|
5380
|
-
if (eff.length === 0) return
|
|
5381
|
-
|
|
5929
|
+
const comparison = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
|
|
5930
|
+
if (func === "MIN" || func === "MAX") {
|
|
5931
|
+
if (eff.length === 0) return 0;
|
|
5932
|
+
const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" ? syntheticSemantics("string") : syntheticSemantics("number"));
|
|
5933
|
+
let result = eff[0];
|
|
5934
|
+
for (const candidate of eff.slice(1)) {
|
|
5935
|
+
const cmp = compareCanonicalValues(candidate, result, semantics);
|
|
5936
|
+
if (func === "MAX" && cmp > 0 || func === "MIN" && cmp < 0) result = candidate;
|
|
5937
|
+
}
|
|
5938
|
+
return result;
|
|
5382
5939
|
}
|
|
5383
5940
|
const nums = eff.map(Number);
|
|
5384
5941
|
switch (func) {
|
|
@@ -5386,37 +5943,12 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
5386
5943
|
return nums.reduce((a, b) => a + b, 0);
|
|
5387
5944
|
case "AVG":
|
|
5388
5945
|
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
5946
|
}
|
|
5395
5947
|
}
|
|
5396
5948
|
function toAggregateFieldRef(field) {
|
|
5397
5949
|
const dot = field.indexOf(".");
|
|
5398
5950
|
return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
|
|
5399
5951
|
}
|
|
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
5952
|
function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
5421
5953
|
if (node.type === "NUMBER") return node.value;
|
|
5422
5954
|
if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
|
|
@@ -5448,9 +5980,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
|
|
|
5448
5980
|
const argStr = aggregateArgLabel(arg);
|
|
5449
5981
|
return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
|
|
5450
5982
|
}
|
|
5451
|
-
function applyHaving(rows, having, resolveFieldType) {
|
|
5983
|
+
function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
|
|
5452
5984
|
if (having === null) return rows;
|
|
5453
|
-
return rows.filter((row) => evalWhere(having, row, resolveFieldType));
|
|
5985
|
+
return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
|
|
5454
5986
|
}
|
|
5455
5987
|
function applyDistinct(rows, columns) {
|
|
5456
5988
|
if (rows.length === 0) return rows;
|
|
@@ -5502,27 +6034,37 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
5502
6034
|
return JSON.stringify(values);
|
|
5503
6035
|
};
|
|
5504
6036
|
}
|
|
5505
|
-
function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
6037
|
+
function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
|
|
5506
6038
|
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
|
-
|
|
6039
|
+
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2).rows.map((item) => item.row);
|
|
6040
|
+
}
|
|
6041
|
+
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
|
|
6042
|
+
const keyMeta = orderBy.map(({ key }) => {
|
|
6043
|
+
if (key.type === "ARITH_KEY") return { semantics: syntheticSemantics("number") };
|
|
6044
|
+
if (key.type === "FUNC_KEY") {
|
|
6045
|
+
return { semantics: syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(key.expr.func) ? "number" : "string") };
|
|
6046
|
+
}
|
|
6047
|
+
const semantics = fieldSemantics2?.get(key.name);
|
|
6048
|
+
if (semantics) return { semantics };
|
|
6049
|
+
const orderMap = optionOrders?.get(key.name);
|
|
6050
|
+
if (orderMap) {
|
|
6051
|
+
return {
|
|
6052
|
+
semantics: {
|
|
6053
|
+
fieldType: "MULTI_SELECT",
|
|
6054
|
+
compareMode: "option",
|
|
6055
|
+
inSubtable: false,
|
|
6056
|
+
requiresCollectionOperators: false,
|
|
6057
|
+
optionOrder: orderMap
|
|
6058
|
+
}
|
|
6059
|
+
};
|
|
6060
|
+
}
|
|
6061
|
+
return { semantics: syntheticSemantics(sortKinds?.get(key.name) ?? "string") };
|
|
6062
|
+
});
|
|
5514
6063
|
const decorated = rows.map((row) => ({
|
|
5515
6064
|
row,
|
|
5516
6065
|
keys: orderBy.map(({ key }, i) => {
|
|
5517
6066
|
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
|
-
};
|
|
6067
|
+
return { s };
|
|
5526
6068
|
})
|
|
5527
6069
|
}));
|
|
5528
6070
|
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
@@ -5537,15 +6079,24 @@ function compareDecoratedRows(a, b, orderBy, keyMeta) {
|
|
|
5537
6079
|
return 0;
|
|
5538
6080
|
}
|
|
5539
6081
|
function compareSortKeys(a, b, meta) {
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
6082
|
+
return compareCanonicalValues(a.s, b.s, meta.semantics);
|
|
6083
|
+
}
|
|
6084
|
+
var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
6085
|
+
"LENGTH",
|
|
6086
|
+
"INSTR",
|
|
6087
|
+
"ROUND",
|
|
6088
|
+
"FLOOR",
|
|
6089
|
+
"CEIL",
|
|
6090
|
+
"TRUNCATE",
|
|
6091
|
+
"YEAR",
|
|
6092
|
+
"MONTH",
|
|
6093
|
+
"DAY",
|
|
6094
|
+
"DATEDIFF",
|
|
6095
|
+
"ABS",
|
|
6096
|
+
"MOD",
|
|
6097
|
+
"POWER",
|
|
6098
|
+
"SQRT"
|
|
6099
|
+
]);
|
|
5549
6100
|
function evalOrderKey(key, row) {
|
|
5550
6101
|
switch (key.type) {
|
|
5551
6102
|
case "FIELD_NAME":
|
|
@@ -5556,30 +6107,7 @@ function evalOrderKey(key, row) {
|
|
|
5556
6107
|
return evalStringFunc(key.expr, row);
|
|
5557
6108
|
}
|
|
5558
6109
|
}
|
|
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) {
|
|
6110
|
+
function applyWindow(rows, columns, optionOrders, sortKinds, fieldSemantics2) {
|
|
5583
6111
|
const windows = columns.filter((column) => column.type === "WINDOW_COL");
|
|
5584
6112
|
if (rows.length === 0 || windows.length === 0) return rows;
|
|
5585
6113
|
for (const window of windows) {
|
|
@@ -5591,7 +6119,7 @@ function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
|
5591
6119
|
else partitions.set(key, [row]);
|
|
5592
6120
|
}
|
|
5593
6121
|
for (const partition of partitions.values()) {
|
|
5594
|
-
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
|
|
6122
|
+
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
|
|
5595
6123
|
const sorted = sortedResult.rows;
|
|
5596
6124
|
let rank = 1;
|
|
5597
6125
|
let denseRank = 1;
|
|
@@ -5616,7 +6144,7 @@ function applyLimit(rows, limit, offset) {
|
|
|
5616
6144
|
if (limit === null) return rows.slice(start);
|
|
5617
6145
|
return rows.slice(start, start + limit);
|
|
5618
6146
|
}
|
|
5619
|
-
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
6147
|
+
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2) {
|
|
5620
6148
|
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
5621
6149
|
const projected2 = rows.map((row) => stripParentShortcutColumns(row));
|
|
5622
6150
|
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
|
|
@@ -5680,7 +6208,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
|
5680
6208
|
}
|
|
5681
6209
|
case "CASE_COL": {
|
|
5682
6210
|
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
5683
|
-
out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
|
|
6211
|
+
out[key] = evalCaseWhen(col.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
5684
6212
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
5685
6213
|
break;
|
|
5686
6214
|
}
|
|
@@ -5835,6 +6363,26 @@ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
|
5835
6363
|
args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
|
|
5836
6364
|
};
|
|
5837
6365
|
}
|
|
6366
|
+
function deriveOutputOrderSemantics(columns) {
|
|
6367
|
+
const result = /* @__PURE__ */ new Map();
|
|
6368
|
+
for (const column of columns) {
|
|
6369
|
+
if (!("alias" in column) || !column.alias) continue;
|
|
6370
|
+
if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
6371
|
+
result.set(column.alias, syntheticSemantics("number"));
|
|
6372
|
+
} else if (column.type === "AGGREGATE") {
|
|
6373
|
+
if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
|
|
6374
|
+
result.set(column.alias, syntheticSemantics("number"));
|
|
6375
|
+
} else if (column.func === "GROUP_CONCAT") {
|
|
6376
|
+
result.set(column.alias, syntheticSemantics("string"));
|
|
6377
|
+
}
|
|
6378
|
+
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
|
|
6379
|
+
result.set(column.alias, syntheticSemantics("string"));
|
|
6380
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
6381
|
+
result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
|
|
6382
|
+
}
|
|
6383
|
+
}
|
|
6384
|
+
return result;
|
|
6385
|
+
}
|
|
5838
6386
|
function runFullScan(input) {
|
|
5839
6387
|
const {
|
|
5840
6388
|
stmt,
|
|
@@ -5842,12 +6390,17 @@ function runFullScan(input) {
|
|
|
5842
6390
|
scalarCache,
|
|
5843
6391
|
optionOrders,
|
|
5844
6392
|
sortKinds,
|
|
6393
|
+
orderSemantics,
|
|
5845
6394
|
fieldTypeResolver,
|
|
6395
|
+
fieldSemanticsResolver,
|
|
5846
6396
|
havingFieldTypeResolver,
|
|
6397
|
+
havingFieldSemanticsResolver,
|
|
5847
6398
|
aggregateSortKindResolver,
|
|
5848
6399
|
appliedKlikes,
|
|
5849
6400
|
sourceColumns
|
|
5850
6401
|
} = input;
|
|
6402
|
+
const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
|
|
6403
|
+
for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
|
|
5851
6404
|
let rows = [];
|
|
5852
6405
|
const mainAlias = stmt.from.alias;
|
|
5853
6406
|
const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
|
|
@@ -5858,18 +6411,18 @@ function runFullScan(input) {
|
|
|
5858
6411
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
5859
6412
|
rows = applyJoin(rows, rightRows, join2);
|
|
5860
6413
|
}
|
|
5861
|
-
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
|
|
6414
|
+
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
|
|
5862
6415
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
5863
6416
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
|
|
5864
6417
|
}
|
|
5865
|
-
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
|
|
5866
|
-
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
|
|
6418
|
+
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
|
|
6419
|
+
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
5867
6420
|
if (stmt.distinct) {
|
|
5868
6421
|
rows = applyDistinct(rows, stmt.columns);
|
|
5869
6422
|
}
|
|
5870
|
-
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
|
|
6423
|
+
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
5871
6424
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
5872
|
-
return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
|
|
6425
|
+
return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns, fieldSemanticsResolver);
|
|
5873
6426
|
}
|
|
5874
6427
|
|
|
5875
6428
|
// src/converter/subtableAdapter.ts
|
|
@@ -6137,6 +6690,192 @@ function renderValidationValue(value) {
|
|
|
6137
6690
|
return String(value);
|
|
6138
6691
|
}
|
|
6139
6692
|
|
|
6693
|
+
// src/core/optimization/whereCapability.ts
|
|
6694
|
+
var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
|
|
6695
|
+
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
6696
|
+
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
6697
|
+
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6698
|
+
["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6699
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6700
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6701
|
+
["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
6702
|
+
["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
6703
|
+
["DATE", new Set(RANGE_AND_EQUALITY)],
|
|
6704
|
+
["TIME", new Set(RANGE_AND_EQUALITY)],
|
|
6705
|
+
["DATETIME", new Set(RANGE_AND_EQUALITY)],
|
|
6706
|
+
["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
6707
|
+
["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
6708
|
+
["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6709
|
+
["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
6710
|
+
["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
6711
|
+
["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
6712
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6713
|
+
["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6714
|
+
["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6715
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6716
|
+
["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
6717
|
+
["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6718
|
+
["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6719
|
+
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
6720
|
+
["STATUS", new Set(EQUALITY_IN)]
|
|
6721
|
+
]);
|
|
6722
|
+
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
6723
|
+
"RECORD_NUMBER",
|
|
6724
|
+
"__ID__",
|
|
6725
|
+
"CREATOR",
|
|
6726
|
+
"MODIFIER",
|
|
6727
|
+
"CREATED_TIME",
|
|
6728
|
+
"UPDATED_TIME",
|
|
6729
|
+
"DATE",
|
|
6730
|
+
"TIME",
|
|
6731
|
+
"DATETIME",
|
|
6732
|
+
"SINGLE_LINE_TEXT",
|
|
6733
|
+
"LINK",
|
|
6734
|
+
"NUMBER",
|
|
6735
|
+
"CALC",
|
|
6736
|
+
"MULTI_LINE_TEXT",
|
|
6737
|
+
"RICH_TEXT",
|
|
6738
|
+
"RADIO_BUTTON",
|
|
6739
|
+
"DROP_DOWN",
|
|
6740
|
+
"STATUS",
|
|
6741
|
+
// 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
|
|
6742
|
+
"KSQL_STRING",
|
|
6743
|
+
"KSQL_NUMBER",
|
|
6744
|
+
"KSQL_BOOLEAN"
|
|
6745
|
+
]);
|
|
6746
|
+
var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
6747
|
+
"CHECK_BOX",
|
|
6748
|
+
"MULTI_SELECT",
|
|
6749
|
+
"FILE",
|
|
6750
|
+
"USER_SELECT",
|
|
6751
|
+
"ORGANIZATION_SELECT",
|
|
6752
|
+
"GROUP_SELECT",
|
|
6753
|
+
"STATUS_ASSIGNEE",
|
|
6754
|
+
"CATEGORY"
|
|
6755
|
+
]);
|
|
6756
|
+
function nativeWhereOperatorsForType(fieldType) {
|
|
6757
|
+
return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
|
|
6758
|
+
}
|
|
6759
|
+
function classifyWhereCapability(where, resolveField2) {
|
|
6760
|
+
if (where === null) {
|
|
6761
|
+
return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
|
|
6762
|
+
}
|
|
6763
|
+
return classifyNode(where, resolveField2);
|
|
6764
|
+
}
|
|
6765
|
+
function classifyNode(where, resolveField2) {
|
|
6766
|
+
switch (where.type) {
|
|
6767
|
+
case "BINARY":
|
|
6768
|
+
return classifyBinary(where.op, where.left, where.right.type, resolveField2);
|
|
6769
|
+
case "NULL_CHECK":
|
|
6770
|
+
if (where.field.type !== "FIELD") return localExpression();
|
|
6771
|
+
return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
|
|
6772
|
+
case "EXISTS":
|
|
6773
|
+
return localExpression();
|
|
6774
|
+
case "GROUP":
|
|
6775
|
+
return classifyNode(where.expr, resolveField2);
|
|
6776
|
+
case "NOT": {
|
|
6777
|
+
const inner = classifyNode(where.expr, resolveField2);
|
|
6778
|
+
return inner.capability === "SUPERSET_PREFILTER" ? { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] } : inner;
|
|
6779
|
+
}
|
|
6780
|
+
case "LOGICAL": {
|
|
6781
|
+
const left = classifyNode(where.left, resolveField2);
|
|
6782
|
+
const right = classifyNode(where.right, resolveField2);
|
|
6783
|
+
return combineLogical(where.op, left, right);
|
|
6784
|
+
}
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
function classifyBinary(op, left, rightType, resolveField2) {
|
|
6788
|
+
if (left.type !== "FIELD") return localExpression();
|
|
6789
|
+
const semantics = resolveField2(left);
|
|
6790
|
+
if (!semantics) {
|
|
6791
|
+
return unsupported("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
|
|
6792
|
+
}
|
|
6793
|
+
if (!hasLocalContract(semantics.fieldType, op)) {
|
|
6794
|
+
return unsupported("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, normalizeOperator(op));
|
|
6795
|
+
}
|
|
6796
|
+
const nativeOp = normalizeOperator(op);
|
|
6797
|
+
const native = nativeWhereOperatorsForType(semantics.fieldType);
|
|
6798
|
+
const rightCanPush = rightType === "STRING" || rightType === "NUMBER" || rightType === "IN_LIST" || rightType === "KINTONE_FUNC";
|
|
6799
|
+
const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
|
|
6800
|
+
const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
|
|
6801
|
+
if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
|
|
6802
|
+
return {
|
|
6803
|
+
capability: "EXACT_PUSHDOWN",
|
|
6804
|
+
reasons: [{
|
|
6805
|
+
code: "WHERE_EXACT",
|
|
6806
|
+
field: left.field,
|
|
6807
|
+
fieldType: semantics.fieldType,
|
|
6808
|
+
operator: nativeOp
|
|
6809
|
+
}]
|
|
6810
|
+
};
|
|
6811
|
+
}
|
|
6812
|
+
return {
|
|
6813
|
+
capability: "LOCAL_ONLY",
|
|
6814
|
+
reasons: [{
|
|
6815
|
+
code: "WHERE_RESIDUAL",
|
|
6816
|
+
field: left.field,
|
|
6817
|
+
fieldType: semantics.fieldType,
|
|
6818
|
+
operator: nativeOp
|
|
6819
|
+
}]
|
|
6820
|
+
};
|
|
6821
|
+
}
|
|
6822
|
+
function classifyLocalOnlyField(field, operator, resolveField2) {
|
|
6823
|
+
const semantics = resolveField2(field);
|
|
6824
|
+
if (!semantics) return unsupported("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
|
|
6825
|
+
if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
|
|
6826
|
+
return unsupported("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
|
|
6827
|
+
}
|
|
6828
|
+
return {
|
|
6829
|
+
capability: "LOCAL_ONLY",
|
|
6830
|
+
reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
|
|
6831
|
+
};
|
|
6832
|
+
}
|
|
6833
|
+
function hasLocalContract(fieldType, op) {
|
|
6834
|
+
if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
|
|
6835
|
+
if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
|
|
6836
|
+
return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
|
|
6837
|
+
}
|
|
6838
|
+
function normalizeOperator(op) {
|
|
6839
|
+
switch (op) {
|
|
6840
|
+
case "<>":
|
|
6841
|
+
return "!=";
|
|
6842
|
+
case "IN":
|
|
6843
|
+
return "in";
|
|
6844
|
+
case "NOT_IN":
|
|
6845
|
+
return "not in";
|
|
6846
|
+
case "LIKE":
|
|
6847
|
+
case "KLIKE":
|
|
6848
|
+
return "like";
|
|
6849
|
+
case "NOT_LIKE":
|
|
6850
|
+
case "NOT_KLIKE":
|
|
6851
|
+
return "not like";
|
|
6852
|
+
default:
|
|
6853
|
+
return op;
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
function combineLogical(op, left, right) {
|
|
6857
|
+
const reasons = [...left.reasons, ...right.reasons];
|
|
6858
|
+
if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
|
|
6859
|
+
return { capability: "UNSUPPORTED", reasons };
|
|
6860
|
+
}
|
|
6861
|
+
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
6862
|
+
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
6863
|
+
}
|
|
6864
|
+
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
6865
|
+
return {
|
|
6866
|
+
capability: "SUPERSET_PREFILTER",
|
|
6867
|
+
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
6868
|
+
};
|
|
6869
|
+
}
|
|
6870
|
+
return { capability: "LOCAL_ONLY", reasons };
|
|
6871
|
+
}
|
|
6872
|
+
function localExpression() {
|
|
6873
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
6874
|
+
}
|
|
6875
|
+
function unsupported(code, field, fieldType, operator) {
|
|
6876
|
+
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
6877
|
+
}
|
|
6878
|
+
|
|
6140
6879
|
// src/execute.ts
|
|
6141
6880
|
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
6881
|
var SearchAbortedError = class extends Error {
|
|
@@ -6147,8 +6886,20 @@ var SearchAbortedError = class extends Error {
|
|
|
6147
6886
|
};
|
|
6148
6887
|
var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
|
|
6149
6888
|
var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
|
|
6889
|
+
var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
|
|
6890
|
+
var nextDefaultCacheContextId = 1;
|
|
6891
|
+
function resolveCacheContext(client, explicit) {
|
|
6892
|
+
if (explicit) return explicit;
|
|
6893
|
+
let context = defaultCacheContextByClient.get(client);
|
|
6894
|
+
if (!context) {
|
|
6895
|
+
context = `client:${nextDefaultCacheContextId++}`;
|
|
6896
|
+
defaultCacheContextByClient.set(client, context);
|
|
6897
|
+
}
|
|
6898
|
+
return context;
|
|
6899
|
+
}
|
|
6150
6900
|
async function execute(sql, client, options = {}) {
|
|
6151
6901
|
const startedAt = Date.now();
|
|
6902
|
+
const cacheContext = resolveCacheContext(client, options.cacheContext);
|
|
6152
6903
|
const stmt = parseSql(sql);
|
|
6153
6904
|
const metrics = createEmptyMetrics();
|
|
6154
6905
|
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
@@ -6162,7 +6913,7 @@ async function execute(sql, client, options = {}) {
|
|
|
6162
6913
|
stmt,
|
|
6163
6914
|
guardedClient,
|
|
6164
6915
|
options,
|
|
6165
|
-
|
|
6916
|
+
cacheContext
|
|
6166
6917
|
);
|
|
6167
6918
|
metrics.elapsedMs = Date.now() - startedAt;
|
|
6168
6919
|
return { ...attachSearchAbortWarning(result, collector), metrics };
|
|
@@ -6176,6 +6927,15 @@ function createEmptyMetrics() {
|
|
|
6176
6927
|
fieldCalls: 0,
|
|
6177
6928
|
appsCalls: 0,
|
|
6178
6929
|
processStatusCalls: 0,
|
|
6930
|
+
cursorCreateCalls: 0,
|
|
6931
|
+
cursorGetCalls: 0,
|
|
6932
|
+
cursorDeleteCalls: 0,
|
|
6933
|
+
cursorRecordsScanned: 0,
|
|
6934
|
+
cursorActiveCurrent: 0,
|
|
6935
|
+
cursorActivePeak: 0,
|
|
6936
|
+
cursorCleanupFailures: 0,
|
|
6937
|
+
cursorCreateOutcomeUnknown: 0,
|
|
6938
|
+
cursorQuarantinedCurrent: 0,
|
|
6179
6939
|
fetchedRows: 0,
|
|
6180
6940
|
elapsedMs: 0
|
|
6181
6941
|
};
|
|
@@ -6188,6 +6948,48 @@ function wrapClientWithMetrics(client, metrics) {
|
|
|
6188
6948
|
metrics.fetchedRows += res.records.length;
|
|
6189
6949
|
return res;
|
|
6190
6950
|
},
|
|
6951
|
+
openCursor: async (params) => {
|
|
6952
|
+
metrics.cursorCreateCalls += 1;
|
|
6953
|
+
let handle;
|
|
6954
|
+
try {
|
|
6955
|
+
handle = await client.openCursor(params);
|
|
6956
|
+
} catch (error) {
|
|
6957
|
+
if (error instanceof Error && error.name === "CursorCreateOutcomeUnknownError") {
|
|
6958
|
+
metrics.cursorCreateOutcomeUnknown += 1;
|
|
6959
|
+
metrics.cursorQuarantinedCurrent += 1;
|
|
6960
|
+
}
|
|
6961
|
+
throw error;
|
|
6962
|
+
}
|
|
6963
|
+
metrics.cursorActiveCurrent += 1;
|
|
6964
|
+
metrics.cursorActivePeak = Math.max(metrics.cursorActivePeak, metrics.cursorActiveCurrent);
|
|
6965
|
+
let released = false;
|
|
6966
|
+
const markReleased = () => {
|
|
6967
|
+
if (released) return;
|
|
6968
|
+
released = true;
|
|
6969
|
+
metrics.cursorActiveCurrent -= 1;
|
|
6970
|
+
};
|
|
6971
|
+
return {
|
|
6972
|
+
totalCount: handle.totalCount,
|
|
6973
|
+
nextPage: async () => {
|
|
6974
|
+
metrics.cursorGetCalls += 1;
|
|
6975
|
+
const page = await handle.nextPage();
|
|
6976
|
+
metrics.cursorRecordsScanned += page.records.length;
|
|
6977
|
+
if (!page.next) markReleased();
|
|
6978
|
+
return page;
|
|
6979
|
+
},
|
|
6980
|
+
close: async () => {
|
|
6981
|
+
if (!released) metrics.cursorDeleteCalls += 1;
|
|
6982
|
+
try {
|
|
6983
|
+
await handle.close();
|
|
6984
|
+
markReleased();
|
|
6985
|
+
} catch (error) {
|
|
6986
|
+
metrics.cursorCleanupFailures += 1;
|
|
6987
|
+
metrics.cursorQuarantinedCurrent += 1;
|
|
6988
|
+
throw error;
|
|
6989
|
+
}
|
|
6990
|
+
}
|
|
6991
|
+
};
|
|
6992
|
+
},
|
|
6191
6993
|
postRecords: (params) => {
|
|
6192
6994
|
metrics.postCalls += 1;
|
|
6193
6995
|
return client.postRecords(params);
|
|
@@ -6227,6 +7029,37 @@ function wrapClientWithSearchAbort(client, collector, failClosed) {
|
|
|
6227
7029
|
}
|
|
6228
7030
|
};
|
|
6229
7031
|
}
|
|
7032
|
+
function wrapClientWithCursorScope(client) {
|
|
7033
|
+
const active = /* @__PURE__ */ new Set();
|
|
7034
|
+
return {
|
|
7035
|
+
client: {
|
|
7036
|
+
...client,
|
|
7037
|
+
openCursor: async (params) => {
|
|
7038
|
+
const handle = await client.openCursor(params);
|
|
7039
|
+
active.add(handle);
|
|
7040
|
+
const remove = () => active.delete(handle);
|
|
7041
|
+
return {
|
|
7042
|
+
totalCount: handle.totalCount,
|
|
7043
|
+
async nextPage() {
|
|
7044
|
+
const page = await handle.nextPage();
|
|
7045
|
+
if (!page.next) remove();
|
|
7046
|
+
return page;
|
|
7047
|
+
},
|
|
7048
|
+
async close() {
|
|
7049
|
+
try {
|
|
7050
|
+
await handle.close();
|
|
7051
|
+
} finally {
|
|
7052
|
+
remove();
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
};
|
|
7056
|
+
}
|
|
7057
|
+
},
|
|
7058
|
+
closeActive: async () => {
|
|
7059
|
+
await Promise.all([...active].map((handle) => handle.close().catch(() => void 0)));
|
|
7060
|
+
}
|
|
7061
|
+
};
|
|
7062
|
+
}
|
|
6230
7063
|
function isSelectLikeStatement(stmt) {
|
|
6231
7064
|
return stmt.type === "SELECT" || stmt.type === "UNION" || stmt.type === "WITH";
|
|
6232
7065
|
}
|
|
@@ -6277,7 +7110,13 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
6277
7110
|
case "DESCRIBE":
|
|
6278
7111
|
return executeDescribe(stmt, client, cacheContext);
|
|
6279
7112
|
case "EXPLAIN":
|
|
6280
|
-
return executeExplain(
|
|
7113
|
+
return executeExplain(
|
|
7114
|
+
stmt,
|
|
7115
|
+
client,
|
|
7116
|
+
cacheContext,
|
|
7117
|
+
options.maxRecords ?? 1e4,
|
|
7118
|
+
options.cursorMaxActive ?? 2
|
|
7119
|
+
);
|
|
6281
7120
|
// 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
|
|
6282
7121
|
case "CREATE_TEMP_TABLE":
|
|
6283
7122
|
throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
@@ -6308,7 +7147,7 @@ function materializedColumnMetaEqual(left, right) {
|
|
|
6308
7147
|
if (!left || !right || left.size !== right.size) return false;
|
|
6309
7148
|
for (const [column, meta] of left) {
|
|
6310
7149
|
const candidate = right.get(column);
|
|
6311
|
-
if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType) return false;
|
|
7150
|
+
if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType || !fieldSemanticsEqual(candidate.semantics, meta.semantics)) return false;
|
|
6312
7151
|
}
|
|
6313
7152
|
return true;
|
|
6314
7153
|
}
|
|
@@ -6339,7 +7178,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
6339
7178
|
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
6340
7179
|
const startedAt = Date.now();
|
|
6341
7180
|
const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
|
|
6342
|
-
const cacheContext = options.cacheContext
|
|
7181
|
+
const cacheContext = resolveCacheContext(client, options.cacheContext);
|
|
6343
7182
|
const tempTables = /* @__PURE__ */ new Map();
|
|
6344
7183
|
const variables = /* @__PURE__ */ new Map();
|
|
6345
7184
|
const results = [];
|
|
@@ -6384,9 +7223,11 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
6384
7223
|
searchAbortCollector,
|
|
6385
7224
|
info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH"
|
|
6386
7225
|
);
|
|
7226
|
+
const cursorScope = wrapClientWithCursorScope(statementClient);
|
|
6387
7227
|
const outcome = await runWithDeadline(
|
|
6388
|
-
executeBatchStatement(statements[i], info,
|
|
6389
|
-
remaining
|
|
7228
|
+
executeBatchStatement(statements[i], info, cursorScope.client, stmtOptions, cacheContext, tempTables, variables),
|
|
7229
|
+
remaining,
|
|
7230
|
+
cursorScope.closeActive
|
|
6390
7231
|
);
|
|
6391
7232
|
if (outcome.result) {
|
|
6392
7233
|
outcome.result = attachSearchAbortWarning(outcome.result, searchAbortCollector);
|
|
@@ -6433,7 +7274,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
6433
7274
|
cacheContext,
|
|
6434
7275
|
tempTables
|
|
6435
7276
|
);
|
|
6436
|
-
|
|
7277
|
+
const first = resolvedStmt2.expr.query.columns[0];
|
|
7278
|
+
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");
|
|
7279
|
+
const numberValue = numeric ? Number(value) : Number.NaN;
|
|
7280
|
+
variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
|
|
6437
7281
|
} catch (e) {
|
|
6438
7282
|
if (e instanceof ScalarSubqueryError) {
|
|
6439
7283
|
throw new Error(`ArgumentError: ${e.message}`);
|
|
@@ -6541,19 +7385,47 @@ async function runSelectLike(query, client, options, cacheContext, tempTables) {
|
|
|
6541
7385
|
}
|
|
6542
7386
|
return executeQueryWithCte(query, client, options, tempTables, cacheContext, true);
|
|
6543
7387
|
}
|
|
6544
|
-
async function runWithDeadline(work, remainingMs) {
|
|
7388
|
+
async function runWithDeadline(work, remainingMs, onTimeout) {
|
|
6545
7389
|
if (remainingMs === null) return work;
|
|
6546
7390
|
if (remainingMs <= 0) {
|
|
7391
|
+
if (onTimeout) await onTimeout();
|
|
6547
7392
|
void work.catch(() => {
|
|
6548
7393
|
});
|
|
6549
7394
|
throw new BatchTimeoutError();
|
|
6550
7395
|
}
|
|
6551
7396
|
let timer;
|
|
7397
|
+
let timedOut = false;
|
|
7398
|
+
const guardedWork = work.then(
|
|
7399
|
+
(value) => timedOut ? new Promise(() => void 0) : value,
|
|
7400
|
+
(error) => {
|
|
7401
|
+
if (timedOut) return new Promise(() => void 0);
|
|
7402
|
+
throw error;
|
|
7403
|
+
}
|
|
7404
|
+
);
|
|
6552
7405
|
try {
|
|
6553
7406
|
return await Promise.race([
|
|
6554
|
-
|
|
7407
|
+
guardedWork,
|
|
6555
7408
|
new Promise((_, reject) => {
|
|
6556
|
-
timer = setTimeout(() =>
|
|
7409
|
+
timer = setTimeout(() => {
|
|
7410
|
+
timedOut = true;
|
|
7411
|
+
void (async () => {
|
|
7412
|
+
if (onTimeout) {
|
|
7413
|
+
let cleanupTimer;
|
|
7414
|
+
try {
|
|
7415
|
+
await Promise.race([
|
|
7416
|
+
onTimeout(),
|
|
7417
|
+
new Promise((resolve2) => {
|
|
7418
|
+
cleanupTimer = setTimeout(resolve2, 5e3);
|
|
7419
|
+
cleanupTimer.unref?.();
|
|
7420
|
+
})
|
|
7421
|
+
]);
|
|
7422
|
+
} finally {
|
|
7423
|
+
if (cleanupTimer) clearTimeout(cleanupTimer);
|
|
7424
|
+
}
|
|
7425
|
+
}
|
|
7426
|
+
reject(new BatchTimeoutError());
|
|
7427
|
+
})();
|
|
7428
|
+
}, remainingMs);
|
|
6557
7429
|
})
|
|
6558
7430
|
]);
|
|
6559
7431
|
} catch (e) {
|
|
@@ -6664,13 +7536,14 @@ var ScalarSubqueryError = class extends Error {
|
|
|
6664
7536
|
};
|
|
6665
7537
|
async function executeAssert(stmt, client, options, cacheContext, tempTables) {
|
|
6666
7538
|
const left = await evalAssertOperand(stmt.left, client, options, cacheContext, tempTables);
|
|
7539
|
+
const semantics = stmt.left.type === "NUMBER" || stmt.left.type === "ARITH" ? syntheticSemantics("number") : syntheticSemantics("string");
|
|
6667
7540
|
if (stmt.op === "BETWEEN") {
|
|
6668
7541
|
if (stmt.low === null || stmt.high === null) {
|
|
6669
7542
|
throw new Error("ArgumentError: malformed ASSERT statement.");
|
|
6670
7543
|
}
|
|
6671
7544
|
const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
|
|
6672
7545
|
const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
|
|
6673
|
-
if (!compareScalarValues(">=", left, low) || !compareScalarValues("<=", left, high)) {
|
|
7546
|
+
if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
|
|
6674
7547
|
throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
|
|
6675
7548
|
}
|
|
6676
7549
|
return { type: "ASSERT", condition: stmt.text };
|
|
@@ -6679,7 +7552,7 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
|
|
|
6679
7552
|
throw new Error("ArgumentError: malformed ASSERT statement.");
|
|
6680
7553
|
}
|
|
6681
7554
|
const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
|
|
6682
|
-
if (!compareScalarValues(stmt.op, left, right)) {
|
|
7555
|
+
if (!compareScalarValues(stmt.op, left, right, semantics)) {
|
|
6683
7556
|
throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
|
|
6684
7557
|
}
|
|
6685
7558
|
return { type: "ASSERT", condition: stmt.text };
|
|
@@ -6752,6 +7625,149 @@ function evalAssertArith(node) {
|
|
|
6752
7625
|
}
|
|
6753
7626
|
throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
|
|
6754
7627
|
}
|
|
7628
|
+
async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables, forcePhysicalMetadata = false) {
|
|
7629
|
+
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
7630
|
+
const physicalAppIds = forcePhysicalMetadata || whereNeedsFieldMetadata(stmt.where) ? [...new Set(tables.filter((table) => table.cteName === null).map((table) => table.appId))] : [];
|
|
7631
|
+
const infosByApp = new Map(
|
|
7632
|
+
await Promise.all(physicalAppIds.map(async (appId) => {
|
|
7633
|
+
const infos = await getFieldsCached(appId, client, cacheContext);
|
|
7634
|
+
return [appId, new Map(infos.map((info) => [info.code, info]))];
|
|
7635
|
+
}))
|
|
7636
|
+
);
|
|
7637
|
+
const orderedFields = /* @__PURE__ */ new Set();
|
|
7638
|
+
const collectOrderedFields = (node) => {
|
|
7639
|
+
if (Array.isArray(node)) {
|
|
7640
|
+
node.forEach(collectOrderedFields);
|
|
7641
|
+
return;
|
|
7642
|
+
}
|
|
7643
|
+
if (node === null || typeof node !== "object") return;
|
|
7644
|
+
const value = node;
|
|
7645
|
+
if (value["type"] === "SELECT") return;
|
|
7646
|
+
if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
|
|
7647
|
+
const left = value["left"];
|
|
7648
|
+
if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
|
|
7649
|
+
orderedFields.add(left["field"]);
|
|
7650
|
+
}
|
|
7651
|
+
}
|
|
7652
|
+
Object.values(value).forEach(collectOrderedFields);
|
|
7653
|
+
};
|
|
7654
|
+
collectOrderedFields(stmt.where);
|
|
7655
|
+
collectOrderedFields(stmt.having);
|
|
7656
|
+
for (const column of stmt.columns) {
|
|
7657
|
+
if (column.type === "CASE_COL") collectOrderedFields(column.expr);
|
|
7658
|
+
}
|
|
7659
|
+
const statusOrdersByApp = /* @__PURE__ */ new Map();
|
|
7660
|
+
await Promise.all([...infosByApp].map(async ([appId, infos]) => {
|
|
7661
|
+
const needsStatus = [...orderedFields].some((field) => infos.get(field)?.fieldType === "STATUS");
|
|
7662
|
+
if (!needsStatus) return;
|
|
7663
|
+
const order = await loadProcessStatusOrder(appId, client, cacheContext);
|
|
7664
|
+
if (order) statusOrdersByApp.set(appId, order);
|
|
7665
|
+
}));
|
|
7666
|
+
const fromPhysical = (table, field) => {
|
|
7667
|
+
if (field === "$id") return withFieldSemanticSource(
|
|
7668
|
+
resolveFieldSemantics({ fieldType: "__ID__" }),
|
|
7669
|
+
table.appId,
|
|
7670
|
+
"$id"
|
|
7671
|
+
);
|
|
7672
|
+
const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field));
|
|
7673
|
+
if (!info) return void 0;
|
|
7674
|
+
const base = info.semantics ?? resolveFieldSemantics(info);
|
|
7675
|
+
const semantics = info.fieldType === "STATUS" && statusOrdersByApp.has(table.appId) ? { ...base, optionOrder: statusOrdersByApp.get(table.appId) } : base;
|
|
7676
|
+
return withFieldSemanticSource(
|
|
7677
|
+
semantics,
|
|
7678
|
+
table.appId,
|
|
7679
|
+
info.code
|
|
7680
|
+
);
|
|
7681
|
+
};
|
|
7682
|
+
return (field) => {
|
|
7683
|
+
if (field.tableAlias !== null) {
|
|
7684
|
+
if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
7685
|
+
return fromPhysical(stmt.from, field.field);
|
|
7686
|
+
}
|
|
7687
|
+
const table = tables.find((candidate) => candidate.alias === field.tableAlias);
|
|
7688
|
+
if (!table) return void 0;
|
|
7689
|
+
if (table.cteName !== null) {
|
|
7690
|
+
return materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
|
|
7691
|
+
}
|
|
7692
|
+
return fromPhysical(table, field.field);
|
|
7693
|
+
}
|
|
7694
|
+
if (stmt.joins.length === 0) {
|
|
7695
|
+
if (stmt.from.cteName !== null) {
|
|
7696
|
+
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
|
|
7697
|
+
}
|
|
7698
|
+
return fromPhysical(stmt.from, field.field);
|
|
7699
|
+
}
|
|
7700
|
+
const matches = tables.flatMap((table) => {
|
|
7701
|
+
const semantics = table.cteName !== null ? materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics : fromPhysical(table, field.field);
|
|
7702
|
+
return semantics ? [semantics] : [];
|
|
7703
|
+
});
|
|
7704
|
+
if (matches.length === 1) return matches[0];
|
|
7705
|
+
return matches.length > 1 ? syntheticSemantics("string") : void 0;
|
|
7706
|
+
};
|
|
7707
|
+
}
|
|
7708
|
+
function selectCaseConditionsNeedFieldMetadata(stmt) {
|
|
7709
|
+
return stmt.columns.some((column) => column.type === "CASE_COL" && column.expr.branches.some((branch) => whereNeedsFieldMetadata(branch.condition)));
|
|
7710
|
+
}
|
|
7711
|
+
function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
|
|
7712
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
7713
|
+
for (const column of stmt.columns) {
|
|
7714
|
+
if (!("alias" in column) || !column.alias) continue;
|
|
7715
|
+
let semantics;
|
|
7716
|
+
if (column.type === "FIELD") semantics = rowResolver(aggregateFieldRef(column.field));
|
|
7717
|
+
else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
7718
|
+
semantics = syntheticSemantics("number");
|
|
7719
|
+
} else if (column.type === "AGGREGATE") {
|
|
7720
|
+
if (column.func === "MIN" || column.func === "MAX") {
|
|
7721
|
+
semantics = column.arg.type === "FIELD_REF" ? rowResolver(aggregateFieldRef(column.arg.field)) : syntheticSemantics("number");
|
|
7722
|
+
} else {
|
|
7723
|
+
semantics = column.func === "GROUP_CONCAT" ? syntheticSemantics("string") : syntheticSemantics("number");
|
|
7724
|
+
}
|
|
7725
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
7726
|
+
semantics = stringFunctionColumnMeta(column.expr).semantics;
|
|
7727
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
|
|
7728
|
+
semantics = syntheticSemantics("string");
|
|
7729
|
+
}
|
|
7730
|
+
if (semantics) aliases.set(column.alias, semantics);
|
|
7731
|
+
}
|
|
7732
|
+
return (field) => field.tableAlias === null && aliases.has(field.field) ? aliases.get(field.field) : rowResolver(field);
|
|
7733
|
+
}
|
|
7734
|
+
async function resolveSelectWhereCapability(stmt, client, cacheContext, materializedTables) {
|
|
7735
|
+
if (stmt.where === null) return classifyWhereCapability(null, () => void 0);
|
|
7736
|
+
const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables);
|
|
7737
|
+
return classifyWhereCapability(stmt.where, resolver);
|
|
7738
|
+
}
|
|
7739
|
+
function formatWhereCapabilityFailure(result) {
|
|
7740
|
+
const reason = result.reasons.find(
|
|
7741
|
+
(candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED"
|
|
7742
|
+
) ?? result.reasons[0];
|
|
7743
|
+
const details = [
|
|
7744
|
+
reason?.field ? `field=${reason.field}` : null,
|
|
7745
|
+
reason?.fieldType ? `type=${reason.fieldType}` : null,
|
|
7746
|
+
reason?.operator ? `operator=${reason.operator}` : null,
|
|
7747
|
+
reason?.code ? `reason=${reason.code}` : null
|
|
7748
|
+
].filter((value) => value !== null).join(", ");
|
|
7749
|
+
return details || "reason=WHERE_UNSUPPORTED";
|
|
7750
|
+
}
|
|
7751
|
+
function hasCanonicalOrder(stmt) {
|
|
7752
|
+
return stmt.orderBy.length > 0 || stmt.columns.some(
|
|
7753
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
7754
|
+
);
|
|
7755
|
+
}
|
|
7756
|
+
async function assertDmlWhereCapability(stmt, client, cacheContext) {
|
|
7757
|
+
if (stmt.subtableCode || stmt.type === "UPDATE" && stmt.from != null) return;
|
|
7758
|
+
const fields = whereNeedsFieldMetadata(stmt.where) ? await getFieldsCached(stmt.appId, client, cacheContext) : [];
|
|
7759
|
+
const byCode = new Map(fields.map((field) => [field.code, field]));
|
|
7760
|
+
const result = classifyWhereCapability(stmt.where, (field) => {
|
|
7761
|
+
if (field.field === "$id") return resolveFieldSemantics({ fieldType: "__ID__" });
|
|
7762
|
+
const info = byCode.get(field.field);
|
|
7763
|
+
return info?.semantics ?? (info ? resolveFieldSemantics(info) : void 0);
|
|
7764
|
+
});
|
|
7765
|
+
if (result.capability !== "EXACT_PUSHDOWN") {
|
|
7766
|
+
throw new DmlConvertError(
|
|
7767
|
+
`WHERE predicate cannot be represented by kintone REST (${formatWhereCapabilityFailure(result)})`
|
|
7768
|
+
);
|
|
7769
|
+
}
|
|
7770
|
+
}
|
|
6755
7771
|
async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
|
|
6756
7772
|
let result;
|
|
6757
7773
|
if (isNoFromSelect(stmt)) {
|
|
@@ -6762,12 +7778,51 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
6762
7778
|
return result;
|
|
6763
7779
|
}
|
|
6764
7780
|
await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
|
|
6765
|
-
const
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
7781
|
+
const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
|
|
7782
|
+
if (whereCapability.capability === "UNSUPPORTED") {
|
|
7783
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
7784
|
+
}
|
|
7785
|
+
const staticMode = resolveSelectMode(stmt);
|
|
7786
|
+
const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
|
|
7787
|
+
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
7788
|
+
const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
7789
|
+
stmt,
|
|
7790
|
+
staticMode: mode,
|
|
7791
|
+
whereCapability: whereCapability.capability,
|
|
7792
|
+
orderSemantics: orderMeta.semantics,
|
|
7793
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
7794
|
+
hasKlike: whereHasKlike(stmt.where)
|
|
7795
|
+
}) : null;
|
|
7796
|
+
await validateSelectFieldCodes(
|
|
7797
|
+
stmt,
|
|
7798
|
+
orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : mode,
|
|
7799
|
+
client,
|
|
7800
|
+
cacheContext
|
|
7801
|
+
);
|
|
7802
|
+
const completeInputRequired = orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR" ? requiresCompleteInput({ ...stmt, orderBy: [] }) : requiresCompleteInput(stmt);
|
|
7803
|
+
const truncateWasDisabled = completeInputRequired && options.onLimitReached === "truncate";
|
|
7804
|
+
const effectiveOptions = truncateWasDisabled ? { ...options, onLimitReached: "error" } : options;
|
|
7805
|
+
try {
|
|
7806
|
+
if (mode === "SIMPLE") {
|
|
7807
|
+
result = await executeSimpleSelect(stmt, client, effectiveOptions, cacheContext, orderPlan, orderMeta);
|
|
7808
|
+
} else {
|
|
7809
|
+
result = await executeFullScanSelect(
|
|
7810
|
+
stmt,
|
|
7811
|
+
client,
|
|
7812
|
+
effectiveOptions,
|
|
7813
|
+
cacheContext,
|
|
7814
|
+
cteCache,
|
|
7815
|
+
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
7816
|
+
orderMeta
|
|
7817
|
+
);
|
|
7818
|
+
}
|
|
7819
|
+
} catch (error) {
|
|
7820
|
+
if (completeInputRequired && error instanceof FetchAllLimitError) {
|
|
7821
|
+
throw new FetchAllLimitError(
|
|
7822
|
+
"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
|
|
7823
|
+
);
|
|
7824
|
+
}
|
|
7825
|
+
throw error;
|
|
6771
7826
|
}
|
|
6772
7827
|
if (captureColumnMeta) {
|
|
6773
7828
|
materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
|
|
@@ -6828,19 +7883,41 @@ function executeNoFromSelect(stmt) {
|
|
|
6828
7883
|
const rows = applyLimit(projected, stmt.limit, stmt.offset);
|
|
6829
7884
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
|
|
6830
7885
|
}
|
|
6831
|
-
async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
6832
|
-
const
|
|
7886
|
+
async function executeSimpleSelect(stmt, client, options, cacheContext, orderPlan, orderMeta) {
|
|
7887
|
+
const restStmt = orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt;
|
|
7888
|
+
const params = selectToKintoneParams(restStmt);
|
|
7889
|
+
const fetchFields = orderPlan?.kind === "CANONICAL_LOCAL" ? selectToFetchAllFields(stmt, stmt.from) : params.fields;
|
|
6833
7890
|
const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
|
|
6834
7891
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
7892
|
+
const projectionSemanticsResolver = stmt.columns.some((column) => column.type === "CASE_COL") ? await buildWhereFieldSemanticsResolver(
|
|
7893
|
+
stmt,
|
|
7894
|
+
client,
|
|
7895
|
+
cacheContext,
|
|
7896
|
+
void 0,
|
|
7897
|
+
selectCaseConditionsNeedFieldMetadata(stmt)
|
|
7898
|
+
) : void 0;
|
|
6835
7899
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
6836
7900
|
const warnings = /* @__PURE__ */ new Set();
|
|
6837
7901
|
const onLimit = options.onLimitReached ?? "error";
|
|
6838
7902
|
const parallel = options.fetchParallel ?? 1;
|
|
6839
|
-
const
|
|
7903
|
+
const useRestWindow = stmt.orderBy.length > 0 ? orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR" : stmt.limit !== null && stmt.limit <= 500;
|
|
6840
7904
|
const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
|
|
6841
7905
|
const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords && !whereHasKlike(stmt.where) ? needed : void 0;
|
|
6842
7906
|
let records;
|
|
6843
|
-
if (
|
|
7907
|
+
if ((orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR") && stmt.limit === 0) {
|
|
7908
|
+
records = [];
|
|
7909
|
+
} else if (orderPlan?.kind === "KORDER_CURSOR") {
|
|
7910
|
+
const cursorResult = await executeKorderCursor({
|
|
7911
|
+
client,
|
|
7912
|
+
app: params.app,
|
|
7913
|
+
fields: params.fields,
|
|
7914
|
+
query: buildKorderCursorQuery(stmt),
|
|
7915
|
+
offset: stmt.offset ?? 0,
|
|
7916
|
+
limit: stmt.limit
|
|
7917
|
+
});
|
|
7918
|
+
records = cursorResult.records;
|
|
7919
|
+
if (cursorResult.cleanupWarning) warnings.add(cursorResult.cleanupWarning);
|
|
7920
|
+
} else if (useRestWindow) {
|
|
6844
7921
|
const res = await client.getRecords({
|
|
6845
7922
|
app: params.app,
|
|
6846
7923
|
query: params.query,
|
|
@@ -6853,7 +7930,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
6853
7930
|
client.getRecords,
|
|
6854
7931
|
params.app,
|
|
6855
7932
|
baseQuery,
|
|
6856
|
-
|
|
7933
|
+
fetchFields,
|
|
6857
7934
|
{
|
|
6858
7935
|
parallel,
|
|
6859
7936
|
maxRecords,
|
|
@@ -6866,16 +7943,23 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
6866
7943
|
);
|
|
6867
7944
|
}
|
|
6868
7945
|
let rows = records.map((r) => flatten(r, null));
|
|
6869
|
-
if (!
|
|
6870
|
-
|
|
6871
|
-
|
|
7946
|
+
if (!useRestWindow) {
|
|
7947
|
+
rows = applyOrderBy(
|
|
7948
|
+
rows,
|
|
7949
|
+
stmt.orderBy,
|
|
7950
|
+
orderMeta.optionOrders,
|
|
7951
|
+
orderMeta.sortKinds,
|
|
7952
|
+
orderMeta.semantics
|
|
7953
|
+
);
|
|
6872
7954
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
6873
7955
|
}
|
|
6874
7956
|
const { rows: projected, columns } = project(
|
|
6875
7957
|
rows,
|
|
6876
7958
|
stmt.columns,
|
|
6877
7959
|
void 0,
|
|
6878
|
-
fieldTypeResolvers.row
|
|
7960
|
+
fieldTypeResolvers.row,
|
|
7961
|
+
void 0,
|
|
7962
|
+
projectionSemanticsResolver
|
|
6879
7963
|
);
|
|
6880
7964
|
return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
|
|
6881
7965
|
}
|
|
@@ -6948,8 +8032,8 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
|
|
|
6948
8032
|
const statusFields = collectCandidateFieldCodes(candidates).filter((fieldCode) => fieldTypes.get(fieldCode) === "STATUS");
|
|
6949
8033
|
if (statusFields.length > 0) {
|
|
6950
8034
|
const process2 = await getProcessStatusesCached(appId, client, cacheContext);
|
|
6951
|
-
if (process2.enable && process2.states.length > 0) {
|
|
6952
|
-
const states = new Set(process2.states);
|
|
8035
|
+
if (process2.enable && process2.states && process2.states.length > 0) {
|
|
8036
|
+
const states = new Set(process2.states.map((state) => state.name));
|
|
6953
8037
|
for (const fieldCode of statusFields) fieldOptions.set(fieldCode, states);
|
|
6954
8038
|
}
|
|
6955
8039
|
}
|
|
@@ -7127,7 +8211,18 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
7127
8211
|
return [appId, new Map(infos.map((info) => [info.code, info]))];
|
|
7128
8212
|
}))
|
|
7129
8213
|
);
|
|
8214
|
+
const statusOrdersByApp = /* @__PURE__ */ new Map();
|
|
8215
|
+
const aggregateFieldNames = new Set(refs.map((ref) => ref.field));
|
|
8216
|
+
await Promise.all([...fieldInfosByApp].map(async ([appId, infos]) => {
|
|
8217
|
+
if (![...aggregateFieldNames].some((field) => infos.get(field)?.fieldType === "STATUS")) return;
|
|
8218
|
+
const order = await loadProcessStatusOrder(appId, client, cacheContext);
|
|
8219
|
+
if (order) statusOrdersByApp.set(appId, order);
|
|
8220
|
+
}));
|
|
7130
8221
|
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
8222
|
+
const semanticsForInfo = (info, appId) => {
|
|
8223
|
+
const base = info.semantics ?? resolveFieldSemantics(info);
|
|
8224
|
+
return info.fieldType === "STATUS" && statusOrdersByApp.has(appId) ? { ...base, optionOrder: statusOrdersByApp.get(appId) } : base;
|
|
8225
|
+
};
|
|
7131
8226
|
return (ref) => {
|
|
7132
8227
|
let info;
|
|
7133
8228
|
if (ref.tableAlias !== null) {
|
|
@@ -7137,40 +8232,130 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
|
|
|
7137
8232
|
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
7138
8233
|
if (!table) return void 0;
|
|
7139
8234
|
if (table.cteName !== null) {
|
|
7140
|
-
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.
|
|
8235
|
+
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
|
|
7141
8236
|
}
|
|
7142
8237
|
info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7143
8238
|
}
|
|
7144
8239
|
} else if (stmt.joins.length === 0) {
|
|
7145
8240
|
if (stmt.from.cteName !== null) {
|
|
7146
|
-
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.
|
|
8241
|
+
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
|
|
7147
8242
|
}
|
|
7148
8243
|
info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
7149
8244
|
} else {
|
|
7150
8245
|
const matches = tables.flatMap((table) => {
|
|
7151
8246
|
if (table.cteName !== null) {
|
|
7152
8247
|
const materialized = materializedTables?.get(table.cteName);
|
|
7153
|
-
return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.
|
|
8248
|
+
return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string")] : [];
|
|
7154
8249
|
}
|
|
7155
8250
|
const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7156
|
-
return candidate ? [
|
|
8251
|
+
return candidate ? [semanticsForInfo(candidate, table.appId)] : [];
|
|
7157
8252
|
});
|
|
7158
8253
|
if (matches.length !== 1) return void 0;
|
|
7159
8254
|
return matches[0];
|
|
7160
8255
|
}
|
|
7161
|
-
|
|
8256
|
+
if (!info) return void 0;
|
|
8257
|
+
const sourceTable = ref.tableAlias !== null ? tables.find((table) => table.alias === ref.tableAlias) : stmt.joins.length === 0 ? stmt.from : void 0;
|
|
8258
|
+
return semanticsForInfo(info, sourceTable?.appId ?? stmt.from.appId);
|
|
7162
8259
|
};
|
|
7163
8260
|
}
|
|
7164
8261
|
function fieldCodeForTypeLookup(table, field) {
|
|
7165
8262
|
if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
|
|
7166
8263
|
return field;
|
|
7167
8264
|
}
|
|
7168
|
-
function materializedMetaFromFieldInfo(info) {
|
|
7169
|
-
|
|
8265
|
+
function materializedMetaFromFieldInfo(info, sourceAppId) {
|
|
8266
|
+
const semantics = info.semantics ?? resolveFieldSemantics(info);
|
|
8267
|
+
return {
|
|
8268
|
+
sortKind: aggregateSortKind(info),
|
|
8269
|
+
fieldType: info.fieldType,
|
|
8270
|
+
semantics: sourceAppId === void 0 ? semantics : withFieldSemanticSource(semantics, sourceAppId, info.code)
|
|
8271
|
+
};
|
|
8272
|
+
}
|
|
8273
|
+
function withCanonicalRestTie(stmt) {
|
|
8274
|
+
const hasId = stmt.orderBy.some(
|
|
8275
|
+
(item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
|
|
8276
|
+
);
|
|
8277
|
+
return hasId ? stmt : {
|
|
8278
|
+
...stmt,
|
|
8279
|
+
orderBy: [...stmt.orderBy, { key: { type: "FIELD_NAME", name: "$id" }, direction: "ASC" }]
|
|
8280
|
+
};
|
|
8281
|
+
}
|
|
8282
|
+
function syntheticColumnMeta(compareMode) {
|
|
8283
|
+
return { sortKind: compareMode, semantics: syntheticSemantics(compareMode) };
|
|
8284
|
+
}
|
|
8285
|
+
function unknownStringColumnMeta() {
|
|
8286
|
+
return { semantics: syntheticSemantics("string", "KSQL_UNKNOWN") };
|
|
8287
|
+
}
|
|
8288
|
+
function unsupportedColumnMeta(fieldType = "KSQL_ARRAY") {
|
|
8289
|
+
return {
|
|
8290
|
+
semantics: { fieldType, compareMode: "unsupported", inSubtable: false, requiresCollectionOperators: false }
|
|
8291
|
+
};
|
|
8292
|
+
}
|
|
8293
|
+
function systemColumnMeta(field) {
|
|
8294
|
+
if (field === "$id" || field === "_rid" || field === "_pid") {
|
|
8295
|
+
return {
|
|
8296
|
+
sortKind: "number",
|
|
8297
|
+
fieldType: "__ID__",
|
|
8298
|
+
semantics: resolveFieldSemantics({ fieldType: "__ID__" })
|
|
8299
|
+
};
|
|
8300
|
+
}
|
|
8301
|
+
if (field === "$revision") return syntheticColumnMeta("number");
|
|
8302
|
+
return void 0;
|
|
8303
|
+
}
|
|
8304
|
+
var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
8305
|
+
"LENGTH",
|
|
8306
|
+
"INSTR",
|
|
8307
|
+
"ROUND",
|
|
8308
|
+
"FLOOR",
|
|
8309
|
+
"CEIL",
|
|
8310
|
+
"TRUNCATE",
|
|
8311
|
+
"YEAR",
|
|
8312
|
+
"MONTH",
|
|
8313
|
+
"DAY",
|
|
8314
|
+
"DATEDIFF",
|
|
8315
|
+
"ABS",
|
|
8316
|
+
"MOD",
|
|
8317
|
+
"POWER",
|
|
8318
|
+
"SQRT"
|
|
8319
|
+
]);
|
|
8320
|
+
function stringFunctionColumnMeta(expr) {
|
|
8321
|
+
if (expr.func === "CAST") {
|
|
8322
|
+
const target = expr.args[1];
|
|
8323
|
+
return target?.type === "STRING" && target.value === "NUMBER" ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
|
|
8324
|
+
}
|
|
8325
|
+
return NUMBER_RETURNING_STRING_FUNCTIONS.has(expr.func) ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
|
|
8326
|
+
}
|
|
8327
|
+
function caseResultColumnMeta(result, resolveField2) {
|
|
8328
|
+
if (result.type === "STRING") return syntheticColumnMeta("string");
|
|
8329
|
+
if (result.type === "ARRAY") return unsupportedColumnMeta();
|
|
8330
|
+
if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
|
|
8331
|
+
if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
|
|
8332
|
+
const source = resolveField2(aggregateFieldRef(result.field));
|
|
8333
|
+
return source ?? unknownStringColumnMeta();
|
|
8334
|
+
}
|
|
8335
|
+
function mergeExpressionColumnMeta(candidates) {
|
|
8336
|
+
if (candidates.length === 0) return unknownStringColumnMeta();
|
|
8337
|
+
const first = candidates[0];
|
|
8338
|
+
const withoutSource = (semantics) => {
|
|
8339
|
+
if (!semantics) return void 0;
|
|
8340
|
+
const { source: _source, ...rest } = semantics;
|
|
8341
|
+
return rest;
|
|
8342
|
+
};
|
|
8343
|
+
if (candidates.every(
|
|
8344
|
+
(candidate) => candidate.sortKind === first.sortKind && candidate.fieldType === first.fieldType && fieldSemanticsEqual(withoutSource(candidate.semantics), withoutSource(first.semantics))
|
|
8345
|
+
)) {
|
|
8346
|
+
const sameSource = candidates.every(
|
|
8347
|
+
(candidate) => fieldSemanticsEqual(candidate.semantics, first.semantics)
|
|
8348
|
+
);
|
|
8349
|
+
return sameSource ? first : { ...first, semantics: withoutSource(first.semantics) };
|
|
8350
|
+
}
|
|
8351
|
+
if (candidates.some((candidate) => candidate.semantics?.compareMode === "unsupported")) {
|
|
8352
|
+
return unsupportedColumnMeta("KSQL_MIXED_UNSUPPORTED");
|
|
8353
|
+
}
|
|
8354
|
+
return unknownStringColumnMeta();
|
|
7170
8355
|
}
|
|
7171
8356
|
function selectNeedsSourceColumnMeta(stmt) {
|
|
7172
8357
|
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"
|
|
8358
|
+
(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
8359
|
);
|
|
7175
8360
|
}
|
|
7176
8361
|
async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
|
|
@@ -7187,18 +8372,18 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7187
8372
|
if (ref.tableAlias !== null) {
|
|
7188
8373
|
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
7189
8374
|
const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
|
|
7190
|
-
return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
|
|
8375
|
+
return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
|
|
7191
8376
|
}
|
|
7192
8377
|
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
7193
8378
|
if (!table) return void 0;
|
|
7194
8379
|
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
7195
8380
|
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7196
|
-
return info ? materializedMetaFromFieldInfo(info) :
|
|
8381
|
+
return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
7197
8382
|
}
|
|
7198
8383
|
if (stmt.joins.length === 0) {
|
|
7199
8384
|
if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
|
|
7200
8385
|
const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
7201
|
-
return info ? materializedMetaFromFieldInfo(info) :
|
|
8386
|
+
return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
|
|
7202
8387
|
}
|
|
7203
8388
|
const matches = tables.flatMap((table) => {
|
|
7204
8389
|
if (table.cteName !== null) {
|
|
@@ -7207,7 +8392,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7207
8392
|
return [materialized.columnMeta?.get(ref.field)];
|
|
7208
8393
|
}
|
|
7209
8394
|
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7210
|
-
|
|
8395
|
+
const meta = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
8396
|
+
return meta ? [meta] : [];
|
|
7211
8397
|
});
|
|
7212
8398
|
return matches.length === 1 ? matches[0] : void 0;
|
|
7213
8399
|
};
|
|
@@ -7237,19 +8423,27 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7237
8423
|
meta = resolveField2(aggregateFieldRef(column.field));
|
|
7238
8424
|
} else if (column.type === "AGGREGATE") {
|
|
7239
8425
|
if (column.func === "GROUP_CONCAT") {
|
|
7240
|
-
meta =
|
|
8426
|
+
meta = syntheticColumnMeta("string");
|
|
7241
8427
|
} else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
|
|
7242
|
-
meta =
|
|
8428
|
+
meta = syntheticColumnMeta("number");
|
|
7243
8429
|
} else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
|
|
7244
8430
|
const source = resolveField2(aggregateFieldRef(column.arg.field));
|
|
7245
|
-
if (source
|
|
8431
|
+
if (source) meta = source;
|
|
7246
8432
|
}
|
|
7247
8433
|
} else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
|
|
7248
|
-
meta =
|
|
8434
|
+
meta = syntheticColumnMeta("number");
|
|
7249
8435
|
} else if (column.type === "LITERAL_COL") {
|
|
7250
|
-
meta =
|
|
8436
|
+
meta = syntheticColumnMeta("string");
|
|
8437
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
8438
|
+
meta = stringFunctionColumnMeta(column.expr);
|
|
7251
8439
|
} else if (column.type === "WINDOW_COL") {
|
|
7252
|
-
meta =
|
|
8440
|
+
meta = syntheticColumnMeta("number");
|
|
8441
|
+
} else if (column.type === "CASE_COL") {
|
|
8442
|
+
const results = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
|
|
8443
|
+
if (column.expr.elseResult) results.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
|
|
8444
|
+
meta = mergeExpressionColumnMeta(results);
|
|
8445
|
+
} else if (column.type === "SCALAR_SUBQUERY_COL") {
|
|
8446
|
+
meta = unknownStringColumnMeta();
|
|
7253
8447
|
}
|
|
7254
8448
|
if (meta) inferred.set(output, meta);
|
|
7255
8449
|
});
|
|
@@ -7263,7 +8457,8 @@ function mergeUnionColumnMeta(left, right) {
|
|
|
7263
8457
|
const a = leftMeta?.get(column);
|
|
7264
8458
|
const rightColumn = right.columns[index];
|
|
7265
8459
|
const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
|
|
7266
|
-
if (a && b
|
|
8460
|
+
if (a && b) merged.set(column, mergeExpressionColumnMeta([a, b]));
|
|
8461
|
+
else if (a || b) merged.set(column, unknownStringColumnMeta());
|
|
7267
8462
|
});
|
|
7268
8463
|
return merged;
|
|
7269
8464
|
}
|
|
@@ -7300,7 +8495,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
7300
8495
|
};
|
|
7301
8496
|
return { row, having };
|
|
7302
8497
|
}
|
|
7303
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
|
|
8498
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
|
|
7304
8499
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
7305
8500
|
const warnings = /* @__PURE__ */ new Set();
|
|
7306
8501
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -7314,6 +8509,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7314
8509
|
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
7315
8510
|
]);
|
|
7316
8511
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
8512
|
+
const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
|
|
8513
|
+
stmt,
|
|
8514
|
+
client,
|
|
8515
|
+
cacheContext,
|
|
8516
|
+
cteCache,
|
|
8517
|
+
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
8518
|
+
);
|
|
8519
|
+
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
7317
8520
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
7318
8521
|
validateKlikePushdownPlan(pushdownPlan);
|
|
7319
8522
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
@@ -7327,7 +8530,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7327
8530
|
true,
|
|
7328
8531
|
options.onLimitReached ?? "error",
|
|
7329
8532
|
warnings,
|
|
7330
|
-
mainPushDown
|
|
8533
|
+
mainPushDown,
|
|
8534
|
+
allowOriginalWherePushdown
|
|
7331
8535
|
);
|
|
7332
8536
|
const parallelJoins = [];
|
|
7333
8537
|
const onOptJoins = [];
|
|
@@ -7353,7 +8557,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7353
8557
|
}
|
|
7354
8558
|
}
|
|
7355
8559
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
7356
|
-
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
8560
|
+
const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
7357
8561
|
scalarCachePromise.catch(() => {
|
|
7358
8562
|
});
|
|
7359
8563
|
orderByMetaPromise.catch(() => {
|
|
@@ -7390,15 +8594,18 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
7390
8594
|
tables.set(join2.table.alias, joinRecords);
|
|
7391
8595
|
}));
|
|
7392
8596
|
const scalarCache = await scalarCachePromise;
|
|
7393
|
-
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
8597
|
+
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
7394
8598
|
const { rows, columns } = runFullScan({
|
|
7395
8599
|
tables,
|
|
7396
8600
|
stmt,
|
|
7397
8601
|
scalarCache,
|
|
7398
8602
|
optionOrders,
|
|
7399
8603
|
sortKinds,
|
|
8604
|
+
orderSemantics: semantics,
|
|
7400
8605
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
8606
|
+
fieldSemanticsResolver,
|
|
7401
8607
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
8608
|
+
havingFieldSemanticsResolver,
|
|
7402
8609
|
aggregateSortKindResolver,
|
|
7403
8610
|
appliedKlikes: pushdownPlan.appliedKlikes
|
|
7404
8611
|
});
|
|
@@ -7495,16 +8702,39 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7495
8702
|
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
|
|
7496
8703
|
resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
|
|
7497
8704
|
]);
|
|
8705
|
+
const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
|
|
8706
|
+
if (whereCapability.capability === "UNSUPPORTED") {
|
|
8707
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
8708
|
+
}
|
|
8709
|
+
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
8710
|
+
if (hasCanonicalOrder(stmt)) {
|
|
8711
|
+
(stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
8712
|
+
stmt,
|
|
8713
|
+
staticMode: "FULL_SCAN",
|
|
8714
|
+
whereCapability: whereCapability.capability,
|
|
8715
|
+
orderSemantics: orderMeta.semantics,
|
|
8716
|
+
maxRecords,
|
|
8717
|
+
hasKlike: whereHasKlike(stmt.where)
|
|
8718
|
+
});
|
|
8719
|
+
}
|
|
7498
8720
|
const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
|
|
7499
8721
|
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
7500
8722
|
loadTypedInFieldTypes(stmt, client, cacheContext),
|
|
7501
8723
|
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
7502
8724
|
]);
|
|
7503
8725
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
8726
|
+
const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
|
|
8727
|
+
stmt,
|
|
8728
|
+
client,
|
|
8729
|
+
cacheContext,
|
|
8730
|
+
cteCache,
|
|
8731
|
+
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
8732
|
+
);
|
|
8733
|
+
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
7504
8734
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
7505
8735
|
validateKlikePushdownPlan(pushdownPlan);
|
|
7506
8736
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
7507
|
-
const orderByMetaPromise =
|
|
8737
|
+
const orderByMetaPromise = Promise.resolve(orderMeta);
|
|
7508
8738
|
scalarCachePromise.catch(() => {
|
|
7509
8739
|
});
|
|
7510
8740
|
orderByMetaPromise.catch(() => {
|
|
@@ -7523,7 +8753,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7523
8753
|
true,
|
|
7524
8754
|
options.onLimitReached ?? "error",
|
|
7525
8755
|
warnings,
|
|
7526
|
-
pushdownPlan.mainCondition
|
|
8756
|
+
pushdownPlan.mainCondition,
|
|
8757
|
+
whereCapability.capability === "EXACT_PUSHDOWN"
|
|
7527
8758
|
);
|
|
7528
8759
|
tables.set(stmt.from.alias, mainRecords);
|
|
7529
8760
|
}
|
|
@@ -7560,7 +8791,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7560
8791
|
});
|
|
7561
8792
|
await Promise.all(joinFetches);
|
|
7562
8793
|
const scalarCache = await scalarCachePromise;
|
|
7563
|
-
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
8794
|
+
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
7564
8795
|
const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
|
|
7565
8796
|
const { rows, columns } = runFullScan({
|
|
7566
8797
|
tables,
|
|
@@ -7568,8 +8799,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7568
8799
|
scalarCache,
|
|
7569
8800
|
optionOrders,
|
|
7570
8801
|
sortKinds,
|
|
8802
|
+
orderSemantics: semantics,
|
|
7571
8803
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
8804
|
+
fieldSemanticsResolver,
|
|
7572
8805
|
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
8806
|
+
havingFieldSemanticsResolver,
|
|
7573
8807
|
aggregateSortKindResolver,
|
|
7574
8808
|
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
7575
8809
|
sourceColumns
|
|
@@ -7581,13 +8815,13 @@ function processRowToKintoneRecord(row) {
|
|
|
7581
8815
|
Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
|
|
7582
8816
|
);
|
|
7583
8817
|
}
|
|
7584
|
-
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null) {
|
|
8818
|
+
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true) {
|
|
7585
8819
|
const fields = selectToFetchAllFields(stmt, table);
|
|
7586
8820
|
const onTruncate = (max) => {
|
|
7587
8821
|
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
8822
|
};
|
|
7589
8823
|
if (!table.subtableCode) {
|
|
7590
|
-
const baseQuery = isMainTable ? selectToFetchAllParams(stmt, table.appId).query : "";
|
|
8824
|
+
const baseQuery = isMainTable && allowOriginalWherePushdown ? selectToFetchAllParams(stmt, table.appId).query : "";
|
|
7591
8825
|
const pushQuery = pushDownCond !== null ? whereToKintone(pushDownCond) : "";
|
|
7592
8826
|
const query = baseQuery && pushQuery ? `(${baseQuery}) and (${pushQuery})` : baseQuery || pushQuery;
|
|
7593
8827
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, table.appId, query, fields, {
|
|
@@ -7791,6 +9025,10 @@ async function getProcessStatusesCached(appId, client, cacheContext) {
|
|
|
7791
9025
|
setScopedCacheValue(processStatusCache, cacheContext, appId, loading);
|
|
7792
9026
|
return loading;
|
|
7793
9027
|
}
|
|
9028
|
+
async function loadProcessStatusOrder(appId, client, cacheContext) {
|
|
9029
|
+
const process2 = await getProcessStatusesCached(appId, client, cacheContext);
|
|
9030
|
+
return process2.enable && process2.states !== null ? new Map(process2.states.map((state) => [state.name, state.index])) : void 0;
|
|
9031
|
+
}
|
|
7794
9032
|
async function getFieldTypeMap(appId, client, cacheContext) {
|
|
7795
9033
|
const cached = getScopedCacheValue(fieldTypeCache, cacheContext, appId);
|
|
7796
9034
|
if (cached) return cached;
|
|
@@ -7834,18 +9072,118 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
|
7834
9072
|
setScopedCacheValue(sortKindCache, cacheContext, appId, map);
|
|
7835
9073
|
return map;
|
|
7836
9074
|
}
|
|
7837
|
-
|
|
9075
|
+
function orderByFieldNames(stmt) {
|
|
9076
|
+
const items = [
|
|
9077
|
+
...stmt.orderBy,
|
|
9078
|
+
...stmt.columns.flatMap((column) => column.type === "WINDOW_COL" ? column.orderBy : [])
|
|
9079
|
+
];
|
|
9080
|
+
return [...new Set(items.flatMap(
|
|
9081
|
+
(item) => item.key.type === "FIELD_NAME" ? [item.key.name] : []
|
|
9082
|
+
))];
|
|
9083
|
+
}
|
|
9084
|
+
async function buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables) {
|
|
9085
|
+
const names = orderByFieldNames(stmt);
|
|
9086
|
+
if (names.length === 0) return /* @__PURE__ */ new Map();
|
|
9087
|
+
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
9088
|
+
const ambiguousFields = /* @__PURE__ */ new Set();
|
|
9089
|
+
const infosByApp = new Map(
|
|
9090
|
+
await Promise.all([...new Set(
|
|
9091
|
+
tables.filter((table) => table.cteName === null).map((table) => table.appId)
|
|
9092
|
+
)].map(async (appId) => {
|
|
9093
|
+
const infos = await getFieldsCached(appId, client, cacheContext);
|
|
9094
|
+
return [appId, new Map(infos.map((info) => [info.code, info]))];
|
|
9095
|
+
}))
|
|
9096
|
+
);
|
|
9097
|
+
const resolveField2 = (ref) => {
|
|
9098
|
+
if (ref.tableAlias !== null) {
|
|
9099
|
+
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
9100
|
+
const info2 = infosByApp.get(stmt.from.appId)?.get(ref.field);
|
|
9101
|
+
return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
|
|
9102
|
+
}
|
|
9103
|
+
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
9104
|
+
if (!table) return void 0;
|
|
9105
|
+
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
9106
|
+
const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
9107
|
+
return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
9108
|
+
}
|
|
9109
|
+
if (stmt.joins.length === 0) {
|
|
9110
|
+
if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
|
|
9111
|
+
const info = infosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
9112
|
+
return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
|
|
9113
|
+
}
|
|
9114
|
+
const matches = tables.flatMap((table) => {
|
|
9115
|
+
if (table.cteName !== null) {
|
|
9116
|
+
const materialized = materializedTables?.get(table.cteName);
|
|
9117
|
+
const meta2 = materialized?.columns.includes(ref.field) ? materialized.columnMeta?.get(ref.field) : void 0;
|
|
9118
|
+
return meta2 ? [meta2] : [];
|
|
9119
|
+
}
|
|
9120
|
+
const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
9121
|
+
const meta = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
|
|
9122
|
+
return meta ? [meta] : [];
|
|
9123
|
+
});
|
|
9124
|
+
if (matches.length > 1) ambiguousFields.add(ref.field);
|
|
9125
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
9126
|
+
};
|
|
9127
|
+
const aliasSemantics = /* @__PURE__ */ new Map();
|
|
9128
|
+
for (const column of stmt.columns) {
|
|
9129
|
+
if (!("alias" in column) || !column.alias) continue;
|
|
9130
|
+
let meta;
|
|
9131
|
+
if (column.type === "FIELD") meta = resolveField2(aggregateFieldRef(column.field));
|
|
9132
|
+
else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
9133
|
+
meta = syntheticColumnMeta("number");
|
|
9134
|
+
} else if (column.type === "LITERAL_COL") meta = syntheticColumnMeta("string");
|
|
9135
|
+
else if (column.type === "STRFUNC_COL") meta = stringFunctionColumnMeta(column.expr);
|
|
9136
|
+
else if (column.type === "SCALAR_SUBQUERY_COL") meta = unknownStringColumnMeta();
|
|
9137
|
+
else if (column.type === "CASE_COL") {
|
|
9138
|
+
const candidates = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
|
|
9139
|
+
if (column.expr.elseResult) candidates.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
|
|
9140
|
+
meta = mergeExpressionColumnMeta(candidates);
|
|
9141
|
+
} else if (column.type === "AGGREGATE") {
|
|
9142
|
+
if (column.func === "MIN" || column.func === "MAX") {
|
|
9143
|
+
meta = column.arg.type === "FIELD_REF" ? resolveField2(aggregateFieldRef(column.arg.field)) : syntheticColumnMeta("number");
|
|
9144
|
+
} else {
|
|
9145
|
+
meta = column.func === "GROUP_CONCAT" ? syntheticColumnMeta("string") : syntheticColumnMeta("number");
|
|
9146
|
+
}
|
|
9147
|
+
}
|
|
9148
|
+
if (meta?.semantics) aliasSemantics.set(column.alias, meta.semantics);
|
|
9149
|
+
}
|
|
9150
|
+
const result = /* @__PURE__ */ new Map();
|
|
9151
|
+
for (const name of names) {
|
|
9152
|
+
const base = aliasSemantics.get(name) ?? resolveField2(aggregateFieldRef(name))?.semantics;
|
|
9153
|
+
if (!base) {
|
|
9154
|
+
const ref = aggregateFieldRef(name);
|
|
9155
|
+
if (ref.tableAlias === null && ambiguousFields.has(ref.field)) {
|
|
9156
|
+
result.set(name, resolveFieldSemantics({ fieldType: "KSQL_AMBIGUOUS" }));
|
|
9157
|
+
}
|
|
9158
|
+
continue;
|
|
9159
|
+
}
|
|
9160
|
+
let semantics = base;
|
|
9161
|
+
if (base.fieldType === "STATUS" && base.source && stmt.orderMode !== "KINTONE_NATIVE") {
|
|
9162
|
+
const process2 = await getProcessStatusesCached(base.source.appId, client, cacheContext);
|
|
9163
|
+
if (process2.enable && process2.states !== null) {
|
|
9164
|
+
semantics = {
|
|
9165
|
+
...base,
|
|
9166
|
+
optionOrder: new Map(process2.states.map((state) => [state.name, state.index]))
|
|
9167
|
+
};
|
|
9168
|
+
}
|
|
9169
|
+
}
|
|
9170
|
+
result.set(name, semantics);
|
|
9171
|
+
}
|
|
9172
|
+
return result;
|
|
9173
|
+
}
|
|
9174
|
+
async function buildOrderByMetaForSelect(stmt, client, cacheContext, materializedTables) {
|
|
7838
9175
|
const hasWindowOrderBy = stmt.columns.some(
|
|
7839
9176
|
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
7840
9177
|
);
|
|
7841
9178
|
if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
|
|
7842
|
-
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
|
|
9179
|
+
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map(), semantics: /* @__PURE__ */ new Map() };
|
|
7843
9180
|
}
|
|
7844
|
-
const [optionOrders, sortKinds] = await Promise.all([
|
|
9181
|
+
const [optionOrders, sortKinds, semantics] = await Promise.all([
|
|
7845
9182
|
buildOptionOrdersForSelect(stmt, client, cacheContext),
|
|
7846
|
-
buildSortKindsForSelect(stmt, client, cacheContext)
|
|
9183
|
+
buildSortKindsForSelect(stmt, client, cacheContext),
|
|
9184
|
+
buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables)
|
|
7847
9185
|
]);
|
|
7848
|
-
return { optionOrders, sortKinds };
|
|
9186
|
+
return { optionOrders, sortKinds, semantics };
|
|
7849
9187
|
}
|
|
7850
9188
|
async function buildOptionOrdersForSelect(stmt, client, cacheContext) {
|
|
7851
9189
|
const optionOrders = /* @__PURE__ */ new Map();
|
|
@@ -7943,6 +9281,9 @@ var RejectLimitExceededError = class extends Error {
|
|
|
7943
9281
|
}
|
|
7944
9282
|
};
|
|
7945
9283
|
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
9284
|
+
if (stmt.type === "UPDATE") {
|
|
9285
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9286
|
+
}
|
|
7946
9287
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
7947
9288
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
7948
9289
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
@@ -7982,18 +9323,22 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
7982
9323
|
const columnMeta = /* @__PURE__ */ new Map();
|
|
7983
9324
|
for (const column of payloadFields) {
|
|
7984
9325
|
if (column === "$id") {
|
|
7985
|
-
columnMeta.set(column, {
|
|
9326
|
+
columnMeta.set(column, {
|
|
9327
|
+
sortKind: "number",
|
|
9328
|
+
fieldType: "RECORD_NUMBER",
|
|
9329
|
+
semantics: resolveFieldSemantics({ fieldType: "RECORD_NUMBER" })
|
|
9330
|
+
});
|
|
7986
9331
|
continue;
|
|
7987
9332
|
}
|
|
7988
9333
|
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",
|
|
9334
|
+
if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info, stmt.appId));
|
|
9335
|
+
}
|
|
9336
|
+
columnMeta.set("$err_statement", syntheticColumnMeta("number"));
|
|
9337
|
+
columnMeta.set("$err_operation", syntheticColumnMeta("string"));
|
|
9338
|
+
columnMeta.set("$err_row", syntheticColumnMeta("number"));
|
|
9339
|
+
columnMeta.set("$err_field", syntheticColumnMeta("string"));
|
|
9340
|
+
columnMeta.set("$err_code", syntheticColumnMeta("string"));
|
|
9341
|
+
columnMeta.set("$err_message", syntheticColumnMeta("string"));
|
|
7997
9342
|
materializedMetaByValidationResult.set(result, columnMeta);
|
|
7998
9343
|
return { result, candidates, invalidRowNumbers, columnMeta };
|
|
7999
9344
|
}
|
|
@@ -8379,6 +9724,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
8379
9724
|
};
|
|
8380
9725
|
}
|
|
8381
9726
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
9727
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
8382
9728
|
if (stmt.subtableCode) {
|
|
8383
9729
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
8384
9730
|
}
|
|
@@ -8456,6 +9802,7 @@ function collectUpdateFromTargetFields(stmt) {
|
|
|
8456
9802
|
return [...fields];
|
|
8457
9803
|
}
|
|
8458
9804
|
async function executeDelete(stmt, client, options, cacheContext) {
|
|
9805
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
8459
9806
|
if (stmt.subtableCode) {
|
|
8460
9807
|
return executeDeleteSubtable(stmt, client, options, cacheContext);
|
|
8461
9808
|
}
|
|
@@ -8828,6 +10175,18 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8828
10175
|
client,
|
|
8829
10176
|
cacheContext
|
|
8830
10177
|
);
|
|
10178
|
+
const reorderFields = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
10179
|
+
const reorderSemanticsByCode = new Map(reorderFields.map((field) => [
|
|
10180
|
+
field.code,
|
|
10181
|
+
field.semantics ?? resolveFieldSemantics(field)
|
|
10182
|
+
]));
|
|
10183
|
+
const resolveReorderSemantics = (field) => {
|
|
10184
|
+
if (field.field === "_idx" || field.field === "_pid" || field.field === "_rid") {
|
|
10185
|
+
return syntheticSemantics("number");
|
|
10186
|
+
}
|
|
10187
|
+
const code = field.field.startsWith("_p.") ? field.field.slice(3) : field.field;
|
|
10188
|
+
return reorderSemanticsByCode.get(code) ?? syntheticSemantics("string");
|
|
10189
|
+
};
|
|
8831
10190
|
const parents = await fetchAll(
|
|
8832
10191
|
client.getRecords,
|
|
8833
10192
|
stmt.appId,
|
|
@@ -8836,7 +10195,13 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8836
10195
|
{ maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
|
|
8837
10196
|
);
|
|
8838
10197
|
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(
|
|
10198
|
+
const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(
|
|
10199
|
+
stmt.where,
|
|
10200
|
+
r.flat,
|
|
10201
|
+
resolveFieldType,
|
|
10202
|
+
void 0,
|
|
10203
|
+
resolveReorderSemantics
|
|
10204
|
+
)).map((r) => r.parentId));
|
|
8840
10205
|
if (options.confirm) {
|
|
8841
10206
|
const ok = await options.confirm(targetParentIds.size, "UPDATE");
|
|
8842
10207
|
if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
|
|
@@ -8847,7 +10212,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
8847
10212
|
if (!parent) continue;
|
|
8848
10213
|
const rows = getMutableTableRows(parent, stmt.subtableCode);
|
|
8849
10214
|
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));
|
|
10215
|
+
sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by, resolveReorderSemantics));
|
|
8851
10216
|
const orderedRowIds = sortable.map((x) => x.row.id ?? "");
|
|
8852
10217
|
await client.putRecords(buildSubtableReorderPutParams(stmt.appId, pid, getRevision(parent), stmt.subtableCode, orderedRowIds));
|
|
8853
10218
|
}
|
|
@@ -8868,14 +10233,12 @@ function buildFlatRowForSort(parent, subtableCode, row, idx) {
|
|
|
8868
10233
|
}
|
|
8869
10234
|
return flat;
|
|
8870
10235
|
}
|
|
8871
|
-
function compareByOrder(a, b, orderBy) {
|
|
10236
|
+
function compareByOrder(a, b, orderBy, resolveSemantics) {
|
|
8872
10237
|
for (const item of orderBy) {
|
|
8873
10238
|
const av = evalOrderKeyForRow(item.key, a);
|
|
8874
10239
|
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");
|
|
10240
|
+
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");
|
|
10241
|
+
const cmp = compareCanonicalValues(av, bv, semantics ?? syntheticSemantics("string"));
|
|
8879
10242
|
if (cmp !== 0) return item.direction === "ASC" ? cmp : -cmp;
|
|
8880
10243
|
}
|
|
8881
10244
|
return 0;
|
|
@@ -9072,35 +10435,140 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
9072
10435
|
pending.forEach(([i], idx) => cache.set(i, values[idx]));
|
|
9073
10436
|
return cache;
|
|
9074
10437
|
}
|
|
9075
|
-
function
|
|
10438
|
+
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4) {
|
|
10439
|
+
const fieldApps = /* @__PURE__ */ new Set();
|
|
10440
|
+
const processStatusApps = /* @__PURE__ */ new Set();
|
|
10441
|
+
const tracedClient = {
|
|
10442
|
+
...client,
|
|
10443
|
+
getFields: async (appId) => {
|
|
10444
|
+
fieldApps.add(appId);
|
|
10445
|
+
return client.getFields(appId);
|
|
10446
|
+
},
|
|
10447
|
+
getProcessStatuses: async (appId) => {
|
|
10448
|
+
processStatusApps.add(appId);
|
|
10449
|
+
return client.getProcessStatuses(appId);
|
|
10450
|
+
}
|
|
10451
|
+
};
|
|
10452
|
+
const capabilities = /* @__PURE__ */ new Map();
|
|
10453
|
+
const orderPlans = /* @__PURE__ */ new Map();
|
|
10454
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10455
|
+
const visit = async (node) => {
|
|
10456
|
+
if (node === null || typeof node !== "object") return;
|
|
10457
|
+
if (seen.has(node)) return;
|
|
10458
|
+
seen.add(node);
|
|
10459
|
+
if (Array.isArray(node)) {
|
|
10460
|
+
await Promise.all(node.map(visit));
|
|
10461
|
+
return;
|
|
10462
|
+
}
|
|
10463
|
+
const typed = node;
|
|
10464
|
+
if (typed["type"] === "SELECT") {
|
|
10465
|
+
const select = node;
|
|
10466
|
+
const physicalApps = [select.from, ...select.joins.map((join2) => join2.table)].filter((table) => table.cteName === null).map((table) => table.appId);
|
|
10467
|
+
const needsWhereSchema = whereNeedsFieldMetadata(select.where);
|
|
10468
|
+
if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
10469
|
+
physicalApps.forEach((appId) => fieldApps.add(appId));
|
|
10470
|
+
}
|
|
10471
|
+
const capability = await resolveSelectWhereCapability(select, tracedClient, cacheContext);
|
|
10472
|
+
if (capability.capability === "UNSUPPORTED") {
|
|
10473
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
10474
|
+
}
|
|
10475
|
+
capabilities.set(select, capability);
|
|
10476
|
+
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
10477
|
+
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
10478
|
+
if (select.orderMode !== "KINTONE_NATIVE") {
|
|
10479
|
+
for (const semantics of meta.semantics.values()) {
|
|
10480
|
+
if (semantics.fieldType === "STATUS" && semantics.source) {
|
|
10481
|
+
processStatusApps.add(semantics.source.appId);
|
|
10482
|
+
}
|
|
10483
|
+
}
|
|
10484
|
+
}
|
|
10485
|
+
const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
|
|
10486
|
+
if (hasCanonicalOrder(select) && !hasUnmaterializedSource) {
|
|
10487
|
+
const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
|
|
10488
|
+
orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
10489
|
+
stmt: select,
|
|
10490
|
+
staticMode: mode,
|
|
10491
|
+
whereCapability: capability.capability,
|
|
10492
|
+
orderSemantics: meta.semantics,
|
|
10493
|
+
maxRecords,
|
|
10494
|
+
hasKlike: whereHasKlike(select.where)
|
|
10495
|
+
}));
|
|
10496
|
+
}
|
|
10497
|
+
}
|
|
10498
|
+
} else if (typed["type"] === "UPDATE" || typed["type"] === "DELETE") {
|
|
10499
|
+
fieldApps.add(node.appId);
|
|
10500
|
+
await assertDmlWhereCapability(
|
|
10501
|
+
node,
|
|
10502
|
+
tracedClient,
|
|
10503
|
+
cacheContext
|
|
10504
|
+
);
|
|
10505
|
+
}
|
|
10506
|
+
await Promise.all(Object.values(typed).map(visit));
|
|
10507
|
+
};
|
|
10508
|
+
await visit(query);
|
|
10509
|
+
if (typeof query === "object" && query !== null && query.type === "WITH" && canInlineSingleCte(query)) {
|
|
10510
|
+
const inlined = buildInlinedQuery(query);
|
|
10511
|
+
const capability = await resolveSelectWhereCapability(inlined, tracedClient, cacheContext);
|
|
10512
|
+
if (capability.capability === "UNSUPPORTED") {
|
|
10513
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
10514
|
+
}
|
|
10515
|
+
capabilities.set(inlined, capability);
|
|
10516
|
+
if (hasCanonicalOrder(inlined)) {
|
|
10517
|
+
const meta = await buildOrderByMetaForSelect(inlined, tracedClient, cacheContext);
|
|
10518
|
+
orderPlans.set(inlined, (inlined.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
10519
|
+
stmt: inlined,
|
|
10520
|
+
staticMode: capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(inlined) : "FULL_SCAN",
|
|
10521
|
+
whereCapability: capability.capability,
|
|
10522
|
+
orderSemantics: meta.semantics,
|
|
10523
|
+
maxRecords,
|
|
10524
|
+
hasKlike: whereHasKlike(inlined.where)
|
|
10525
|
+
}));
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
return { capabilities, orderPlans, fieldApps, processStatusApps };
|
|
10529
|
+
}
|
|
10530
|
+
function explainMetadataLines(analysis) {
|
|
10531
|
+
return [
|
|
10532
|
+
...[...analysis.fieldApps].sort((a, b) => a - b).map((appId) => ` metadata API: form definition APP${appId}`),
|
|
10533
|
+
...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`)
|
|
10534
|
+
];
|
|
10535
|
+
}
|
|
10536
|
+
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2) {
|
|
9076
10537
|
const statements = parseSqlBatch(sql);
|
|
9077
10538
|
const analysis = analyzeBatch(statements);
|
|
9078
10539
|
validateDeclaredBatchVariables(statements, injectedVariables);
|
|
9079
10540
|
const variables = /* @__PURE__ */ new Map();
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
10541
|
+
const plans = [];
|
|
10542
|
+
for (let i = 0; i < statements.length; i++) {
|
|
10543
|
+
const stmt = statements[i];
|
|
10544
|
+
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
|
|
10545
|
+
validateKlikeStatement(planStmt);
|
|
10546
|
+
const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords);
|
|
10547
|
+
const statementPlan = addCursorConcurrency(buildBatchStatementPlan(
|
|
10548
|
+
planStmt,
|
|
10549
|
+
analysis.statements[i],
|
|
10550
|
+
whereAnalysis.capabilities,
|
|
10551
|
+
whereAnalysis.orderPlans
|
|
10552
|
+
), cursorMaxActive);
|
|
10553
|
+
const metadataPlan = explainMetadataLines(whereAnalysis);
|
|
10554
|
+
plans.push({
|
|
10555
|
+
index: i,
|
|
10556
|
+
type: analysis.statements[i].statementType,
|
|
10557
|
+
plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
|
|
10558
|
+
});
|
|
10559
|
+
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
10560
|
+
variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
|
|
10561
|
+
}
|
|
10562
|
+
}
|
|
10563
|
+
return { statementCount: statements.length, statements: plans };
|
|
9096
10564
|
}
|
|
9097
|
-
function buildBatchStatementPlan(stmt, info) {
|
|
10565
|
+
function buildBatchStatementPlan(stmt, info, capabilities, orderPlans) {
|
|
9098
10566
|
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
9099
10567
|
return [
|
|
9100
10568
|
`CREATE TEMP TABLE ${stmt.name}`,
|
|
9101
10569
|
` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
|
|
9102
10570
|
` 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}`)
|
|
10571
|
+
...buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans).map((l) => ` ${l}`)
|
|
9104
10572
|
];
|
|
9105
10573
|
}
|
|
9106
10574
|
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
@@ -9116,7 +10584,7 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9116
10584
|
`SET @${stmt.name} = (SELECT ...)`,
|
|
9117
10585
|
" 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
10586
|
" subquery:",
|
|
9119
|
-
...buildPlanForBatchQuery(stmt.expr.query, subInfo).map((l) => ` ${l}`)
|
|
10587
|
+
...buildPlanForBatchQuery(stmt.expr.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`)
|
|
9120
10588
|
];
|
|
9121
10589
|
}
|
|
9122
10590
|
return [
|
|
@@ -9132,7 +10600,7 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9132
10600
|
}
|
|
9133
10601
|
if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
|
|
9134
10602
|
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);
|
|
10603
|
+
if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans);
|
|
9136
10604
|
if (stmt.type === "ASSERT") {
|
|
9137
10605
|
const lines = [
|
|
9138
10606
|
`ASSERT ${stmt.text}`,
|
|
@@ -9144,11 +10612,11 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
9144
10612
|
subqueries.forEach((sq, i) => {
|
|
9145
10613
|
lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
|
|
9146
10614
|
const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
|
|
9147
|
-
lines.push(...buildPlanForBatchQuery(sq.query, subInfo).map((l) => ` ${l}`));
|
|
10615
|
+
lines.push(...buildPlanForBatchQuery(sq.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`));
|
|
9148
10616
|
});
|
|
9149
10617
|
return lines;
|
|
9150
10618
|
}
|
|
9151
|
-
return buildPlanForBatchQuery(stmt, info);
|
|
10619
|
+
return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
|
|
9152
10620
|
}
|
|
9153
10621
|
function hasTempTableRef(node) {
|
|
9154
10622
|
if (Array.isArray(node)) return node.some(hasTempTableRef);
|
|
@@ -9160,9 +10628,9 @@ function hasTempTableRef(node) {
|
|
|
9160
10628
|
}
|
|
9161
10629
|
return false;
|
|
9162
10630
|
}
|
|
9163
|
-
function buildPlanForBatchQuery(query, info) {
|
|
10631
|
+
function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
|
|
9164
10632
|
if (info.tempTablesReferenced.length === 0) {
|
|
9165
|
-
return buildExplainPlan(query);
|
|
10633
|
+
return buildExplainPlan(query, void 0, capabilities, orderPlans);
|
|
9166
10634
|
}
|
|
9167
10635
|
const lines = [];
|
|
9168
10636
|
if (query.type === "INSERT_SELECT") {
|
|
@@ -9187,8 +10655,15 @@ function buildPlanForBatchQuery(query, info) {
|
|
|
9187
10655
|
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
10656
|
return lines;
|
|
9189
10657
|
}
|
|
9190
|
-
function executeExplain(stmt) {
|
|
9191
|
-
const
|
|
10658
|
+
async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxActive) {
|
|
10659
|
+
const analysis = await buildExplainWhereAnalysis(stmt.query, client, cacheContext, maxRecords);
|
|
10660
|
+
const lines = [
|
|
10661
|
+
...explainMetadataLines(analysis),
|
|
10662
|
+
...addCursorConcurrency(
|
|
10663
|
+
buildExplainPlan(stmt.query, void 0, analysis.capabilities, analysis.orderPlans),
|
|
10664
|
+
cursorMaxActive
|
|
10665
|
+
)
|
|
10666
|
+
];
|
|
9192
10667
|
return {
|
|
9193
10668
|
type: "SELECT",
|
|
9194
10669
|
columns: ["plan"],
|
|
@@ -9196,31 +10671,64 @@ function executeExplain(stmt) {
|
|
|
9196
10671
|
rowCount: lines.length
|
|
9197
10672
|
};
|
|
9198
10673
|
}
|
|
9199
|
-
function
|
|
9200
|
-
|
|
9201
|
-
|
|
10674
|
+
function addCursorConcurrency(lines, cursorMaxActive) {
|
|
10675
|
+
const result = [];
|
|
10676
|
+
for (const line of lines) {
|
|
10677
|
+
result.push(line);
|
|
10678
|
+
if (line.trim() === "cursor page size: 500") {
|
|
10679
|
+
const indent = line.match(/^\s*/)?.[0] ?? "";
|
|
10680
|
+
result.push(`${indent}cursor concurrency: ${cursorMaxActive} per domain (process-local)`);
|
|
10681
|
+
}
|
|
10682
|
+
}
|
|
10683
|
+
return result;
|
|
10684
|
+
}
|
|
10685
|
+
function buildExplainPlan(query, label, capabilities, orderPlans) {
|
|
10686
|
+
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
|
|
10687
|
+
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
|
|
9202
10688
|
if (query.type === "INSERT") return buildInsertPlan(query, label);
|
|
9203
|
-
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label);
|
|
10689
|
+
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
|
|
9204
10690
|
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);
|
|
10691
|
+
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans);
|
|
10692
|
+
if (query.type === "UPDATE") return buildUpdatePlan(query, label, capabilities, orderPlans);
|
|
9207
10693
|
if (query.type === "DELETE") return buildDeletePlan(query, label);
|
|
9208
10694
|
if (query.type === "REORDER") return buildReorderPlan(query, label);
|
|
9209
|
-
return buildSelectPlan(query, label);
|
|
10695
|
+
return buildSelectPlan(query, label, capabilities, orderPlans);
|
|
9210
10696
|
}
|
|
9211
|
-
function buildSelectPlan(stmt, label) {
|
|
9212
|
-
const
|
|
10697
|
+
function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
10698
|
+
const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
10699
|
+
const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
10700
|
+
const mode = orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN" ? "FULL_SCAN" : resolveSelectMode(stmt);
|
|
9213
10701
|
const reasons = collectFullScanReasons(stmt);
|
|
10702
|
+
if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
|
|
10703
|
+
reasons.push(...whereCapability.reasons.map((reason) => reason.code));
|
|
10704
|
+
}
|
|
9214
10705
|
const lines = [];
|
|
9215
10706
|
if (label) lines.push(label);
|
|
9216
10707
|
lines.push(` mode: ${mode}`);
|
|
10708
|
+
if (orderPlan) {
|
|
10709
|
+
lines.push(` order plan: ${orderPlan.kind}`);
|
|
10710
|
+
if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
|
|
10711
|
+
if (orderPlan.kind === "KORDER_NATIVE") {
|
|
10712
|
+
lines.push(" order semantics: kintone native (not kSQL canonical)");
|
|
10713
|
+
lines.push(" REST execution: single GET");
|
|
10714
|
+
} else if (orderPlan.kind === "KORDER_CURSOR") {
|
|
10715
|
+
lines.push(" order semantics: kintone native (not kSQL canonical)");
|
|
10716
|
+
lines.push(" fetch API: POST/GET/DELETE records/cursor.json");
|
|
10717
|
+
lines.push(" cursor page size: 500");
|
|
10718
|
+
lines.push(` scan rows: ${orderPlan.scanRows}`);
|
|
10719
|
+
}
|
|
10720
|
+
}
|
|
10721
|
+
if (orderPlan?.requiresCompleteInput ?? requiresCompleteInput(stmt)) {
|
|
10722
|
+
lines.push(" complete input: required (ORDER BY / window ORDER BY; onLimit=truncate disabled)");
|
|
10723
|
+
}
|
|
9217
10724
|
if (mode === "FULL_SCAN" && reasons.length > 0) {
|
|
9218
10725
|
lines.push(` reason: ${reasons.join(", ")}`);
|
|
9219
10726
|
}
|
|
9220
10727
|
if (mode === "SIMPLE") {
|
|
9221
|
-
const params = selectToKintoneParams(stmt);
|
|
10728
|
+
const params = selectToKintoneParams(orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt);
|
|
9222
10729
|
lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
|
|
9223
|
-
|
|
10730
|
+
const displayedQuery = orderPlan?.kind === "KORDER_CURSOR" ? buildKorderCursorQuery(stmt) : params.query;
|
|
10731
|
+
lines.push(` kintone query: ${displayedQuery || "(\u306A\u3057)"}`);
|
|
9224
10732
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
9225
10733
|
} else {
|
|
9226
10734
|
const pushdownPlan = buildKlikePushdownPlan(stmt);
|
|
@@ -9228,7 +10736,8 @@ function buildSelectPlan(stmt, label) {
|
|
|
9228
10736
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
9229
10737
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
9230
10738
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
9231
|
-
const
|
|
10739
|
+
const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
|
|
10740
|
+
const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)";
|
|
9232
10741
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
9233
10742
|
lines.push(` kintone query: ${mainQ}`);
|
|
9234
10743
|
if (mainCandidate !== null) {
|
|
@@ -9250,10 +10759,10 @@ function buildSelectPlan(stmt, label) {
|
|
|
9250
10759
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|
|
9251
10760
|
}
|
|
9252
10761
|
}
|
|
9253
|
-
lines.push(...collectSubqueryPlans(stmt));
|
|
10762
|
+
lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
|
|
9254
10763
|
return lines;
|
|
9255
10764
|
}
|
|
9256
|
-
function buildUnionPlan(stmt) {
|
|
10765
|
+
function buildUnionPlan(stmt, capabilities, orderPlans) {
|
|
9257
10766
|
const selects = [];
|
|
9258
10767
|
const collect = (u) => {
|
|
9259
10768
|
if (u.type === "SELECT") {
|
|
@@ -9267,24 +10776,25 @@ function buildUnionPlan(stmt) {
|
|
|
9267
10776
|
const lines = [];
|
|
9268
10777
|
selects.forEach((sel, i) => {
|
|
9269
10778
|
if (i > 0) lines.push("");
|
|
9270
|
-
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]
|
|
10779
|
+
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
|
|
9271
10780
|
});
|
|
9272
10781
|
return lines;
|
|
9273
10782
|
}
|
|
9274
|
-
function buildWithPlan(stmt) {
|
|
10783
|
+
function buildWithPlan(stmt, capabilities, orderPlans) {
|
|
9275
10784
|
const lines = [];
|
|
9276
10785
|
for (const cte of stmt.ctes) {
|
|
9277
10786
|
if (cte.query.type === "SELECT") {
|
|
9278
|
-
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]
|
|
10787
|
+
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
|
|
9279
10788
|
lines.push("");
|
|
9280
10789
|
}
|
|
9281
10790
|
}
|
|
9282
10791
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
9283
|
-
lines.push(...buildExplainPlan(stmt.query, "[main]"));
|
|
10792
|
+
lines.push(...buildExplainPlan(stmt.query, "[main]", capabilities, orderPlans));
|
|
9284
10793
|
}
|
|
9285
10794
|
if (canInlineSingleCte(stmt)) {
|
|
9286
10795
|
lines.push("");
|
|
9287
|
-
|
|
10796
|
+
const inlined = buildInlinedQuery(stmt);
|
|
10797
|
+
lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
|
|
9288
10798
|
}
|
|
9289
10799
|
return lines;
|
|
9290
10800
|
}
|
|
@@ -9312,7 +10822,7 @@ function collectFullScanReasons(stmt) {
|
|
|
9312
10822
|
r.push("ORDER BY \u306B\u5F0F");
|
|
9313
10823
|
return r;
|
|
9314
10824
|
}
|
|
9315
|
-
function collectSubqueryPlans(stmt) {
|
|
10825
|
+
function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
9316
10826
|
const lines = [];
|
|
9317
10827
|
let idx = 1;
|
|
9318
10828
|
const visitWhere = (w) => {
|
|
@@ -9321,16 +10831,16 @@ function collectSubqueryPlans(stmt) {
|
|
|
9321
10831
|
case "BINARY":
|
|
9322
10832
|
if (w.right.type === "SCALAR_SUBQUERY") {
|
|
9323
10833
|
lines.push("");
|
|
9324
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]
|
|
10834
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9325
10835
|
}
|
|
9326
10836
|
if (w.right.type === "SUBQUERY_IN_LIST") {
|
|
9327
10837
|
lines.push("");
|
|
9328
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]
|
|
10838
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9329
10839
|
}
|
|
9330
10840
|
break;
|
|
9331
10841
|
case "EXISTS":
|
|
9332
10842
|
lines.push("");
|
|
9333
|
-
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]
|
|
10843
|
+
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9334
10844
|
break;
|
|
9335
10845
|
case "LOGICAL":
|
|
9336
10846
|
visitWhere(w.left);
|
|
@@ -9348,7 +10858,7 @@ function collectSubqueryPlans(stmt) {
|
|
|
9348
10858
|
for (const col of stmt.columns) {
|
|
9349
10859
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
9350
10860
|
lines.push("");
|
|
9351
|
-
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]
|
|
10861
|
+
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
9352
10862
|
}
|
|
9353
10863
|
}
|
|
9354
10864
|
if (stmt.having) visitWhere(stmt.having);
|
|
@@ -9366,7 +10876,7 @@ function buildInsertPlan(stmt, label) {
|
|
|
9366
10876
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
9367
10877
|
return lines;
|
|
9368
10878
|
}
|
|
9369
|
-
function buildInsertSelectPlan(stmt, label) {
|
|
10879
|
+
function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
9370
10880
|
const lines = [];
|
|
9371
10881
|
if (label) lines.push(label);
|
|
9372
10882
|
lines.push(` [INSERT SELECT]`);
|
|
@@ -9374,10 +10884,10 @@ function buildInsertSelectPlan(stmt, label) {
|
|
|
9374
10884
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
9375
10885
|
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
10886
|
lines.push("");
|
|
9377
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
|
|
10887
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
9378
10888
|
return lines;
|
|
9379
10889
|
}
|
|
9380
|
-
function buildUpdatePlan(stmt, label) {
|
|
10890
|
+
function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
9381
10891
|
const isArith = hasArithAssignment(stmt);
|
|
9382
10892
|
const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
|
|
9383
10893
|
const lines = [];
|
|
@@ -9411,7 +10921,7 @@ function buildUpdatePlan(stmt, label) {
|
|
|
9411
10921
|
for (const a of stmt.assignments) {
|
|
9412
10922
|
if (a.value.type === "SCALAR_SUBQUERY") {
|
|
9413
10923
|
lines.push("");
|
|
9414
|
-
lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]
|
|
10924
|
+
lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans));
|
|
9415
10925
|
}
|
|
9416
10926
|
}
|
|
9417
10927
|
return lines;
|
|
@@ -9438,7 +10948,7 @@ function buildUpsertPlan(stmt, label) {
|
|
|
9438
10948
|
` 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
10949
|
];
|
|
9440
10950
|
}
|
|
9441
|
-
function buildUpsertSelectPlan(stmt, label) {
|
|
10951
|
+
function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
9442
10952
|
const lines = [
|
|
9443
10953
|
...label ? [label] : [],
|
|
9444
10954
|
` [UPSERT SELECT]`,
|
|
@@ -9448,7 +10958,7 @@ function buildUpsertSelectPlan(stmt, label) {
|
|
|
9448
10958
|
` 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
10959
|
``
|
|
9450
10960
|
];
|
|
9451
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
|
|
10961
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
9452
10962
|
return lines;
|
|
9453
10963
|
}
|
|
9454
10964
|
function buildReorderPlan(stmt, label) {
|
|
@@ -9786,6 +11296,12 @@ function validateKsqlConfig(config) {
|
|
|
9786
11296
|
}
|
|
9787
11297
|
const logicalApps = normalizeLogicalApps(profileName, profile.logicalApps);
|
|
9788
11298
|
if (logicalApps !== void 0) profile.logicalApps = logicalApps;
|
|
11299
|
+
if (profile.query?.cursorMaxActive !== void 0) {
|
|
11300
|
+
const value = profile.query.cursorMaxActive;
|
|
11301
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 5) {
|
|
11302
|
+
throw argumentError(`query.cursorMaxActive for profile "${profileName}" must be an integer from 1 to 5.`);
|
|
11303
|
+
}
|
|
11304
|
+
}
|
|
9789
11305
|
}
|
|
9790
11306
|
return config;
|
|
9791
11307
|
}
|
|
@@ -9930,6 +11446,10 @@ var RequestGate = class {
|
|
|
9930
11446
|
async runMutation(fn) {
|
|
9931
11447
|
return this.withSlot(fn);
|
|
9932
11448
|
}
|
|
11449
|
+
/** Cursor Create/Get/Delete: セマフォのみ。GETでも位置を進めるため再試行しない。 */
|
|
11450
|
+
async runCursorStep(fn) {
|
|
11451
|
+
return this.withSlot(fn);
|
|
11452
|
+
}
|
|
9933
11453
|
async withSlot(fn) {
|
|
9934
11454
|
await this.acquire();
|
|
9935
11455
|
try {
|
|
@@ -9961,6 +11481,14 @@ var RequestGate = class {
|
|
|
9961
11481
|
function withRequestGate(client, gate) {
|
|
9962
11482
|
return {
|
|
9963
11483
|
getRecords: (params) => gate.runReadOnly(() => client.getRecords(params)),
|
|
11484
|
+
openCursor: async (params) => {
|
|
11485
|
+
const handle = await gate.runCursorStep(() => client.openCursor(params));
|
|
11486
|
+
return {
|
|
11487
|
+
totalCount: handle.totalCount,
|
|
11488
|
+
nextPage: () => gate.runCursorStep(() => handle.nextPage()),
|
|
11489
|
+
close: () => gate.runCursorStep(() => handle.close())
|
|
11490
|
+
};
|
|
11491
|
+
},
|
|
9964
11492
|
getApps: () => gate.runReadOnly(() => client.getApps()),
|
|
9965
11493
|
getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
|
|
9966
11494
|
getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
|
|
@@ -9990,12 +11518,14 @@ function flattenFormFieldProperties(properties) {
|
|
|
9990
11518
|
function flattenFields(properties, lookupCopyFields, inSubtable = false) {
|
|
9991
11519
|
const out = [];
|
|
9992
11520
|
for (const field of Object.values(properties)) {
|
|
9993
|
-
|
|
11521
|
+
const optionOrder = toOptionOrderMap(field.options);
|
|
11522
|
+
const sortKind = detectSortKind(field.type, field.format);
|
|
11523
|
+
const info = {
|
|
9994
11524
|
code: field.code,
|
|
9995
11525
|
label: field.label,
|
|
9996
11526
|
fieldType: field.type,
|
|
9997
|
-
optionOrder
|
|
9998
|
-
sortKind
|
|
11527
|
+
optionOrder,
|
|
11528
|
+
sortKind,
|
|
9999
11529
|
required: field.required,
|
|
10000
11530
|
minValue: normalizeConstraintValue(field.minValue),
|
|
10001
11531
|
maxValue: normalizeConstraintValue(field.maxValue),
|
|
@@ -10004,7 +11534,9 @@ function flattenFields(properties, lookupCopyFields, inSubtable = false) {
|
|
|
10004
11534
|
defaultValue: field.defaultValue,
|
|
10005
11535
|
inSubtable,
|
|
10006
11536
|
writable: !lookupCopyFields.has(field.code) && !NON_WRITABLE_FIELD_TYPES2.has(field.type)
|
|
10007
|
-
}
|
|
11537
|
+
};
|
|
11538
|
+
info.semantics = resolveFieldSemantics(info);
|
|
11539
|
+
out.push(info);
|
|
10008
11540
|
if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true));
|
|
10009
11541
|
}
|
|
10010
11542
|
return out;
|
|
@@ -10059,7 +11591,230 @@ function detectSortKind(fieldType, calcFormat) {
|
|
|
10059
11591
|
return void 0;
|
|
10060
11592
|
}
|
|
10061
11593
|
|
|
11594
|
+
// src/core/processStatus.ts
|
|
11595
|
+
function normalizeProcessStatusStates(states) {
|
|
11596
|
+
if (states === null) return null;
|
|
11597
|
+
return Object.values(states).map((state) => {
|
|
11598
|
+
const index = Number(state.index);
|
|
11599
|
+
if (!Number.isSafeInteger(index) || index < 0) {
|
|
11600
|
+
throw new Error(`ArgumentError: invalid process status index: ${String(state.index)}`);
|
|
11601
|
+
}
|
|
11602
|
+
return { name: state.name, index };
|
|
11603
|
+
});
|
|
11604
|
+
}
|
|
11605
|
+
|
|
11606
|
+
// src/api/kintoneCursor.ts
|
|
11607
|
+
function isAlreadyReleasedCursorError(error) {
|
|
11608
|
+
const shaped = error;
|
|
11609
|
+
return shaped?.status === 404 && shaped.code === "GAIA_CN01";
|
|
11610
|
+
}
|
|
11611
|
+
async function deleteCursorWithConfirmation(deleteCursor, sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)), isAlreadyReleased = isAlreadyReleasedCursorError) {
|
|
11612
|
+
try {
|
|
11613
|
+
await deleteCursor();
|
|
11614
|
+
return;
|
|
11615
|
+
} catch (firstError) {
|
|
11616
|
+
if (isAlreadyReleased(firstError)) return;
|
|
11617
|
+
}
|
|
11618
|
+
await sleep(250);
|
|
11619
|
+
try {
|
|
11620
|
+
await deleteCursor();
|
|
11621
|
+
} catch (confirmationError) {
|
|
11622
|
+
if (isAlreadyReleased(confirmationError)) return;
|
|
11623
|
+
throw confirmationError;
|
|
11624
|
+
}
|
|
11625
|
+
}
|
|
11626
|
+
async function withTimeout(promise, timeoutMs) {
|
|
11627
|
+
let timer;
|
|
11628
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
11629
|
+
timer = setTimeout(() => reject(new Error(`CursorCleanupTimeoutError: cleanup exceeded ${timeoutMs}ms.`)), timeoutMs);
|
|
11630
|
+
timer.unref?.();
|
|
11631
|
+
});
|
|
11632
|
+
try {
|
|
11633
|
+
return await Promise.race([promise, timeout]);
|
|
11634
|
+
} finally {
|
|
11635
|
+
if (timer) clearTimeout(timer);
|
|
11636
|
+
}
|
|
11637
|
+
}
|
|
11638
|
+
function createKintoneCursorHandle(totalCount, operations) {
|
|
11639
|
+
let released = false;
|
|
11640
|
+
let closing = false;
|
|
11641
|
+
let pageTail = Promise.resolve();
|
|
11642
|
+
let closePromise = null;
|
|
11643
|
+
const nextPage = () => {
|
|
11644
|
+
if (closing || released) return Promise.resolve({ records: [], next: false });
|
|
11645
|
+
const result = pageTail.then(async () => {
|
|
11646
|
+
if (closing || released) return { records: [], next: false };
|
|
11647
|
+
const page = await operations.get();
|
|
11648
|
+
if (!page.next) {
|
|
11649
|
+
released = true;
|
|
11650
|
+
operations.onReleased?.();
|
|
11651
|
+
}
|
|
11652
|
+
return page;
|
|
11653
|
+
});
|
|
11654
|
+
pageTail = result.then(() => void 0, () => void 0);
|
|
11655
|
+
return result;
|
|
11656
|
+
};
|
|
11657
|
+
const close = () => {
|
|
11658
|
+
if (released) return Promise.resolve();
|
|
11659
|
+
if (closePromise) return closePromise;
|
|
11660
|
+
closing = true;
|
|
11661
|
+
closePromise = pageTail.then(async () => {
|
|
11662
|
+
if (released) return;
|
|
11663
|
+
try {
|
|
11664
|
+
await withTimeout(
|
|
11665
|
+
deleteCursorWithConfirmation(
|
|
11666
|
+
operations.delete,
|
|
11667
|
+
operations.sleep,
|
|
11668
|
+
operations.isAlreadyReleasedError
|
|
11669
|
+
),
|
|
11670
|
+
operations.cleanupTimeoutMs ?? 5e3
|
|
11671
|
+
);
|
|
11672
|
+
released = true;
|
|
11673
|
+
operations.onReleased?.();
|
|
11674
|
+
} catch (error) {
|
|
11675
|
+
operations.onReleaseUnknown?.();
|
|
11676
|
+
throw error;
|
|
11677
|
+
}
|
|
11678
|
+
});
|
|
11679
|
+
return closePromise;
|
|
11680
|
+
};
|
|
11681
|
+
return { totalCount, nextPage, close };
|
|
11682
|
+
}
|
|
11683
|
+
|
|
11684
|
+
// src/api/cursorLeaseManager.ts
|
|
11685
|
+
var DEFAULT_MAX_ACTIVE = 2;
|
|
11686
|
+
var MAX_ACTIVE = 5;
|
|
11687
|
+
var DEFAULT_WAIT_MS = 3e4;
|
|
11688
|
+
var DEFAULT_QUARANTINE_MS = 10 * 6e4 + 3e4;
|
|
11689
|
+
var CursorLeaseManager = class {
|
|
11690
|
+
constructor(host, options = {}) {
|
|
11691
|
+
this.host = host;
|
|
11692
|
+
this.active = 0;
|
|
11693
|
+
this.peak = 0;
|
|
11694
|
+
this.quarantined = 0;
|
|
11695
|
+
this.waiters = [];
|
|
11696
|
+
this.createTail = Promise.resolve();
|
|
11697
|
+
const maxActive = options.maxActive ?? DEFAULT_MAX_ACTIVE;
|
|
11698
|
+
if (!Number.isSafeInteger(maxActive) || maxActive < 1 || maxActive > MAX_ACTIVE) {
|
|
11699
|
+
throw new Error(`ArgumentError: cursorMaxActive must be an integer from 1 to ${MAX_ACTIVE}.`);
|
|
11700
|
+
}
|
|
11701
|
+
this.maxActive = maxActive;
|
|
11702
|
+
this.waitTimeoutMs = options.waitTimeoutMs ?? DEFAULT_WAIT_MS;
|
|
11703
|
+
this.quarantineMs = options.quarantineMs ?? DEFAULT_QUARANTINE_MS;
|
|
11704
|
+
}
|
|
11705
|
+
acquire() {
|
|
11706
|
+
if (this.active < this.maxActive) {
|
|
11707
|
+
this.active += 1;
|
|
11708
|
+
this.peak = Math.max(this.peak, this.active);
|
|
11709
|
+
return Promise.resolve(this.makeLease());
|
|
11710
|
+
}
|
|
11711
|
+
return new Promise((resolve2, reject) => {
|
|
11712
|
+
const waiter = {};
|
|
11713
|
+
waiter.resolve = resolve2;
|
|
11714
|
+
waiter.reject = reject;
|
|
11715
|
+
waiter.timer = setTimeout(() => {
|
|
11716
|
+
const index = this.waiters.indexOf(waiter);
|
|
11717
|
+
if (index >= 0) this.waiters.splice(index, 1);
|
|
11718
|
+
reject(new CursorCapacityError(this.host, this.maxActive, this.waitTimeoutMs));
|
|
11719
|
+
}, this.waitTimeoutMs);
|
|
11720
|
+
waiter.timer.unref?.();
|
|
11721
|
+
this.waiters.push(waiter);
|
|
11722
|
+
});
|
|
11723
|
+
}
|
|
11724
|
+
/**
|
|
11725
|
+
* 同一hostを共有する後続surfaceの設定を反映する。
|
|
11726
|
+
* 縮小時は既存leaseを強制終了せず、activeが新上限を下回るまで新規取得だけを止める。
|
|
11727
|
+
*/
|
|
11728
|
+
setMaxActive(maxActive) {
|
|
11729
|
+
this.validateMaxActive(maxActive);
|
|
11730
|
+
if (this.maxActive === maxActive) return;
|
|
11731
|
+
this.maxActive = maxActive;
|
|
11732
|
+
this.dispatchWaiters();
|
|
11733
|
+
}
|
|
11734
|
+
async runCreate(fn) {
|
|
11735
|
+
const previous = this.createTail;
|
|
11736
|
+
let unlock;
|
|
11737
|
+
this.createTail = new Promise((resolve2) => {
|
|
11738
|
+
unlock = resolve2;
|
|
11739
|
+
});
|
|
11740
|
+
await previous;
|
|
11741
|
+
try {
|
|
11742
|
+
return await fn();
|
|
11743
|
+
} finally {
|
|
11744
|
+
unlock();
|
|
11745
|
+
}
|
|
11746
|
+
}
|
|
11747
|
+
snapshot() {
|
|
11748
|
+
return {
|
|
11749
|
+
active: this.active,
|
|
11750
|
+
peak: this.peak,
|
|
11751
|
+
quarantined: this.quarantined,
|
|
11752
|
+
waiting: this.waiters.length,
|
|
11753
|
+
limit: this.maxActive
|
|
11754
|
+
};
|
|
11755
|
+
}
|
|
11756
|
+
makeLease() {
|
|
11757
|
+
let done = false;
|
|
11758
|
+
return {
|
|
11759
|
+
release: () => {
|
|
11760
|
+
if (done) return;
|
|
11761
|
+
done = true;
|
|
11762
|
+
this.returnPermit();
|
|
11763
|
+
},
|
|
11764
|
+
quarantine: (durationMs = this.quarantineMs) => {
|
|
11765
|
+
if (done) return;
|
|
11766
|
+
done = true;
|
|
11767
|
+
this.quarantined += 1;
|
|
11768
|
+
const timer = setTimeout(() => {
|
|
11769
|
+
this.quarantined -= 1;
|
|
11770
|
+
this.returnPermit();
|
|
11771
|
+
}, durationMs);
|
|
11772
|
+
timer.unref?.();
|
|
11773
|
+
}
|
|
11774
|
+
};
|
|
11775
|
+
}
|
|
11776
|
+
returnPermit() {
|
|
11777
|
+
this.active -= 1;
|
|
11778
|
+
this.dispatchWaiters();
|
|
11779
|
+
}
|
|
11780
|
+
dispatchWaiters() {
|
|
11781
|
+
while (this.active < this.maxActive) {
|
|
11782
|
+
const waiter = this.waiters.shift();
|
|
11783
|
+
if (!waiter) return;
|
|
11784
|
+
clearTimeout(waiter.timer);
|
|
11785
|
+
this.active += 1;
|
|
11786
|
+
this.peak = Math.max(this.peak, this.active);
|
|
11787
|
+
waiter.resolve(this.makeLease());
|
|
11788
|
+
}
|
|
11789
|
+
}
|
|
11790
|
+
validateMaxActive(maxActive) {
|
|
11791
|
+
if (!Number.isSafeInteger(maxActive) || maxActive < 1 || maxActive > MAX_ACTIVE) {
|
|
11792
|
+
throw new Error(`ArgumentError: cursorMaxActive must be an integer from 1 to ${MAX_ACTIVE}.`);
|
|
11793
|
+
}
|
|
11794
|
+
}
|
|
11795
|
+
};
|
|
11796
|
+
var managers = /* @__PURE__ */ new Map();
|
|
11797
|
+
function getCursorLeaseManager(host, maxActive = DEFAULT_MAX_ACTIVE) {
|
|
11798
|
+
const key = host.toLowerCase();
|
|
11799
|
+
let manager = managers.get(key);
|
|
11800
|
+
if (!manager) {
|
|
11801
|
+
manager = new CursorLeaseManager(key, { maxActive });
|
|
11802
|
+
managers.set(key, manager);
|
|
11803
|
+
} else {
|
|
11804
|
+
manager.setMaxActive(maxActive);
|
|
11805
|
+
}
|
|
11806
|
+
return manager;
|
|
11807
|
+
}
|
|
11808
|
+
|
|
10062
11809
|
// src/cli/nodeKintoneClient.ts
|
|
11810
|
+
var KintoneApiError = class extends Error {
|
|
11811
|
+
constructor(status, code, bodyText) {
|
|
11812
|
+
super(`kintone API error ${status}: ${bodyText}`);
|
|
11813
|
+
this.status = status;
|
|
11814
|
+
this.code = code;
|
|
11815
|
+
this.name = "KintoneApiError";
|
|
11816
|
+
}
|
|
11817
|
+
};
|
|
10063
11818
|
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
10064
11819
|
function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
10065
11820
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
@@ -10107,7 +11862,13 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
10107
11862
|
if (tokenResolver.debug) {
|
|
10108
11863
|
tokenResolver.log?.(`[debug] response status=${res.status} body=${bodyText}`);
|
|
10109
11864
|
}
|
|
10110
|
-
|
|
11865
|
+
let code;
|
|
11866
|
+
try {
|
|
11867
|
+
const body = JSON.parse(bodyText);
|
|
11868
|
+
if (typeof body.code === "string") code = body.code;
|
|
11869
|
+
} catch {
|
|
11870
|
+
}
|
|
11871
|
+
throw new KintoneApiError(res.status, code, bodyText);
|
|
10111
11872
|
}
|
|
10112
11873
|
if (tokenResolver.debug) {
|
|
10113
11874
|
tokenResolver.log?.(`[debug] response status=${res.status}`);
|
|
@@ -10174,6 +11935,48 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
10174
11935
|
return response.searchAborted ? { ...response.body, searchAborted: true } : response.body;
|
|
10175
11936
|
}
|
|
10176
11937
|
},
|
|
11938
|
+
async openCursor(params) {
|
|
11939
|
+
const manager = getCursorLeaseManager(new URL(normalizedBaseUrl).host, tokenResolver.cursorMaxActive);
|
|
11940
|
+
const lease = await manager.acquire();
|
|
11941
|
+
let created;
|
|
11942
|
+
try {
|
|
11943
|
+
created = await manager.runCreate(() => requestJson(
|
|
11944
|
+
`${apiBasePath}/records/cursor.json`,
|
|
11945
|
+
{
|
|
11946
|
+
method: "POST",
|
|
11947
|
+
body: JSON.stringify({
|
|
11948
|
+
app: params.app,
|
|
11949
|
+
query: params.query,
|
|
11950
|
+
size: params.size,
|
|
11951
|
+
fields: params.fields && params.fields.length > 0 ? params.fields : void 0
|
|
11952
|
+
})
|
|
11953
|
+
},
|
|
11954
|
+
params.app
|
|
11955
|
+
));
|
|
11956
|
+
} catch (error) {
|
|
11957
|
+
if (error instanceof KintoneApiError) {
|
|
11958
|
+
lease.release();
|
|
11959
|
+
throw error;
|
|
11960
|
+
}
|
|
11961
|
+
lease.quarantine();
|
|
11962
|
+
throw new CursorCreateOutcomeUnknownError(error);
|
|
11963
|
+
}
|
|
11964
|
+
const cursorId = created.id;
|
|
11965
|
+
return createKintoneCursorHandle(Number(created.totalCount), {
|
|
11966
|
+
get: () => requestJson(
|
|
11967
|
+
`${apiBasePath}/records/cursor.json?id=${encodeURIComponent(cursorId)}`,
|
|
11968
|
+
{ method: "GET" },
|
|
11969
|
+
params.app
|
|
11970
|
+
),
|
|
11971
|
+
delete: () => requestJson(
|
|
11972
|
+
`${apiBasePath}/records/cursor.json`,
|
|
11973
|
+
{ method: "DELETE", body: JSON.stringify({ id: cursorId }) },
|
|
11974
|
+
params.app
|
|
11975
|
+
),
|
|
11976
|
+
onReleased: () => lease.release(),
|
|
11977
|
+
onReleaseUnknown: () => lease.quarantine()
|
|
11978
|
+
});
|
|
11979
|
+
},
|
|
10177
11980
|
async postRecords(_params) {
|
|
10178
11981
|
const res = await requestJson(
|
|
10179
11982
|
`${apiBasePath}/records.json`,
|
|
@@ -10256,7 +12059,7 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
10256
12059
|
const res = await requestJson(`${apiBasePath}/app/status.json?${qs.toString()}`, { method: "GET" }, appId);
|
|
10257
12060
|
return {
|
|
10258
12061
|
enable: res.enable,
|
|
10259
|
-
states:
|
|
12062
|
+
states: normalizeProcessStatusStates(res.states)
|
|
10260
12063
|
};
|
|
10261
12064
|
}
|
|
10262
12065
|
};
|
|
@@ -10726,11 +12529,12 @@ Options:
|
|
|
10726
12529
|
(batch + json: prints one JSON envelope for the whole batch)
|
|
10727
12530
|
--max-records <n> Max records to fetch (default: 500)
|
|
10728
12531
|
--fetch-parallel <n> Parallel page fetches per query: 1-10 (default: 3)
|
|
10729
|
-
--on-limit <mode> On record limit: error | truncate
|
|
12532
|
+
--on-limit <mode> On record limit: error | truncate (local ORDER BY needs complete input)
|
|
10730
12533
|
--temp-table-max-rows <n> Max rows per temp table (default: 10000, always errors on overflow)
|
|
10731
12534
|
--timeout <ms> Request timeout in milliseconds (default: 30000)
|
|
10732
12535
|
--max-concurrent <n> Max concurrent kintone requests: 1-50 (default: 10)
|
|
10733
12536
|
(process-wide; fixed at first resolution; KSQL_MAX_CONCURRENT wins)
|
|
12537
|
+
--cursor-max-active <n> Max active cursors per host: 1-5 (default: 2; KSQL_CURSOR_MAX_ACTIVE wins)
|
|
10734
12538
|
--retry <n> GET retry count: 0-10, 0 disables (default: 3; KSQL_RETRY wins)
|
|
10735
12539
|
--retry-base-delay <ms> GET retry backoff base delay (default: 500)
|
|
10736
12540
|
--retry-max-delay <ms> GET retry backoff max delay (default: 8000)
|
|
@@ -10811,6 +12615,7 @@ function parseArgs(argv) {
|
|
|
10811
12615
|
continueOnError: false,
|
|
10812
12616
|
dmlMaxRows: null,
|
|
10813
12617
|
maxConcurrent: null,
|
|
12618
|
+
cursorMaxActive: null,
|
|
10814
12619
|
retry: null,
|
|
10815
12620
|
retryBaseDelay: null,
|
|
10816
12621
|
retryMaxDelay: null,
|
|
@@ -11067,6 +12872,13 @@ function parseArgs(argv) {
|
|
|
11067
12872
|
i++;
|
|
11068
12873
|
continue;
|
|
11069
12874
|
}
|
|
12875
|
+
if (a === "--cursor-max-active") {
|
|
12876
|
+
const n = Number(v);
|
|
12877
|
+
if (!Number.isInteger(n) || n < 1 || n > 5) throw new Error("ArgumentError: --cursor-max-active must be an integer between 1 and 5.");
|
|
12878
|
+
out.cursorMaxActive = n;
|
|
12879
|
+
i++;
|
|
12880
|
+
continue;
|
|
12881
|
+
}
|
|
11070
12882
|
if (a === "--retry") {
|
|
11071
12883
|
const n = Number(v);
|
|
11072
12884
|
if (!Number.isInteger(n) || n < 0 || n > 10) throw new Error("ArgumentError: --retry must be an integer between 0 and 10 (0 disables retry).");
|
|
@@ -11362,6 +13174,7 @@ function createDryRunClient() {
|
|
|
11362
13174
|
};
|
|
11363
13175
|
return {
|
|
11364
13176
|
getRecords: notUsed,
|
|
13177
|
+
openCursor: notUsed,
|
|
11365
13178
|
postRecords: notUsed,
|
|
11366
13179
|
putRecords: notUsed,
|
|
11367
13180
|
deleteRecords: notUsed,
|
|
@@ -11504,6 +13317,7 @@ function buildReplExecArgv(base, sql, dryRun, format) {
|
|
|
11504
13317
|
pushOpt(argv, "--attachment-format", base.attachmentFormat);
|
|
11505
13318
|
pushOpt(argv, "--dml-max-rows", base.dmlMaxRows);
|
|
11506
13319
|
pushOpt(argv, "--max-concurrent", base.maxConcurrent);
|
|
13320
|
+
pushOpt(argv, "--cursor-max-active", base.cursorMaxActive);
|
|
11507
13321
|
pushOpt(argv, "--retry", base.retry);
|
|
11508
13322
|
pushOpt(argv, "--retry-base-delay", base.retryBaseDelay);
|
|
11509
13323
|
pushOpt(argv, "--retry-max-delay", base.retryMaxDelay);
|
|
@@ -12010,7 +13824,7 @@ async function run() {
|
|
|
12010
13824
|
let isBatchSql = false;
|
|
12011
13825
|
let batchContainsDml = false;
|
|
12012
13826
|
let batchAnalysis = null;
|
|
12013
|
-
let
|
|
13827
|
+
let dryRunNeedsMetadata = false;
|
|
12014
13828
|
if (args.diagRecordId === null) {
|
|
12015
13829
|
sql = args.executeSql;
|
|
12016
13830
|
if (!sql && args.filePath) sql = (0, import_fs2.readFileSync)(args.filePath, "utf-8");
|
|
@@ -12037,17 +13851,16 @@ async function run() {
|
|
|
12037
13851
|
}
|
|
12038
13852
|
try {
|
|
12039
13853
|
const statements = parseSqlStatements(sql);
|
|
13854
|
+
dryRunNeedsMetadata = statements.some(explainNeedsAppMetadata);
|
|
12040
13855
|
if (statements.length > 1) {
|
|
12041
13856
|
batchAnalysis = analyzeBatch(statements);
|
|
12042
13857
|
isBatchSql = true;
|
|
12043
13858
|
batchContainsDml = batchAnalysis.containsDml;
|
|
12044
|
-
needsCompleteInput = batchAnalysis.requiresCompleteInput;
|
|
12045
13859
|
} else {
|
|
12046
13860
|
const stmt = parseSqlStatement(sql);
|
|
12047
13861
|
parsedStmt = stmt;
|
|
12048
13862
|
stmtType = getStatementType(stmt);
|
|
12049
13863
|
isDmlStatement = writesKintone(stmt);
|
|
12050
|
-
needsCompleteInput = requiresCompleteInput(stmt);
|
|
12051
13864
|
hasWhere = hasWhereClause(stmt);
|
|
12052
13865
|
insertValuesCount = getInsertValuesCount(stmt);
|
|
12053
13866
|
const supported = stmtType === "SELECT" || stmtType === "UNION" || stmtType === "WITH" || stmtType === "EXPLAIN" || stmtType === "SHOW_APPS" || stmtType === "DESCRIBE" || stmtType === "ASSERT" || isDmlType(stmtType);
|
|
@@ -12072,6 +13885,11 @@ async function run() {
|
|
|
12072
13885
|
const onLimit = args.onLimit ?? envOnLimit2("KSQL_ON_LIMIT") ?? profile.query?.onLimit ?? "error";
|
|
12073
13886
|
const timeout = args.timeout ?? envInt2("KSQL_TIMEOUT") ?? profile.query?.timeout ?? 3e4;
|
|
12074
13887
|
const tempTableMaxRows = args.tempTableMaxRows ?? envInt2("KSQL_TEMP_TABLE_MAX_ROWS") ?? profile.query?.tempTableMaxRows ?? void 0;
|
|
13888
|
+
const cursorMaxActive = args.cursorMaxActive ?? envInt2("KSQL_CURSOR_MAX_ACTIVE") ?? profile.query?.cursorMaxActive ?? 2;
|
|
13889
|
+
if (!Number.isSafeInteger(cursorMaxActive) || cursorMaxActive < 1 || cursorMaxActive > 5) {
|
|
13890
|
+
process.stderr.write("ArgumentError: cursorMaxActive must be an integer from 1 to 5.\n");
|
|
13891
|
+
return 2;
|
|
13892
|
+
}
|
|
12075
13893
|
if (!Number.isInteger(fetchParallel) || fetchParallel < 1 || fetchParallel > 10) {
|
|
12076
13894
|
process.stderr.write("ArgumentError: fetch-parallel must be an integer between 1 and 10.\n");
|
|
12077
13895
|
return 2;
|
|
@@ -12094,10 +13912,13 @@ async function run() {
|
|
|
12094
13912
|
const yes = args.yes || envBool("KSQL_YES") === true || Boolean(profile.dml?.yes);
|
|
12095
13913
|
const allowWithoutWhere = args.allowWithoutWhere || envBool("KSQL_ALLOW_WITHOUT_WHERE") === true || Boolean(profile.dml?.allowWithoutWhere);
|
|
12096
13914
|
const dmlMaxRows = args.dmlMaxRows ?? envInt2("KSQL_DML_MAX_ROWS") ?? profile.dml?.maxRows ?? 100;
|
|
12097
|
-
const
|
|
12098
|
-
const
|
|
12099
|
-
|
|
12100
|
-
|
|
13915
|
+
const isValidationOnly = batchAnalysis?.containsValidationOnly === true || parsedStmt !== null && typeof parsedStmt === "object" && "validateOnly" in parsedStmt && parsedStmt.validateOnly === true;
|
|
13916
|
+
const surfaceForcesOnLimitError = isDmlStatement || batchContainsDml || isValidationOnly;
|
|
13917
|
+
const effectiveOnLimit = surfaceForcesOnLimitError ? "error" : onLimit;
|
|
13918
|
+
if (surfaceForcesOnLimitError && onLimit === "truncate" && !quiet && !args.dryRun) {
|
|
13919
|
+
const reason = isDmlStatement || batchContainsDml ? "DML" : "VALIDATE ONLY";
|
|
13920
|
+
process.stderr.write(`note: onLimit=truncate is ignored for ${reason} (forced to error)
|
|
13921
|
+
`);
|
|
12101
13922
|
}
|
|
12102
13923
|
if (format === "markdown" && noHeader) {
|
|
12103
13924
|
process.stderr.write("ArgumentError: --no-header cannot be used with --format markdown|md.\n");
|
|
@@ -12128,30 +13949,6 @@ async function run() {
|
|
|
12128
13949
|
process.stderr.write("ArgumentError: DML is disabled. Use --allow-dml to enable UPDATE/DELETE/INSERT/UPSERT/REORDER.\n");
|
|
12129
13950
|
return 2;
|
|
12130
13951
|
}
|
|
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
13952
|
}
|
|
12156
13953
|
if (isDmlStatement) {
|
|
12157
13954
|
if (hasProfileSyntax && stmtType === "DELETE") {
|
|
@@ -12178,7 +13975,7 @@ async function run() {
|
|
|
12178
13975
|
appProfileByApp.set(appId, appBindingByMappedApp.get(appId)?.profile ?? profileName.toLowerCase());
|
|
12179
13976
|
}
|
|
12180
13977
|
const cacheContext = buildCacheContext(profileName, appBindingByMappedApp);
|
|
12181
|
-
if (args.dryRun) {
|
|
13978
|
+
if (args.dryRun && !dryRunNeedsMetadata) {
|
|
12182
13979
|
client = createDryRunClient();
|
|
12183
13980
|
} else {
|
|
12184
13981
|
for (const explicitProfile of appProfileByApp.values()) {
|
|
@@ -12223,6 +14020,7 @@ async function run() {
|
|
|
12223
14020
|
}
|
|
12224
14021
|
profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
|
|
12225
14022
|
guestSpaceId,
|
|
14023
|
+
cursorMaxActive,
|
|
12226
14024
|
timeoutMs: timeout,
|
|
12227
14025
|
debug,
|
|
12228
14026
|
debugHeaders,
|
|
@@ -12255,6 +14053,7 @@ async function run() {
|
|
|
12255
14053
|
missingAppProfiles.push(...resolvedTokens.missing);
|
|
12256
14054
|
profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
|
|
12257
14055
|
guestSpaceId,
|
|
14056
|
+
cursorMaxActive,
|
|
12258
14057
|
timeoutMs: timeout,
|
|
12259
14058
|
debug,
|
|
12260
14059
|
debugHeaders,
|
|
@@ -12366,6 +14165,12 @@ async function run() {
|
|
|
12366
14165
|
if (!routed) throw new Error(`AuthError: profile "${pName}" is not resolved for APP${params.app}.`);
|
|
12367
14166
|
return routed.getRecords({ ...params, app: binding.appId });
|
|
12368
14167
|
},
|
|
14168
|
+
openCursor: (params) => {
|
|
14169
|
+
const binding = appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
|
|
14170
|
+
const routed = profileClientMap.get(binding.profile);
|
|
14171
|
+
if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
|
|
14172
|
+
return routed.openCursor({ ...params, app: binding.appId });
|
|
14173
|
+
},
|
|
12369
14174
|
postRecords: (params) => {
|
|
12370
14175
|
const binding = appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
|
|
12371
14176
|
const pName = binding.profile;
|
|
@@ -12412,6 +14217,36 @@ async function run() {
|
|
|
12412
14217
|
maxDelayMs: args.retryMaxDelay ?? profile.query?.retryMaxDelayMs
|
|
12413
14218
|
})));
|
|
12414
14219
|
}
|
|
14220
|
+
if (isBatchSql && args.dryRun) {
|
|
14221
|
+
try {
|
|
14222
|
+
const plans = await buildBatchExplainPlans(
|
|
14223
|
+
sql,
|
|
14224
|
+
client,
|
|
14225
|
+
args.variables,
|
|
14226
|
+
cacheContext,
|
|
14227
|
+
maxRecords,
|
|
14228
|
+
cursorMaxActive
|
|
14229
|
+
);
|
|
14230
|
+
const out = [];
|
|
14231
|
+
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|
|
14232
|
+
restoredStatements.forEach((p) => {
|
|
14233
|
+
if (p.index > 0) out.push("");
|
|
14234
|
+
out.push(`[${p.index + 1}] ${p.type}`);
|
|
14235
|
+
out.push(...p.plan);
|
|
14236
|
+
});
|
|
14237
|
+
process.stdout.write(`${out.join("\n")}
|
|
14238
|
+
`);
|
|
14239
|
+
return 0;
|
|
14240
|
+
} catch (err) {
|
|
14241
|
+
const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
|
|
14242
|
+
bindings: sqlDiagnosticContext.appBindingByMappedApp,
|
|
14243
|
+
rewriteSegments: sqlDiagnosticContext.rewriteSegments
|
|
14244
|
+
}) : err;
|
|
14245
|
+
process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
|
|
14246
|
+
`);
|
|
14247
|
+
return toExitCodeFromError(restored);
|
|
14248
|
+
}
|
|
14249
|
+
}
|
|
12415
14250
|
try {
|
|
12416
14251
|
if (isDmlStatement && !args.dryRun) {
|
|
12417
14252
|
const stmtAppId = parsedStmt && typeof parsedStmt === "object" && typeof parsedStmt.appId === "number" ? parsedStmt.appId : appIds[0];
|
|
@@ -12455,6 +14290,7 @@ query=${label}`);
|
|
|
12455
14290
|
continueOnError: args.continueOnError,
|
|
12456
14291
|
tempTableMaxRows,
|
|
12457
14292
|
timeoutMs: timeout,
|
|
14293
|
+
cursorMaxActive,
|
|
12458
14294
|
variables: args.variables,
|
|
12459
14295
|
confirm: batchContainsDml ? async (count, operation) => {
|
|
12460
14296
|
if (count > dmlMaxRows) {
|
|
@@ -12465,12 +14301,18 @@ query=${label}`);
|
|
|
12465
14301
|
});
|
|
12466
14302
|
return writeBatchOutput(batchResult, { format, noHeader, pretty, displayOptions, outputPath, quiet });
|
|
12467
14303
|
}
|
|
12468
|
-
let result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, {
|
|
14304
|
+
let result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, {
|
|
14305
|
+
maxRecords,
|
|
14306
|
+
onLimitReached: onLimit,
|
|
14307
|
+
cacheContext,
|
|
14308
|
+
cursorMaxActive
|
|
14309
|
+
}) : await execute(sql, client, {
|
|
12469
14310
|
maxRecords,
|
|
12470
14311
|
fetchParallel,
|
|
12471
14312
|
onLimitReached: effectiveOnLimit,
|
|
12472
14313
|
confirm: isDmlStatement ? confirm : void 0,
|
|
12473
|
-
cacheContext
|
|
14314
|
+
cacheContext,
|
|
14315
|
+
cursorMaxActive
|
|
12474
14316
|
});
|
|
12475
14317
|
if (args.dryRun && sqlDiagnosticContext) {
|
|
12476
14318
|
result = restoreSqlDiagnosticValue(result, sqlDiagnosticContext.appBindingByMappedApp);
|