@rex0220/kintone-sql-tools 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cli/ksql.js +328 -54
- package/dist-mcp/ksql-mcp.js +329 -55
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -183,6 +183,7 @@ var Lexer = class {
|
|
|
183
183
|
if (opTok) return opTok;
|
|
184
184
|
if (isIdentStart(ch)) return this.readIdentOrKeyword(start);
|
|
185
185
|
if (ch === "#") return this.readHashIdent(start);
|
|
186
|
+
if (ch === "@") return this.readVariable(start);
|
|
186
187
|
throw new LexError(
|
|
187
188
|
`\u4E88\u671F\u3057\u306A\u3044\u6587\u5B57 \u300C${ch}\u300D \u3067\u3059`,
|
|
188
189
|
this.pos,
|
|
@@ -362,6 +363,23 @@ var Lexer = class {
|
|
|
362
363
|
}
|
|
363
364
|
return this.makeToken("IDENT" /* IDENT */, value, start);
|
|
364
365
|
}
|
|
366
|
+
/** バッチ変数: @[A-Za-z_][A-Za-z0-9_]{0,63} */
|
|
367
|
+
readVariable(start) {
|
|
368
|
+
this.pos++;
|
|
369
|
+
const first = this.input[this.pos] ?? "";
|
|
370
|
+
if (!/[A-Za-z_]/.test(first)) {
|
|
371
|
+
throw new LexError("\u300C@\u300D \u306E\u76F4\u5F8C\u306B\u306F\u82F1\u5B57\u307E\u305F\u306F _ \u3067\u59CB\u307E\u308B\u5909\u6570\u540D\u304C\u5FC5\u8981\u3067\u3059", start, this.input);
|
|
372
|
+
}
|
|
373
|
+
this.pos++;
|
|
374
|
+
while (this.pos < this.input.length && /[A-Za-z0-9_]/.test(this.input[this.pos])) {
|
|
375
|
+
this.pos++;
|
|
376
|
+
}
|
|
377
|
+
const nameLength = this.pos - start - 1;
|
|
378
|
+
if (nameLength > 64) {
|
|
379
|
+
throw new LexError("\u5909\u6570\u540D\u306F 64 \u6587\u5B57\u4EE5\u5185\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", start, this.input);
|
|
380
|
+
}
|
|
381
|
+
return this.makeToken("VARIABLE" /* VARIABLE */, this.input.slice(start, this.pos), start);
|
|
382
|
+
}
|
|
365
383
|
// ----------------------------------------------------------
|
|
366
384
|
// 空白・コメントをスキップ
|
|
367
385
|
// ----------------------------------------------------------
|
|
@@ -573,6 +591,8 @@ var Parser = class {
|
|
|
573
591
|
return this.parseDescribe();
|
|
574
592
|
case "EXPLAIN" /* EXPLAIN */:
|
|
575
593
|
return this.parseExplain();
|
|
594
|
+
case "SET" /* SET */:
|
|
595
|
+
return this.parseSetVariable();
|
|
576
596
|
case "ASSERT" /* ASSERT */:
|
|
577
597
|
return this.parseAssert();
|
|
578
598
|
case "IDENT" /* IDENT */: {
|
|
@@ -585,11 +605,65 @@ var Parser = class {
|
|
|
585
605
|
break;
|
|
586
606
|
}
|
|
587
607
|
throw new ParseError(
|
|
588
|
-
"SELECT / INSERT / UPDATE / DELETE / REORDER / WITH / SHOW / DESCRIBE / EXPLAIN / CREATE TEMP TABLE / DROP TEMP TABLE / ASSERT \u306E\u3044\u305A\u308C\u304B\u3067\u59CB\u307E\u308B SQL \u6587\u304C\u5FC5\u8981\u3067\u3059",
|
|
608
|
+
"SELECT / INSERT / UPDATE / DELETE / REORDER / WITH / SHOW / DESCRIBE / EXPLAIN / CREATE TEMP TABLE / DROP TEMP TABLE / SET / ASSERT \u306E\u3044\u305A\u308C\u304B\u3067\u59CB\u307E\u308B SQL \u6587\u304C\u5FC5\u8981\u3067\u3059",
|
|
589
609
|
tok
|
|
590
610
|
);
|
|
591
611
|
}
|
|
592
612
|
// ----------------------------------------------------------
|
|
613
|
+
// SET @name = <ScalarExpr>
|
|
614
|
+
// ----------------------------------------------------------
|
|
615
|
+
parseSetVariable() {
|
|
616
|
+
this.expect("SET" /* SET */);
|
|
617
|
+
const variable = this.expect("VARIABLE" /* VARIABLE */, "SET \u306E\u5F8C\u306B\u306F\u5909\u6570\u540D\uFF08\u4F8B: @name\uFF09\u304C\u5FC5\u8981\u3067\u3059");
|
|
618
|
+
this.expect("=" /* EQ */);
|
|
619
|
+
const expr = this.parseScalarExpr();
|
|
620
|
+
return { type: "SET_VARIABLE", name: variable.value.slice(1).toLowerCase(), expr };
|
|
621
|
+
}
|
|
622
|
+
/** SET RHS 専用。既存式パーサーで構文を読み、フィールド参照を明示的に拒否する。 */
|
|
623
|
+
parseScalarExpr() {
|
|
624
|
+
const tok = this.peek();
|
|
625
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
626
|
+
throw new ParseError("SET \u306E\u53F3\u8FBA\u3067\u306F\u4ED6\u306E\u5909\u6570\u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\uFF08Phase 1a\uFF09", tok);
|
|
627
|
+
}
|
|
628
|
+
if (tok.kind === "NULL" /* NULL */) {
|
|
629
|
+
throw new ParseError("SET \u306E\u53F3\u8FBA\u3067 NULL \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\uFF08Phase 1a\uFF09", tok);
|
|
630
|
+
}
|
|
631
|
+
if (tok.kind === "(" /* LPAREN */ && this.peekAt(1).kind === "SELECT" /* SELECT */) {
|
|
632
|
+
throw new ParseError("SET \u306E\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u4EE3\u5165\u306F Phase 1b \u3067\u5BFE\u5FDC\u4E88\u5B9A\u3067\u3059", tok);
|
|
633
|
+
}
|
|
634
|
+
if (tok.kind === "STRING" /* STRING */) {
|
|
635
|
+
this.advance();
|
|
636
|
+
return { type: "STRING", value: tok.value };
|
|
637
|
+
}
|
|
638
|
+
if (tok.kind === "LOGINUSER" /* LOGINUSER */) {
|
|
639
|
+
throw new ParseError(
|
|
640
|
+
"SET \u306E\u53F3\u8FBA\u3067 LOGINUSER() \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\uFF08\u5B9F\u884C\u74B0\u5883\u5171\u901A\u306E\u30ED\u30B0\u30A4\u30F3\u30E6\u30FC\u30B6\u30FC\u89E3\u6C7A\u306F\u672A\u5BFE\u5FDC\u3067\u3059\uFF09",
|
|
641
|
+
tok
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
if (tok.kind === "TODAY" /* TODAY */ || tok.kind === "NOW" /* NOW */) {
|
|
645
|
+
return this.parseSqlValue();
|
|
646
|
+
}
|
|
647
|
+
const expr = this.parseArithAddSub();
|
|
648
|
+
this.rejectNonScalarExpr(expr, tok);
|
|
649
|
+
if (expr.type === "NUMBER" || expr.type === "STRING_FUNC" || expr.type === "ARITH") return expr;
|
|
650
|
+
throw new ParseError("SET \u306E\u53F3\u8FBA\u306B\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u542B\u307E\u306A\u3044\u30B9\u30AB\u30E9\u30FC\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", tok);
|
|
651
|
+
}
|
|
652
|
+
rejectNonScalarExpr(node, tok) {
|
|
653
|
+
if (node.type === "STRING" || node.type === "NUMBER") return;
|
|
654
|
+
if (node.type === "FIELD_REF" || node.type === "AGG_REF") {
|
|
655
|
+
throw new ParseError("SET \u306E\u53F3\u8FBA\u3067\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u30FB\u96C6\u8A08\u95A2\u6570\u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
656
|
+
}
|
|
657
|
+
if (node.type === "ARITH" || node.type === "AGG_ARITH") {
|
|
658
|
+
this.rejectNonScalarExpr(node.left, tok);
|
|
659
|
+
this.rejectNonScalarExpr(node.right, tok);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (node.type === "STRING_FUNC") {
|
|
663
|
+
for (const arg of node.args) this.rejectNonScalarExpr(arg, tok);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
// ----------------------------------------------------------
|
|
593
667
|
// CREATE TEMP TABLE / DROP TEMP TABLE(バッチ内一時テーブル)
|
|
594
668
|
// CREATE / DROP / TEMP / TABLE は予約語にしない(ソフトキーワード)
|
|
595
669
|
// ----------------------------------------------------------
|
|
@@ -733,6 +807,10 @@ var Parser = class {
|
|
|
733
807
|
/** ASSERT のオペランド: 文字列 / スカラーサブクエリ / 数値算術式 */
|
|
734
808
|
parseAssertOperand() {
|
|
735
809
|
const tok = this.peek();
|
|
810
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
811
|
+
this.advance();
|
|
812
|
+
return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
|
|
813
|
+
}
|
|
736
814
|
if (tok.kind === "STRING" /* STRING */) {
|
|
737
815
|
this.advance();
|
|
738
816
|
return { type: "STRING", value: tok.value };
|
|
@@ -1640,6 +1718,10 @@ var Parser = class {
|
|
|
1640
1718
|
// 右辺の値
|
|
1641
1719
|
parseSqlValue() {
|
|
1642
1720
|
const tok = this.peek();
|
|
1721
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
1722
|
+
this.advance();
|
|
1723
|
+
return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
|
|
1724
|
+
}
|
|
1643
1725
|
if (tok.kind === "STRING" /* STRING */) {
|
|
1644
1726
|
this.advance();
|
|
1645
1727
|
return { type: "STRING", value: tok.value };
|
|
@@ -1702,8 +1784,10 @@ var Parser = class {
|
|
|
1702
1784
|
values.push({ type: "STRING", value: tok.value });
|
|
1703
1785
|
} else if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
1704
1786
|
values.push({ type: "NUMBER", value: Number(tok.value) });
|
|
1787
|
+
} else if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
1788
|
+
values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
|
|
1705
1789
|
} else {
|
|
1706
|
-
throw new ParseError("IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u307E\u305F\u306F\u6570\
|
|
1790
|
+
throw new ParseError("IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u3001\u6570\u5024\u3001\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
1707
1791
|
}
|
|
1708
1792
|
} while (this.consume("," /* COMMA */));
|
|
1709
1793
|
return values;
|
|
@@ -1935,6 +2019,7 @@ var Parser = class {
|
|
|
1935
2019
|
*/
|
|
1936
2020
|
parseAssignmentValue() {
|
|
1937
2021
|
const tok = this.peek();
|
|
2022
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) return this.parseSqlValue();
|
|
1938
2023
|
if (tok.kind === "STRING" /* STRING */) return this.parseSqlValue();
|
|
1939
2024
|
if (tok.kind === "TODAY" /* TODAY */ || tok.kind === "NOW" /* NOW */ || tok.kind === "LOGINUSER" /* LOGINUSER */) return this.parseSqlValue();
|
|
1940
2025
|
if (tok.kind === "IN" /* IN */) return this.parseSqlValue();
|
|
@@ -2196,7 +2281,7 @@ function isDmlType(type) {
|
|
|
2196
2281
|
return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
|
|
2197
2282
|
}
|
|
2198
2283
|
function isReadOnlyType(type) {
|
|
2199
|
-
return type === "SELECT" || type === "UNION" || type === "WITH" || type === "EXPLAIN" || type === "SHOW_APPS" || type === "DESCRIBE" || type === "CREATE_TEMP_TABLE" || type === "DROP_TEMP_TABLE" || type === "ASSERT";
|
|
2284
|
+
return type === "SELECT" || type === "UNION" || type === "WITH" || type === "EXPLAIN" || type === "SHOW_APPS" || type === "DESCRIBE" || type === "CREATE_TEMP_TABLE" || type === "DROP_TEMP_TABLE" || type === "SET_VARIABLE" || type === "ASSERT";
|
|
2200
2285
|
}
|
|
2201
2286
|
function hasWhereClause(stmt) {
|
|
2202
2287
|
if (!stmt || typeof stmt !== "object") return false;
|
|
@@ -2229,6 +2314,7 @@ function collectDmlTargetFields(stmt) {
|
|
|
2229
2314
|
|
|
2230
2315
|
// src/core/batch.ts
|
|
2231
2316
|
var MAX_TEMP_TABLES = 16;
|
|
2317
|
+
var MAX_BATCH_VARIABLES = 64;
|
|
2232
2318
|
var BatchAnalysisError = class extends Error {
|
|
2233
2319
|
constructor(message, statementIndex) {
|
|
2234
2320
|
super(message);
|
|
@@ -2249,12 +2335,29 @@ function collectRefs(node, tempRefs, appIds) {
|
|
|
2249
2335
|
for (const v of Object.values(obj)) collectRefs(v, tempRefs, appIds);
|
|
2250
2336
|
}
|
|
2251
2337
|
}
|
|
2338
|
+
function collectVariableRefs(node, refs) {
|
|
2339
|
+
if (Array.isArray(node)) {
|
|
2340
|
+
for (const v of node) collectVariableRefs(v, refs);
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
if (node !== null && typeof node === "object") {
|
|
2344
|
+
const obj = node;
|
|
2345
|
+
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
|
|
2346
|
+
refs.add(obj["name"]);
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
for (const v of Object.values(obj)) collectVariableRefs(v, refs);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2252
2352
|
function analyzeBatch(statements) {
|
|
2253
2353
|
if (statements.length === 0) {
|
|
2254
2354
|
throw new BatchAnalysisError("ArgumentError: SQL is empty.", 0);
|
|
2255
2355
|
}
|
|
2256
2356
|
if (statements.length === 1) {
|
|
2257
2357
|
const t = statements[0].type;
|
|
2358
|
+
if (t === "SET_VARIABLE") {
|
|
2359
|
+
throw new BatchAnalysisError("ArgumentError: SET variable requires a batch.", 0);
|
|
2360
|
+
}
|
|
2258
2361
|
if (t === "CREATE_TEMP_TABLE" || t === "DROP_TEMP_TABLE") {
|
|
2259
2362
|
const verb = t === "CREATE_TEMP_TABLE" ? "CREATE TEMP TABLE" : "DROP TEMP TABLE";
|
|
2260
2363
|
throw new BatchAnalysisError(
|
|
@@ -2266,6 +2369,8 @@ function analyzeBatch(statements) {
|
|
|
2266
2369
|
const defined = /* @__PURE__ */ new Map();
|
|
2267
2370
|
const createdOrder = [];
|
|
2268
2371
|
const results = [];
|
|
2372
|
+
const variableDefs = /* @__PURE__ */ new Map();
|
|
2373
|
+
const variableOrder = [];
|
|
2269
2374
|
statements.forEach((stmt, index) => {
|
|
2270
2375
|
const statementType = getStatementType(stmt);
|
|
2271
2376
|
const created = [];
|
|
@@ -2273,6 +2378,31 @@ function analyzeBatch(statements) {
|
|
|
2273
2378
|
const refs = /* @__PURE__ */ new Set();
|
|
2274
2379
|
const stmtAppIds = /* @__PURE__ */ new Set();
|
|
2275
2380
|
const dependsOn = /* @__PURE__ */ new Set();
|
|
2381
|
+
const variableRefs = /* @__PURE__ */ new Set();
|
|
2382
|
+
collectVariableRefs(stmt, variableRefs);
|
|
2383
|
+
for (const name of variableRefs) {
|
|
2384
|
+
const def = variableDefs.get(name);
|
|
2385
|
+
if (def === void 0) {
|
|
2386
|
+
throw new BatchAnalysisError(
|
|
2387
|
+
`ParseError: variable @${name} is not defined before statement ${index + 1}.`,
|
|
2388
|
+
index
|
|
2389
|
+
);
|
|
2390
|
+
}
|
|
2391
|
+
def.referencedBy.push(index);
|
|
2392
|
+
}
|
|
2393
|
+
if (stmt.type === "SET_VARIABLE") {
|
|
2394
|
+
if (variableDefs.has(stmt.name)) {
|
|
2395
|
+
throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
|
|
2396
|
+
}
|
|
2397
|
+
variableDefs.set(stmt.name, { index, referencedBy: [] });
|
|
2398
|
+
variableOrder.push(stmt.name);
|
|
2399
|
+
if (variableOrder.length > MAX_BATCH_VARIABLES) {
|
|
2400
|
+
throw new BatchAnalysisError(
|
|
2401
|
+
`ParseError: batch exceeds ${MAX_BATCH_VARIABLES} variables.`,
|
|
2402
|
+
index
|
|
2403
|
+
);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2276
2406
|
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
2277
2407
|
collectRefs(stmt.query, refs, stmtAppIds);
|
|
2278
2408
|
} else if (stmt.type === "DROP_TEMP_TABLE") {
|
|
@@ -2342,11 +2472,17 @@ function analyzeBatch(statements) {
|
|
|
2342
2472
|
});
|
|
2343
2473
|
});
|
|
2344
2474
|
const containsDml = results.some((r) => r.isDml);
|
|
2475
|
+
const variables = variableOrder.map((name) => ({
|
|
2476
|
+
name,
|
|
2477
|
+
referencedBy: [...variableDefs.get(name).referencedBy]
|
|
2478
|
+
}));
|
|
2345
2479
|
return {
|
|
2346
2480
|
statementCount: statements.length,
|
|
2347
2481
|
isReadOnlyBatch: !containsDml && results.every((r) => r.isReadOnly),
|
|
2348
2482
|
containsDml,
|
|
2349
2483
|
tempTables: createdOrder,
|
|
2484
|
+
variables,
|
|
2485
|
+
warnings: variables.filter((v) => v.referencedBy.length === 0).map((v) => `variable @${v.name} is never used.`),
|
|
2350
2486
|
statements: results
|
|
2351
2487
|
};
|
|
2352
2488
|
}
|
|
@@ -2517,6 +2653,8 @@ function convertField(field) {
|
|
|
2517
2653
|
}
|
|
2518
2654
|
function convertValue(value, op) {
|
|
2519
2655
|
switch (value.type) {
|
|
2656
|
+
case "VARIABLE":
|
|
2657
|
+
throw new KintoneQueryError(`\u672A\u89E3\u6C7A\u306E\u30D0\u30C3\u30C1\u5909\u6570 @${value.name} \u304C\u3042\u308A\u307E\u3059`);
|
|
2520
2658
|
case "STRING":
|
|
2521
2659
|
return convertString(value);
|
|
2522
2660
|
case "NUMBER":
|
|
@@ -2547,11 +2685,18 @@ function convertInList(v, op) {
|
|
|
2547
2685
|
if (op !== "IN" && op !== "NOT_IN") {
|
|
2548
2686
|
throw new KintoneQueryError("IN_LIST \u306F IN / NOT IN \u6F14\u7B97\u5B50\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059");
|
|
2549
2687
|
}
|
|
2688
|
+
assertResolvedInListValues(v.values);
|
|
2550
2689
|
const values = v.values.map(
|
|
2551
2690
|
(item) => item.type === "STRING" ? convertString(item) : String(item.value)
|
|
2552
2691
|
).join(",");
|
|
2553
2692
|
return `(${values})`;
|
|
2554
2693
|
}
|
|
2694
|
+
function assertResolvedInListValues(values) {
|
|
2695
|
+
const unresolved = values.find((item) => item.type === "VARIABLE");
|
|
2696
|
+
if (unresolved?.type === "VARIABLE") {
|
|
2697
|
+
throw new KintoneQueryError(`\u672A\u89E3\u6C7A\u306E\u30D0\u30C3\u30C1\u5909\u6570 @${unresolved.name} \u304C\u3042\u308A\u307E\u3059`);
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2555
2700
|
function quoteIdentifier(name) {
|
|
2556
2701
|
if (/^[\w$\u3000-\u9FFF]+$/u.test(name)) {
|
|
2557
2702
|
return name;
|
|
@@ -3295,15 +3440,17 @@ function evalBinary(expr, row) {
|
|
|
3295
3440
|
return evalOp(expr.op, left, expr.right, row);
|
|
3296
3441
|
}
|
|
3297
3442
|
function evalOp(op, leftStr, right, row) {
|
|
3298
|
-
if (op === "IN") {
|
|
3299
|
-
if (right.type === "IN_LIST")
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
if (right.type === "
|
|
3305
|
-
|
|
3306
|
-
|
|
3443
|
+
if (op === "IN" || op === "NOT_IN") {
|
|
3444
|
+
if (right.type === "IN_LIST") {
|
|
3445
|
+
assertResolvedInListValues2(right.values);
|
|
3446
|
+
const contains = right.values.some((v) => leftStr === String(v.value));
|
|
3447
|
+
return op === "IN" ? contains : !contains;
|
|
3448
|
+
}
|
|
3449
|
+
if (right.type === "SUBQUERY_IN_LIST") {
|
|
3450
|
+
const contains = right.resolved.has(leftStr);
|
|
3451
|
+
return op === "IN" ? contains : !contains;
|
|
3452
|
+
}
|
|
3453
|
+
return op === "NOT_IN";
|
|
3307
3454
|
}
|
|
3308
3455
|
if (op === "LIKE") {
|
|
3309
3456
|
const pattern = resolveValue(right, row);
|
|
@@ -3333,6 +3480,12 @@ function evalOp(op, leftStr, right, row) {
|
|
|
3333
3480
|
return numeric ? leftNum <= rightNum : leftStr <= rightStr;
|
|
3334
3481
|
}
|
|
3335
3482
|
}
|
|
3483
|
+
function assertResolvedInListValues2(values) {
|
|
3484
|
+
const unresolved = values.find((item) => item.type === "VARIABLE");
|
|
3485
|
+
if (unresolved?.type === "VARIABLE") {
|
|
3486
|
+
throw new Error(`ParseError: unresolved batch variable @${unresolved.name}.`);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3336
3489
|
function evalNullCheck(expr, row) {
|
|
3337
3490
|
const val = resolveField(expr.field, row);
|
|
3338
3491
|
return expr.not ? val !== "" : val === "";
|
|
@@ -3352,6 +3505,8 @@ function resolveField(field, row) {
|
|
|
3352
3505
|
}
|
|
3353
3506
|
function resolveValue(value, row) {
|
|
3354
3507
|
switch (value.type) {
|
|
3508
|
+
case "VARIABLE":
|
|
3509
|
+
throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
|
|
3355
3510
|
case "STRING":
|
|
3356
3511
|
return value.value;
|
|
3357
3512
|
case "NUMBER":
|
|
@@ -3715,6 +3870,8 @@ function evalCaseWhenValue(expr, row, fieldType) {
|
|
|
3715
3870
|
}
|
|
3716
3871
|
function toKintoneValue(value, fieldType) {
|
|
3717
3872
|
switch (value.type) {
|
|
3873
|
+
case "VARIABLE":
|
|
3874
|
+
throw new DmlConvertError(`\u672A\u89E3\u6C7A\u306E\u30D0\u30C3\u30C1\u5909\u6570 @${value.name} \u304C\u3042\u308A\u307E\u3059`);
|
|
3718
3875
|
case "STRING":
|
|
3719
3876
|
return convertString2(value.value, fieldType);
|
|
3720
3877
|
case "NUMBER":
|
|
@@ -4305,8 +4462,12 @@ function project(rows, columns, scalarCache) {
|
|
|
4305
4462
|
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [];
|
|
4306
4463
|
return { rows: projected2, columns: cols };
|
|
4307
4464
|
}
|
|
4308
|
-
const orderedKeys = [];
|
|
4309
4465
|
const defaultFieldKeys = buildDefaultFieldOutputKeys(columns);
|
|
4466
|
+
const hasWildcard = columns.some(
|
|
4467
|
+
(col) => col.type === "WILDCARD" || col.type === "PARENT_WILDCARD"
|
|
4468
|
+
);
|
|
4469
|
+
const outputKeys = hasWildcard ? null : computeOutputKeys(columns, defaultFieldKeys);
|
|
4470
|
+
const orderedKeys = outputKeys ?? [];
|
|
4310
4471
|
const projected = rows.map((row, rowIdx) => {
|
|
4311
4472
|
const out = {};
|
|
4312
4473
|
for (const [colIdx, col] of columns.entries()) {
|
|
@@ -4323,58 +4484,58 @@ function project(rows, columns, scalarCache) {
|
|
|
4323
4484
|
break;
|
|
4324
4485
|
}
|
|
4325
4486
|
case "FIELD": {
|
|
4326
|
-
const key = col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
4487
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
4327
4488
|
out[key] = resolveFieldRef(row, col.field);
|
|
4328
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
4489
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4329
4490
|
break;
|
|
4330
4491
|
}
|
|
4331
4492
|
case "LITERAL_COL": {
|
|
4332
|
-
const key = col.alias ?? `'${col.value}'`;
|
|
4493
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? `'${col.value}'`;
|
|
4333
4494
|
out[key] = col.value;
|
|
4334
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
4495
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4335
4496
|
break;
|
|
4336
4497
|
}
|
|
4337
4498
|
case "AGGREGATE": {
|
|
4338
4499
|
const srcKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
|
|
4339
|
-
const dstKey = col.alias ?? srcKey;
|
|
4500
|
+
const dstKey = outputKeys?.[colIdx] ?? col.alias ?? srcKey;
|
|
4340
4501
|
out[dstKey] = row[col.alias ?? srcKey] ?? row[srcKey] ?? "0";
|
|
4341
|
-
if (rowIdx === 0) orderedKeys.push(dstKey);
|
|
4502
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(dstKey);
|
|
4342
4503
|
break;
|
|
4343
4504
|
}
|
|
4344
4505
|
case "ARITH_AGG_COL": {
|
|
4345
|
-
const key = col.alias ?? aggArithDefaultKey(col.expr);
|
|
4506
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? aggArithDefaultKey(col.expr);
|
|
4346
4507
|
out[key] = row[key] ?? "0";
|
|
4347
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
4508
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4348
4509
|
break;
|
|
4349
4510
|
}
|
|
4350
4511
|
case "ARITH_COL": {
|
|
4351
4512
|
const val = evalArithExpr(col.expr, row);
|
|
4352
|
-
const key = col.alias ?? arithColDefaultKey(col.expr);
|
|
4513
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? arithColDefaultKey(col.expr);
|
|
4353
4514
|
out[key] = String(val);
|
|
4354
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
4515
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4355
4516
|
break;
|
|
4356
4517
|
}
|
|
4357
4518
|
case "CASE_COL": {
|
|
4358
|
-
const key = col.alias ?? "case";
|
|
4519
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
4359
4520
|
out[key] = evalCaseWhen(col.expr, row);
|
|
4360
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
4521
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4361
4522
|
break;
|
|
4362
4523
|
}
|
|
4363
4524
|
case "STRFUNC_COL": {
|
|
4364
|
-
const key = col.alias ?? stringFuncDefaultKey(col.expr);
|
|
4525
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? stringFuncDefaultKey(col.expr);
|
|
4365
4526
|
if (hasAggregateInStringFuncExpr2(col.expr)) {
|
|
4366
4527
|
const srcKey = stringFuncDefaultKey(col.expr);
|
|
4367
4528
|
out[key] = row[col.alias ?? srcKey] ?? row[srcKey] ?? evalStringFunc(col.expr, row);
|
|
4368
4529
|
} else {
|
|
4369
4530
|
out[key] = evalStringFunc(col.expr, row);
|
|
4370
4531
|
}
|
|
4371
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
4532
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4372
4533
|
break;
|
|
4373
4534
|
}
|
|
4374
4535
|
case "SCALAR_SUBQUERY_COL": {
|
|
4375
|
-
const key = col.alias ?? "(subquery)";
|
|
4536
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? "(subquery)";
|
|
4376
4537
|
out[key] = scalarCache?.get(colIdx) ?? "";
|
|
4377
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
4538
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4378
4539
|
break;
|
|
4379
4540
|
}
|
|
4380
4541
|
}
|
|
@@ -4383,6 +4544,31 @@ function project(rows, columns, scalarCache) {
|
|
|
4383
4544
|
});
|
|
4384
4545
|
return { rows: projected, columns: orderedKeys };
|
|
4385
4546
|
}
|
|
4547
|
+
function computeOutputKeys(columns, defaultFieldKeys) {
|
|
4548
|
+
return columns.map((col, colIdx) => {
|
|
4549
|
+
switch (col.type) {
|
|
4550
|
+
case "FIELD":
|
|
4551
|
+
return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
4552
|
+
case "LITERAL_COL":
|
|
4553
|
+
return col.alias ?? `'${col.value}'`;
|
|
4554
|
+
case "AGGREGATE":
|
|
4555
|
+
return col.alias ?? aggregateSyntheticName2(col.func, col.distinct, col.arg);
|
|
4556
|
+
case "ARITH_AGG_COL":
|
|
4557
|
+
return col.alias ?? aggArithDefaultKey(col.expr);
|
|
4558
|
+
case "ARITH_COL":
|
|
4559
|
+
return col.alias ?? arithColDefaultKey(col.expr);
|
|
4560
|
+
case "CASE_COL":
|
|
4561
|
+
return col.alias ?? "case";
|
|
4562
|
+
case "STRFUNC_COL":
|
|
4563
|
+
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
4564
|
+
case "SCALAR_SUBQUERY_COL":
|
|
4565
|
+
return col.alias ?? "(subquery)";
|
|
4566
|
+
case "WILDCARD":
|
|
4567
|
+
case "PARENT_WILDCARD":
|
|
4568
|
+
throw new Error("internal: computeOutputKeys received a wildcard column");
|
|
4569
|
+
}
|
|
4570
|
+
});
|
|
4571
|
+
}
|
|
4386
4572
|
function buildDefaultFieldOutputKeys(columns) {
|
|
4387
4573
|
const qualifierCollisionCount = /* @__PURE__ */ new Map();
|
|
4388
4574
|
for (const col of columns) {
|
|
@@ -4590,6 +4776,10 @@ async function executeStatement(sql, client, options) {
|
|
|
4590
4776
|
return executeParsedStatement(stmt, client, options, cacheContext);
|
|
4591
4777
|
}
|
|
4592
4778
|
async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
4779
|
+
const unresolved = findVariableRef(stmt);
|
|
4780
|
+
if (unresolved !== null) {
|
|
4781
|
+
throw new Error(`ParseError: variable @${unresolved} is not defined in a batch.`);
|
|
4782
|
+
}
|
|
4593
4783
|
switch (stmt.type) {
|
|
4594
4784
|
case "SELECT":
|
|
4595
4785
|
return executeSelect(stmt, client, options, cacheContext);
|
|
@@ -4622,6 +4812,8 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
4622
4812
|
throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
4623
4813
|
case "DROP_TEMP_TABLE":
|
|
4624
4814
|
throw new Error("ArgumentError: DROP TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
4815
|
+
case "SET_VARIABLE":
|
|
4816
|
+
throw new Error("ArgumentError: SET variable requires a batch.");
|
|
4625
4817
|
case "ASSERT":
|
|
4626
4818
|
return executeAssert(stmt, client, options, cacheContext);
|
|
4627
4819
|
}
|
|
@@ -4652,6 +4844,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
4652
4844
|
const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
|
|
4653
4845
|
const cacheContext = options.cacheContext ?? "default";
|
|
4654
4846
|
const tempTables = /* @__PURE__ */ new Map();
|
|
4847
|
+
const variables = /* @__PURE__ */ new Map();
|
|
4655
4848
|
const results = [];
|
|
4656
4849
|
const failed = /* @__PURE__ */ new Set();
|
|
4657
4850
|
let aborted = null;
|
|
@@ -4689,7 +4882,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
4689
4882
|
})
|
|
4690
4883
|
} : options;
|
|
4691
4884
|
const outcome = await runWithDeadline(
|
|
4692
|
-
executeBatchStatement(statements[i], info, countedClient, stmtOptions, cacheContext, tempTables),
|
|
4885
|
+
executeBatchStatement(statements[i], info, countedClient, stmtOptions, cacheContext, tempTables, variables),
|
|
4693
4886
|
remaining
|
|
4694
4887
|
);
|
|
4695
4888
|
results.push({ ...base, status: "success", ...outcome });
|
|
@@ -4700,6 +4893,8 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
4700
4893
|
aborted = "timeout";
|
|
4701
4894
|
} else if (e instanceof AssertError) {
|
|
4702
4895
|
aborted = "assertion";
|
|
4896
|
+
} else if (info.statementType === "SET_VARIABLE") {
|
|
4897
|
+
aborted = "fail-fast";
|
|
4703
4898
|
} else if (!options.continueOnError) {
|
|
4704
4899
|
aborted = "fail-fast";
|
|
4705
4900
|
}
|
|
@@ -4714,44 +4909,49 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
4714
4909
|
metrics
|
|
4715
4910
|
};
|
|
4716
4911
|
}
|
|
4717
|
-
async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables) {
|
|
4718
|
-
if (stmt.type === "
|
|
4912
|
+
async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables) {
|
|
4913
|
+
if (stmt.type === "SET_VARIABLE") {
|
|
4914
|
+
variables.set(stmt.name, evaluateScalarExpr(stmt.expr));
|
|
4915
|
+
return {};
|
|
4916
|
+
}
|
|
4917
|
+
const resolvedStmt = resolveVariableRefs(stmt, variables);
|
|
4918
|
+
if (resolvedStmt.type === "CREATE_TEMP_TABLE") {
|
|
4719
4919
|
const materializeOptions = {
|
|
4720
4920
|
...options,
|
|
4721
4921
|
maxRecords: options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
|
|
4722
4922
|
onLimitReached: "error"
|
|
4723
4923
|
};
|
|
4724
|
-
const result = await runSelectLike(
|
|
4725
|
-
tempTables.set(
|
|
4726
|
-
return { tempTable:
|
|
4924
|
+
const result = await runSelectLike(resolvedStmt.query, client, materializeOptions, cacheContext, tempTables);
|
|
4925
|
+
tempTables.set(resolvedStmt.name, result.rows);
|
|
4926
|
+
return { tempTable: resolvedStmt.name, rowCount: result.rows.length };
|
|
4727
4927
|
}
|
|
4728
4928
|
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
4729
4929
|
tempTables.delete(stmt.name);
|
|
4730
4930
|
return { tempTable: stmt.name };
|
|
4731
4931
|
}
|
|
4732
4932
|
if (stmt.type === "EXPLAIN") {
|
|
4733
|
-
return { result: await executeParsedStatement(
|
|
4933
|
+
return { result: await executeParsedStatement(resolvedStmt, client, options, cacheContext) };
|
|
4734
4934
|
}
|
|
4735
|
-
if (
|
|
4736
|
-
await executeAssert(
|
|
4935
|
+
if (resolvedStmt.type === "ASSERT") {
|
|
4936
|
+
await executeAssert(resolvedStmt, client, options, cacheContext, tempTables);
|
|
4737
4937
|
return {};
|
|
4738
4938
|
}
|
|
4739
4939
|
if (info.tempTablesReferenced.length > 0) {
|
|
4740
|
-
if (
|
|
4741
|
-
return { result: await executeQueryWithCte(
|
|
4940
|
+
if (resolvedStmt.type === "SELECT" || resolvedStmt.type === "UNION") {
|
|
4941
|
+
return { result: await executeQueryWithCte(resolvedStmt, client, options, tempTables, cacheContext) };
|
|
4742
4942
|
}
|
|
4743
|
-
if (
|
|
4744
|
-
return { result: await executeWith(
|
|
4943
|
+
if (resolvedStmt.type === "WITH") {
|
|
4944
|
+
return { result: await executeWith(resolvedStmt, client, options, cacheContext, tempTables) };
|
|
4745
4945
|
}
|
|
4746
|
-
if (
|
|
4747
|
-
return { result: await executeInsertSelect(
|
|
4946
|
+
if (resolvedStmt.type === "INSERT_SELECT") {
|
|
4947
|
+
return { result: await executeInsertSelect(resolvedStmt, client, options, cacheContext, tempTables) };
|
|
4748
4948
|
}
|
|
4749
|
-
if (
|
|
4750
|
-
return { result: await executeUpsertSelect(
|
|
4949
|
+
if (resolvedStmt.type === "UPSERT_SELECT") {
|
|
4950
|
+
return { result: await executeUpsertSelect(resolvedStmt, client, options, cacheContext, tempTables) };
|
|
4751
4951
|
}
|
|
4752
4952
|
throw new Error(`ArgumentError: temp table references in ${stmt.type} are not supported yet.`);
|
|
4753
4953
|
}
|
|
4754
|
-
return { result: await executeParsedStatement(
|
|
4954
|
+
return { result: await executeParsedStatement(resolvedStmt, client, options, cacheContext) };
|
|
4755
4955
|
}
|
|
4756
4956
|
async function runSelectLike(query, client, options, cacheContext, tempTables) {
|
|
4757
4957
|
if (query.type === "WITH") {
|
|
@@ -4812,6 +5012,62 @@ function parseSqlBatch(sql) {
|
|
|
4812
5012
|
const tokens = new Lexer(sql).tokenize();
|
|
4813
5013
|
return new Parser(tokens).parseStatements();
|
|
4814
5014
|
}
|
|
5015
|
+
function evaluateScalarExpr(expr) {
|
|
5016
|
+
switch (expr.type) {
|
|
5017
|
+
case "STRING":
|
|
5018
|
+
return { type: "string", value: expr.value };
|
|
5019
|
+
case "NUMBER":
|
|
5020
|
+
return { type: "number", value: expr.value };
|
|
5021
|
+
case "KINTONE_FUNC":
|
|
5022
|
+
return { type: "string", value: resolveKintoneFunc(expr.name) };
|
|
5023
|
+
case "STRING_FUNC":
|
|
5024
|
+
return { type: "string", value: evalStringFunc(expr, {}) };
|
|
5025
|
+
case "ARITH": {
|
|
5026
|
+
const value = evalArithExpr(expr, {});
|
|
5027
|
+
if (!Number.isFinite(value)) {
|
|
5028
|
+
throw new Error("ArgumentError: SET scalar arithmetic produced a non-finite number.");
|
|
5029
|
+
}
|
|
5030
|
+
return { type: "number", value };
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
5034
|
+
function resolveVariableRefs(node, variables) {
|
|
5035
|
+
if (Array.isArray(node)) {
|
|
5036
|
+
return node.map((v) => resolveVariableRefs(v, variables));
|
|
5037
|
+
}
|
|
5038
|
+
if (node !== null && typeof node === "object") {
|
|
5039
|
+
const obj = node;
|
|
5040
|
+
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
|
|
5041
|
+
const value = variables.get(obj["name"]);
|
|
5042
|
+
if (value === void 0) {
|
|
5043
|
+
throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
|
|
5044
|
+
}
|
|
5045
|
+
return value.type === "number" ? { type: "NUMBER", value: value.value } : { type: "STRING", value: value.value };
|
|
5046
|
+
}
|
|
5047
|
+
return Object.fromEntries(
|
|
5048
|
+
Object.entries(obj).map(([key, value]) => [key, resolveVariableRefs(value, variables)])
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
return node;
|
|
5052
|
+
}
|
|
5053
|
+
function findVariableRef(node) {
|
|
5054
|
+
if (Array.isArray(node)) {
|
|
5055
|
+
for (const value of node) {
|
|
5056
|
+
const found = findVariableRef(value);
|
|
5057
|
+
if (found !== null) return found;
|
|
5058
|
+
}
|
|
5059
|
+
return null;
|
|
5060
|
+
}
|
|
5061
|
+
if (node !== null && typeof node === "object") {
|
|
5062
|
+
const obj = node;
|
|
5063
|
+
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") return obj["name"];
|
|
5064
|
+
for (const value of Object.values(obj)) {
|
|
5065
|
+
const found = findVariableRef(value);
|
|
5066
|
+
if (found !== null) return found;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
return null;
|
|
5070
|
+
}
|
|
4815
5071
|
var AssertError = class extends Error {
|
|
4816
5072
|
constructor(message) {
|
|
4817
5073
|
super(`AssertError: ${message}`);
|
|
@@ -4842,6 +5098,8 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
|
|
|
4842
5098
|
}
|
|
4843
5099
|
async function evalAssertOperand(operand, client, options, cacheContext, tempTables) {
|
|
4844
5100
|
switch (operand.type) {
|
|
5101
|
+
case "VARIABLE":
|
|
5102
|
+
throw new Error(`ParseError: unresolved batch variable @${operand.name}.`);
|
|
4845
5103
|
case "NUMBER":
|
|
4846
5104
|
return String(operand.value);
|
|
4847
5105
|
case "STRING":
|
|
@@ -5701,8 +5959,9 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
5701
5959
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
5702
5960
|
const { rows, columns } = selectResult;
|
|
5703
5961
|
if (columns.length !== stmt.fields.length) {
|
|
5962
|
+
const emptySourceHint = columns.length === 0 && rows.length === 0 ? "\u3002\u7D50\u679C\u304C 0 \u884C\u306E\u305F\u3081\u5217\u3092\u7279\u5B9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08SELECT * \u3092\u7A7A\u30BD\u30FC\u30B9\u306B\u4F7F\u3046\u3068\u5217\u3092\u6C7A\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002\u660E\u793A\u5217\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF09" : "";
|
|
5704
5963
|
throw new Error(
|
|
5705
|
-
`SELECT \u306E\u5217\u6570\uFF08${columns.length}\uFF09\u3068 INSERT \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`
|
|
5964
|
+
`SELECT \u306E\u5217\u6570\uFF08${columns.length}\uFF09\u3068 INSERT \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093${emptySourceHint}`
|
|
5706
5965
|
);
|
|
5707
5966
|
}
|
|
5708
5967
|
if (options.confirm) {
|
|
@@ -6175,8 +6434,9 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
6175
6434
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
6176
6435
|
const { rows, columns } = selectResult;
|
|
6177
6436
|
if (columns.length !== stmt.fields.length) {
|
|
6437
|
+
const emptySourceHint = columns.length === 0 && rows.length === 0 ? "\u3002\u7D50\u679C\u304C 0 \u884C\u306E\u305F\u3081\u5217\u3092\u7279\u5B9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08SELECT * \u3092\u7A7A\u30BD\u30FC\u30B9\u306B\u4F7F\u3046\u3068\u5217\u3092\u6C7A\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002\u660E\u793A\u5217\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF09" : "";
|
|
6178
6438
|
throw new Error(
|
|
6179
|
-
`SELECT \u306E\u5217\u6570\uFF08${columns.length}\uFF09\u3068 UPSERT \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`
|
|
6439
|
+
`SELECT \u306E\u5217\u6570\uFF08${columns.length}\uFF09\u3068 UPSERT \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093${emptySourceHint}`
|
|
6180
6440
|
);
|
|
6181
6441
|
}
|
|
6182
6442
|
for (const key of stmt.keyFields) {
|
|
@@ -6343,13 +6603,21 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
6343
6603
|
function buildBatchExplainPlans(sql) {
|
|
6344
6604
|
const statements = parseSqlBatch(sql);
|
|
6345
6605
|
const analysis = analyzeBatch(statements);
|
|
6606
|
+
const variables = /* @__PURE__ */ new Map();
|
|
6346
6607
|
return {
|
|
6347
6608
|
statementCount: statements.length,
|
|
6348
|
-
statements: statements.map((stmt, i) =>
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6609
|
+
statements: statements.map((stmt, i) => {
|
|
6610
|
+
const planStmt = stmt.type === "SET_VARIABLE" ? stmt : resolveVariableRefs(stmt, variables);
|
|
6611
|
+
const result = {
|
|
6612
|
+
index: i,
|
|
6613
|
+
type: analysis.statements[i].statementType,
|
|
6614
|
+
plan: buildBatchStatementPlan(planStmt, analysis.statements[i])
|
|
6615
|
+
};
|
|
6616
|
+
if (stmt.type === "SET_VARIABLE") {
|
|
6617
|
+
variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
|
|
6618
|
+
}
|
|
6619
|
+
return result;
|
|
6620
|
+
})
|
|
6353
6621
|
};
|
|
6354
6622
|
}
|
|
6355
6623
|
function buildBatchStatementPlan(stmt, info) {
|
|
@@ -6367,6 +6635,12 @@ function buildBatchStatementPlan(stmt, info) {
|
|
|
6367
6635
|
" \u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u30B9\u30C8\u30A2\u306E\u89E3\u653E\u306E\u307F\uFF08kintone \u30A2\u30AF\u30BB\u30B9\u306A\u3057\uFF09"
|
|
6368
6636
|
];
|
|
6369
6637
|
}
|
|
6638
|
+
if (stmt.type === "SET_VARIABLE") {
|
|
6639
|
+
return [
|
|
6640
|
+
`SET @${stmt.name} = <scalar expression>`,
|
|
6641
|
+
" value: \u5B9F\u884C\u6642\u306B1\u56DE\u8A55\u4FA1\uFF08\u30D0\u30C3\u30C1\u5185\u5B9A\u6570\u30FB\u7D50\u679C\u30E1\u30BF\u30C7\u30FC\u30BF\u306B\u306F\u975E\u516C\u958B\uFF09"
|
|
6642
|
+
];
|
|
6643
|
+
}
|
|
6370
6644
|
if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
|
|
6371
6645
|
if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
|
|
6372
6646
|
if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
|