@rex0220/kintone-sql-tools 3.0.0 → 3.2.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 +1 -0
- package/dist-cli/ksql.js +771 -56
- package/dist-mcp/ksql-mcp.js +771 -63
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -117,6 +117,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
117
117
|
["LTRIM", "LTRIM" /* LTRIM */],
|
|
118
118
|
["RTRIM", "RTRIM" /* RTRIM */],
|
|
119
119
|
["LENGTH", "LENGTH" /* LENGTH */],
|
|
120
|
+
["LENGTH_CHAR", "LENGTH_CHAR" /* LENGTH_CHAR */],
|
|
120
121
|
["SUBSTRING", "SUBSTRING" /* SUBSTRING */],
|
|
121
122
|
["SUBSTR", "SUBSTR" /* SUBSTR */],
|
|
122
123
|
["CONCAT", "CONCAT" /* CONCAT */],
|
|
@@ -129,6 +130,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
129
130
|
["LEAST", "LEAST" /* LEAST */],
|
|
130
131
|
["LPAD", "LPAD" /* LPAD */],
|
|
131
132
|
["RPAD", "RPAD" /* RPAD */],
|
|
133
|
+
["TRANSLATE", "TRANSLATE" /* TRANSLATE */],
|
|
132
134
|
["CAST", "CAST" /* CAST */],
|
|
133
135
|
["CONVERT", "CONVERT" /* CONVERT */],
|
|
134
136
|
["FORMAT", "FORMAT" /* FORMAT */],
|
|
@@ -488,10 +490,12 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
488
490
|
"LTRIM" /* LTRIM */,
|
|
489
491
|
"RTRIM" /* RTRIM */,
|
|
490
492
|
"LENGTH" /* LENGTH */,
|
|
493
|
+
"LENGTH_CHAR" /* LENGTH_CHAR */,
|
|
491
494
|
"SUBSTRING" /* SUBSTRING */,
|
|
492
495
|
"SUBSTR" /* SUBSTR */,
|
|
493
496
|
"CONCAT" /* CONCAT */,
|
|
494
497
|
"REPLACE" /* REPLACE */,
|
|
498
|
+
"TRANSLATE" /* TRANSLATE */,
|
|
495
499
|
"COALESCE" /* COALESCE */,
|
|
496
500
|
"NULLIF" /* NULLIF */,
|
|
497
501
|
"ISNULL" /* ISNULL */,
|
|
@@ -543,6 +547,7 @@ var ParseError = class extends Error {
|
|
|
543
547
|
var Parser = class {
|
|
544
548
|
constructor(tokens) {
|
|
545
549
|
this.tokens = tokens;
|
|
550
|
+
this.allowUnaryPlusNumber = false;
|
|
546
551
|
this.pos = 0;
|
|
547
552
|
/** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
|
|
548
553
|
this.cteNames = /* @__PURE__ */ new Set();
|
|
@@ -1311,8 +1316,16 @@ var Parser = class {
|
|
|
1311
1316
|
this.expect(")" /* RPAREN */);
|
|
1312
1317
|
return expr;
|
|
1313
1318
|
}
|
|
1319
|
+
if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
|
|
1320
|
+
this.advance();
|
|
1321
|
+
const number = this.expect("NUMBER" /* NUMBER */, "\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
1322
|
+
return { type: "NUMBER", value: Number(number.value) };
|
|
1323
|
+
}
|
|
1314
1324
|
if (this.peek().kind === "-" /* MINUS */) {
|
|
1315
1325
|
this.advance();
|
|
1326
|
+
if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
|
|
1327
|
+
throw new ParseError("\u5358\u9805\u7B26\u53F7\u3092\u91CD\u306D\u3066\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
1328
|
+
}
|
|
1316
1329
|
const operand = this.parseArithPrimary();
|
|
1317
1330
|
if (operand.type === "NUMBER") return { type: "NUMBER", value: -operand.value };
|
|
1318
1331
|
return { type: "ARITH", left: { type: "NUMBER", value: 0 }, op: "-", right: operand };
|
|
@@ -1422,10 +1435,12 @@ var Parser = class {
|
|
|
1422
1435
|
["LTRIM" /* LTRIM */]: "LTRIM",
|
|
1423
1436
|
["RTRIM" /* RTRIM */]: "RTRIM",
|
|
1424
1437
|
["LENGTH" /* LENGTH */]: "LENGTH",
|
|
1438
|
+
["LENGTH_CHAR" /* LENGTH_CHAR */]: "LENGTH_CHAR",
|
|
1425
1439
|
["SUBSTRING" /* SUBSTRING */]: "SUBSTRING",
|
|
1426
1440
|
["SUBSTR" /* SUBSTR */]: "SUBSTRING",
|
|
1427
1441
|
["CONCAT" /* CONCAT */]: "CONCAT",
|
|
1428
1442
|
["REPLACE" /* REPLACE */]: "REPLACE",
|
|
1443
|
+
["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
|
|
1429
1444
|
["COALESCE" /* COALESCE */]: "COALESCE",
|
|
1430
1445
|
["NULLIF" /* NULLIF */]: "NULLIF",
|
|
1431
1446
|
["ISNULL" /* ISNULL */]: "ISNULL",
|
|
@@ -2186,6 +2201,11 @@ var Parser = class {
|
|
|
2186
2201
|
} else if (this.peek().kind === "IF" /* IF */) {
|
|
2187
2202
|
const expr = this.parseIfExpr();
|
|
2188
2203
|
row.push({ type: "CASE_VALUE", expr });
|
|
2204
|
+
} else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
|
|
2205
|
+
const sign = this.advance();
|
|
2206
|
+
const number = this.expect("NUMBER" /* NUMBER */, "INSERT \u306E\u5358\u9805\u7B26\u53F7\u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
2207
|
+
const value = Number(number.value);
|
|
2208
|
+
row.push({ type: "NUMBER", value: sign.kind === "-" /* MINUS */ ? -value : value });
|
|
2189
2209
|
} else {
|
|
2190
2210
|
const tok = this.advance();
|
|
2191
2211
|
if (tok.kind === "STRING" /* STRING */) {
|
|
@@ -2215,6 +2235,12 @@ var Parser = class {
|
|
|
2215
2235
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
2216
2236
|
this.expect("SET" /* SET */);
|
|
2217
2237
|
const assignments = this.parseAssignments();
|
|
2238
|
+
if (subtableCode && assignments.some((a) => a.value.type === "STRING_FUNC")) {
|
|
2239
|
+
throw new ParseError(
|
|
2240
|
+
"\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
2241
|
+
this.prev()
|
|
2242
|
+
);
|
|
2243
|
+
}
|
|
2218
2244
|
let from = null;
|
|
2219
2245
|
if (this.consume("FROM" /* FROM */)) {
|
|
2220
2246
|
const table = this.parseTableRef();
|
|
@@ -2258,7 +2284,14 @@ var Parser = class {
|
|
|
2258
2284
|
from.targetFilter = decomposed.targetFilter;
|
|
2259
2285
|
} else if (assignments.some((a) => a.value.type === "SOURCE_FIELD")) {
|
|
2260
2286
|
throw new ParseError(
|
|
2261
|
-
"SET \u306E\u5024\u306B\
|
|
2287
|
+
"SET \u306E\u5024\u306B\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u5358\u72EC\u3067\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
|
|
2288
|
+
whereTok
|
|
2289
|
+
);
|
|
2290
|
+
} else if (assignments.some(
|
|
2291
|
+
(a) => a.value.type === "STRING_FUNC" && this.nodeContainsAnyQualifier(a.value)
|
|
2292
|
+
)) {
|
|
2293
|
+
throw new ParseError(
|
|
2294
|
+
"UPDATE SET \u306E\u6587\u5B57\u5217\u95A2\u6570\u3067\u306F\u66F4\u65B0\u5148\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u4FEE\u98FE\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044",
|
|
2262
2295
|
whereTok
|
|
2263
2296
|
);
|
|
2264
2297
|
}
|
|
@@ -2325,6 +2358,12 @@ var Parser = class {
|
|
|
2325
2358
|
}
|
|
2326
2359
|
validateUpdateFromAssignments(assignments, sourceAlias, tok) {
|
|
2327
2360
|
for (const assignment of assignments) {
|
|
2361
|
+
if (assignment.value.type === "STRING_FUNC") {
|
|
2362
|
+
throw new ParseError(
|
|
2363
|
+
"UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
2364
|
+
tok
|
|
2365
|
+
);
|
|
2366
|
+
}
|
|
2328
2367
|
if (assignment.value.type === "SOURCE_FIELD") {
|
|
2329
2368
|
if (assignment.value.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
|
|
2330
2369
|
throw new ParseError(`UPDATE ... FROM \u306E SET \u53C2\u7167\u306F\u30BD\u30FC\u30B9 alias ${sourceAlias} \u3067\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`, tok);
|
|
@@ -2464,9 +2503,17 @@ var Parser = class {
|
|
|
2464
2503
|
this.expect(")" /* RPAREN */);
|
|
2465
2504
|
return { type: "SCALAR_SUBQUERY", query };
|
|
2466
2505
|
}
|
|
2467
|
-
const
|
|
2506
|
+
const previousAllowUnaryPlusNumber = this.allowUnaryPlusNumber;
|
|
2507
|
+
this.allowUnaryPlusNumber = true;
|
|
2508
|
+
let node;
|
|
2509
|
+
try {
|
|
2510
|
+
node = this.parseArithAddSub();
|
|
2511
|
+
} finally {
|
|
2512
|
+
this.allowUnaryPlusNumber = previousAllowUnaryPlusNumber;
|
|
2513
|
+
}
|
|
2468
2514
|
if (node.type === "NUMBER") return node;
|
|
2469
2515
|
if (node.type === "ARITH") return node;
|
|
2516
|
+
if (node.type === "STRING_FUNC") return node;
|
|
2470
2517
|
if (node.type === "FIELD_REF") {
|
|
2471
2518
|
const dot = node.field.indexOf(".");
|
|
2472
2519
|
if (dot > 0 && dot < node.field.length - 1) {
|
|
@@ -2474,7 +2521,7 @@ var Parser = class {
|
|
|
2474
2521
|
}
|
|
2475
2522
|
}
|
|
2476
2523
|
throw new ParseError(
|
|
2477
|
-
"SET \u306E\u5024\u306B\
|
|
2524
|
+
"SET \u306E\u5024\u306B\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u5358\u72EC\u3067\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
|
|
2478
2525
|
tok
|
|
2479
2526
|
);
|
|
2480
2527
|
}
|
|
@@ -4499,6 +4546,43 @@ function applyRoundOp(op, num, digits) {
|
|
|
4499
4546
|
if (digits > 0) return String(parseFloat(raw.toFixed(digits)));
|
|
4500
4547
|
return String(raw);
|
|
4501
4548
|
}
|
|
4549
|
+
function isHighSurrogate(codeUnit) {
|
|
4550
|
+
return codeUnit >= 55296 && codeUnit <= 56319;
|
|
4551
|
+
}
|
|
4552
|
+
function isLowSurrogate(codeUnit) {
|
|
4553
|
+
return codeUnit >= 56320 && codeUnit <= 57343;
|
|
4554
|
+
}
|
|
4555
|
+
function splitsSurrogatePair(value, index) {
|
|
4556
|
+
return index > 0 && index < value.length && isHighSurrogate(value.charCodeAt(index - 1)) && isLowSurrogate(value.charCodeAt(index));
|
|
4557
|
+
}
|
|
4558
|
+
function normalizeSliceIndex(index, length) {
|
|
4559
|
+
if (Number.isNaN(index) || index === Number.NEGATIVE_INFINITY) return 0;
|
|
4560
|
+
if (index === Number.POSITIVE_INFINITY) return length;
|
|
4561
|
+
const integer = Math.trunc(index);
|
|
4562
|
+
return integer < 0 ? Math.max(length + integer, 0) : Math.min(integer, length);
|
|
4563
|
+
}
|
|
4564
|
+
function sliceSafePrefix(value, budget) {
|
|
4565
|
+
let end = Math.min(Math.max(0, budget), value.length);
|
|
4566
|
+
if (splitsSurrogatePair(value, end)) end -= 1;
|
|
4567
|
+
return value.slice(0, end);
|
|
4568
|
+
}
|
|
4569
|
+
function sliceSafeSuffix(value, budget) {
|
|
4570
|
+
let start = Math.max(0, value.length - budget);
|
|
4571
|
+
if (splitsSurrogatePair(value, start)) start += 1;
|
|
4572
|
+
return value.slice(start);
|
|
4573
|
+
}
|
|
4574
|
+
function sliceSafeRange(value, rawStart, rawEnd) {
|
|
4575
|
+
let start = normalizeSliceIndex(rawStart, value.length);
|
|
4576
|
+
let end = normalizeSliceIndex(rawEnd, value.length);
|
|
4577
|
+
if (end <= start) return "";
|
|
4578
|
+
if (splitsSurrogatePair(value, start)) start += 1;
|
|
4579
|
+
if (splitsSurrogatePair(value, end)) end -= 1;
|
|
4580
|
+
return value.slice(start, Math.max(start, end));
|
|
4581
|
+
}
|
|
4582
|
+
function makeSafePadding(pad, gap) {
|
|
4583
|
+
const repeated = pad.repeat(Math.ceil(gap / pad.length));
|
|
4584
|
+
return sliceSafePrefix(repeated, gap);
|
|
4585
|
+
}
|
|
4502
4586
|
function evalStringFunc(expr, row) {
|
|
4503
4587
|
const args = expr.args.map((a) => evalStringFuncArg(a, row));
|
|
4504
4588
|
switch (expr.func) {
|
|
@@ -4514,23 +4598,26 @@ function evalStringFunc(expr, row) {
|
|
|
4514
4598
|
return (args[0] ?? "").trimEnd();
|
|
4515
4599
|
case "LENGTH":
|
|
4516
4600
|
return String((args[0] ?? "").length);
|
|
4601
|
+
case "LENGTH_CHAR":
|
|
4602
|
+
assertArity("LENGTH_CHAR", args, 1, 1);
|
|
4603
|
+
return String([...args[0] ?? ""].length);
|
|
4517
4604
|
case "SUBSTRING": {
|
|
4518
4605
|
const str = args[0] ?? "";
|
|
4519
4606
|
const start = Math.max(0, Number(args[1] ?? "1") - 1);
|
|
4520
4607
|
const len = args[2] !== void 0 ? Number(args[2]) : void 0;
|
|
4521
|
-
return len !== void 0 ?
|
|
4608
|
+
return sliceSafeRange(str, start, len !== void 0 ? start + len : str.length);
|
|
4522
4609
|
}
|
|
4523
4610
|
case "LEFT": {
|
|
4524
4611
|
assertArity("LEFT", args, 2, 2);
|
|
4525
4612
|
const str = args[0];
|
|
4526
4613
|
const n = Math.trunc(Number(args[1]));
|
|
4527
|
-
return Number.isNaN(n) || n <= 0 ? "" : str
|
|
4614
|
+
return Number.isNaN(n) || n <= 0 ? "" : sliceSafePrefix(str, n);
|
|
4528
4615
|
}
|
|
4529
4616
|
case "RIGHT": {
|
|
4530
4617
|
assertArity("RIGHT", args, 2, 2);
|
|
4531
4618
|
const str = args[0];
|
|
4532
4619
|
const n = Math.trunc(Number(args[1]));
|
|
4533
|
-
return Number.isNaN(n) || n <= 0 ? "" : str
|
|
4620
|
+
return Number.isNaN(n) || n <= 0 ? "" : sliceSafeSuffix(str, n);
|
|
4534
4621
|
}
|
|
4535
4622
|
case "INSTR":
|
|
4536
4623
|
assertArity("INSTR", args, 2, 2);
|
|
@@ -4541,10 +4628,11 @@ function evalStringFunc(expr, row) {
|
|
|
4541
4628
|
const str = args[0];
|
|
4542
4629
|
const n = Math.trunc(Number(args[1]));
|
|
4543
4630
|
if (Number.isNaN(n) || n <= 0) return "";
|
|
4544
|
-
if (str.length >= n) return str
|
|
4631
|
+
if (str.length >= n) return sliceSafePrefix(str, n);
|
|
4545
4632
|
const pad = args[2] ?? " ";
|
|
4546
4633
|
if (pad === "") return str;
|
|
4547
|
-
|
|
4634
|
+
const padding = makeSafePadding(pad, n - str.length);
|
|
4635
|
+
return expr.func === "LPAD" ? padding + str : str + padding;
|
|
4548
4636
|
}
|
|
4549
4637
|
case "GREATEST":
|
|
4550
4638
|
case "LEAST":
|
|
@@ -4558,6 +4646,21 @@ function evalStringFunc(expr, row) {
|
|
|
4558
4646
|
const to = args[2] ?? "";
|
|
4559
4647
|
return from === "" ? str : str.split(from).join(to);
|
|
4560
4648
|
}
|
|
4649
|
+
case "TRANSLATE": {
|
|
4650
|
+
assertArity("TRANSLATE", args, 3, 3);
|
|
4651
|
+
const from = [...args[1]];
|
|
4652
|
+
const to = [...args[2]];
|
|
4653
|
+
if (from.length !== to.length) {
|
|
4654
|
+
throw new Error(
|
|
4655
|
+
`ArgumentError: TRANSLATE \u306E from \u3068 to \u306F\u540C\u3058\u6587\u5B57\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08from=${from.length}, to=${to.length}\uFF09`
|
|
4656
|
+
);
|
|
4657
|
+
}
|
|
4658
|
+
const map = /* @__PURE__ */ new Map();
|
|
4659
|
+
from.forEach((ch, i) => {
|
|
4660
|
+
if (!map.has(ch)) map.set(ch, to[i]);
|
|
4661
|
+
});
|
|
4662
|
+
return [...args[0]].map((ch) => map.get(ch) ?? ch).join("");
|
|
4663
|
+
}
|
|
4561
4664
|
case "COALESCE":
|
|
4562
4665
|
return args.find((a) => a !== "") ?? "";
|
|
4563
4666
|
case "NULLIF":
|
|
@@ -4795,6 +4898,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
|
|
|
4795
4898
|
}
|
|
4796
4899
|
var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
4797
4900
|
"LENGTH",
|
|
4901
|
+
"LENGTH_CHAR",
|
|
4798
4902
|
"INSTR",
|
|
4799
4903
|
"ROUND",
|
|
4800
4904
|
"FLOOR",
|
|
@@ -5042,7 +5146,7 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
|
5042
5146
|
function buildUpdateRecord(assignments, fieldTypes) {
|
|
5043
5147
|
const record = {};
|
|
5044
5148
|
for (const { field, value } of assignments) {
|
|
5045
|
-
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "SOURCE_FIELD") continue;
|
|
5149
|
+
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
5046
5150
|
record[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
|
|
5047
5151
|
}
|
|
5048
5152
|
return record;
|
|
@@ -5052,12 +5156,19 @@ function hasArithAssignment(stmt) {
|
|
|
5052
5156
|
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
|
|
5053
5157
|
);
|
|
5054
5158
|
}
|
|
5159
|
+
function hasRowDependentAssignment(stmt) {
|
|
5160
|
+
return stmt.assignments.some(
|
|
5161
|
+
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
5162
|
+
);
|
|
5163
|
+
}
|
|
5055
5164
|
function updateToGetQueryForArith(stmt) {
|
|
5056
5165
|
assertDmlWhereIsSafe(stmt.where);
|
|
5057
5166
|
const refFields = /* @__PURE__ */ new Set();
|
|
5058
5167
|
for (const { value } of stmt.assignments) {
|
|
5059
5168
|
if (value.type === "ARITH") {
|
|
5060
5169
|
collectArithFields2(value, refFields);
|
|
5170
|
+
} else if (value.type === "STRING_FUNC") {
|
|
5171
|
+
collectStringFuncFields2(value, refFields);
|
|
5061
5172
|
} else if (value.type === "CASE_VALUE") {
|
|
5062
5173
|
collectCaseFields(value.expr, refFields);
|
|
5063
5174
|
}
|
|
@@ -5144,6 +5255,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
|
|
|
5144
5255
|
for (const { field, value } of stmt.assignments) {
|
|
5145
5256
|
if (value.type === "ARITH") {
|
|
5146
5257
|
record[field] = { value: String(evalArith(value, raw)) };
|
|
5258
|
+
} else if (value.type === "STRING_FUNC") {
|
|
5259
|
+
record[field] = { value: evalStringFunc(value, row) };
|
|
5147
5260
|
} else if (value.type === "CASE_VALUE") {
|
|
5148
5261
|
record[field] = { value: evalCaseWhenValue(value.expr, row, fieldTypes.get(field)) };
|
|
5149
5262
|
} else if (value.type === "SOURCE_FIELD") {
|
|
@@ -5192,6 +5305,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
|
|
|
5192
5305
|
throw new DmlConvertError(`\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u306B\u5909\u63DB\u3067\u304D\u306A\u3044\u5024\u3067\u3059: ${raw}`);
|
|
5193
5306
|
}
|
|
5194
5307
|
record[field] = { value: toKintoneValue({ type: "STRING", value: raw }, fieldType) };
|
|
5308
|
+
} else if (value.type === "STRING_FUNC") {
|
|
5309
|
+
throw new DmlConvertError("UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
5195
5310
|
} else if (value.type === "ARITH") {
|
|
5196
5311
|
record[field] = { value: String(evalArith(value, target)) };
|
|
5197
5312
|
} else if (value.type === "CASE_VALUE") {
|
|
@@ -5643,7 +5758,7 @@ var KORDER_NATIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
5643
5758
|
"CREATOR",
|
|
5644
5759
|
"MODIFIER"
|
|
5645
5760
|
]);
|
|
5646
|
-
function
|
|
5761
|
+
function planKorder(input) {
|
|
5647
5762
|
const { stmt } = input;
|
|
5648
5763
|
const reasons = [];
|
|
5649
5764
|
if (stmt.orderMode !== "KINTONE_NATIVE") reasons.push("KORDER_MODE_REQUIRED");
|
|
@@ -5673,29 +5788,120 @@ function planKorderNative(input) {
|
|
|
5673
5788
|
reasons.push(`KORDER_TYPE_UNSUPPORTED(field=${name}, type=${semantics.fieldType})`);
|
|
5674
5789
|
}
|
|
5675
5790
|
}
|
|
5676
|
-
if (stmt.limit === null || stmt.limit
|
|
5791
|
+
if (stmt.limit === null || !Number.isSafeInteger(stmt.limit) || stmt.limit < 0) {
|
|
5677
5792
|
reasons.push(`KORDER_LIMIT_INVALID(limit=${String(stmt.limit)})`);
|
|
5678
5793
|
}
|
|
5679
|
-
if (stmt.limit !== null && stmt.limit > input.maxRecords) {
|
|
5680
|
-
reasons.push(`KORDER_LIMIT_EXCEEDS_MAX_RECORDS(limit=${stmt.limit}, maxRecords=${input.maxRecords})`);
|
|
5681
|
-
}
|
|
5682
5794
|
const offset = stmt.offset ?? 0;
|
|
5683
|
-
if (offset
|
|
5795
|
+
if (!Number.isSafeInteger(offset) || offset < 0) {
|
|
5796
|
+
reasons.push(`KORDER_OFFSET_INVALID(offset=${offset})`);
|
|
5797
|
+
}
|
|
5798
|
+
const scanRows = stmt.limit === null ? Number.NaN : offset + stmt.limit;
|
|
5799
|
+
if (stmt.limit !== null && !Number.isSafeInteger(scanRows)) {
|
|
5800
|
+
reasons.push(`KORDER_SCAN_ROWS_INVALID(offset=${offset}, limit=${stmt.limit})`);
|
|
5801
|
+
}
|
|
5684
5802
|
const unique = [...new Set(reasons)];
|
|
5685
5803
|
if (unique.length > 0) {
|
|
5686
5804
|
throw new Error(
|
|
5687
5805
|
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
|
|
5688
5806
|
);
|
|
5689
5807
|
}
|
|
5808
|
+
const native = stmt.limit <= 500 && offset <= 1e4 && stmt.limit <= input.maxRecords;
|
|
5809
|
+
if (!native && scanRows > input.maxRecords) {
|
|
5810
|
+
throw new Error(
|
|
5811
|
+
`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.`
|
|
5812
|
+
);
|
|
5813
|
+
}
|
|
5690
5814
|
return {
|
|
5691
|
-
kind: "KORDER_NATIVE",
|
|
5815
|
+
kind: native ? "KORDER_NATIVE" : "KORDER_CURSOR",
|
|
5692
5816
|
requiresCompleteInput: false,
|
|
5693
5817
|
localOrderBy: false,
|
|
5694
5818
|
applyLocalOffsetLimit: false,
|
|
5695
|
-
reasonCodes: []
|
|
5819
|
+
reasonCodes: [],
|
|
5820
|
+
scanRows
|
|
5696
5821
|
};
|
|
5697
5822
|
}
|
|
5698
5823
|
|
|
5824
|
+
// src/core/errors/cursorErrors.ts
|
|
5825
|
+
var CursorCapacityError = class extends Error {
|
|
5826
|
+
constructor(host, limit, waitMs) {
|
|
5827
|
+
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`);
|
|
5828
|
+
this.name = "CursorCapacityError";
|
|
5829
|
+
}
|
|
5830
|
+
};
|
|
5831
|
+
var CursorCreateOutcomeUnknownError = class extends Error {
|
|
5832
|
+
constructor(cause) {
|
|
5833
|
+
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");
|
|
5834
|
+
this.name = "CursorCreateOutcomeUnknownError";
|
|
5835
|
+
this.cause = cause;
|
|
5836
|
+
}
|
|
5837
|
+
};
|
|
5838
|
+
var CursorCleanupWarning = class extends Error {
|
|
5839
|
+
constructor(cause) {
|
|
5840
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
5841
|
+
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}`);
|
|
5842
|
+
this.name = "CursorCleanupWarning";
|
|
5843
|
+
this.cause = cause;
|
|
5844
|
+
}
|
|
5845
|
+
};
|
|
5846
|
+
|
|
5847
|
+
// src/core/optimization/korderCursorExecutor.ts
|
|
5848
|
+
async function executeKorderCursor(input) {
|
|
5849
|
+
const handle = await input.client.openCursor({
|
|
5850
|
+
app: input.app,
|
|
5851
|
+
fields: input.fields.length > 0 ? input.fields : void 0,
|
|
5852
|
+
query: input.query,
|
|
5853
|
+
size: 500
|
|
5854
|
+
});
|
|
5855
|
+
const records = [];
|
|
5856
|
+
let seen = 0;
|
|
5857
|
+
let primaryError;
|
|
5858
|
+
let cleanupWarning;
|
|
5859
|
+
try {
|
|
5860
|
+
if (handle.totalCount > input.offset) {
|
|
5861
|
+
while (records.length < input.limit) {
|
|
5862
|
+
const page = await handle.nextPage();
|
|
5863
|
+
for (const record of page.records) {
|
|
5864
|
+
if (seen < input.offset) seen += 1;
|
|
5865
|
+
else if (records.length < input.limit) records.push(record);
|
|
5866
|
+
else break;
|
|
5867
|
+
}
|
|
5868
|
+
if (!page.next) break;
|
|
5869
|
+
}
|
|
5870
|
+
}
|
|
5871
|
+
} catch (error) {
|
|
5872
|
+
primaryError = error;
|
|
5873
|
+
throw error;
|
|
5874
|
+
} finally {
|
|
5875
|
+
try {
|
|
5876
|
+
await handle.close();
|
|
5877
|
+
} catch (cleanupError) {
|
|
5878
|
+
if (primaryError && primaryError instanceof Error) {
|
|
5879
|
+
Object.defineProperty(primaryError, "cursorCleanupError", {
|
|
5880
|
+
value: cleanupError,
|
|
5881
|
+
configurable: true
|
|
5882
|
+
});
|
|
5883
|
+
} else {
|
|
5884
|
+
cleanupWarning = new CursorCleanupWarning(cleanupError).message;
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
}
|
|
5888
|
+
return { records, cleanupWarning };
|
|
5889
|
+
}
|
|
5890
|
+
|
|
5891
|
+
// src/converter/korderCursorQuery.ts
|
|
5892
|
+
function buildKorderCursorQuery(stmt) {
|
|
5893
|
+
const parts = [];
|
|
5894
|
+
if (stmt.where) parts.push(whereToKintone(stmt.where));
|
|
5895
|
+
const order = stmt.orderBy.map((item) => {
|
|
5896
|
+
if (item.key.type !== "FIELD_NAME") {
|
|
5897
|
+
throw new Error("ArgumentError: KORDER cursor key must be a direct field.");
|
|
5898
|
+
}
|
|
5899
|
+
return `${item.key.name} ${item.direction === "ASC" ? "asc" : "desc"}`;
|
|
5900
|
+
});
|
|
5901
|
+
parts.push(`order by ${order.join(", ")}`);
|
|
5902
|
+
return parts.join(" ");
|
|
5903
|
+
}
|
|
5904
|
+
|
|
5699
5905
|
// src/engine/process.ts
|
|
5700
5906
|
function flatten(record, alias) {
|
|
5701
5907
|
const row = {};
|
|
@@ -5992,6 +6198,7 @@ function compareSortKeys(a, b, meta) {
|
|
|
5992
6198
|
}
|
|
5993
6199
|
var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
5994
6200
|
"LENGTH",
|
|
6201
|
+
"LENGTH_CHAR",
|
|
5995
6202
|
"INSTR",
|
|
5996
6203
|
"ROUND",
|
|
5997
6204
|
"FLOOR",
|
|
@@ -6836,6 +7043,15 @@ function createEmptyMetrics() {
|
|
|
6836
7043
|
fieldCalls: 0,
|
|
6837
7044
|
appsCalls: 0,
|
|
6838
7045
|
processStatusCalls: 0,
|
|
7046
|
+
cursorCreateCalls: 0,
|
|
7047
|
+
cursorGetCalls: 0,
|
|
7048
|
+
cursorDeleteCalls: 0,
|
|
7049
|
+
cursorRecordsScanned: 0,
|
|
7050
|
+
cursorActiveCurrent: 0,
|
|
7051
|
+
cursorActivePeak: 0,
|
|
7052
|
+
cursorCleanupFailures: 0,
|
|
7053
|
+
cursorCreateOutcomeUnknown: 0,
|
|
7054
|
+
cursorQuarantinedCurrent: 0,
|
|
6839
7055
|
fetchedRows: 0,
|
|
6840
7056
|
elapsedMs: 0
|
|
6841
7057
|
};
|
|
@@ -6848,6 +7064,48 @@ function wrapClientWithMetrics(client, metrics) {
|
|
|
6848
7064
|
metrics.fetchedRows += res.records.length;
|
|
6849
7065
|
return res;
|
|
6850
7066
|
},
|
|
7067
|
+
openCursor: async (params) => {
|
|
7068
|
+
metrics.cursorCreateCalls += 1;
|
|
7069
|
+
let handle;
|
|
7070
|
+
try {
|
|
7071
|
+
handle = await client.openCursor(params);
|
|
7072
|
+
} catch (error) {
|
|
7073
|
+
if (error instanceof Error && error.name === "CursorCreateOutcomeUnknownError") {
|
|
7074
|
+
metrics.cursorCreateOutcomeUnknown += 1;
|
|
7075
|
+
metrics.cursorQuarantinedCurrent += 1;
|
|
7076
|
+
}
|
|
7077
|
+
throw error;
|
|
7078
|
+
}
|
|
7079
|
+
metrics.cursorActiveCurrent += 1;
|
|
7080
|
+
metrics.cursorActivePeak = Math.max(metrics.cursorActivePeak, metrics.cursorActiveCurrent);
|
|
7081
|
+
let released = false;
|
|
7082
|
+
const markReleased = () => {
|
|
7083
|
+
if (released) return;
|
|
7084
|
+
released = true;
|
|
7085
|
+
metrics.cursorActiveCurrent -= 1;
|
|
7086
|
+
};
|
|
7087
|
+
return {
|
|
7088
|
+
totalCount: handle.totalCount,
|
|
7089
|
+
nextPage: async () => {
|
|
7090
|
+
metrics.cursorGetCalls += 1;
|
|
7091
|
+
const page = await handle.nextPage();
|
|
7092
|
+
metrics.cursorRecordsScanned += page.records.length;
|
|
7093
|
+
if (!page.next) markReleased();
|
|
7094
|
+
return page;
|
|
7095
|
+
},
|
|
7096
|
+
close: async () => {
|
|
7097
|
+
if (!released) metrics.cursorDeleteCalls += 1;
|
|
7098
|
+
try {
|
|
7099
|
+
await handle.close();
|
|
7100
|
+
markReleased();
|
|
7101
|
+
} catch (error) {
|
|
7102
|
+
metrics.cursorCleanupFailures += 1;
|
|
7103
|
+
metrics.cursorQuarantinedCurrent += 1;
|
|
7104
|
+
throw error;
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
};
|
|
7108
|
+
},
|
|
6851
7109
|
postRecords: (params) => {
|
|
6852
7110
|
metrics.postCalls += 1;
|
|
6853
7111
|
return client.postRecords(params);
|
|
@@ -6887,6 +7145,37 @@ function wrapClientWithSearchAbort(client, collector, failClosed) {
|
|
|
6887
7145
|
}
|
|
6888
7146
|
};
|
|
6889
7147
|
}
|
|
7148
|
+
function wrapClientWithCursorScope(client) {
|
|
7149
|
+
const active = /* @__PURE__ */ new Set();
|
|
7150
|
+
return {
|
|
7151
|
+
client: {
|
|
7152
|
+
...client,
|
|
7153
|
+
openCursor: async (params) => {
|
|
7154
|
+
const handle = await client.openCursor(params);
|
|
7155
|
+
active.add(handle);
|
|
7156
|
+
const remove = () => active.delete(handle);
|
|
7157
|
+
return {
|
|
7158
|
+
totalCount: handle.totalCount,
|
|
7159
|
+
async nextPage() {
|
|
7160
|
+
const page = await handle.nextPage();
|
|
7161
|
+
if (!page.next) remove();
|
|
7162
|
+
return page;
|
|
7163
|
+
},
|
|
7164
|
+
async close() {
|
|
7165
|
+
try {
|
|
7166
|
+
await handle.close();
|
|
7167
|
+
} finally {
|
|
7168
|
+
remove();
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
};
|
|
7172
|
+
}
|
|
7173
|
+
},
|
|
7174
|
+
closeActive: async () => {
|
|
7175
|
+
await Promise.all([...active].map((handle) => handle.close().catch(() => void 0)));
|
|
7176
|
+
}
|
|
7177
|
+
};
|
|
7178
|
+
}
|
|
6890
7179
|
function isSelectLikeStatement(stmt) {
|
|
6891
7180
|
return stmt.type === "SELECT" || stmt.type === "UNION" || stmt.type === "WITH";
|
|
6892
7181
|
}
|
|
@@ -6937,7 +7226,13 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
6937
7226
|
case "DESCRIBE":
|
|
6938
7227
|
return executeDescribe(stmt, client, cacheContext);
|
|
6939
7228
|
case "EXPLAIN":
|
|
6940
|
-
return executeExplain(
|
|
7229
|
+
return executeExplain(
|
|
7230
|
+
stmt,
|
|
7231
|
+
client,
|
|
7232
|
+
cacheContext,
|
|
7233
|
+
options.maxRecords ?? 1e4,
|
|
7234
|
+
options.cursorMaxActive ?? 2
|
|
7235
|
+
);
|
|
6941
7236
|
// 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
|
|
6942
7237
|
case "CREATE_TEMP_TABLE":
|
|
6943
7238
|
throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
@@ -7044,9 +7339,11 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
7044
7339
|
searchAbortCollector,
|
|
7045
7340
|
info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH"
|
|
7046
7341
|
);
|
|
7342
|
+
const cursorScope = wrapClientWithCursorScope(statementClient);
|
|
7047
7343
|
const outcome = await runWithDeadline(
|
|
7048
|
-
executeBatchStatement(statements[i], info,
|
|
7049
|
-
remaining
|
|
7344
|
+
executeBatchStatement(statements[i], info, cursorScope.client, stmtOptions, cacheContext, tempTables, variables),
|
|
7345
|
+
remaining,
|
|
7346
|
+
cursorScope.closeActive
|
|
7050
7347
|
);
|
|
7051
7348
|
if (outcome.result) {
|
|
7052
7349
|
outcome.result = attachSearchAbortWarning(outcome.result, searchAbortCollector);
|
|
@@ -7204,19 +7501,47 @@ async function runSelectLike(query, client, options, cacheContext, tempTables) {
|
|
|
7204
7501
|
}
|
|
7205
7502
|
return executeQueryWithCte(query, client, options, tempTables, cacheContext, true);
|
|
7206
7503
|
}
|
|
7207
|
-
async function runWithDeadline(work, remainingMs) {
|
|
7504
|
+
async function runWithDeadline(work, remainingMs, onTimeout) {
|
|
7208
7505
|
if (remainingMs === null) return work;
|
|
7209
7506
|
if (remainingMs <= 0) {
|
|
7507
|
+
if (onTimeout) await onTimeout();
|
|
7210
7508
|
void work.catch(() => {
|
|
7211
7509
|
});
|
|
7212
7510
|
throw new BatchTimeoutError();
|
|
7213
7511
|
}
|
|
7214
7512
|
let timer;
|
|
7513
|
+
let timedOut = false;
|
|
7514
|
+
const guardedWork = work.then(
|
|
7515
|
+
(value) => timedOut ? new Promise(() => void 0) : value,
|
|
7516
|
+
(error) => {
|
|
7517
|
+
if (timedOut) return new Promise(() => void 0);
|
|
7518
|
+
throw error;
|
|
7519
|
+
}
|
|
7520
|
+
);
|
|
7215
7521
|
try {
|
|
7216
7522
|
return await Promise.race([
|
|
7217
|
-
|
|
7523
|
+
guardedWork,
|
|
7218
7524
|
new Promise((_, reject) => {
|
|
7219
|
-
timer = setTimeout(() =>
|
|
7525
|
+
timer = setTimeout(() => {
|
|
7526
|
+
timedOut = true;
|
|
7527
|
+
void (async () => {
|
|
7528
|
+
if (onTimeout) {
|
|
7529
|
+
let cleanupTimer;
|
|
7530
|
+
try {
|
|
7531
|
+
await Promise.race([
|
|
7532
|
+
onTimeout(),
|
|
7533
|
+
new Promise((resolve2) => {
|
|
7534
|
+
cleanupTimer = setTimeout(resolve2, 5e3);
|
|
7535
|
+
cleanupTimer.unref?.();
|
|
7536
|
+
})
|
|
7537
|
+
]);
|
|
7538
|
+
} finally {
|
|
7539
|
+
if (cleanupTimer) clearTimeout(cleanupTimer);
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7542
|
+
reject(new BatchTimeoutError());
|
|
7543
|
+
})();
|
|
7544
|
+
}, remainingMs);
|
|
7220
7545
|
})
|
|
7221
7546
|
]);
|
|
7222
7547
|
} catch (e) {
|
|
@@ -7576,7 +7901,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
7576
7901
|
const staticMode = resolveSelectMode(stmt);
|
|
7577
7902
|
const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
|
|
7578
7903
|
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
7579
|
-
const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ?
|
|
7904
|
+
const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
7580
7905
|
stmt,
|
|
7581
7906
|
staticMode: mode,
|
|
7582
7907
|
whereCapability: whereCapability.capability,
|
|
@@ -7590,7 +7915,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
7590
7915
|
client,
|
|
7591
7916
|
cacheContext
|
|
7592
7917
|
);
|
|
7593
|
-
const completeInputRequired = orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" ? requiresCompleteInput({ ...stmt, orderBy: [] }) : requiresCompleteInput(stmt);
|
|
7918
|
+
const completeInputRequired = orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR" ? requiresCompleteInput({ ...stmt, orderBy: [] }) : requiresCompleteInput(stmt);
|
|
7594
7919
|
const truncateWasDisabled = completeInputRequired && options.onLimitReached === "truncate";
|
|
7595
7920
|
const effectiveOptions = truncateWasDisabled ? { ...options, onLimitReached: "error" } : options;
|
|
7596
7921
|
try {
|
|
@@ -7691,12 +8016,23 @@ async function executeSimpleSelect(stmt, client, options, cacheContext, orderPla
|
|
|
7691
8016
|
const warnings = /* @__PURE__ */ new Set();
|
|
7692
8017
|
const onLimit = options.onLimitReached ?? "error";
|
|
7693
8018
|
const parallel = options.fetchParallel ?? 1;
|
|
7694
|
-
const useRestWindow = stmt.orderBy.length > 0 ? orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" : stmt.limit !== null && stmt.limit <= 500;
|
|
8019
|
+
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;
|
|
7695
8020
|
const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
|
|
7696
8021
|
const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords && !whereHasKlike(stmt.where) ? needed : void 0;
|
|
7697
8022
|
let records;
|
|
7698
|
-
if (orderPlan?.kind === "KORDER_NATIVE" && stmt.limit === 0) {
|
|
8023
|
+
if ((orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR") && stmt.limit === 0) {
|
|
7699
8024
|
records = [];
|
|
8025
|
+
} else if (orderPlan?.kind === "KORDER_CURSOR") {
|
|
8026
|
+
const cursorResult = await executeKorderCursor({
|
|
8027
|
+
client,
|
|
8028
|
+
app: params.app,
|
|
8029
|
+
fields: params.fields,
|
|
8030
|
+
query: buildKorderCursorQuery(stmt),
|
|
8031
|
+
offset: stmt.offset ?? 0,
|
|
8032
|
+
limit: stmt.limit
|
|
8033
|
+
});
|
|
8034
|
+
records = cursorResult.records;
|
|
8035
|
+
if (cursorResult.cleanupWarning) warnings.add(cursorResult.cleanupWarning);
|
|
7700
8036
|
} else if (useRestWindow) {
|
|
7701
8037
|
const res = await client.getRecords({
|
|
7702
8038
|
app: params.app,
|
|
@@ -8083,6 +8419,7 @@ function systemColumnMeta(field) {
|
|
|
8083
8419
|
}
|
|
8084
8420
|
var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
8085
8421
|
"LENGTH",
|
|
8422
|
+
"LENGTH_CHAR",
|
|
8086
8423
|
"INSTR",
|
|
8087
8424
|
"ROUND",
|
|
8088
8425
|
"FLOOR",
|
|
@@ -8488,7 +8825,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
8488
8825
|
}
|
|
8489
8826
|
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
8490
8827
|
if (hasCanonicalOrder(stmt)) {
|
|
8491
|
-
(stmt.orderMode === "KINTONE_NATIVE" ?
|
|
8828
|
+
(stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
8492
8829
|
stmt,
|
|
8493
8830
|
staticMode: "FULL_SCAN",
|
|
8494
8831
|
whereCapability: whereCapability.capability,
|
|
@@ -9050,6 +9387,28 @@ var NON_WRITABLE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
9050
9387
|
"CATEGORY",
|
|
9051
9388
|
"REFERENCE_TABLE"
|
|
9052
9389
|
]);
|
|
9390
|
+
function assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos) {
|
|
9391
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
9392
|
+
for (const code of targetFields) {
|
|
9393
|
+
const info = infoByCode.get(code);
|
|
9394
|
+
if (!info) {
|
|
9395
|
+
throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
|
|
9396
|
+
}
|
|
9397
|
+
if (info.inSubtable) {
|
|
9398
|
+
throw new Error(
|
|
9399
|
+
`ArgumentError: DML target field ${code} is inside a subtable. Use subtable DML syntax (for example, APP${appId}$\u30C6\u30FC\u30D6\u30EB).`
|
|
9400
|
+
);
|
|
9401
|
+
}
|
|
9402
|
+
if (info.writable === false || NON_WRITABLE_FIELD_TYPES.has(info.fieldType)) {
|
|
9403
|
+
throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
|
|
9404
|
+
}
|
|
9405
|
+
}
|
|
9406
|
+
}
|
|
9407
|
+
async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheContext) {
|
|
9408
|
+
const fieldInfos = await getFieldsCached(appId, client, cacheContext);
|
|
9409
|
+
assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
|
|
9410
|
+
return fieldInfos;
|
|
9411
|
+
}
|
|
9053
9412
|
async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
9054
9413
|
return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
|
|
9055
9414
|
}
|
|
@@ -9061,24 +9420,22 @@ var RejectLimitExceededError = class extends Error {
|
|
|
9061
9420
|
}
|
|
9062
9421
|
};
|
|
9063
9422
|
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
9064
|
-
if (stmt.type === "UPDATE") {
|
|
9065
|
-
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9066
|
-
}
|
|
9067
9423
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
9068
9424
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
9069
9425
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
9070
9426
|
throw new Error("ArgumentError: DML target fields contain duplicates.");
|
|
9071
9427
|
}
|
|
9072
|
-
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
9073
|
-
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
9074
9428
|
const targetFields = stmt.type === "UPDATE" ? stmt.assignments.map((a) => a.field) : stmt.fields;
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9429
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(
|
|
9430
|
+
stmt.appId,
|
|
9431
|
+
targetFields,
|
|
9432
|
+
client,
|
|
9433
|
+
cacheContext
|
|
9434
|
+
);
|
|
9435
|
+
if (stmt.type === "UPDATE") {
|
|
9436
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9081
9437
|
}
|
|
9438
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
9082
9439
|
const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
|
|
9083
9440
|
const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
|
|
9084
9441
|
candidates,
|
|
@@ -9247,7 +9604,7 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
|
|
|
9247
9604
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
9248
9605
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
9249
9606
|
let records;
|
|
9250
|
-
if (
|
|
9607
|
+
if (hasRowDependentAssignment(stmt)) {
|
|
9251
9608
|
const getParams = updateToGetQueryForArith(stmt);
|
|
9252
9609
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [...getParams.fields], {
|
|
9253
9610
|
maxRecords: options.maxRecords ?? 1e4,
|
|
@@ -9456,6 +9813,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
|
|
|
9456
9813
|
if (stmt.subtableCode) {
|
|
9457
9814
|
return executeInsertSubtable(stmt, client, options, cacheContext);
|
|
9458
9815
|
}
|
|
9816
|
+
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
9459
9817
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
9460
9818
|
const batches = insertToPostBatches(stmt, fieldTypes);
|
|
9461
9819
|
const createdIds = [];
|
|
@@ -9470,6 +9828,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
|
|
|
9470
9828
|
};
|
|
9471
9829
|
}
|
|
9472
9830
|
async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
9831
|
+
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
9473
9832
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
9474
9833
|
const { rows, columns } = selectResult;
|
|
9475
9834
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -9504,17 +9863,24 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
9504
9863
|
};
|
|
9505
9864
|
}
|
|
9506
9865
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
9507
|
-
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9508
9866
|
if (stmt.subtableCode) {
|
|
9867
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9509
9868
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
9510
9869
|
}
|
|
9870
|
+
await loadWritableTopLevelDmlFields(
|
|
9871
|
+
stmt.appId,
|
|
9872
|
+
stmt.assignments.map((assignment) => assignment.field),
|
|
9873
|
+
client,
|
|
9874
|
+
cacheContext
|
|
9875
|
+
);
|
|
9876
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9511
9877
|
if (stmt.from != null) {
|
|
9512
9878
|
return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
9513
9879
|
}
|
|
9514
9880
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
9515
9881
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
9516
9882
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
9517
|
-
if (
|
|
9883
|
+
if (hasRowDependentAssignment(stmt)) {
|
|
9518
9884
|
const getParams2 = updateToGetQueryForArith(stmt);
|
|
9519
9885
|
const resolved2 = await fetchRecordsForSharedPlan(
|
|
9520
9886
|
client.getRecords,
|
|
@@ -9608,6 +9974,7 @@ async function executeDelete(stmt, client, options, cacheContext) {
|
|
|
9608
9974
|
return { type: "DELETE", deletedCount: ids.length };
|
|
9609
9975
|
}
|
|
9610
9976
|
async function executeUpsert(stmt, client, options, cacheContext) {
|
|
9977
|
+
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
9611
9978
|
const toInsert = [];
|
|
9612
9979
|
const toUpdate = [];
|
|
9613
9980
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
@@ -10034,6 +10401,7 @@ function evalOrderKeyForRow(key, row) {
|
|
|
10034
10401
|
}
|
|
10035
10402
|
}
|
|
10036
10403
|
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
10404
|
+
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10037
10405
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
10038
10406
|
const { rows, columns } = selectResult;
|
|
10039
10407
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -10265,7 +10633,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
10265
10633
|
const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
|
|
10266
10634
|
if (hasCanonicalOrder(select) && !hasUnmaterializedSource) {
|
|
10267
10635
|
const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
|
|
10268
|
-
orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ?
|
|
10636
|
+
orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
10269
10637
|
stmt: select,
|
|
10270
10638
|
staticMode: mode,
|
|
10271
10639
|
whereCapability: capability.capability,
|
|
@@ -10295,7 +10663,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
10295
10663
|
capabilities.set(inlined, capability);
|
|
10296
10664
|
if (hasCanonicalOrder(inlined)) {
|
|
10297
10665
|
const meta = await buildOrderByMetaForSelect(inlined, tracedClient, cacheContext);
|
|
10298
|
-
orderPlans.set(inlined, (inlined.orderMode === "KINTONE_NATIVE" ?
|
|
10666
|
+
orderPlans.set(inlined, (inlined.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
10299
10667
|
stmt: inlined,
|
|
10300
10668
|
staticMode: capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(inlined) : "FULL_SCAN",
|
|
10301
10669
|
whereCapability: capability.capability,
|
|
@@ -10313,7 +10681,7 @@ function explainMetadataLines(analysis) {
|
|
|
10313
10681
|
...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`)
|
|
10314
10682
|
];
|
|
10315
10683
|
}
|
|
10316
|
-
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4) {
|
|
10684
|
+
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2) {
|
|
10317
10685
|
const statements = parseSqlBatch(sql);
|
|
10318
10686
|
const analysis = analyzeBatch(statements);
|
|
10319
10687
|
validateDeclaredBatchVariables(statements, injectedVariables);
|
|
@@ -10324,12 +10692,12 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
10324
10692
|
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
|
|
10325
10693
|
validateKlikeStatement(planStmt);
|
|
10326
10694
|
const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords);
|
|
10327
|
-
const statementPlan = buildBatchStatementPlan(
|
|
10695
|
+
const statementPlan = addCursorConcurrency(buildBatchStatementPlan(
|
|
10328
10696
|
planStmt,
|
|
10329
10697
|
analysis.statements[i],
|
|
10330
10698
|
whereAnalysis.capabilities,
|
|
10331
10699
|
whereAnalysis.orderPlans
|
|
10332
|
-
);
|
|
10700
|
+
), cursorMaxActive);
|
|
10333
10701
|
const metadataPlan = explainMetadataLines(whereAnalysis);
|
|
10334
10702
|
plans.push({
|
|
10335
10703
|
index: i,
|
|
@@ -10435,11 +10803,14 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
|
|
|
10435
10803
|
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");
|
|
10436
10804
|
return lines;
|
|
10437
10805
|
}
|
|
10438
|
-
async function executeExplain(stmt, client, cacheContext, maxRecords) {
|
|
10806
|
+
async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxActive) {
|
|
10439
10807
|
const analysis = await buildExplainWhereAnalysis(stmt.query, client, cacheContext, maxRecords);
|
|
10440
10808
|
const lines = [
|
|
10441
10809
|
...explainMetadataLines(analysis),
|
|
10442
|
-
...
|
|
10810
|
+
...addCursorConcurrency(
|
|
10811
|
+
buildExplainPlan(stmt.query, void 0, analysis.capabilities, analysis.orderPlans),
|
|
10812
|
+
cursorMaxActive
|
|
10813
|
+
)
|
|
10443
10814
|
];
|
|
10444
10815
|
return {
|
|
10445
10816
|
type: "SELECT",
|
|
@@ -10448,6 +10819,17 @@ async function executeExplain(stmt, client, cacheContext, maxRecords) {
|
|
|
10448
10819
|
rowCount: lines.length
|
|
10449
10820
|
};
|
|
10450
10821
|
}
|
|
10822
|
+
function addCursorConcurrency(lines, cursorMaxActive) {
|
|
10823
|
+
const result = [];
|
|
10824
|
+
for (const line of lines) {
|
|
10825
|
+
result.push(line);
|
|
10826
|
+
if (line.trim() === "cursor page size: 500") {
|
|
10827
|
+
const indent = line.match(/^\s*/)?.[0] ?? "";
|
|
10828
|
+
result.push(`${indent}cursor concurrency: ${cursorMaxActive} per domain (process-local)`);
|
|
10829
|
+
}
|
|
10830
|
+
}
|
|
10831
|
+
return result;
|
|
10832
|
+
}
|
|
10451
10833
|
function buildExplainPlan(query, label, capabilities, orderPlans) {
|
|
10452
10834
|
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
|
|
10453
10835
|
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
|
|
@@ -10477,6 +10859,11 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
10477
10859
|
if (orderPlan.kind === "KORDER_NATIVE") {
|
|
10478
10860
|
lines.push(" order semantics: kintone native (not kSQL canonical)");
|
|
10479
10861
|
lines.push(" REST execution: single GET");
|
|
10862
|
+
} else if (orderPlan.kind === "KORDER_CURSOR") {
|
|
10863
|
+
lines.push(" order semantics: kintone native (not kSQL canonical)");
|
|
10864
|
+
lines.push(" fetch API: POST/GET/DELETE records/cursor.json");
|
|
10865
|
+
lines.push(" cursor page size: 500");
|
|
10866
|
+
lines.push(` scan rows: ${orderPlan.scanRows}`);
|
|
10480
10867
|
}
|
|
10481
10868
|
}
|
|
10482
10869
|
if (orderPlan?.requiresCompleteInput ?? requiresCompleteInput(stmt)) {
|
|
@@ -10488,7 +10875,8 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
10488
10875
|
if (mode === "SIMPLE") {
|
|
10489
10876
|
const params = selectToKintoneParams(orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt);
|
|
10490
10877
|
lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
|
|
10491
|
-
|
|
10878
|
+
const displayedQuery = orderPlan?.kind === "KORDER_CURSOR" ? buildKorderCursorQuery(stmt) : params.query;
|
|
10879
|
+
lines.push(` kintone query: ${displayedQuery || "(\u306A\u3057)"}`);
|
|
10492
10880
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
10493
10881
|
} else {
|
|
10494
10882
|
const pushdownPlan = buildKlikePushdownPlan(stmt);
|
|
@@ -10649,6 +11037,8 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
10649
11037
|
}
|
|
10650
11038
|
function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
10651
11039
|
const isArith = hasArithAssignment(stmt);
|
|
11040
|
+
const isStringFunc = stmt.assignments.some((a) => a.value.type === "STRING_FUNC");
|
|
11041
|
+
const isRowDependent = hasRowDependentAssignment(stmt);
|
|
10652
11042
|
const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
|
|
10653
11043
|
const lines = [];
|
|
10654
11044
|
if (label) lines.push(label);
|
|
@@ -10665,10 +11055,11 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
|
10665
11055
|
lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
|
|
10666
11056
|
const setTypes = [];
|
|
10667
11057
|
if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
|
|
11058
|
+
if (isStringFunc) setTypes.push("\u6587\u5B57\u5217\u95A2\u6570 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A55\u4FA1\uFF09");
|
|
10668
11059
|
if (isSubq) setTypes.push("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA SET");
|
|
10669
|
-
if (!
|
|
11060
|
+
if (!isRowDependent && !isSubq) setTypes.push("\u5358\u7D14 SET");
|
|
10670
11061
|
lines.push(` set type: ${setTypes.join(", ")}`);
|
|
10671
|
-
if (
|
|
11062
|
+
if (isRowDependent) {
|
|
10672
11063
|
const refFields = collectArithRefFields(stmt);
|
|
10673
11064
|
if (refFields.length > 0) {
|
|
10674
11065
|
lines.push(` ref fields: ${refFields.join(", ")}\uFF08GET \u306B\u542B\u3081\u308B\uFF09`);
|
|
@@ -10754,6 +11145,7 @@ function collectArithRefFields(stmt) {
|
|
|
10754
11145
|
const refs = /* @__PURE__ */ new Set();
|
|
10755
11146
|
for (const { value } of stmt.assignments) {
|
|
10756
11147
|
if (value.type === "ARITH") collectArithNodeRefs(value, refs);
|
|
11148
|
+
if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
|
|
10757
11149
|
}
|
|
10758
11150
|
return [...refs];
|
|
10759
11151
|
}
|
|
@@ -10766,6 +11158,13 @@ function collectArithNodeRefs(node, out) {
|
|
|
10766
11158
|
collectArithNodeRefs(node.left, out);
|
|
10767
11159
|
collectArithNodeRefs(node.right, out);
|
|
10768
11160
|
}
|
|
11161
|
+
if (node.type === "STRING_FUNC") {
|
|
11162
|
+
for (const arg of node.args) {
|
|
11163
|
+
if (arg.type !== "STRING" && arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") {
|
|
11164
|
+
collectArithNodeRefs(arg, out);
|
|
11165
|
+
}
|
|
11166
|
+
}
|
|
11167
|
+
}
|
|
10769
11168
|
}
|
|
10770
11169
|
function formatAssignment(a) {
|
|
10771
11170
|
const v = a.value;
|
|
@@ -10773,6 +11172,7 @@ function formatAssignment(a) {
|
|
|
10773
11172
|
if (v.type === "NUMBER") return `${a.field} = ${v.value}`;
|
|
10774
11173
|
if (v.type === "ARITH") return `${a.field} = ${formatArithExprStr(v)}`;
|
|
10775
11174
|
if (v.type === "CASE_VALUE") return `${a.field} = CASE WHEN ...`;
|
|
11175
|
+
if (v.type === "STRING_FUNC") return `${a.field} = ${v.func}(...)`;
|
|
10776
11176
|
if (v.type === "SCALAR_SUBQUERY") return `${a.field} = (SELECT ...)`;
|
|
10777
11177
|
if (v.type === "SOURCE_FIELD") return `${a.field} = ${v.alias}.${v.field}`;
|
|
10778
11178
|
return `${a.field} = (${v.type})`;
|
|
@@ -11056,6 +11456,12 @@ function validateKsqlConfig(config) {
|
|
|
11056
11456
|
}
|
|
11057
11457
|
const logicalApps = normalizeLogicalApps(profileName, profile.logicalApps);
|
|
11058
11458
|
if (logicalApps !== void 0) profile.logicalApps = logicalApps;
|
|
11459
|
+
if (profile.query?.cursorMaxActive !== void 0) {
|
|
11460
|
+
const value = profile.query.cursorMaxActive;
|
|
11461
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 5) {
|
|
11462
|
+
throw argumentError(`query.cursorMaxActive for profile "${profileName}" must be an integer from 1 to 5.`);
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11059
11465
|
}
|
|
11060
11466
|
return config;
|
|
11061
11467
|
}
|
|
@@ -11200,6 +11606,10 @@ var RequestGate = class {
|
|
|
11200
11606
|
async runMutation(fn) {
|
|
11201
11607
|
return this.withSlot(fn);
|
|
11202
11608
|
}
|
|
11609
|
+
/** Cursor Create/Get/Delete: セマフォのみ。GETでも位置を進めるため再試行しない。 */
|
|
11610
|
+
async runCursorStep(fn) {
|
|
11611
|
+
return this.withSlot(fn);
|
|
11612
|
+
}
|
|
11203
11613
|
async withSlot(fn) {
|
|
11204
11614
|
await this.acquire();
|
|
11205
11615
|
try {
|
|
@@ -11231,6 +11641,14 @@ var RequestGate = class {
|
|
|
11231
11641
|
function withRequestGate(client, gate) {
|
|
11232
11642
|
return {
|
|
11233
11643
|
getRecords: (params) => gate.runReadOnly(() => client.getRecords(params)),
|
|
11644
|
+
openCursor: async (params) => {
|
|
11645
|
+
const handle = await gate.runCursorStep(() => client.openCursor(params));
|
|
11646
|
+
return {
|
|
11647
|
+
totalCount: handle.totalCount,
|
|
11648
|
+
nextPage: () => gate.runCursorStep(() => handle.nextPage()),
|
|
11649
|
+
close: () => gate.runCursorStep(() => handle.close())
|
|
11650
|
+
};
|
|
11651
|
+
},
|
|
11234
11652
|
getApps: () => gate.runReadOnly(() => client.getApps()),
|
|
11235
11653
|
getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
|
|
11236
11654
|
getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
|
|
@@ -11345,7 +11763,218 @@ function normalizeProcessStatusStates(states) {
|
|
|
11345
11763
|
});
|
|
11346
11764
|
}
|
|
11347
11765
|
|
|
11766
|
+
// src/api/kintoneCursor.ts
|
|
11767
|
+
function isAlreadyReleasedCursorError(error) {
|
|
11768
|
+
const shaped = error;
|
|
11769
|
+
return shaped?.status === 404 && shaped.code === "GAIA_CN01";
|
|
11770
|
+
}
|
|
11771
|
+
async function deleteCursorWithConfirmation(deleteCursor, sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)), isAlreadyReleased = isAlreadyReleasedCursorError) {
|
|
11772
|
+
try {
|
|
11773
|
+
await deleteCursor();
|
|
11774
|
+
return;
|
|
11775
|
+
} catch (firstError) {
|
|
11776
|
+
if (isAlreadyReleased(firstError)) return;
|
|
11777
|
+
}
|
|
11778
|
+
await sleep(250);
|
|
11779
|
+
try {
|
|
11780
|
+
await deleteCursor();
|
|
11781
|
+
} catch (confirmationError) {
|
|
11782
|
+
if (isAlreadyReleased(confirmationError)) return;
|
|
11783
|
+
throw confirmationError;
|
|
11784
|
+
}
|
|
11785
|
+
}
|
|
11786
|
+
async function withTimeout(promise, timeoutMs) {
|
|
11787
|
+
let timer;
|
|
11788
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
11789
|
+
timer = setTimeout(() => reject(new Error(`CursorCleanupTimeoutError: cleanup exceeded ${timeoutMs}ms.`)), timeoutMs);
|
|
11790
|
+
timer.unref?.();
|
|
11791
|
+
});
|
|
11792
|
+
try {
|
|
11793
|
+
return await Promise.race([promise, timeout]);
|
|
11794
|
+
} finally {
|
|
11795
|
+
if (timer) clearTimeout(timer);
|
|
11796
|
+
}
|
|
11797
|
+
}
|
|
11798
|
+
function createKintoneCursorHandle(totalCount, operations) {
|
|
11799
|
+
let released = false;
|
|
11800
|
+
let closing = false;
|
|
11801
|
+
let pageTail = Promise.resolve();
|
|
11802
|
+
let closePromise = null;
|
|
11803
|
+
const nextPage = () => {
|
|
11804
|
+
if (closing || released) return Promise.resolve({ records: [], next: false });
|
|
11805
|
+
const result = pageTail.then(async () => {
|
|
11806
|
+
if (closing || released) return { records: [], next: false };
|
|
11807
|
+
const page = await operations.get();
|
|
11808
|
+
if (!page.next) {
|
|
11809
|
+
released = true;
|
|
11810
|
+
operations.onReleased?.();
|
|
11811
|
+
}
|
|
11812
|
+
return page;
|
|
11813
|
+
});
|
|
11814
|
+
pageTail = result.then(() => void 0, () => void 0);
|
|
11815
|
+
return result;
|
|
11816
|
+
};
|
|
11817
|
+
const close = () => {
|
|
11818
|
+
if (released) return Promise.resolve();
|
|
11819
|
+
if (closePromise) return closePromise;
|
|
11820
|
+
closing = true;
|
|
11821
|
+
closePromise = pageTail.then(async () => {
|
|
11822
|
+
if (released) return;
|
|
11823
|
+
try {
|
|
11824
|
+
await withTimeout(
|
|
11825
|
+
deleteCursorWithConfirmation(
|
|
11826
|
+
operations.delete,
|
|
11827
|
+
operations.sleep,
|
|
11828
|
+
operations.isAlreadyReleasedError
|
|
11829
|
+
),
|
|
11830
|
+
operations.cleanupTimeoutMs ?? 5e3
|
|
11831
|
+
);
|
|
11832
|
+
released = true;
|
|
11833
|
+
operations.onReleased?.();
|
|
11834
|
+
} catch (error) {
|
|
11835
|
+
operations.onReleaseUnknown?.();
|
|
11836
|
+
throw error;
|
|
11837
|
+
}
|
|
11838
|
+
});
|
|
11839
|
+
return closePromise;
|
|
11840
|
+
};
|
|
11841
|
+
return { totalCount, nextPage, close };
|
|
11842
|
+
}
|
|
11843
|
+
|
|
11844
|
+
// src/api/cursorLeaseManager.ts
|
|
11845
|
+
var DEFAULT_MAX_ACTIVE = 2;
|
|
11846
|
+
var MAX_ACTIVE = 5;
|
|
11847
|
+
var DEFAULT_WAIT_MS = 3e4;
|
|
11848
|
+
var DEFAULT_QUARANTINE_MS = 10 * 6e4 + 3e4;
|
|
11849
|
+
var CursorLeaseManager = class {
|
|
11850
|
+
constructor(host, options = {}) {
|
|
11851
|
+
this.host = host;
|
|
11852
|
+
this.active = 0;
|
|
11853
|
+
this.peak = 0;
|
|
11854
|
+
this.quarantined = 0;
|
|
11855
|
+
this.waiters = [];
|
|
11856
|
+
this.createTail = Promise.resolve();
|
|
11857
|
+
const maxActive = options.maxActive ?? DEFAULT_MAX_ACTIVE;
|
|
11858
|
+
if (!Number.isSafeInteger(maxActive) || maxActive < 1 || maxActive > MAX_ACTIVE) {
|
|
11859
|
+
throw new Error(`ArgumentError: cursorMaxActive must be an integer from 1 to ${MAX_ACTIVE}.`);
|
|
11860
|
+
}
|
|
11861
|
+
this.maxActive = maxActive;
|
|
11862
|
+
this.waitTimeoutMs = options.waitTimeoutMs ?? DEFAULT_WAIT_MS;
|
|
11863
|
+
this.quarantineMs = options.quarantineMs ?? DEFAULT_QUARANTINE_MS;
|
|
11864
|
+
}
|
|
11865
|
+
acquire() {
|
|
11866
|
+
if (this.active < this.maxActive) {
|
|
11867
|
+
this.active += 1;
|
|
11868
|
+
this.peak = Math.max(this.peak, this.active);
|
|
11869
|
+
return Promise.resolve(this.makeLease());
|
|
11870
|
+
}
|
|
11871
|
+
return new Promise((resolve2, reject) => {
|
|
11872
|
+
const waiter = {};
|
|
11873
|
+
waiter.resolve = resolve2;
|
|
11874
|
+
waiter.reject = reject;
|
|
11875
|
+
waiter.timer = setTimeout(() => {
|
|
11876
|
+
const index = this.waiters.indexOf(waiter);
|
|
11877
|
+
if (index >= 0) this.waiters.splice(index, 1);
|
|
11878
|
+
reject(new CursorCapacityError(this.host, this.maxActive, this.waitTimeoutMs));
|
|
11879
|
+
}, this.waitTimeoutMs);
|
|
11880
|
+
waiter.timer.unref?.();
|
|
11881
|
+
this.waiters.push(waiter);
|
|
11882
|
+
});
|
|
11883
|
+
}
|
|
11884
|
+
/**
|
|
11885
|
+
* 同一hostを共有する後続surfaceの設定を反映する。
|
|
11886
|
+
* 縮小時は既存leaseを強制終了せず、activeが新上限を下回るまで新規取得だけを止める。
|
|
11887
|
+
*/
|
|
11888
|
+
setMaxActive(maxActive) {
|
|
11889
|
+
this.validateMaxActive(maxActive);
|
|
11890
|
+
if (this.maxActive === maxActive) return;
|
|
11891
|
+
this.maxActive = maxActive;
|
|
11892
|
+
this.dispatchWaiters();
|
|
11893
|
+
}
|
|
11894
|
+
async runCreate(fn) {
|
|
11895
|
+
const previous = this.createTail;
|
|
11896
|
+
let unlock;
|
|
11897
|
+
this.createTail = new Promise((resolve2) => {
|
|
11898
|
+
unlock = resolve2;
|
|
11899
|
+
});
|
|
11900
|
+
await previous;
|
|
11901
|
+
try {
|
|
11902
|
+
return await fn();
|
|
11903
|
+
} finally {
|
|
11904
|
+
unlock();
|
|
11905
|
+
}
|
|
11906
|
+
}
|
|
11907
|
+
snapshot() {
|
|
11908
|
+
return {
|
|
11909
|
+
active: this.active,
|
|
11910
|
+
peak: this.peak,
|
|
11911
|
+
quarantined: this.quarantined,
|
|
11912
|
+
waiting: this.waiters.length,
|
|
11913
|
+
limit: this.maxActive
|
|
11914
|
+
};
|
|
11915
|
+
}
|
|
11916
|
+
makeLease() {
|
|
11917
|
+
let done = false;
|
|
11918
|
+
return {
|
|
11919
|
+
release: () => {
|
|
11920
|
+
if (done) return;
|
|
11921
|
+
done = true;
|
|
11922
|
+
this.returnPermit();
|
|
11923
|
+
},
|
|
11924
|
+
quarantine: (durationMs = this.quarantineMs) => {
|
|
11925
|
+
if (done) return;
|
|
11926
|
+
done = true;
|
|
11927
|
+
this.quarantined += 1;
|
|
11928
|
+
const timer = setTimeout(() => {
|
|
11929
|
+
this.quarantined -= 1;
|
|
11930
|
+
this.returnPermit();
|
|
11931
|
+
}, durationMs);
|
|
11932
|
+
timer.unref?.();
|
|
11933
|
+
}
|
|
11934
|
+
};
|
|
11935
|
+
}
|
|
11936
|
+
returnPermit() {
|
|
11937
|
+
this.active -= 1;
|
|
11938
|
+
this.dispatchWaiters();
|
|
11939
|
+
}
|
|
11940
|
+
dispatchWaiters() {
|
|
11941
|
+
while (this.active < this.maxActive) {
|
|
11942
|
+
const waiter = this.waiters.shift();
|
|
11943
|
+
if (!waiter) return;
|
|
11944
|
+
clearTimeout(waiter.timer);
|
|
11945
|
+
this.active += 1;
|
|
11946
|
+
this.peak = Math.max(this.peak, this.active);
|
|
11947
|
+
waiter.resolve(this.makeLease());
|
|
11948
|
+
}
|
|
11949
|
+
}
|
|
11950
|
+
validateMaxActive(maxActive) {
|
|
11951
|
+
if (!Number.isSafeInteger(maxActive) || maxActive < 1 || maxActive > MAX_ACTIVE) {
|
|
11952
|
+
throw new Error(`ArgumentError: cursorMaxActive must be an integer from 1 to ${MAX_ACTIVE}.`);
|
|
11953
|
+
}
|
|
11954
|
+
}
|
|
11955
|
+
};
|
|
11956
|
+
var managers = /* @__PURE__ */ new Map();
|
|
11957
|
+
function getCursorLeaseManager(host, maxActive = DEFAULT_MAX_ACTIVE) {
|
|
11958
|
+
const key = host.toLowerCase();
|
|
11959
|
+
let manager = managers.get(key);
|
|
11960
|
+
if (!manager) {
|
|
11961
|
+
manager = new CursorLeaseManager(key, { maxActive });
|
|
11962
|
+
managers.set(key, manager);
|
|
11963
|
+
} else {
|
|
11964
|
+
manager.setMaxActive(maxActive);
|
|
11965
|
+
}
|
|
11966
|
+
return manager;
|
|
11967
|
+
}
|
|
11968
|
+
|
|
11348
11969
|
// src/cli/nodeKintoneClient.ts
|
|
11970
|
+
var KintoneApiError = class extends Error {
|
|
11971
|
+
constructor(status, code, bodyText) {
|
|
11972
|
+
super(`kintone API error ${status}: ${bodyText}`);
|
|
11973
|
+
this.status = status;
|
|
11974
|
+
this.code = code;
|
|
11975
|
+
this.name = "KintoneApiError";
|
|
11976
|
+
}
|
|
11977
|
+
};
|
|
11349
11978
|
var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
|
|
11350
11979
|
function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
11351
11980
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
@@ -11393,7 +12022,13 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
11393
12022
|
if (tokenResolver.debug) {
|
|
11394
12023
|
tokenResolver.log?.(`[debug] response status=${res.status} body=${bodyText}`);
|
|
11395
12024
|
}
|
|
11396
|
-
|
|
12025
|
+
let code;
|
|
12026
|
+
try {
|
|
12027
|
+
const body = JSON.parse(bodyText);
|
|
12028
|
+
if (typeof body.code === "string") code = body.code;
|
|
12029
|
+
} catch {
|
|
12030
|
+
}
|
|
12031
|
+
throw new KintoneApiError(res.status, code, bodyText);
|
|
11397
12032
|
}
|
|
11398
12033
|
if (tokenResolver.debug) {
|
|
11399
12034
|
tokenResolver.log?.(`[debug] response status=${res.status}`);
|
|
@@ -11460,6 +12095,48 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
11460
12095
|
return response.searchAborted ? { ...response.body, searchAborted: true } : response.body;
|
|
11461
12096
|
}
|
|
11462
12097
|
},
|
|
12098
|
+
async openCursor(params) {
|
|
12099
|
+
const manager = getCursorLeaseManager(new URL(normalizedBaseUrl).host, tokenResolver.cursorMaxActive);
|
|
12100
|
+
const lease = await manager.acquire();
|
|
12101
|
+
let created;
|
|
12102
|
+
try {
|
|
12103
|
+
created = await manager.runCreate(() => requestJson(
|
|
12104
|
+
`${apiBasePath}/records/cursor.json`,
|
|
12105
|
+
{
|
|
12106
|
+
method: "POST",
|
|
12107
|
+
body: JSON.stringify({
|
|
12108
|
+
app: params.app,
|
|
12109
|
+
query: params.query,
|
|
12110
|
+
size: params.size,
|
|
12111
|
+
fields: params.fields && params.fields.length > 0 ? params.fields : void 0
|
|
12112
|
+
})
|
|
12113
|
+
},
|
|
12114
|
+
params.app
|
|
12115
|
+
));
|
|
12116
|
+
} catch (error) {
|
|
12117
|
+
if (error instanceof KintoneApiError) {
|
|
12118
|
+
lease.release();
|
|
12119
|
+
throw error;
|
|
12120
|
+
}
|
|
12121
|
+
lease.quarantine();
|
|
12122
|
+
throw new CursorCreateOutcomeUnknownError(error);
|
|
12123
|
+
}
|
|
12124
|
+
const cursorId = created.id;
|
|
12125
|
+
return createKintoneCursorHandle(Number(created.totalCount), {
|
|
12126
|
+
get: () => requestJson(
|
|
12127
|
+
`${apiBasePath}/records/cursor.json?id=${encodeURIComponent(cursorId)}`,
|
|
12128
|
+
{ method: "GET" },
|
|
12129
|
+
params.app
|
|
12130
|
+
),
|
|
12131
|
+
delete: () => requestJson(
|
|
12132
|
+
`${apiBasePath}/records/cursor.json`,
|
|
12133
|
+
{ method: "DELETE", body: JSON.stringify({ id: cursorId }) },
|
|
12134
|
+
params.app
|
|
12135
|
+
),
|
|
12136
|
+
onReleased: () => lease.release(),
|
|
12137
|
+
onReleaseUnknown: () => lease.quarantine()
|
|
12138
|
+
});
|
|
12139
|
+
},
|
|
11463
12140
|
async postRecords(_params) {
|
|
11464
12141
|
const res = await requestJson(
|
|
11465
12142
|
`${apiBasePath}/records.json`,
|
|
@@ -12017,6 +12694,7 @@ Options:
|
|
|
12017
12694
|
--timeout <ms> Request timeout in milliseconds (default: 30000)
|
|
12018
12695
|
--max-concurrent <n> Max concurrent kintone requests: 1-50 (default: 10)
|
|
12019
12696
|
(process-wide; fixed at first resolution; KSQL_MAX_CONCURRENT wins)
|
|
12697
|
+
--cursor-max-active <n> Max active cursors per host: 1-5 (default: 2; KSQL_CURSOR_MAX_ACTIVE wins)
|
|
12020
12698
|
--retry <n> GET retry count: 0-10, 0 disables (default: 3; KSQL_RETRY wins)
|
|
12021
12699
|
--retry-base-delay <ms> GET retry backoff base delay (default: 500)
|
|
12022
12700
|
--retry-max-delay <ms> GET retry backoff max delay (default: 8000)
|
|
@@ -12097,6 +12775,7 @@ function parseArgs(argv) {
|
|
|
12097
12775
|
continueOnError: false,
|
|
12098
12776
|
dmlMaxRows: null,
|
|
12099
12777
|
maxConcurrent: null,
|
|
12778
|
+
cursorMaxActive: null,
|
|
12100
12779
|
retry: null,
|
|
12101
12780
|
retryBaseDelay: null,
|
|
12102
12781
|
retryMaxDelay: null,
|
|
@@ -12353,6 +13032,13 @@ function parseArgs(argv) {
|
|
|
12353
13032
|
i++;
|
|
12354
13033
|
continue;
|
|
12355
13034
|
}
|
|
13035
|
+
if (a === "--cursor-max-active") {
|
|
13036
|
+
const n = Number(v);
|
|
13037
|
+
if (!Number.isInteger(n) || n < 1 || n > 5) throw new Error("ArgumentError: --cursor-max-active must be an integer between 1 and 5.");
|
|
13038
|
+
out.cursorMaxActive = n;
|
|
13039
|
+
i++;
|
|
13040
|
+
continue;
|
|
13041
|
+
}
|
|
12356
13042
|
if (a === "--retry") {
|
|
12357
13043
|
const n = Number(v);
|
|
12358
13044
|
if (!Number.isInteger(n) || n < 0 || n > 10) throw new Error("ArgumentError: --retry must be an integer between 0 and 10 (0 disables retry).");
|
|
@@ -12648,6 +13334,7 @@ function createDryRunClient() {
|
|
|
12648
13334
|
};
|
|
12649
13335
|
return {
|
|
12650
13336
|
getRecords: notUsed,
|
|
13337
|
+
openCursor: notUsed,
|
|
12651
13338
|
postRecords: notUsed,
|
|
12652
13339
|
putRecords: notUsed,
|
|
12653
13340
|
deleteRecords: notUsed,
|
|
@@ -12790,6 +13477,7 @@ function buildReplExecArgv(base, sql, dryRun, format) {
|
|
|
12790
13477
|
pushOpt(argv, "--attachment-format", base.attachmentFormat);
|
|
12791
13478
|
pushOpt(argv, "--dml-max-rows", base.dmlMaxRows);
|
|
12792
13479
|
pushOpt(argv, "--max-concurrent", base.maxConcurrent);
|
|
13480
|
+
pushOpt(argv, "--cursor-max-active", base.cursorMaxActive);
|
|
12793
13481
|
pushOpt(argv, "--retry", base.retry);
|
|
12794
13482
|
pushOpt(argv, "--retry-base-delay", base.retryBaseDelay);
|
|
12795
13483
|
pushOpt(argv, "--retry-max-delay", base.retryMaxDelay);
|
|
@@ -13357,6 +14045,11 @@ async function run() {
|
|
|
13357
14045
|
const onLimit = args.onLimit ?? envOnLimit2("KSQL_ON_LIMIT") ?? profile.query?.onLimit ?? "error";
|
|
13358
14046
|
const timeout = args.timeout ?? envInt2("KSQL_TIMEOUT") ?? profile.query?.timeout ?? 3e4;
|
|
13359
14047
|
const tempTableMaxRows = args.tempTableMaxRows ?? envInt2("KSQL_TEMP_TABLE_MAX_ROWS") ?? profile.query?.tempTableMaxRows ?? void 0;
|
|
14048
|
+
const cursorMaxActive = args.cursorMaxActive ?? envInt2("KSQL_CURSOR_MAX_ACTIVE") ?? profile.query?.cursorMaxActive ?? 2;
|
|
14049
|
+
if (!Number.isSafeInteger(cursorMaxActive) || cursorMaxActive < 1 || cursorMaxActive > 5) {
|
|
14050
|
+
process.stderr.write("ArgumentError: cursorMaxActive must be an integer from 1 to 5.\n");
|
|
14051
|
+
return 2;
|
|
14052
|
+
}
|
|
13360
14053
|
if (!Number.isInteger(fetchParallel) || fetchParallel < 1 || fetchParallel > 10) {
|
|
13361
14054
|
process.stderr.write("ArgumentError: fetch-parallel must be an integer between 1 and 10.\n");
|
|
13362
14055
|
return 2;
|
|
@@ -13487,6 +14180,7 @@ async function run() {
|
|
|
13487
14180
|
}
|
|
13488
14181
|
profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
|
|
13489
14182
|
guestSpaceId,
|
|
14183
|
+
cursorMaxActive,
|
|
13490
14184
|
timeoutMs: timeout,
|
|
13491
14185
|
debug,
|
|
13492
14186
|
debugHeaders,
|
|
@@ -13519,6 +14213,7 @@ async function run() {
|
|
|
13519
14213
|
missingAppProfiles.push(...resolvedTokens.missing);
|
|
13520
14214
|
profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
|
|
13521
14215
|
guestSpaceId,
|
|
14216
|
+
cursorMaxActive,
|
|
13522
14217
|
timeoutMs: timeout,
|
|
13523
14218
|
debug,
|
|
13524
14219
|
debugHeaders,
|
|
@@ -13630,6 +14325,12 @@ async function run() {
|
|
|
13630
14325
|
if (!routed) throw new Error(`AuthError: profile "${pName}" is not resolved for APP${params.app}.`);
|
|
13631
14326
|
return routed.getRecords({ ...params, app: binding.appId });
|
|
13632
14327
|
},
|
|
14328
|
+
openCursor: (params) => {
|
|
14329
|
+
const binding = appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
|
|
14330
|
+
const routed = profileClientMap.get(binding.profile);
|
|
14331
|
+
if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
|
|
14332
|
+
return routed.openCursor({ ...params, app: binding.appId });
|
|
14333
|
+
},
|
|
13633
14334
|
postRecords: (params) => {
|
|
13634
14335
|
const binding = appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
|
|
13635
14336
|
const pName = binding.profile;
|
|
@@ -13678,7 +14379,14 @@ async function run() {
|
|
|
13678
14379
|
}
|
|
13679
14380
|
if (isBatchSql && args.dryRun) {
|
|
13680
14381
|
try {
|
|
13681
|
-
const plans = await buildBatchExplainPlans(
|
|
14382
|
+
const plans = await buildBatchExplainPlans(
|
|
14383
|
+
sql,
|
|
14384
|
+
client,
|
|
14385
|
+
args.variables,
|
|
14386
|
+
cacheContext,
|
|
14387
|
+
maxRecords,
|
|
14388
|
+
cursorMaxActive
|
|
14389
|
+
);
|
|
13682
14390
|
const out = [];
|
|
13683
14391
|
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|
|
13684
14392
|
restoredStatements.forEach((p) => {
|
|
@@ -13742,6 +14450,7 @@ query=${label}`);
|
|
|
13742
14450
|
continueOnError: args.continueOnError,
|
|
13743
14451
|
tempTableMaxRows,
|
|
13744
14452
|
timeoutMs: timeout,
|
|
14453
|
+
cursorMaxActive,
|
|
13745
14454
|
variables: args.variables,
|
|
13746
14455
|
confirm: batchContainsDml ? async (count, operation) => {
|
|
13747
14456
|
if (count > dmlMaxRows) {
|
|
@@ -13752,12 +14461,18 @@ query=${label}`);
|
|
|
13752
14461
|
});
|
|
13753
14462
|
return writeBatchOutput(batchResult, { format, noHeader, pretty, displayOptions, outputPath, quiet });
|
|
13754
14463
|
}
|
|
13755
|
-
let result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, {
|
|
14464
|
+
let result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, {
|
|
14465
|
+
maxRecords,
|
|
14466
|
+
onLimitReached: onLimit,
|
|
14467
|
+
cacheContext,
|
|
14468
|
+
cursorMaxActive
|
|
14469
|
+
}) : await execute(sql, client, {
|
|
13756
14470
|
maxRecords,
|
|
13757
14471
|
fetchParallel,
|
|
13758
14472
|
onLimitReached: effectiveOnLimit,
|
|
13759
14473
|
confirm: isDmlStatement ? confirm : void 0,
|
|
13760
|
-
cacheContext
|
|
14474
|
+
cacheContext,
|
|
14475
|
+
cursorMaxActive
|
|
13761
14476
|
});
|
|
13762
14477
|
if (args.dryRun && sqlDiagnosticContext) {
|
|
13763
14478
|
result = restoreSqlDiagnosticValue(result, sqlDiagnosticContext.appBindingByMappedApp);
|