@rex0220/kintone-sql-tools 2.7.0 → 2.8.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/dist-cli/ksql.js +559 -294
- package/dist-mcp/ksql-mcp.js +506 -241
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -94,6 +94,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
94
94
|
["IS", "IS" /* IS */],
|
|
95
95
|
["NULL", "NULL" /* NULL */],
|
|
96
96
|
["LIKE", "LIKE" /* LIKE */],
|
|
97
|
+
["KLIKE", "KLIKE" /* KLIKE */],
|
|
97
98
|
["IN", "IN" /* IN */],
|
|
98
99
|
["BETWEEN", "BETWEEN" /* BETWEEN */],
|
|
99
100
|
["TODAY", "TODAY" /* TODAY */],
|
|
@@ -1619,7 +1620,7 @@ var Parser = class {
|
|
|
1619
1620
|
}
|
|
1620
1621
|
return false;
|
|
1621
1622
|
}
|
|
1622
|
-
// 比較演算子: =, !=, <>, >, <, >=, <=, LIKE, IN, IS NULL
|
|
1623
|
+
// 比較演算子: =, !=, <>, >, <, >=, <=, LIKE, KLIKE, IN, IS NULL
|
|
1623
1624
|
parseCompareExpr() {
|
|
1624
1625
|
if (this.peek().kind === "(" /* LPAREN */ && !this.isArithParen()) {
|
|
1625
1626
|
this.advance();
|
|
@@ -1655,8 +1656,12 @@ var Parser = class {
|
|
|
1655
1656
|
const pattern = this.parseSqlValue();
|
|
1656
1657
|
return { type: "BINARY", op: "NOT_LIKE", left: field, right: pattern };
|
|
1657
1658
|
}
|
|
1659
|
+
if (this.consume("KLIKE" /* KLIKE */)) {
|
|
1660
|
+
const pattern = this.parseKlikePattern();
|
|
1661
|
+
return { type: "BINARY", op: "NOT_KLIKE", left: field, right: pattern };
|
|
1662
|
+
}
|
|
1658
1663
|
throw new ParseError(
|
|
1659
|
-
"NOT \u306E\u5F8C\u306B\u306F IN \
|
|
1664
|
+
"NOT \u306E\u5F8C\u306B\u306F IN\u3001LIKE\u3001KLIKE \u306E\u3044\u305A\u308C\u304B\u304C\u5FC5\u8981\u3067\u3059",
|
|
1660
1665
|
this.peek()
|
|
1661
1666
|
);
|
|
1662
1667
|
}
|
|
@@ -1666,6 +1671,10 @@ var Parser = class {
|
|
|
1666
1671
|
this.expect(")" /* RPAREN */);
|
|
1667
1672
|
return { type: "BINARY", op: "IN", left: field, right: right2 };
|
|
1668
1673
|
}
|
|
1674
|
+
if (this.consume("KLIKE" /* KLIKE */)) {
|
|
1675
|
+
const pattern = this.parseKlikePattern();
|
|
1676
|
+
return { type: "BINARY", op: "KLIKE", left: field, right: pattern };
|
|
1677
|
+
}
|
|
1669
1678
|
const op = this.parseCompareOp();
|
|
1670
1679
|
const right = this.parseSqlValue();
|
|
1671
1680
|
return { type: "BINARY", op, left: field, right };
|
|
@@ -1691,11 +1700,27 @@ var Parser = class {
|
|
|
1691
1700
|
return "LIKE";
|
|
1692
1701
|
default:
|
|
1693
1702
|
throw new ParseError(
|
|
1694
|
-
"\u6BD4\u8F03\u6F14\u7B97\u5B50\uFF08=, !=, >, <, >=, <=, LIKE, IN, IS\uFF09\u304C\u5FC5\u8981\u3067\u3059",
|
|
1703
|
+
"\u6BD4\u8F03\u6F14\u7B97\u5B50\uFF08=, !=, >, <, >=, <=, LIKE, KLIKE, IN, IS\uFF09\u304C\u5FC5\u8981\u3067\u3059",
|
|
1695
1704
|
tok
|
|
1696
1705
|
);
|
|
1697
1706
|
}
|
|
1698
1707
|
}
|
|
1708
|
+
/** KLIKE / NOT KLIKE の右辺。kintone キーワードは文字列値だけを受け付ける。 */
|
|
1709
|
+
parseKlikePattern() {
|
|
1710
|
+
const tok = this.peek();
|
|
1711
|
+
if (tok.kind === "STRING" /* STRING */) {
|
|
1712
|
+
this.advance();
|
|
1713
|
+
return { type: "STRING", value: tok.value };
|
|
1714
|
+
}
|
|
1715
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
1716
|
+
this.advance();
|
|
1717
|
+
return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
|
|
1718
|
+
}
|
|
1719
|
+
throw new ParseError(
|
|
1720
|
+
"KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059",
|
|
1721
|
+
tok
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1699
1724
|
// WHERE / HAVING の左辺
|
|
1700
1725
|
// - 文字列・数値関数: UPPER(f) / LENGTH(f) / ROUND(f, 2) ...
|
|
1701
1726
|
// - 集計関数(HAVING のみ): COUNT(*) / SUM(f) ...
|
|
@@ -2346,238 +2371,6 @@ function collectDmlTargetFields(stmt) {
|
|
|
2346
2371
|
return [];
|
|
2347
2372
|
}
|
|
2348
2373
|
|
|
2349
|
-
// src/core/batch.ts
|
|
2350
|
-
var MAX_TEMP_TABLES = 16;
|
|
2351
|
-
var MAX_BATCH_VARIABLES = 64;
|
|
2352
|
-
var BatchAnalysisError = class extends Error {
|
|
2353
|
-
constructor(message, statementIndex) {
|
|
2354
|
-
super(message);
|
|
2355
|
-
this.statementIndex = statementIndex;
|
|
2356
|
-
}
|
|
2357
|
-
};
|
|
2358
|
-
function collectRefs(node, tempRefs, appIds) {
|
|
2359
|
-
if (Array.isArray(node)) {
|
|
2360
|
-
for (const v of node) collectRefs(v, tempRefs, appIds);
|
|
2361
|
-
return;
|
|
2362
|
-
}
|
|
2363
|
-
if (node !== null && typeof node === "object") {
|
|
2364
|
-
const obj = node;
|
|
2365
|
-
const cte = obj["cteName"];
|
|
2366
|
-
if (typeof cte === "string" && cte.startsWith("#")) tempRefs.add(cte);
|
|
2367
|
-
const appId = obj["appId"];
|
|
2368
|
-
if (typeof appId === "number" && appId > 0) appIds.add(appId);
|
|
2369
|
-
for (const v of Object.values(obj)) collectRefs(v, tempRefs, appIds);
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2372
|
-
function collectVariableRefs(node, refs) {
|
|
2373
|
-
if (Array.isArray(node)) {
|
|
2374
|
-
for (const v of node) collectVariableRefs(v, refs);
|
|
2375
|
-
return;
|
|
2376
|
-
}
|
|
2377
|
-
if (node !== null && typeof node === "object") {
|
|
2378
|
-
const obj = node;
|
|
2379
|
-
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
|
|
2380
|
-
refs.add(obj["name"]);
|
|
2381
|
-
return;
|
|
2382
|
-
}
|
|
2383
|
-
for (const v of Object.values(obj)) collectVariableRefs(v, refs);
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
function analyzeBatch(statements) {
|
|
2387
|
-
if (statements.length === 0) {
|
|
2388
|
-
throw new BatchAnalysisError("ArgumentError: SQL is empty.", 0);
|
|
2389
|
-
}
|
|
2390
|
-
if (statements.length === 1) {
|
|
2391
|
-
const t = statements[0].type;
|
|
2392
|
-
if (t === "SET_VARIABLE" || t === "DECLARE_VARIABLE") {
|
|
2393
|
-
const verb = t === "SET_VARIABLE" ? "SET" : "DECLARE";
|
|
2394
|
-
throw new BatchAnalysisError(`ArgumentError: ${verb} variable requires a batch.`, 0);
|
|
2395
|
-
}
|
|
2396
|
-
if (t === "CREATE_TEMP_TABLE" || t === "DROP_TEMP_TABLE") {
|
|
2397
|
-
const verb = t === "CREATE_TEMP_TABLE" ? "CREATE TEMP TABLE" : "DROP TEMP TABLE";
|
|
2398
|
-
throw new BatchAnalysisError(
|
|
2399
|
-
`ArgumentError: ${verb} requires a batch (temp tables are batch-scoped).`,
|
|
2400
|
-
0
|
|
2401
|
-
);
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
const defined = /* @__PURE__ */ new Map();
|
|
2405
|
-
const createdOrder = [];
|
|
2406
|
-
const results = [];
|
|
2407
|
-
const variableDefs = /* @__PURE__ */ new Map();
|
|
2408
|
-
const variableOrder = [];
|
|
2409
|
-
statements.forEach((stmt, index) => {
|
|
2410
|
-
const statementType = getStatementType(stmt);
|
|
2411
|
-
const created = [];
|
|
2412
|
-
const dropped = [];
|
|
2413
|
-
const refs = /* @__PURE__ */ new Set();
|
|
2414
|
-
const stmtAppIds = /* @__PURE__ */ new Set();
|
|
2415
|
-
const dependsOn = /* @__PURE__ */ new Set();
|
|
2416
|
-
const variableRefs = /* @__PURE__ */ new Set();
|
|
2417
|
-
collectVariableRefs(stmt, variableRefs);
|
|
2418
|
-
for (const name of variableRefs) {
|
|
2419
|
-
const def = variableDefs.get(name);
|
|
2420
|
-
if (def === void 0) {
|
|
2421
|
-
throw new BatchAnalysisError(
|
|
2422
|
-
`ParseError: variable @${name} is not defined before statement ${index + 1}.`,
|
|
2423
|
-
index
|
|
2424
|
-
);
|
|
2425
|
-
}
|
|
2426
|
-
def.referencedBy.push(index);
|
|
2427
|
-
}
|
|
2428
|
-
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
2429
|
-
if (variableDefs.has(stmt.name)) {
|
|
2430
|
-
throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
|
|
2431
|
-
}
|
|
2432
|
-
variableDefs.set(stmt.name, { index, referencedBy: [] });
|
|
2433
|
-
variableOrder.push(stmt.name);
|
|
2434
|
-
if (variableOrder.length > MAX_BATCH_VARIABLES) {
|
|
2435
|
-
throw new BatchAnalysisError(
|
|
2436
|
-
`ParseError: batch exceeds ${MAX_BATCH_VARIABLES} variables.`,
|
|
2437
|
-
index
|
|
2438
|
-
);
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
2442
|
-
collectRefs(stmt.query, refs, stmtAppIds);
|
|
2443
|
-
} else if (stmt.type === "DROP_TEMP_TABLE") {
|
|
2444
|
-
} else {
|
|
2445
|
-
collectRefs(stmt, refs, stmtAppIds);
|
|
2446
|
-
}
|
|
2447
|
-
let tempOnlySource = false;
|
|
2448
|
-
if (stmt.type === "INSERT_SELECT" || stmt.type === "UPSERT_SELECT") {
|
|
2449
|
-
const srcTemp = /* @__PURE__ */ new Set();
|
|
2450
|
-
const srcApps = /* @__PURE__ */ new Set();
|
|
2451
|
-
collectRefs(stmt.select, srcTemp, srcApps);
|
|
2452
|
-
tempOnlySource = srcTemp.size > 0 && srcApps.size === 0;
|
|
2453
|
-
}
|
|
2454
|
-
for (const name of refs) {
|
|
2455
|
-
const at = defined.get(name);
|
|
2456
|
-
if (at === void 0) {
|
|
2457
|
-
throw new BatchAnalysisError(
|
|
2458
|
-
`ParseError: temp table ${name} is not defined in this batch.`,
|
|
2459
|
-
index
|
|
2460
|
-
);
|
|
2461
|
-
}
|
|
2462
|
-
dependsOn.add(at);
|
|
2463
|
-
}
|
|
2464
|
-
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
2465
|
-
if (defined.has(stmt.name)) {
|
|
2466
|
-
throw new BatchAnalysisError(
|
|
2467
|
-
`ParseError: temp table ${stmt.name} is already defined.`,
|
|
2468
|
-
index
|
|
2469
|
-
);
|
|
2470
|
-
}
|
|
2471
|
-
defined.set(stmt.name, index);
|
|
2472
|
-
createdOrder.push(stmt.name);
|
|
2473
|
-
created.push(stmt.name);
|
|
2474
|
-
if (defined.size > MAX_TEMP_TABLES) {
|
|
2475
|
-
throw new BatchAnalysisError(
|
|
2476
|
-
`ParseError: batch exceeds ${MAX_TEMP_TABLES} temp tables.`,
|
|
2477
|
-
index
|
|
2478
|
-
);
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
2482
|
-
const at = defined.get(stmt.name);
|
|
2483
|
-
if (at === void 0) {
|
|
2484
|
-
throw new BatchAnalysisError(
|
|
2485
|
-
`ParseError: temp table ${stmt.name} is not defined in this batch.`,
|
|
2486
|
-
index
|
|
2487
|
-
);
|
|
2488
|
-
}
|
|
2489
|
-
dependsOn.add(at);
|
|
2490
|
-
dropped.push(stmt.name);
|
|
2491
|
-
defined.delete(stmt.name);
|
|
2492
|
-
}
|
|
2493
|
-
results.push({
|
|
2494
|
-
index,
|
|
2495
|
-
statementType,
|
|
2496
|
-
isDml: isDmlType(statementType),
|
|
2497
|
-
isReadOnly: isReadOnlyType(statementType),
|
|
2498
|
-
hasWhere: hasWhereClause(stmt),
|
|
2499
|
-
insertValuesCount: getInsertValuesCount(stmt),
|
|
2500
|
-
appIds: [...stmtAppIds].sort((a, b) => a - b),
|
|
2501
|
-
tempTablesCreated: created,
|
|
2502
|
-
tempTablesReferenced: [...refs],
|
|
2503
|
-
tempTablesDropped: dropped,
|
|
2504
|
-
dependsOn: [...dependsOn].sort((a, b) => a - b),
|
|
2505
|
-
tempOnlySource,
|
|
2506
|
-
targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null
|
|
2507
|
-
});
|
|
2508
|
-
});
|
|
2509
|
-
const containsDml = results.some((r) => r.isDml);
|
|
2510
|
-
const variables = variableOrder.map((name) => ({
|
|
2511
|
-
name,
|
|
2512
|
-
referencedBy: [...variableDefs.get(name).referencedBy]
|
|
2513
|
-
}));
|
|
2514
|
-
return {
|
|
2515
|
-
statementCount: statements.length,
|
|
2516
|
-
isReadOnlyBatch: !containsDml && results.every((r) => r.isReadOnly),
|
|
2517
|
-
containsDml,
|
|
2518
|
-
tempTables: createdOrder,
|
|
2519
|
-
variables,
|
|
2520
|
-
warnings: variables.filter((v) => v.referencedBy.length === 0).map((v) => `variable @${v.name} is never used.`),
|
|
2521
|
-
statements: results
|
|
2522
|
-
};
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
// src/core/batchVariables.ts
|
|
2526
|
-
var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
2527
|
-
function normalizeBatchVariableName(name) {
|
|
2528
|
-
if (!VARIABLE_NAME_RE.test(name)) {
|
|
2529
|
-
throw new Error(
|
|
2530
|
-
`ArgumentError: invalid variable name "${name}". Use a name without @ matching [A-Za-z_][A-Za-z0-9_]{0,63}.`
|
|
2531
|
-
);
|
|
2532
|
-
}
|
|
2533
|
-
return name.toLowerCase();
|
|
2534
|
-
}
|
|
2535
|
-
function normalizeBatchVariables(input) {
|
|
2536
|
-
const normalized = /* @__PURE__ */ Object.create(null);
|
|
2537
|
-
for (const [rawName, value] of Object.entries(input ?? {})) {
|
|
2538
|
-
const name = normalizeBatchVariableName(rawName);
|
|
2539
|
-
if (Object.prototype.hasOwnProperty.call(normalized, name)) {
|
|
2540
|
-
throw new Error(`ArgumentError: variable "${rawName}" is specified more than once.`);
|
|
2541
|
-
}
|
|
2542
|
-
normalized[name] = value;
|
|
2543
|
-
}
|
|
2544
|
-
return normalized;
|
|
2545
|
-
}
|
|
2546
|
-
function validateDeclaredBatchVariables(statements, input) {
|
|
2547
|
-
const normalized = normalizeBatchVariables(input);
|
|
2548
|
-
const declared = new Set(
|
|
2549
|
-
statements.filter((stmt) => stmt.type === "DECLARE_VARIABLE").map((stmt) => stmt.name)
|
|
2550
|
-
);
|
|
2551
|
-
for (const name of Object.keys(normalized)) {
|
|
2552
|
-
if (!declared.has(name)) {
|
|
2553
|
-
throw new Error(`ArgumentError: injected variable @${name} is not declared.`);
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
return normalized;
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
// src/core/scalarCompare.ts
|
|
2560
|
-
function compareScalarValues(op, leftStr, rightStr) {
|
|
2561
|
-
if (op === "=") return leftStr === rightStr;
|
|
2562
|
-
if (op === "!=" || op === "<>") return leftStr !== rightStr;
|
|
2563
|
-
const rightNum = Number(rightStr);
|
|
2564
|
-
if (leftStr === "" && rightStr !== "" && Number.isFinite(rightNum)) {
|
|
2565
|
-
return op === "<" || op === "<=";
|
|
2566
|
-
}
|
|
2567
|
-
const leftNum = Number(leftStr);
|
|
2568
|
-
const numeric = !Number.isNaN(leftNum) && !Number.isNaN(rightNum);
|
|
2569
|
-
switch (op) {
|
|
2570
|
-
case ">":
|
|
2571
|
-
return numeric ? leftNum > rightNum : leftStr > rightStr;
|
|
2572
|
-
case "<":
|
|
2573
|
-
return numeric ? leftNum < rightNum : leftStr < rightStr;
|
|
2574
|
-
case ">=":
|
|
2575
|
-
return numeric ? leftNum >= rightNum : leftStr >= rightStr;
|
|
2576
|
-
case "<=":
|
|
2577
|
-
return numeric ? leftNum <= rightNum : leftStr <= rightStr;
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
|
|
2581
2374
|
// src/engine/pushDownNot.ts
|
|
2582
2375
|
function pushDownNot(expr) {
|
|
2583
2376
|
switch (expr.type) {
|
|
@@ -2627,6 +2420,10 @@ function negateOp(op) {
|
|
|
2627
2420
|
return "NOT_LIKE";
|
|
2628
2421
|
case "NOT_LIKE":
|
|
2629
2422
|
return "LIKE";
|
|
2423
|
+
case "KLIKE":
|
|
2424
|
+
return "NOT_KLIKE";
|
|
2425
|
+
case "NOT_KLIKE":
|
|
2426
|
+
return "KLIKE";
|
|
2630
2427
|
case "IN":
|
|
2631
2428
|
return "NOT_IN";
|
|
2632
2429
|
case "NOT_IN":
|
|
@@ -2641,6 +2438,9 @@ function likePatternHasWildcard(pattern) {
|
|
|
2641
2438
|
function isLike(where) {
|
|
2642
2439
|
return where.type === "BINARY" && (where.op === "LIKE" || where.op === "NOT_LIKE");
|
|
2643
2440
|
}
|
|
2441
|
+
function isKlike(where) {
|
|
2442
|
+
return where.type === "BINARY" && (where.op === "KLIKE" || where.op === "NOT_KLIKE");
|
|
2443
|
+
}
|
|
2644
2444
|
function whereHasLike(where) {
|
|
2645
2445
|
if (where === null) return false;
|
|
2646
2446
|
if (isLike(where)) return true;
|
|
@@ -2656,6 +2456,21 @@ function whereHasLike(where) {
|
|
|
2656
2456
|
return false;
|
|
2657
2457
|
}
|
|
2658
2458
|
}
|
|
2459
|
+
function whereHasKlike(where) {
|
|
2460
|
+
if (where === null) return false;
|
|
2461
|
+
if (isKlike(where)) return true;
|
|
2462
|
+
switch (where.type) {
|
|
2463
|
+
case "LOGICAL":
|
|
2464
|
+
return whereHasKlike(where.left) || whereHasKlike(where.right);
|
|
2465
|
+
case "NOT":
|
|
2466
|
+
case "GROUP":
|
|
2467
|
+
return whereHasKlike(where.expr);
|
|
2468
|
+
case "BINARY":
|
|
2469
|
+
case "NULL_CHECK":
|
|
2470
|
+
case "EXISTS":
|
|
2471
|
+
return false;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2659
2474
|
|
|
2660
2475
|
// src/converter/whereToKintone.ts
|
|
2661
2476
|
function whereToKintone(expr) {
|
|
@@ -2704,6 +2519,10 @@ function convertOp(op) {
|
|
|
2704
2519
|
return "like";
|
|
2705
2520
|
case "NOT_LIKE":
|
|
2706
2521
|
return "not like";
|
|
2522
|
+
case "KLIKE":
|
|
2523
|
+
return "like";
|
|
2524
|
+
case "NOT_KLIKE":
|
|
2525
|
+
return "not like";
|
|
2707
2526
|
case "IN":
|
|
2708
2527
|
return "in";
|
|
2709
2528
|
case "NOT_IN":
|
|
@@ -3225,76 +3044,504 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3225
3044
|
break;
|
|
3226
3045
|
}
|
|
3227
3046
|
}
|
|
3228
|
-
for (const join2 of stmt.joins) {
|
|
3229
|
-
addFieldRef(join2.on.left.field, join2.on.left.tableAlias, "where");
|
|
3230
|
-
addFieldRef(join2.on.right.field, join2.on.right.tableAlias, "where");
|
|
3047
|
+
for (const join2 of stmt.joins) {
|
|
3048
|
+
addFieldRef(join2.on.left.field, join2.on.left.tableAlias, "where");
|
|
3049
|
+
addFieldRef(join2.on.right.field, join2.on.right.tableAlias, "where");
|
|
3050
|
+
}
|
|
3051
|
+
walkWhere(stmt.where, "where");
|
|
3052
|
+
for (const gk of stmt.groupBy) walkGroupByKey(gk);
|
|
3053
|
+
walkWhere(stmt.having, "having");
|
|
3054
|
+
for (const ob of stmt.orderBy) walkOrderByKey(ob.key);
|
|
3055
|
+
return states;
|
|
3056
|
+
}
|
|
3057
|
+
function collectSelectOutputNames(columns) {
|
|
3058
|
+
const names = /* @__PURE__ */ new Set();
|
|
3059
|
+
for (const col of columns) {
|
|
3060
|
+
if (col.type === "FIELD" && col.alias) {
|
|
3061
|
+
names.add(col.alias);
|
|
3062
|
+
continue;
|
|
3063
|
+
}
|
|
3064
|
+
if (col.type === "LITERAL_COL") {
|
|
3065
|
+
names.add(col.alias ?? `'${col.value}'`);
|
|
3066
|
+
continue;
|
|
3067
|
+
}
|
|
3068
|
+
if (col.type === "AGGREGATE") {
|
|
3069
|
+
if (col.alias) names.add(col.alias);
|
|
3070
|
+
else names.add(aggregateSyntheticName(col.func, col.distinct, col.arg));
|
|
3071
|
+
continue;
|
|
3072
|
+
}
|
|
3073
|
+
if (col.type === "ARITH_AGG_COL") {
|
|
3074
|
+
if (col.alias) names.add(col.alias);
|
|
3075
|
+
continue;
|
|
3076
|
+
}
|
|
3077
|
+
if (col.type === "ARITH_COL") {
|
|
3078
|
+
if (col.alias) names.add(col.alias);
|
|
3079
|
+
continue;
|
|
3080
|
+
}
|
|
3081
|
+
if (col.type === "CASE_COL") {
|
|
3082
|
+
names.add(col.alias ?? "case");
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3085
|
+
if (col.type === "STRFUNC_COL") {
|
|
3086
|
+
if (col.alias) names.add(col.alias);
|
|
3087
|
+
continue;
|
|
3088
|
+
}
|
|
3089
|
+
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
3090
|
+
names.add(col.alias ?? "(subquery)");
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
return names;
|
|
3094
|
+
}
|
|
3095
|
+
function aggregateSyntheticName(func, distinct, arg) {
|
|
3096
|
+
const argStr = arg.type === "WILDCARD" ? "*" : arithNodeLabel(arg);
|
|
3097
|
+
return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
|
|
3098
|
+
}
|
|
3099
|
+
function arithNodeLabel(node) {
|
|
3100
|
+
if (node.type === "FIELD_REF") return node.field;
|
|
3101
|
+
if (node.type === "NUMBER") return String(node.value);
|
|
3102
|
+
if (node.type === "STRING_FUNC") return stringFuncLabel(node);
|
|
3103
|
+
return `(${arithNodeLabel(node.left)}${node.op}${arithNodeLabel(node.right)})`;
|
|
3104
|
+
}
|
|
3105
|
+
function stringFuncLabel(expr) {
|
|
3106
|
+
const args = expr.args.map((a) => {
|
|
3107
|
+
if (a.type === "STRING") return `'${a.value}'`;
|
|
3108
|
+
if (a.type === "STRING_FUNC") return stringFuncLabel(a);
|
|
3109
|
+
if (a.type === "AGG_REF") return aggregateSyntheticName(a.func, a.distinct, a.arg);
|
|
3110
|
+
if (a.type === "AGG_ARITH") return "agg_arith";
|
|
3111
|
+
return arithNodeLabel(a);
|
|
3112
|
+
});
|
|
3113
|
+
return `${expr.func}(${args.join(",")})`;
|
|
3114
|
+
}
|
|
3115
|
+
function isAggregateSyntheticName(name) {
|
|
3116
|
+
return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
// src/core/klikeValidation.ts
|
|
3120
|
+
var KlikeValidationError = class extends Error {
|
|
3121
|
+
constructor(message) {
|
|
3122
|
+
super(`ArgumentError: ${message}`);
|
|
3123
|
+
this.name = "ArgumentError";
|
|
3124
|
+
}
|
|
3125
|
+
};
|
|
3126
|
+
function validateKlikeStatement(stmt) {
|
|
3127
|
+
validateStatement(stmt);
|
|
3128
|
+
}
|
|
3129
|
+
function validateStatement(stmt) {
|
|
3130
|
+
switch (stmt.type) {
|
|
3131
|
+
case "SELECT":
|
|
3132
|
+
validateSelect(stmt);
|
|
3133
|
+
return;
|
|
3134
|
+
case "UNION":
|
|
3135
|
+
validateUnion(stmt);
|
|
3136
|
+
return;
|
|
3137
|
+
case "WITH":
|
|
3138
|
+
validateWith(stmt);
|
|
3139
|
+
return;
|
|
3140
|
+
case "EXPLAIN":
|
|
3141
|
+
validateStatement(stmt.query);
|
|
3142
|
+
return;
|
|
3143
|
+
case "CREATE_TEMP_TABLE":
|
|
3144
|
+
validateSelectLike(stmt.query);
|
|
3145
|
+
return;
|
|
3146
|
+
case "SET_VARIABLE":
|
|
3147
|
+
case "DECLARE_VARIABLE":
|
|
3148
|
+
case "ASSERT":
|
|
3149
|
+
validateNestedSelects(stmt);
|
|
3150
|
+
return;
|
|
3151
|
+
case "INSERT":
|
|
3152
|
+
case "INSERT_SELECT":
|
|
3153
|
+
case "UPSERT":
|
|
3154
|
+
case "UPSERT_SELECT":
|
|
3155
|
+
case "UPDATE":
|
|
3156
|
+
case "DELETE":
|
|
3157
|
+
case "REORDER":
|
|
3158
|
+
if (containsKlike(stmt)) {
|
|
3159
|
+
throw new KlikeValidationError(
|
|
3160
|
+
"KLIKE / NOT KLIKE \u306F v1 \u3067\u306F\u5168 DML\uFF08UPDATE / DELETE / INSERT / UPSERT / REORDER\uFF09\u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
3161
|
+
);
|
|
3162
|
+
}
|
|
3163
|
+
return;
|
|
3164
|
+
case "SHOW_APPS":
|
|
3165
|
+
case "DESCRIBE":
|
|
3166
|
+
case "DROP_TEMP_TABLE":
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
function validateSelectLike(query) {
|
|
3171
|
+
if (query.type === "SELECT") validateSelect(query);
|
|
3172
|
+
else if (query.type === "UNION") validateUnion(query);
|
|
3173
|
+
else validateWith(query);
|
|
3174
|
+
}
|
|
3175
|
+
function validateUnion(stmt) {
|
|
3176
|
+
validateSelectLike(stmt.left);
|
|
3177
|
+
validateSelect(stmt.right);
|
|
3178
|
+
}
|
|
3179
|
+
function validateWith(stmt) {
|
|
3180
|
+
const inlined = buildEffectiveInlineSelect(stmt);
|
|
3181
|
+
if (inlined !== null) {
|
|
3182
|
+
validateSelect(inlined);
|
|
3183
|
+
return;
|
|
3184
|
+
}
|
|
3185
|
+
for (const cte of stmt.ctes) {
|
|
3186
|
+
if (cte.query.type === "SELECT") validateSelect(cte.query);
|
|
3187
|
+
else if (cte.query.type === "UNION") validateUnion(cte.query);
|
|
3188
|
+
}
|
|
3189
|
+
validateSelectLike(stmt.query);
|
|
3190
|
+
}
|
|
3191
|
+
function validateSelect(stmt) {
|
|
3192
|
+
validateOwnKlikeExpressions(stmt);
|
|
3193
|
+
if (whereHasKlike(stmt.where)) {
|
|
3194
|
+
const inMemorySource = stmt.from.cteName !== null || stmt.joins.some((join2) => join2.table.cteName !== null);
|
|
3195
|
+
if (inMemorySource || resolveSelectMode(stmt) === "FULL_SCAN") {
|
|
3196
|
+
throw new KlikeValidationError(
|
|
3197
|
+
"KLIKE / NOT KLIKE \u306F kintone \u3078\u62BC\u3057\u4E0B\u3052\u308B SIMPLE SELECT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\u3053\u306E SELECT \u306F FULL_SCAN \u306B\u306A\u308A\u307E\u3059"
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
validateNestedSelects(stmt);
|
|
3202
|
+
}
|
|
3203
|
+
function validateOwnKlikeExpressions(stmt) {
|
|
3204
|
+
walkWithoutNestedSelects(stmt, (where) => {
|
|
3205
|
+
if (!isKlike(where)) return;
|
|
3206
|
+
if (!isDescendantOf(stmt.where, where)) {
|
|
3207
|
+
throw new KlikeValidationError("KLIKE / NOT KLIKE \u306F SELECT \u306E WHERE \u53E5\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059");
|
|
3208
|
+
}
|
|
3209
|
+
const right = where.right;
|
|
3210
|
+
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
3211
|
+
throw new KlikeValidationError(
|
|
3212
|
+
"KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u6587\u5B57\u5217\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
3213
|
+
);
|
|
3214
|
+
}
|
|
3215
|
+
if (right.type === "STRING" && right.value.includes("%")) {
|
|
3216
|
+
throw new KlikeValidationError(
|
|
3217
|
+
"KLIKE / NOT KLIKE \u306E\u691C\u7D22\u8A9E\u306B % \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002SQL \u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u691C\u7D22\u306B\u306F LIKE \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
3218
|
+
);
|
|
3219
|
+
}
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
function validateNestedSelects(node) {
|
|
3223
|
+
walkObjects(node, (obj) => {
|
|
3224
|
+
if (obj.type === "SELECT") validateSelect(obj);
|
|
3225
|
+
}, true);
|
|
3226
|
+
}
|
|
3227
|
+
function buildEffectiveInlineSelect(stmt) {
|
|
3228
|
+
if (stmt.ctes.length !== 1) return null;
|
|
3229
|
+
const cte = stmt.ctes[0];
|
|
3230
|
+
if (cte.query.type !== "SELECT" || resolveSelectMode(cte.query) !== "SIMPLE") return null;
|
|
3231
|
+
if (stmt.query.type !== "SELECT") return null;
|
|
3232
|
+
const final = stmt.query;
|
|
3233
|
+
if (final.from.cteName !== cte.name || final.joins.length > 0) return null;
|
|
3234
|
+
if (final.groupBy.length > 0 || final.distinct) return null;
|
|
3235
|
+
if (final.columns.some(
|
|
3236
|
+
(column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
|
|
3237
|
+
)) return null;
|
|
3238
|
+
const where = cte.query.where === null ? final.where : final.where === null ? cte.query.where : { type: "LOGICAL", op: "AND", left: cte.query.where, right: final.where };
|
|
3239
|
+
const columns = final.columns.every((column) => column.type === "WILDCARD") ? cte.query.columns : final.columns;
|
|
3240
|
+
return {
|
|
3241
|
+
type: "SELECT",
|
|
3242
|
+
from: cte.query.from,
|
|
3243
|
+
joins: [],
|
|
3244
|
+
columns,
|
|
3245
|
+
where,
|
|
3246
|
+
groupBy: [],
|
|
3247
|
+
having: null,
|
|
3248
|
+
orderBy: final.orderBy.length > 0 ? final.orderBy : cte.query.orderBy,
|
|
3249
|
+
limit: final.limit ?? cte.query.limit,
|
|
3250
|
+
offset: final.offset ?? cte.query.offset,
|
|
3251
|
+
distinct: false
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
function containsKlike(node) {
|
|
3255
|
+
let found = false;
|
|
3256
|
+
walkObjects(node, (obj) => {
|
|
3257
|
+
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) found = true;
|
|
3258
|
+
});
|
|
3259
|
+
return found;
|
|
3260
|
+
}
|
|
3261
|
+
function isDescendantOf(root, target) {
|
|
3262
|
+
if (root === null) return false;
|
|
3263
|
+
if (root === target) return true;
|
|
3264
|
+
switch (root.type) {
|
|
3265
|
+
case "LOGICAL":
|
|
3266
|
+
return isDescendantOf(root.left, target) || isDescendantOf(root.right, target);
|
|
3267
|
+
case "NOT":
|
|
3268
|
+
case "GROUP":
|
|
3269
|
+
return isDescendantOf(root.expr, target);
|
|
3270
|
+
case "BINARY":
|
|
3271
|
+
case "NULL_CHECK":
|
|
3272
|
+
case "EXISTS":
|
|
3273
|
+
return false;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
function walkWithoutNestedSelects(node, visitWhere) {
|
|
3277
|
+
if (Array.isArray(node)) {
|
|
3278
|
+
for (const value of node) walkWithoutNestedSelects(value, visitWhere);
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3281
|
+
if (node === null || typeof node !== "object") return;
|
|
3282
|
+
const obj = node;
|
|
3283
|
+
if (obj.type === "BINARY" || obj.type === "NULL_CHECK" || obj.type === "LOGICAL" || obj.type === "NOT" || obj.type === "GROUP" || obj.type === "EXISTS") {
|
|
3284
|
+
visitWhere(obj);
|
|
3285
|
+
}
|
|
3286
|
+
for (const value of Object.values(obj)) {
|
|
3287
|
+
if (value !== node && isSelectObject(value)) continue;
|
|
3288
|
+
walkWithoutNestedSelects(value, visitWhere);
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
function walkObjects(node, visit, skipRoot = false) {
|
|
3292
|
+
if (Array.isArray(node)) {
|
|
3293
|
+
for (const value of node) walkObjects(value, visit);
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
if (node === null || typeof node !== "object") return;
|
|
3297
|
+
const obj = node;
|
|
3298
|
+
if (!skipRoot) visit(obj);
|
|
3299
|
+
for (const value of Object.values(obj)) walkObjects(value, visit);
|
|
3300
|
+
}
|
|
3301
|
+
function isSelectObject(value) {
|
|
3302
|
+
return value !== null && typeof value === "object" && value.type === "SELECT";
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
// src/core/batch.ts
|
|
3306
|
+
var MAX_TEMP_TABLES = 16;
|
|
3307
|
+
var MAX_BATCH_VARIABLES = 64;
|
|
3308
|
+
var BatchAnalysisError = class extends Error {
|
|
3309
|
+
constructor(message, statementIndex) {
|
|
3310
|
+
super(message);
|
|
3311
|
+
this.statementIndex = statementIndex;
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
function collectRefs(node, tempRefs, appIds) {
|
|
3315
|
+
if (Array.isArray(node)) {
|
|
3316
|
+
for (const v of node) collectRefs(v, tempRefs, appIds);
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
if (node !== null && typeof node === "object") {
|
|
3320
|
+
const obj = node;
|
|
3321
|
+
const cte = obj["cteName"];
|
|
3322
|
+
if (typeof cte === "string" && cte.startsWith("#")) tempRefs.add(cte);
|
|
3323
|
+
const appId = obj["appId"];
|
|
3324
|
+
if (typeof appId === "number" && appId > 0) appIds.add(appId);
|
|
3325
|
+
for (const v of Object.values(obj)) collectRefs(v, tempRefs, appIds);
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
function collectVariableRefs(node, refs) {
|
|
3329
|
+
if (Array.isArray(node)) {
|
|
3330
|
+
for (const v of node) collectVariableRefs(v, refs);
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
if (node !== null && typeof node === "object") {
|
|
3334
|
+
const obj = node;
|
|
3335
|
+
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
|
|
3336
|
+
refs.add(obj["name"]);
|
|
3337
|
+
return;
|
|
3338
|
+
}
|
|
3339
|
+
for (const v of Object.values(obj)) collectVariableRefs(v, refs);
|
|
3231
3340
|
}
|
|
3232
|
-
walkWhere(stmt.where, "where");
|
|
3233
|
-
for (const gk of stmt.groupBy) walkGroupByKey(gk);
|
|
3234
|
-
walkWhere(stmt.having, "having");
|
|
3235
|
-
for (const ob of stmt.orderBy) walkOrderByKey(ob.key);
|
|
3236
|
-
return states;
|
|
3237
3341
|
}
|
|
3238
|
-
function
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3342
|
+
function analyzeBatch(statements) {
|
|
3343
|
+
if (statements.length === 0) {
|
|
3344
|
+
throw new BatchAnalysisError("ArgumentError: SQL is empty.", 0);
|
|
3345
|
+
}
|
|
3346
|
+
statements.forEach((stmt, index) => {
|
|
3347
|
+
try {
|
|
3348
|
+
validateKlikeStatement(stmt);
|
|
3349
|
+
} catch (error) {
|
|
3350
|
+
if (error instanceof KlikeValidationError) {
|
|
3351
|
+
throw new BatchAnalysisError(error.message, index);
|
|
3352
|
+
}
|
|
3353
|
+
throw error;
|
|
3244
3354
|
}
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3355
|
+
});
|
|
3356
|
+
if (statements.length === 1) {
|
|
3357
|
+
const t = statements[0].type;
|
|
3358
|
+
if (t === "SET_VARIABLE" || t === "DECLARE_VARIABLE") {
|
|
3359
|
+
const verb = t === "SET_VARIABLE" ? "SET" : "DECLARE";
|
|
3360
|
+
throw new BatchAnalysisError(`ArgumentError: ${verb} variable requires a batch.`, 0);
|
|
3248
3361
|
}
|
|
3249
|
-
if (
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3362
|
+
if (t === "CREATE_TEMP_TABLE" || t === "DROP_TEMP_TABLE") {
|
|
3363
|
+
const verb = t === "CREATE_TEMP_TABLE" ? "CREATE TEMP TABLE" : "DROP TEMP TABLE";
|
|
3364
|
+
throw new BatchAnalysisError(
|
|
3365
|
+
`ArgumentError: ${verb} requires a batch (temp tables are batch-scoped).`,
|
|
3366
|
+
0
|
|
3367
|
+
);
|
|
3253
3368
|
}
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3369
|
+
}
|
|
3370
|
+
const defined = /* @__PURE__ */ new Map();
|
|
3371
|
+
const createdOrder = [];
|
|
3372
|
+
const results = [];
|
|
3373
|
+
const variableDefs = /* @__PURE__ */ new Map();
|
|
3374
|
+
const variableOrder = [];
|
|
3375
|
+
statements.forEach((stmt, index) => {
|
|
3376
|
+
const statementType = getStatementType(stmt);
|
|
3377
|
+
const created = [];
|
|
3378
|
+
const dropped = [];
|
|
3379
|
+
const refs = /* @__PURE__ */ new Set();
|
|
3380
|
+
const stmtAppIds = /* @__PURE__ */ new Set();
|
|
3381
|
+
const dependsOn = /* @__PURE__ */ new Set();
|
|
3382
|
+
const variableRefs = /* @__PURE__ */ new Set();
|
|
3383
|
+
collectVariableRefs(stmt, variableRefs);
|
|
3384
|
+
for (const name of variableRefs) {
|
|
3385
|
+
const def = variableDefs.get(name);
|
|
3386
|
+
if (def === void 0) {
|
|
3387
|
+
throw new BatchAnalysisError(
|
|
3388
|
+
`ParseError: variable @${name} is not defined before statement ${index + 1}.`,
|
|
3389
|
+
index
|
|
3390
|
+
);
|
|
3391
|
+
}
|
|
3392
|
+
def.referencedBy.push(index);
|
|
3257
3393
|
}
|
|
3258
|
-
if (
|
|
3259
|
-
if (
|
|
3260
|
-
|
|
3394
|
+
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
3395
|
+
if (variableDefs.has(stmt.name)) {
|
|
3396
|
+
throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
|
|
3397
|
+
}
|
|
3398
|
+
variableDefs.set(stmt.name, { index, referencedBy: [] });
|
|
3399
|
+
variableOrder.push(stmt.name);
|
|
3400
|
+
if (variableOrder.length > MAX_BATCH_VARIABLES) {
|
|
3401
|
+
throw new BatchAnalysisError(
|
|
3402
|
+
`ParseError: batch exceeds ${MAX_BATCH_VARIABLES} variables.`,
|
|
3403
|
+
index
|
|
3404
|
+
);
|
|
3405
|
+
}
|
|
3261
3406
|
}
|
|
3262
|
-
if (
|
|
3263
|
-
|
|
3264
|
-
|
|
3407
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
3408
|
+
collectRefs(stmt.query, refs, stmtAppIds);
|
|
3409
|
+
} else if (stmt.type === "DROP_TEMP_TABLE") {
|
|
3410
|
+
} else {
|
|
3411
|
+
collectRefs(stmt, refs, stmtAppIds);
|
|
3265
3412
|
}
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3413
|
+
let tempOnlySource = false;
|
|
3414
|
+
if (stmt.type === "INSERT_SELECT" || stmt.type === "UPSERT_SELECT") {
|
|
3415
|
+
const srcTemp = /* @__PURE__ */ new Set();
|
|
3416
|
+
const srcApps = /* @__PURE__ */ new Set();
|
|
3417
|
+
collectRefs(stmt.select, srcTemp, srcApps);
|
|
3418
|
+
tempOnlySource = srcTemp.size > 0 && srcApps.size === 0;
|
|
3269
3419
|
}
|
|
3270
|
-
|
|
3271
|
-
|
|
3420
|
+
for (const name of refs) {
|
|
3421
|
+
const at = defined.get(name);
|
|
3422
|
+
if (at === void 0) {
|
|
3423
|
+
throw new BatchAnalysisError(
|
|
3424
|
+
`ParseError: temp table ${name} is not defined in this batch.`,
|
|
3425
|
+
index
|
|
3426
|
+
);
|
|
3427
|
+
}
|
|
3428
|
+
dependsOn.add(at);
|
|
3272
3429
|
}
|
|
3273
|
-
|
|
3274
|
-
|
|
3430
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
3431
|
+
if (defined.has(stmt.name)) {
|
|
3432
|
+
throw new BatchAnalysisError(
|
|
3433
|
+
`ParseError: temp table ${stmt.name} is already defined.`,
|
|
3434
|
+
index
|
|
3435
|
+
);
|
|
3436
|
+
}
|
|
3437
|
+
defined.set(stmt.name, index);
|
|
3438
|
+
createdOrder.push(stmt.name);
|
|
3439
|
+
created.push(stmt.name);
|
|
3440
|
+
if (defined.size > MAX_TEMP_TABLES) {
|
|
3441
|
+
throw new BatchAnalysisError(
|
|
3442
|
+
`ParseError: batch exceeds ${MAX_TEMP_TABLES} temp tables.`,
|
|
3443
|
+
index
|
|
3444
|
+
);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
3448
|
+
const at = defined.get(stmt.name);
|
|
3449
|
+
if (at === void 0) {
|
|
3450
|
+
throw new BatchAnalysisError(
|
|
3451
|
+
`ParseError: temp table ${stmt.name} is not defined in this batch.`,
|
|
3452
|
+
index
|
|
3453
|
+
);
|
|
3454
|
+
}
|
|
3455
|
+
dependsOn.add(at);
|
|
3456
|
+
dropped.push(stmt.name);
|
|
3457
|
+
defined.delete(stmt.name);
|
|
3458
|
+
}
|
|
3459
|
+
results.push({
|
|
3460
|
+
index,
|
|
3461
|
+
statementType,
|
|
3462
|
+
isDml: isDmlType(statementType),
|
|
3463
|
+
isReadOnly: isReadOnlyType(statementType),
|
|
3464
|
+
hasWhere: hasWhereClause(stmt),
|
|
3465
|
+
insertValuesCount: getInsertValuesCount(stmt),
|
|
3466
|
+
appIds: [...stmtAppIds].sort((a, b) => a - b),
|
|
3467
|
+
tempTablesCreated: created,
|
|
3468
|
+
tempTablesReferenced: [...refs],
|
|
3469
|
+
tempTablesDropped: dropped,
|
|
3470
|
+
dependsOn: [...dependsOn].sort((a, b) => a - b),
|
|
3471
|
+
tempOnlySource,
|
|
3472
|
+
targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null
|
|
3473
|
+
});
|
|
3474
|
+
});
|
|
3475
|
+
const containsDml = results.some((r) => r.isDml);
|
|
3476
|
+
const variables = variableOrder.map((name) => ({
|
|
3477
|
+
name,
|
|
3478
|
+
referencedBy: [...variableDefs.get(name).referencedBy]
|
|
3479
|
+
}));
|
|
3480
|
+
return {
|
|
3481
|
+
statementCount: statements.length,
|
|
3482
|
+
isReadOnlyBatch: !containsDml && results.every((r) => r.isReadOnly),
|
|
3483
|
+
containsDml,
|
|
3484
|
+
tempTables: createdOrder,
|
|
3485
|
+
variables,
|
|
3486
|
+
warnings: variables.filter((v) => v.referencedBy.length === 0).map((v) => `variable @${v.name} is never used.`),
|
|
3487
|
+
statements: results
|
|
3488
|
+
};
|
|
3275
3489
|
}
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3490
|
+
|
|
3491
|
+
// src/core/batchVariables.ts
|
|
3492
|
+
var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
3493
|
+
function normalizeBatchVariableName(name) {
|
|
3494
|
+
if (!VARIABLE_NAME_RE.test(name)) {
|
|
3495
|
+
throw new Error(
|
|
3496
|
+
`ArgumentError: invalid variable name "${name}". Use a name without @ matching [A-Za-z_][A-Za-z0-9_]{0,63}.`
|
|
3497
|
+
);
|
|
3498
|
+
}
|
|
3499
|
+
return name.toLowerCase();
|
|
3279
3500
|
}
|
|
3280
|
-
function
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3501
|
+
function normalizeBatchVariables(input) {
|
|
3502
|
+
const normalized = /* @__PURE__ */ Object.create(null);
|
|
3503
|
+
for (const [rawName, value] of Object.entries(input ?? {})) {
|
|
3504
|
+
const name = normalizeBatchVariableName(rawName);
|
|
3505
|
+
if (Object.prototype.hasOwnProperty.call(normalized, name)) {
|
|
3506
|
+
throw new Error(`ArgumentError: variable "${rawName}" is specified more than once.`);
|
|
3507
|
+
}
|
|
3508
|
+
normalized[name] = value;
|
|
3509
|
+
}
|
|
3510
|
+
return normalized;
|
|
3285
3511
|
}
|
|
3286
|
-
function
|
|
3287
|
-
const
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3512
|
+
function validateDeclaredBatchVariables(statements, input) {
|
|
3513
|
+
const normalized = normalizeBatchVariables(input);
|
|
3514
|
+
const declared = new Set(
|
|
3515
|
+
statements.filter((stmt) => stmt.type === "DECLARE_VARIABLE").map((stmt) => stmt.name)
|
|
3516
|
+
);
|
|
3517
|
+
for (const name of Object.keys(normalized)) {
|
|
3518
|
+
if (!declared.has(name)) {
|
|
3519
|
+
throw new Error(`ArgumentError: injected variable @${name} is not declared.`);
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
return normalized;
|
|
3295
3523
|
}
|
|
3296
|
-
|
|
3297
|
-
|
|
3524
|
+
|
|
3525
|
+
// src/core/scalarCompare.ts
|
|
3526
|
+
function compareScalarValues(op, leftStr, rightStr) {
|
|
3527
|
+
if (op === "=") return leftStr === rightStr;
|
|
3528
|
+
if (op === "!=" || op === "<>") return leftStr !== rightStr;
|
|
3529
|
+
const rightNum = Number(rightStr);
|
|
3530
|
+
if (leftStr === "" && rightStr !== "" && Number.isFinite(rightNum)) {
|
|
3531
|
+
return op === "<" || op === "<=";
|
|
3532
|
+
}
|
|
3533
|
+
const leftNum = Number(leftStr);
|
|
3534
|
+
const numeric = !Number.isNaN(leftNum) && !Number.isNaN(rightNum);
|
|
3535
|
+
switch (op) {
|
|
3536
|
+
case ">":
|
|
3537
|
+
return numeric ? leftNum > rightNum : leftStr > rightStr;
|
|
3538
|
+
case "<":
|
|
3539
|
+
return numeric ? leftNum < rightNum : leftStr < rightStr;
|
|
3540
|
+
case ">=":
|
|
3541
|
+
return numeric ? leftNum >= rightNum : leftStr >= rightStr;
|
|
3542
|
+
case "<=":
|
|
3543
|
+
return numeric ? leftNum <= rightNum : leftStr <= rightStr;
|
|
3544
|
+
}
|
|
3298
3545
|
}
|
|
3299
3546
|
|
|
3300
3547
|
// src/engine/evalFunc.ts
|
|
@@ -3553,6 +3800,9 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
|
|
|
3553
3800
|
const pattern = resolveValue(right, row, resolveFieldType);
|
|
3554
3801
|
return !matchLike(leftStr, pattern);
|
|
3555
3802
|
}
|
|
3803
|
+
if (op === "KLIKE" || op === "NOT_KLIKE") {
|
|
3804
|
+
throw new Error("KLIKE / NOT KLIKE \u306F JavaScript \u5074\u3067\u306F\u8A55\u4FA1\u3067\u304D\u307E\u305B\u3093\uFF08SIMPLE SELECT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\uFF09");
|
|
3805
|
+
}
|
|
3556
3806
|
const rightStr = resolveValue(right, row, resolveFieldType);
|
|
3557
3807
|
return compareScalarValues(op, leftStr, rightStr);
|
|
3558
3808
|
}
|
|
@@ -3708,6 +3958,11 @@ function matchLike(value, pattern) {
|
|
|
3708
3958
|
|
|
3709
3959
|
// src/converter/dmlToKintone.ts
|
|
3710
3960
|
function assertDmlWhereIsSafe(where) {
|
|
3961
|
+
if (whereHasKlike(where)) {
|
|
3962
|
+
throw new DmlConvertError(
|
|
3963
|
+
"UPDATE / DELETE \u306E WHERE \u306B KLIKE / NOT KLIKE \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002kintone \u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22\u306E\u6253\u3061\u5207\u308A\u3092\u691C\u51FA\u3067\u304D\u306A\u3044\u305F\u3081\u3001v1 \u3067\u306F\u5168 DML \u3067\u5B89\u5168\u4E0A\u62D2\u5426\u3057\u307E\u3057\u305F\u3002"
|
|
3964
|
+
);
|
|
3965
|
+
}
|
|
3711
3966
|
if (!whereHasLike(where)) return;
|
|
3712
3967
|
throw new DmlConvertError(
|
|
3713
3968
|
"UPDATE / DELETE \u306E WHERE \u306B LIKE / NOT LIKE \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002LIKE \u306F kSQL \u306E\u610F\u5473\u8AD6\u306B\u5F93\u3063\u3066 JS \u3067\u8A55\u4FA1\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u304C\u3001\u89AA\u30EC\u30B3\u30FC\u30C9 DML \u306B\u306F JS \u8A55\u4FA1\u7D4C\u8DEF\u304C\u306A\u3044\u305F\u3081\u3001\u5B89\u5168\u4E0A\u62D2\u5426\u3057\u307E\u3057\u305F\u3002SELECT \u3067\u5BFE\u8C61\u30EC\u30B3\u30FC\u30C9\u756A\u53F7\u3092\u78BA\u8A8D\u3057\u3001IN \u307E\u305F\u306F\u5B8C\u5168\u4E00\u81F4\u3067\u5BFE\u8C61\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
@@ -4937,6 +5192,7 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
4937
5192
|
if (unresolved !== null) {
|
|
4938
5193
|
throw new Error(`ParseError: variable @${unresolved} is not defined in a batch.`);
|
|
4939
5194
|
}
|
|
5195
|
+
validateKlikeStatement(stmt);
|
|
4940
5196
|
switch (stmt.type) {
|
|
4941
5197
|
case "SELECT":
|
|
4942
5198
|
return executeSelect(stmt, client, options, cacheContext);
|
|
@@ -5073,6 +5329,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
5073
5329
|
async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables) {
|
|
5074
5330
|
if (stmt.type === "SET_VARIABLE") {
|
|
5075
5331
|
const resolvedStmt2 = resolveVariableRefs(stmt, variables);
|
|
5332
|
+
validateKlikeStatement(resolvedStmt2);
|
|
5076
5333
|
if (resolvedStmt2.expr.type === "SCALAR_SUBQUERY") {
|
|
5077
5334
|
try {
|
|
5078
5335
|
const value = await evaluateScalarSubquery(
|
|
@@ -5105,6 +5362,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
5105
5362
|
return {};
|
|
5106
5363
|
}
|
|
5107
5364
|
const resolvedStmt = resolveVariableRefs(stmt, variables);
|
|
5365
|
+
validateKlikeStatement(resolvedStmt);
|
|
5108
5366
|
if (resolvedStmt.type === "CREATE_TEMP_TABLE") {
|
|
5109
5367
|
const materializeOptions = {
|
|
5110
5368
|
...options,
|
|
@@ -6973,7 +7231,9 @@ async function executeDescribe(stmt, client, cacheContext) {
|
|
|
6973
7231
|
function parseSql(sql) {
|
|
6974
7232
|
try {
|
|
6975
7233
|
const tokens = new Lexer(sql).tokenize();
|
|
6976
|
-
|
|
7234
|
+
const stmt = new Parser(tokens).parse();
|
|
7235
|
+
validateKlikeStatement(stmt);
|
|
7236
|
+
return stmt;
|
|
6977
7237
|
} catch (e) {
|
|
6978
7238
|
if (e instanceof LexError || e instanceof ParseError) {
|
|
6979
7239
|
throw e;
|
|
@@ -7084,6 +7344,7 @@ function buildBatchExplainPlans(sql, injectedVariables) {
|
|
|
7084
7344
|
statementCount: statements.length,
|
|
7085
7345
|
statements: statements.map((stmt, i) => {
|
|
7086
7346
|
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
|
|
7347
|
+
validateKlikeStatement(planStmt);
|
|
7087
7348
|
const result = {
|
|
7088
7349
|
index: i,
|
|
7089
7350
|
type: analysis.statements[i].statementType,
|
|
@@ -7517,11 +7778,15 @@ var OperationCancelledError = class extends Error {
|
|
|
7517
7778
|
// src/core/sql.ts
|
|
7518
7779
|
function parseSqlStatement(sql) {
|
|
7519
7780
|
const tokens = new Lexer(sql).tokenize();
|
|
7520
|
-
|
|
7781
|
+
const stmt = new Parser(tokens).parse();
|
|
7782
|
+
validateKlikeStatement(stmt);
|
|
7783
|
+
return stmt;
|
|
7521
7784
|
}
|
|
7522
7785
|
function parseSqlStatements(sql) {
|
|
7523
7786
|
const tokens = new Lexer(sql).tokenize();
|
|
7524
|
-
|
|
7787
|
+
const statements = new Parser(tokens).parseStatements();
|
|
7788
|
+
statements.forEach(validateKlikeStatement);
|
|
7789
|
+
return statements;
|
|
7525
7790
|
}
|
|
7526
7791
|
|
|
7527
7792
|
// src/core/displayFormat.ts
|