@rex0220/kintone-sql-tools 2.7.0 → 2.9.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 +832 -526
- package/dist-mcp/ksql-mcp.js +765 -459
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -1811,7 +1811,7 @@ var require_code2 = __commonJS({
|
|
|
1811
1811
|
}
|
|
1812
1812
|
}
|
|
1813
1813
|
exports2.validateArray = validateArray;
|
|
1814
|
-
function
|
|
1814
|
+
function validateUnion2(cxt) {
|
|
1815
1815
|
const { gen, schema, keyword, it } = cxt;
|
|
1816
1816
|
if (!Array.isArray(schema))
|
|
1817
1817
|
throw new Error("ajv implementation error");
|
|
@@ -1833,7 +1833,7 @@ var require_code2 = __commonJS({
|
|
|
1833
1833
|
}));
|
|
1834
1834
|
cxt.result(valid, () => cxt.reset(), () => cxt.error(true));
|
|
1835
1835
|
}
|
|
1836
|
-
exports2.validateUnion =
|
|
1836
|
+
exports2.validateUnion = validateUnion2;
|
|
1837
1837
|
}
|
|
1838
1838
|
});
|
|
1839
1839
|
|
|
@@ -31007,6 +31007,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31007
31007
|
["IS", "IS" /* IS */],
|
|
31008
31008
|
["NULL", "NULL" /* NULL */],
|
|
31009
31009
|
["LIKE", "LIKE" /* LIKE */],
|
|
31010
|
+
["KLIKE", "KLIKE" /* KLIKE */],
|
|
31010
31011
|
["IN", "IN" /* IN */],
|
|
31011
31012
|
["BETWEEN", "BETWEEN" /* BETWEEN */],
|
|
31012
31013
|
["TODAY", "TODAY" /* TODAY */],
|
|
@@ -32532,7 +32533,7 @@ var Parser = class {
|
|
|
32532
32533
|
}
|
|
32533
32534
|
return false;
|
|
32534
32535
|
}
|
|
32535
|
-
// 比較演算子: =, !=, <>, >, <, >=, <=, LIKE, IN, IS NULL
|
|
32536
|
+
// 比較演算子: =, !=, <>, >, <, >=, <=, LIKE, KLIKE, IN, IS NULL
|
|
32536
32537
|
parseCompareExpr() {
|
|
32537
32538
|
if (this.peek().kind === "(" /* LPAREN */ && !this.isArithParen()) {
|
|
32538
32539
|
this.advance();
|
|
@@ -32568,8 +32569,12 @@ var Parser = class {
|
|
|
32568
32569
|
const pattern = this.parseSqlValue();
|
|
32569
32570
|
return { type: "BINARY", op: "NOT_LIKE", left: field, right: pattern };
|
|
32570
32571
|
}
|
|
32572
|
+
if (this.consume("KLIKE" /* KLIKE */)) {
|
|
32573
|
+
const pattern = this.parseKlikePattern();
|
|
32574
|
+
return { type: "BINARY", op: "NOT_KLIKE", left: field, right: pattern };
|
|
32575
|
+
}
|
|
32571
32576
|
throw new ParseError(
|
|
32572
|
-
"NOT \u306E\u5F8C\u306B\u306F IN \
|
|
32577
|
+
"NOT \u306E\u5F8C\u306B\u306F IN\u3001LIKE\u3001KLIKE \u306E\u3044\u305A\u308C\u304B\u304C\u5FC5\u8981\u3067\u3059",
|
|
32573
32578
|
this.peek()
|
|
32574
32579
|
);
|
|
32575
32580
|
}
|
|
@@ -32579,6 +32584,10 @@ var Parser = class {
|
|
|
32579
32584
|
this.expect(")" /* RPAREN */);
|
|
32580
32585
|
return { type: "BINARY", op: "IN", left: field, right: right2 };
|
|
32581
32586
|
}
|
|
32587
|
+
if (this.consume("KLIKE" /* KLIKE */)) {
|
|
32588
|
+
const pattern = this.parseKlikePattern();
|
|
32589
|
+
return { type: "BINARY", op: "KLIKE", left: field, right: pattern };
|
|
32590
|
+
}
|
|
32582
32591
|
const op = this.parseCompareOp();
|
|
32583
32592
|
const right = this.parseSqlValue();
|
|
32584
32593
|
return { type: "BINARY", op, left: field, right };
|
|
@@ -32604,11 +32613,27 @@ var Parser = class {
|
|
|
32604
32613
|
return "LIKE";
|
|
32605
32614
|
default:
|
|
32606
32615
|
throw new ParseError(
|
|
32607
|
-
"\u6BD4\u8F03\u6F14\u7B97\u5B50\uFF08=, !=, >, <, >=, <=, LIKE, IN, IS\uFF09\u304C\u5FC5\u8981\u3067\u3059",
|
|
32616
|
+
"\u6BD4\u8F03\u6F14\u7B97\u5B50\uFF08=, !=, >, <, >=, <=, LIKE, KLIKE, IN, IS\uFF09\u304C\u5FC5\u8981\u3067\u3059",
|
|
32608
32617
|
tok
|
|
32609
32618
|
);
|
|
32610
32619
|
}
|
|
32611
32620
|
}
|
|
32621
|
+
/** KLIKE / NOT KLIKE の右辺。kintone キーワードは文字列値だけを受け付ける。 */
|
|
32622
|
+
parseKlikePattern() {
|
|
32623
|
+
const tok = this.peek();
|
|
32624
|
+
if (tok.kind === "STRING" /* STRING */) {
|
|
32625
|
+
this.advance();
|
|
32626
|
+
return { type: "STRING", value: tok.value };
|
|
32627
|
+
}
|
|
32628
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
32629
|
+
this.advance();
|
|
32630
|
+
return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
|
|
32631
|
+
}
|
|
32632
|
+
throw new ParseError(
|
|
32633
|
+
"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",
|
|
32634
|
+
tok
|
|
32635
|
+
);
|
|
32636
|
+
}
|
|
32612
32637
|
// WHERE / HAVING の左辺
|
|
32613
32638
|
// - 文字列・数値関数: UPPER(f) / LENGTH(f) / ROUND(f, 2) ...
|
|
32614
32639
|
// - 集計関数(HAVING のみ): COUNT(*) / SUM(f) ...
|
|
@@ -33247,238 +33272,6 @@ function getInsertValuesCount(stmt) {
|
|
|
33247
33272
|
return Array.isArray(obj.values) ? obj.values.length : null;
|
|
33248
33273
|
}
|
|
33249
33274
|
|
|
33250
|
-
// src/core/batch.ts
|
|
33251
|
-
var MAX_TEMP_TABLES = 16;
|
|
33252
|
-
var MAX_BATCH_VARIABLES = 64;
|
|
33253
|
-
var BatchAnalysisError = class extends Error {
|
|
33254
|
-
constructor(message, statementIndex) {
|
|
33255
|
-
super(message);
|
|
33256
|
-
this.statementIndex = statementIndex;
|
|
33257
|
-
}
|
|
33258
|
-
};
|
|
33259
|
-
function collectRefs(node, tempRefs, appIds) {
|
|
33260
|
-
if (Array.isArray(node)) {
|
|
33261
|
-
for (const v of node) collectRefs(v, tempRefs, appIds);
|
|
33262
|
-
return;
|
|
33263
|
-
}
|
|
33264
|
-
if (node !== null && typeof node === "object") {
|
|
33265
|
-
const obj = node;
|
|
33266
|
-
const cte = obj["cteName"];
|
|
33267
|
-
if (typeof cte === "string" && cte.startsWith("#")) tempRefs.add(cte);
|
|
33268
|
-
const appId = obj["appId"];
|
|
33269
|
-
if (typeof appId === "number" && appId > 0) appIds.add(appId);
|
|
33270
|
-
for (const v of Object.values(obj)) collectRefs(v, tempRefs, appIds);
|
|
33271
|
-
}
|
|
33272
|
-
}
|
|
33273
|
-
function collectVariableRefs(node, refs) {
|
|
33274
|
-
if (Array.isArray(node)) {
|
|
33275
|
-
for (const v of node) collectVariableRefs(v, refs);
|
|
33276
|
-
return;
|
|
33277
|
-
}
|
|
33278
|
-
if (node !== null && typeof node === "object") {
|
|
33279
|
-
const obj = node;
|
|
33280
|
-
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
|
|
33281
|
-
refs.add(obj["name"]);
|
|
33282
|
-
return;
|
|
33283
|
-
}
|
|
33284
|
-
for (const v of Object.values(obj)) collectVariableRefs(v, refs);
|
|
33285
|
-
}
|
|
33286
|
-
}
|
|
33287
|
-
function analyzeBatch(statements) {
|
|
33288
|
-
if (statements.length === 0) {
|
|
33289
|
-
throw new BatchAnalysisError("ArgumentError: SQL is empty.", 0);
|
|
33290
|
-
}
|
|
33291
|
-
if (statements.length === 1) {
|
|
33292
|
-
const t = statements[0].type;
|
|
33293
|
-
if (t === "SET_VARIABLE" || t === "DECLARE_VARIABLE") {
|
|
33294
|
-
const verb = t === "SET_VARIABLE" ? "SET" : "DECLARE";
|
|
33295
|
-
throw new BatchAnalysisError(`ArgumentError: ${verb} variable requires a batch.`, 0);
|
|
33296
|
-
}
|
|
33297
|
-
if (t === "CREATE_TEMP_TABLE" || t === "DROP_TEMP_TABLE") {
|
|
33298
|
-
const verb = t === "CREATE_TEMP_TABLE" ? "CREATE TEMP TABLE" : "DROP TEMP TABLE";
|
|
33299
|
-
throw new BatchAnalysisError(
|
|
33300
|
-
`ArgumentError: ${verb} requires a batch (temp tables are batch-scoped).`,
|
|
33301
|
-
0
|
|
33302
|
-
);
|
|
33303
|
-
}
|
|
33304
|
-
}
|
|
33305
|
-
const defined = /* @__PURE__ */ new Map();
|
|
33306
|
-
const createdOrder = [];
|
|
33307
|
-
const results = [];
|
|
33308
|
-
const variableDefs = /* @__PURE__ */ new Map();
|
|
33309
|
-
const variableOrder = [];
|
|
33310
|
-
statements.forEach((stmt, index) => {
|
|
33311
|
-
const statementType = getStatementType(stmt);
|
|
33312
|
-
const created = [];
|
|
33313
|
-
const dropped = [];
|
|
33314
|
-
const refs = /* @__PURE__ */ new Set();
|
|
33315
|
-
const stmtAppIds = /* @__PURE__ */ new Set();
|
|
33316
|
-
const dependsOn = /* @__PURE__ */ new Set();
|
|
33317
|
-
const variableRefs = /* @__PURE__ */ new Set();
|
|
33318
|
-
collectVariableRefs(stmt, variableRefs);
|
|
33319
|
-
for (const name of variableRefs) {
|
|
33320
|
-
const def = variableDefs.get(name);
|
|
33321
|
-
if (def === void 0) {
|
|
33322
|
-
throw new BatchAnalysisError(
|
|
33323
|
-
`ParseError: variable @${name} is not defined before statement ${index + 1}.`,
|
|
33324
|
-
index
|
|
33325
|
-
);
|
|
33326
|
-
}
|
|
33327
|
-
def.referencedBy.push(index);
|
|
33328
|
-
}
|
|
33329
|
-
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
33330
|
-
if (variableDefs.has(stmt.name)) {
|
|
33331
|
-
throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
|
|
33332
|
-
}
|
|
33333
|
-
variableDefs.set(stmt.name, { index, referencedBy: [] });
|
|
33334
|
-
variableOrder.push(stmt.name);
|
|
33335
|
-
if (variableOrder.length > MAX_BATCH_VARIABLES) {
|
|
33336
|
-
throw new BatchAnalysisError(
|
|
33337
|
-
`ParseError: batch exceeds ${MAX_BATCH_VARIABLES} variables.`,
|
|
33338
|
-
index
|
|
33339
|
-
);
|
|
33340
|
-
}
|
|
33341
|
-
}
|
|
33342
|
-
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
33343
|
-
collectRefs(stmt.query, refs, stmtAppIds);
|
|
33344
|
-
} else if (stmt.type === "DROP_TEMP_TABLE") {
|
|
33345
|
-
} else {
|
|
33346
|
-
collectRefs(stmt, refs, stmtAppIds);
|
|
33347
|
-
}
|
|
33348
|
-
let tempOnlySource = false;
|
|
33349
|
-
if (stmt.type === "INSERT_SELECT" || stmt.type === "UPSERT_SELECT") {
|
|
33350
|
-
const srcTemp = /* @__PURE__ */ new Set();
|
|
33351
|
-
const srcApps = /* @__PURE__ */ new Set();
|
|
33352
|
-
collectRefs(stmt.select, srcTemp, srcApps);
|
|
33353
|
-
tempOnlySource = srcTemp.size > 0 && srcApps.size === 0;
|
|
33354
|
-
}
|
|
33355
|
-
for (const name of refs) {
|
|
33356
|
-
const at = defined.get(name);
|
|
33357
|
-
if (at === void 0) {
|
|
33358
|
-
throw new BatchAnalysisError(
|
|
33359
|
-
`ParseError: temp table ${name} is not defined in this batch.`,
|
|
33360
|
-
index
|
|
33361
|
-
);
|
|
33362
|
-
}
|
|
33363
|
-
dependsOn.add(at);
|
|
33364
|
-
}
|
|
33365
|
-
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
33366
|
-
if (defined.has(stmt.name)) {
|
|
33367
|
-
throw new BatchAnalysisError(
|
|
33368
|
-
`ParseError: temp table ${stmt.name} is already defined.`,
|
|
33369
|
-
index
|
|
33370
|
-
);
|
|
33371
|
-
}
|
|
33372
|
-
defined.set(stmt.name, index);
|
|
33373
|
-
createdOrder.push(stmt.name);
|
|
33374
|
-
created.push(stmt.name);
|
|
33375
|
-
if (defined.size > MAX_TEMP_TABLES) {
|
|
33376
|
-
throw new BatchAnalysisError(
|
|
33377
|
-
`ParseError: batch exceeds ${MAX_TEMP_TABLES} temp tables.`,
|
|
33378
|
-
index
|
|
33379
|
-
);
|
|
33380
|
-
}
|
|
33381
|
-
}
|
|
33382
|
-
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
33383
|
-
const at = defined.get(stmt.name);
|
|
33384
|
-
if (at === void 0) {
|
|
33385
|
-
throw new BatchAnalysisError(
|
|
33386
|
-
`ParseError: temp table ${stmt.name} is not defined in this batch.`,
|
|
33387
|
-
index
|
|
33388
|
-
);
|
|
33389
|
-
}
|
|
33390
|
-
dependsOn.add(at);
|
|
33391
|
-
dropped.push(stmt.name);
|
|
33392
|
-
defined.delete(stmt.name);
|
|
33393
|
-
}
|
|
33394
|
-
results.push({
|
|
33395
|
-
index,
|
|
33396
|
-
statementType,
|
|
33397
|
-
isDml: isDmlType(statementType),
|
|
33398
|
-
isReadOnly: isReadOnlyType(statementType),
|
|
33399
|
-
hasWhere: hasWhereClause(stmt),
|
|
33400
|
-
insertValuesCount: getInsertValuesCount(stmt),
|
|
33401
|
-
appIds: [...stmtAppIds].sort((a, b) => a - b),
|
|
33402
|
-
tempTablesCreated: created,
|
|
33403
|
-
tempTablesReferenced: [...refs],
|
|
33404
|
-
tempTablesDropped: dropped,
|
|
33405
|
-
dependsOn: [...dependsOn].sort((a, b) => a - b),
|
|
33406
|
-
tempOnlySource,
|
|
33407
|
-
targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null
|
|
33408
|
-
});
|
|
33409
|
-
});
|
|
33410
|
-
const containsDml = results.some((r) => r.isDml);
|
|
33411
|
-
const variables = variableOrder.map((name) => ({
|
|
33412
|
-
name,
|
|
33413
|
-
referencedBy: [...variableDefs.get(name).referencedBy]
|
|
33414
|
-
}));
|
|
33415
|
-
return {
|
|
33416
|
-
statementCount: statements.length,
|
|
33417
|
-
isReadOnlyBatch: !containsDml && results.every((r) => r.isReadOnly),
|
|
33418
|
-
containsDml,
|
|
33419
|
-
tempTables: createdOrder,
|
|
33420
|
-
variables,
|
|
33421
|
-
warnings: variables.filter((v) => v.referencedBy.length === 0).map((v) => `variable @${v.name} is never used.`),
|
|
33422
|
-
statements: results
|
|
33423
|
-
};
|
|
33424
|
-
}
|
|
33425
|
-
|
|
33426
|
-
// src/core/batchVariables.ts
|
|
33427
|
-
var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
33428
|
-
function normalizeBatchVariableName(name) {
|
|
33429
|
-
if (!VARIABLE_NAME_RE.test(name)) {
|
|
33430
|
-
throw new Error(
|
|
33431
|
-
`ArgumentError: invalid variable name "${name}". Use a name without @ matching [A-Za-z_][A-Za-z0-9_]{0,63}.`
|
|
33432
|
-
);
|
|
33433
|
-
}
|
|
33434
|
-
return name.toLowerCase();
|
|
33435
|
-
}
|
|
33436
|
-
function normalizeBatchVariables(input) {
|
|
33437
|
-
const normalized = /* @__PURE__ */ Object.create(null);
|
|
33438
|
-
for (const [rawName, value] of Object.entries(input ?? {})) {
|
|
33439
|
-
const name = normalizeBatchVariableName(rawName);
|
|
33440
|
-
if (Object.prototype.hasOwnProperty.call(normalized, name)) {
|
|
33441
|
-
throw new Error(`ArgumentError: variable "${rawName}" is specified more than once.`);
|
|
33442
|
-
}
|
|
33443
|
-
normalized[name] = value;
|
|
33444
|
-
}
|
|
33445
|
-
return normalized;
|
|
33446
|
-
}
|
|
33447
|
-
function validateDeclaredBatchVariables(statements, input) {
|
|
33448
|
-
const normalized = normalizeBatchVariables(input);
|
|
33449
|
-
const declared = new Set(
|
|
33450
|
-
statements.filter((stmt) => stmt.type === "DECLARE_VARIABLE").map((stmt) => stmt.name)
|
|
33451
|
-
);
|
|
33452
|
-
for (const name of Object.keys(normalized)) {
|
|
33453
|
-
if (!declared.has(name)) {
|
|
33454
|
-
throw new Error(`ArgumentError: injected variable @${name} is not declared.`);
|
|
33455
|
-
}
|
|
33456
|
-
}
|
|
33457
|
-
return normalized;
|
|
33458
|
-
}
|
|
33459
|
-
|
|
33460
|
-
// src/core/scalarCompare.ts
|
|
33461
|
-
function compareScalarValues(op, leftStr, rightStr) {
|
|
33462
|
-
if (op === "=") return leftStr === rightStr;
|
|
33463
|
-
if (op === "!=" || op === "<>") return leftStr !== rightStr;
|
|
33464
|
-
const rightNum = Number(rightStr);
|
|
33465
|
-
if (leftStr === "" && rightStr !== "" && Number.isFinite(rightNum)) {
|
|
33466
|
-
return op === "<" || op === "<=";
|
|
33467
|
-
}
|
|
33468
|
-
const leftNum = Number(leftStr);
|
|
33469
|
-
const numeric = !Number.isNaN(leftNum) && !Number.isNaN(rightNum);
|
|
33470
|
-
switch (op) {
|
|
33471
|
-
case ">":
|
|
33472
|
-
return numeric ? leftNum > rightNum : leftStr > rightStr;
|
|
33473
|
-
case "<":
|
|
33474
|
-
return numeric ? leftNum < rightNum : leftStr < rightStr;
|
|
33475
|
-
case ">=":
|
|
33476
|
-
return numeric ? leftNum >= rightNum : leftStr >= rightStr;
|
|
33477
|
-
case "<=":
|
|
33478
|
-
return numeric ? leftNum <= rightNum : leftStr <= rightStr;
|
|
33479
|
-
}
|
|
33480
|
-
}
|
|
33481
|
-
|
|
33482
33275
|
// src/engine/pushDownNot.ts
|
|
33483
33276
|
function pushDownNot(expr) {
|
|
33484
33277
|
switch (expr.type) {
|
|
@@ -33528,6 +33321,10 @@ function negateOp(op) {
|
|
|
33528
33321
|
return "NOT_LIKE";
|
|
33529
33322
|
case "NOT_LIKE":
|
|
33530
33323
|
return "LIKE";
|
|
33324
|
+
case "KLIKE":
|
|
33325
|
+
return "NOT_KLIKE";
|
|
33326
|
+
case "NOT_KLIKE":
|
|
33327
|
+
return "KLIKE";
|
|
33531
33328
|
case "IN":
|
|
33532
33329
|
return "NOT_IN";
|
|
33533
33330
|
case "NOT_IN":
|
|
@@ -33542,6 +33339,9 @@ function likePatternHasWildcard(pattern) {
|
|
|
33542
33339
|
function isLike(where) {
|
|
33543
33340
|
return where.type === "BINARY" && (where.op === "LIKE" || where.op === "NOT_LIKE");
|
|
33544
33341
|
}
|
|
33342
|
+
function isKlike(where) {
|
|
33343
|
+
return where.type === "BINARY" && (where.op === "KLIKE" || where.op === "NOT_KLIKE");
|
|
33344
|
+
}
|
|
33545
33345
|
function whereHasLike(where) {
|
|
33546
33346
|
if (where === null) return false;
|
|
33547
33347
|
if (isLike(where)) return true;
|
|
@@ -33557,6 +33357,21 @@ function whereHasLike(where) {
|
|
|
33557
33357
|
return false;
|
|
33558
33358
|
}
|
|
33559
33359
|
}
|
|
33360
|
+
function whereHasKlike(where) {
|
|
33361
|
+
if (where === null) return false;
|
|
33362
|
+
if (isKlike(where)) return true;
|
|
33363
|
+
switch (where.type) {
|
|
33364
|
+
case "LOGICAL":
|
|
33365
|
+
return whereHasKlike(where.left) || whereHasKlike(where.right);
|
|
33366
|
+
case "NOT":
|
|
33367
|
+
case "GROUP":
|
|
33368
|
+
return whereHasKlike(where.expr);
|
|
33369
|
+
case "BINARY":
|
|
33370
|
+
case "NULL_CHECK":
|
|
33371
|
+
case "EXISTS":
|
|
33372
|
+
return false;
|
|
33373
|
+
}
|
|
33374
|
+
}
|
|
33560
33375
|
|
|
33561
33376
|
// src/converter/whereToKintone.ts
|
|
33562
33377
|
function whereToKintone(expr) {
|
|
@@ -33605,6 +33420,10 @@ function convertOp(op) {
|
|
|
33605
33420
|
return "like";
|
|
33606
33421
|
case "NOT_LIKE":
|
|
33607
33422
|
return "not like";
|
|
33423
|
+
case "KLIKE":
|
|
33424
|
+
return "like";
|
|
33425
|
+
case "NOT_KLIKE":
|
|
33426
|
+
return "not like";
|
|
33608
33427
|
case "IN":
|
|
33609
33428
|
return "in";
|
|
33610
33429
|
case "NOT_IN":
|
|
@@ -34198,6 +34017,647 @@ function isAggregateSyntheticName(name) {
|
|
|
34198
34017
|
return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
|
|
34199
34018
|
}
|
|
34200
34019
|
|
|
34020
|
+
// src/core/cteInlining.ts
|
|
34021
|
+
function canInlineSingleCte(stmt) {
|
|
34022
|
+
if (stmt.ctes.length !== 1) return false;
|
|
34023
|
+
const cteDef = stmt.ctes[0];
|
|
34024
|
+
if (cteDef.query.type !== "SELECT" || resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
|
|
34025
|
+
const finalQuery = stmt.query;
|
|
34026
|
+
if (finalQuery.type !== "SELECT") return false;
|
|
34027
|
+
if (finalQuery.from.cteName !== cteDef.name || finalQuery.joins.length > 0) return false;
|
|
34028
|
+
if (finalQuery.groupBy.length > 0 || finalQuery.distinct) return false;
|
|
34029
|
+
return !finalQuery.columns.some(
|
|
34030
|
+
(column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
|
|
34031
|
+
);
|
|
34032
|
+
}
|
|
34033
|
+
function buildInlinedQuery(stmt) {
|
|
34034
|
+
const cteBody = stmt.ctes[0].query;
|
|
34035
|
+
const final = stmt.query;
|
|
34036
|
+
const finalWhere = stripCteAlias(final.where, final.from.alias);
|
|
34037
|
+
const where = cteBody.where === null ? finalWhere : finalWhere === null ? cteBody.where : { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
|
|
34038
|
+
const columns = final.columns.every((column) => column.type === "WILDCARD") ? cteBody.columns : final.columns;
|
|
34039
|
+
return {
|
|
34040
|
+
type: "SELECT",
|
|
34041
|
+
from: cteBody.from,
|
|
34042
|
+
joins: [],
|
|
34043
|
+
columns,
|
|
34044
|
+
where,
|
|
34045
|
+
groupBy: [],
|
|
34046
|
+
having: null,
|
|
34047
|
+
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
34048
|
+
limit: final.limit ?? cteBody.limit,
|
|
34049
|
+
offset: final.offset ?? cteBody.offset,
|
|
34050
|
+
distinct: false
|
|
34051
|
+
};
|
|
34052
|
+
}
|
|
34053
|
+
function stripCteAlias(where, alias) {
|
|
34054
|
+
if (where === null || alias === null) return where;
|
|
34055
|
+
switch (where.type) {
|
|
34056
|
+
case "BINARY":
|
|
34057
|
+
return { ...where, left: stripCteAliasFromFieldValue(where.left, alias) };
|
|
34058
|
+
case "NULL_CHECK":
|
|
34059
|
+
return { ...where, field: stripCteAliasFromFieldValue(where.field, alias) };
|
|
34060
|
+
case "LOGICAL":
|
|
34061
|
+
return {
|
|
34062
|
+
...where,
|
|
34063
|
+
left: stripCteAlias(where.left, alias),
|
|
34064
|
+
right: stripCteAlias(where.right, alias)
|
|
34065
|
+
};
|
|
34066
|
+
case "NOT":
|
|
34067
|
+
case "GROUP":
|
|
34068
|
+
return { ...where, expr: stripCteAlias(where.expr, alias) };
|
|
34069
|
+
case "EXISTS":
|
|
34070
|
+
return where;
|
|
34071
|
+
}
|
|
34072
|
+
}
|
|
34073
|
+
function stripCteAliasFromFieldValue(value, alias) {
|
|
34074
|
+
if (value.type === "FIELD" && value.tableAlias === alias) {
|
|
34075
|
+
return { ...value, tableAlias: null };
|
|
34076
|
+
}
|
|
34077
|
+
return value;
|
|
34078
|
+
}
|
|
34079
|
+
|
|
34080
|
+
// src/core/optimization/wherePredicatePushdown.ts
|
|
34081
|
+
function extractSafePushdownLeaves(where, options = {}) {
|
|
34082
|
+
return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
|
|
34083
|
+
}
|
|
34084
|
+
function extractTypedPushdownCandidates(where, options = {}) {
|
|
34085
|
+
return extractAndLeaves(
|
|
34086
|
+
where,
|
|
34087
|
+
(expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
|
|
34088
|
+
);
|
|
34089
|
+
}
|
|
34090
|
+
function extractAndLeaves(where, accept) {
|
|
34091
|
+
switch (where.type) {
|
|
34092
|
+
case "BINARY":
|
|
34093
|
+
return accept(where) ? where : null;
|
|
34094
|
+
case "LOGICAL":
|
|
34095
|
+
if (where.op !== "AND") return null;
|
|
34096
|
+
{
|
|
34097
|
+
const left = extractAndLeaves(where.left, accept);
|
|
34098
|
+
const right = extractAndLeaves(where.right, accept);
|
|
34099
|
+
if (left && right) return { ...where, left, right };
|
|
34100
|
+
return left ?? right ?? null;
|
|
34101
|
+
}
|
|
34102
|
+
case "GROUP":
|
|
34103
|
+
return extractAndLeaves(where.expr, accept);
|
|
34104
|
+
case "NULL_CHECK":
|
|
34105
|
+
case "NOT":
|
|
34106
|
+
case "EXISTS":
|
|
34107
|
+
return null;
|
|
34108
|
+
}
|
|
34109
|
+
}
|
|
34110
|
+
function isSafeComparison(expr, options) {
|
|
34111
|
+
if (isKlikeComparison(expr, options)) return true;
|
|
34112
|
+
if (isSafeIdComparison(expr, options)) return true;
|
|
34113
|
+
if (isNumericCandidate(expr, options)) {
|
|
34114
|
+
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
34115
|
+
}
|
|
34116
|
+
return isSelectionInComparison(expr, options);
|
|
34117
|
+
}
|
|
34118
|
+
function isKlikeComparison(expr, options) {
|
|
34119
|
+
if (options.allowKlike === false) return false;
|
|
34120
|
+
if (expr.op !== "KLIKE" && expr.op !== "NOT_KLIKE") return false;
|
|
34121
|
+
if (expr.left.type !== "FIELD" || !isTargetField(expr.left, options)) return false;
|
|
34122
|
+
return expr.right.type === "STRING" || options.allowUnresolvedKlikeVariables === true && expr.right.type === "VARIABLE";
|
|
34123
|
+
}
|
|
34124
|
+
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
34125
|
+
"DROP_DOWN",
|
|
34126
|
+
"RADIO_BUTTON",
|
|
34127
|
+
"CHECK_BOX",
|
|
34128
|
+
"MULTI_SELECT",
|
|
34129
|
+
"STATUS"
|
|
34130
|
+
]);
|
|
34131
|
+
function isSelectionInComparison(expr, options) {
|
|
34132
|
+
if (!isSelectionInCandidate(expr, options)) return false;
|
|
34133
|
+
if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
|
|
34134
|
+
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
34135
|
+
if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
|
|
34136
|
+
const validOptions = options.fieldOptions?.get(expr.left.field);
|
|
34137
|
+
if (validOptions === void 0) return false;
|
|
34138
|
+
return expr.right.values.every(
|
|
34139
|
+
(value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
|
|
34140
|
+
);
|
|
34141
|
+
}
|
|
34142
|
+
function isSafeIdComparison(expr, options) {
|
|
34143
|
+
if (!isTargetIdField(expr.left, options)) return false;
|
|
34144
|
+
if (expr.right.type !== "NUMBER") return false;
|
|
34145
|
+
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
34146
|
+
}
|
|
34147
|
+
function isTargetIdField(field, options) {
|
|
34148
|
+
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
34149
|
+
const targetAlias = options.tableAlias ?? null;
|
|
34150
|
+
if (field.tableAlias === targetAlias) return true;
|
|
34151
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
34152
|
+
}
|
|
34153
|
+
function isNumericCandidate(expr, options) {
|
|
34154
|
+
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
34155
|
+
if (!isTargetField(expr.left, options)) return false;
|
|
34156
|
+
if (expr.right.type !== "NUMBER") return false;
|
|
34157
|
+
if (expr.op === "=") return true;
|
|
34158
|
+
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
34159
|
+
}
|
|
34160
|
+
function isSelectionInCandidate(expr, options) {
|
|
34161
|
+
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
34162
|
+
if (!isTargetField(expr.left, options)) return false;
|
|
34163
|
+
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
34164
|
+
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
34165
|
+
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
34166
|
+
}
|
|
34167
|
+
function isTargetField(field, options) {
|
|
34168
|
+
const targetAlias = options.tableAlias ?? null;
|
|
34169
|
+
if (field.tableAlias === targetAlias) return true;
|
|
34170
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
34171
|
+
}
|
|
34172
|
+
|
|
34173
|
+
// src/core/optimization/klikePushdownPlan.ts
|
|
34174
|
+
function buildKlikePushdownPlan(stmt, options = {}) {
|
|
34175
|
+
const joinsAreSafeForKlike = stmt.joins.every((join) => join.type === "INNER");
|
|
34176
|
+
const common = {
|
|
34177
|
+
allowKlike: joinsAreSafeForKlike,
|
|
34178
|
+
allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
|
|
34179
|
+
};
|
|
34180
|
+
let mainCondition = null;
|
|
34181
|
+
if (stmt.where !== null && !stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
34182
|
+
if (stmt.joins.length === 0) {
|
|
34183
|
+
mainCondition = extractSafePushdownLeaves(stmt.where, {
|
|
34184
|
+
...common,
|
|
34185
|
+
tableAlias: stmt.from.alias ?? void 0,
|
|
34186
|
+
allowUnqualifiedFields: true,
|
|
34187
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
34188
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
34189
|
+
});
|
|
34190
|
+
} else if (stmt.from.alias) {
|
|
34191
|
+
mainCondition = extractSafePushdownLeaves(stmt.where, {
|
|
34192
|
+
...common,
|
|
34193
|
+
tableAlias: stmt.from.alias,
|
|
34194
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
34195
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
34196
|
+
});
|
|
34197
|
+
}
|
|
34198
|
+
}
|
|
34199
|
+
const joinConditions = /* @__PURE__ */ new Map();
|
|
34200
|
+
if (stmt.where !== null) {
|
|
34201
|
+
for (const join of stmt.joins) {
|
|
34202
|
+
if (!join.table.alias || join.table.subtableCode || join.table.cteName !== null) continue;
|
|
34203
|
+
const condition = extractSafePushdownLeaves(stmt.where, {
|
|
34204
|
+
...common,
|
|
34205
|
+
tableAlias: join.table.alias,
|
|
34206
|
+
fieldTypes: options.fieldTypesByApp?.get(join.table.appId),
|
|
34207
|
+
fieldOptions: options.fieldOptionsByApp?.get(join.table.appId)
|
|
34208
|
+
});
|
|
34209
|
+
if (condition !== null) joinConditions.set(join.table.alias, condition);
|
|
34210
|
+
}
|
|
34211
|
+
}
|
|
34212
|
+
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
34213
|
+
collectKlikes(mainCondition, appliedKlikes);
|
|
34214
|
+
for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
|
|
34215
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
34216
|
+
collectKlikes(stmt.where, allKlikes);
|
|
34217
|
+
return {
|
|
34218
|
+
mainCondition,
|
|
34219
|
+
joinConditions,
|
|
34220
|
+
appliedKlikes,
|
|
34221
|
+
allKlikes: [...allKlikes]
|
|
34222
|
+
};
|
|
34223
|
+
}
|
|
34224
|
+
function unappliedKlikes(plan) {
|
|
34225
|
+
return plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr));
|
|
34226
|
+
}
|
|
34227
|
+
function collectKlikes(where, out) {
|
|
34228
|
+
if (where === null) return;
|
|
34229
|
+
if (isKlike(where)) {
|
|
34230
|
+
out.add(where);
|
|
34231
|
+
return;
|
|
34232
|
+
}
|
|
34233
|
+
switch (where.type) {
|
|
34234
|
+
case "LOGICAL":
|
|
34235
|
+
collectKlikes(where.left, out);
|
|
34236
|
+
collectKlikes(where.right, out);
|
|
34237
|
+
return;
|
|
34238
|
+
case "NOT":
|
|
34239
|
+
case "GROUP":
|
|
34240
|
+
collectKlikes(where.expr, out);
|
|
34241
|
+
return;
|
|
34242
|
+
case "BINARY":
|
|
34243
|
+
case "NULL_CHECK":
|
|
34244
|
+
case "EXISTS":
|
|
34245
|
+
return;
|
|
34246
|
+
}
|
|
34247
|
+
}
|
|
34248
|
+
|
|
34249
|
+
// src/core/klikeValidation.ts
|
|
34250
|
+
var KlikeValidationError = class extends Error {
|
|
34251
|
+
constructor(message) {
|
|
34252
|
+
super(`ArgumentError: ${message}`);
|
|
34253
|
+
this.name = "ArgumentError";
|
|
34254
|
+
}
|
|
34255
|
+
};
|
|
34256
|
+
function validateKlikeStatement(stmt) {
|
|
34257
|
+
validateStatement(stmt);
|
|
34258
|
+
}
|
|
34259
|
+
function validateKlikePushdownPlan(plan) {
|
|
34260
|
+
if (unappliedKlikes(plan).length > 0) {
|
|
34261
|
+
throw new KlikeValidationError(
|
|
34262
|
+
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u3092\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u307E\u305B\u3093\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
34263
|
+
);
|
|
34264
|
+
}
|
|
34265
|
+
}
|
|
34266
|
+
function validateStatement(stmt) {
|
|
34267
|
+
switch (stmt.type) {
|
|
34268
|
+
case "SELECT":
|
|
34269
|
+
validateSelect(stmt);
|
|
34270
|
+
return;
|
|
34271
|
+
case "UNION":
|
|
34272
|
+
validateUnion(stmt);
|
|
34273
|
+
return;
|
|
34274
|
+
case "WITH":
|
|
34275
|
+
validateWith(stmt);
|
|
34276
|
+
return;
|
|
34277
|
+
case "EXPLAIN":
|
|
34278
|
+
validateStatement(stmt.query);
|
|
34279
|
+
return;
|
|
34280
|
+
case "CREATE_TEMP_TABLE":
|
|
34281
|
+
validateSelectLike(stmt.query);
|
|
34282
|
+
return;
|
|
34283
|
+
case "SET_VARIABLE":
|
|
34284
|
+
case "DECLARE_VARIABLE":
|
|
34285
|
+
case "ASSERT":
|
|
34286
|
+
validateNestedSelects(stmt);
|
|
34287
|
+
return;
|
|
34288
|
+
case "INSERT":
|
|
34289
|
+
case "INSERT_SELECT":
|
|
34290
|
+
case "UPSERT":
|
|
34291
|
+
case "UPSERT_SELECT":
|
|
34292
|
+
case "UPDATE":
|
|
34293
|
+
case "DELETE":
|
|
34294
|
+
case "REORDER":
|
|
34295
|
+
if (containsKlike(stmt)) {
|
|
34296
|
+
throw new KlikeValidationError(
|
|
34297
|
+
"KLIKE / NOT KLIKE \u306F\u5168 DML\uFF08UPDATE / DELETE / INSERT / UPSERT / REORDER\uFF09\u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
34298
|
+
);
|
|
34299
|
+
}
|
|
34300
|
+
return;
|
|
34301
|
+
case "SHOW_APPS":
|
|
34302
|
+
case "DESCRIBE":
|
|
34303
|
+
case "DROP_TEMP_TABLE":
|
|
34304
|
+
return;
|
|
34305
|
+
}
|
|
34306
|
+
}
|
|
34307
|
+
function validateSelectLike(query) {
|
|
34308
|
+
if (query.type === "SELECT") validateSelect(query);
|
|
34309
|
+
else if (query.type === "UNION") validateUnion(query);
|
|
34310
|
+
else validateWith(query);
|
|
34311
|
+
}
|
|
34312
|
+
function validateUnion(stmt) {
|
|
34313
|
+
validateSelectLike(stmt.left);
|
|
34314
|
+
validateSelect(stmt.right);
|
|
34315
|
+
}
|
|
34316
|
+
function validateWith(stmt) {
|
|
34317
|
+
if (canInlineSingleCte(stmt)) {
|
|
34318
|
+
validateSelect(buildInlinedQuery(stmt));
|
|
34319
|
+
return;
|
|
34320
|
+
}
|
|
34321
|
+
for (const cte of stmt.ctes) {
|
|
34322
|
+
if (cte.query.type === "SELECT") validateSelect(cte.query);
|
|
34323
|
+
else if (cte.query.type === "UNION") validateUnion(cte.query);
|
|
34324
|
+
}
|
|
34325
|
+
validateSelectLike(stmt.query);
|
|
34326
|
+
}
|
|
34327
|
+
function validateSelect(stmt) {
|
|
34328
|
+
validateOwnKlikeExpressions(stmt);
|
|
34329
|
+
if (whereHasKlike(stmt.where)) {
|
|
34330
|
+
const directKintoneSimple = resolveSelectMode(stmt) === "SIMPLE" && stmt.from.cteName === null && stmt.joins.every((join) => join.table.cteName === null);
|
|
34331
|
+
if (!directKintoneSimple) {
|
|
34332
|
+
const plan = buildKlikePushdownPlan(stmt, { allowUnresolvedVariables: true });
|
|
34333
|
+
if (unappliedKlikes(plan).length === 0) {
|
|
34334
|
+
validateNestedSelects(stmt);
|
|
34335
|
+
return;
|
|
34336
|
+
}
|
|
34337
|
+
throw new KlikeValidationError(
|
|
34338
|
+
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u306F\u3001\u7269\u7406\u30C6\u30FC\u30D6\u30EB\u306B\u5BFE\u3059\u308B AND \u30EA\u30FC\u30D5\u3068\u3057\u3066\u5FC5\u305A\u62BC\u3057\u4E0B\u3052\u3089\u308C\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
34339
|
+
);
|
|
34340
|
+
}
|
|
34341
|
+
}
|
|
34342
|
+
validateNestedSelects(stmt);
|
|
34343
|
+
}
|
|
34344
|
+
function validateOwnKlikeExpressions(stmt) {
|
|
34345
|
+
walkWithoutNestedSelects(stmt, (where) => {
|
|
34346
|
+
if (!isKlike(where)) return;
|
|
34347
|
+
if (!isDescendantOf(stmt.where, where)) {
|
|
34348
|
+
throw new KlikeValidationError("KLIKE / NOT KLIKE \u306F SELECT \u306E WHERE \u53E5\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059");
|
|
34349
|
+
}
|
|
34350
|
+
const right = where.right;
|
|
34351
|
+
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
34352
|
+
throw new KlikeValidationError(
|
|
34353
|
+
"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"
|
|
34354
|
+
);
|
|
34355
|
+
}
|
|
34356
|
+
if (right.type === "STRING" && right.value.includes("%")) {
|
|
34357
|
+
throw new KlikeValidationError(
|
|
34358
|
+
"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"
|
|
34359
|
+
);
|
|
34360
|
+
}
|
|
34361
|
+
});
|
|
34362
|
+
}
|
|
34363
|
+
function validateNestedSelects(node) {
|
|
34364
|
+
walkObjects(node, (obj) => {
|
|
34365
|
+
if (obj.type === "SELECT") validateSelect(obj);
|
|
34366
|
+
}, true);
|
|
34367
|
+
}
|
|
34368
|
+
function containsKlike(node) {
|
|
34369
|
+
let found = false;
|
|
34370
|
+
walkObjects(node, (obj) => {
|
|
34371
|
+
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) found = true;
|
|
34372
|
+
});
|
|
34373
|
+
return found;
|
|
34374
|
+
}
|
|
34375
|
+
function isDescendantOf(root, target) {
|
|
34376
|
+
if (root === null) return false;
|
|
34377
|
+
if (root === target) return true;
|
|
34378
|
+
switch (root.type) {
|
|
34379
|
+
case "LOGICAL":
|
|
34380
|
+
return isDescendantOf(root.left, target) || isDescendantOf(root.right, target);
|
|
34381
|
+
case "NOT":
|
|
34382
|
+
case "GROUP":
|
|
34383
|
+
return isDescendantOf(root.expr, target);
|
|
34384
|
+
case "BINARY":
|
|
34385
|
+
case "NULL_CHECK":
|
|
34386
|
+
case "EXISTS":
|
|
34387
|
+
return false;
|
|
34388
|
+
}
|
|
34389
|
+
}
|
|
34390
|
+
function walkWithoutNestedSelects(node, visitWhere) {
|
|
34391
|
+
if (Array.isArray(node)) {
|
|
34392
|
+
for (const value of node) walkWithoutNestedSelects(value, visitWhere);
|
|
34393
|
+
return;
|
|
34394
|
+
}
|
|
34395
|
+
if (node === null || typeof node !== "object") return;
|
|
34396
|
+
const obj = node;
|
|
34397
|
+
if (obj.type === "BINARY" || obj.type === "NULL_CHECK" || obj.type === "LOGICAL" || obj.type === "NOT" || obj.type === "GROUP" || obj.type === "EXISTS") {
|
|
34398
|
+
visitWhere(obj);
|
|
34399
|
+
}
|
|
34400
|
+
for (const value of Object.values(obj)) {
|
|
34401
|
+
if (value !== node && isSelectObject(value)) continue;
|
|
34402
|
+
walkWithoutNestedSelects(value, visitWhere);
|
|
34403
|
+
}
|
|
34404
|
+
}
|
|
34405
|
+
function walkObjects(node, visit, skipRoot = false) {
|
|
34406
|
+
if (Array.isArray(node)) {
|
|
34407
|
+
for (const value of node) walkObjects(value, visit);
|
|
34408
|
+
return;
|
|
34409
|
+
}
|
|
34410
|
+
if (node === null || typeof node !== "object") return;
|
|
34411
|
+
const obj = node;
|
|
34412
|
+
if (!skipRoot) visit(obj);
|
|
34413
|
+
for (const value of Object.values(obj)) walkObjects(value, visit);
|
|
34414
|
+
}
|
|
34415
|
+
function isSelectObject(value) {
|
|
34416
|
+
return value !== null && typeof value === "object" && value.type === "SELECT";
|
|
34417
|
+
}
|
|
34418
|
+
|
|
34419
|
+
// src/core/batch.ts
|
|
34420
|
+
var MAX_TEMP_TABLES = 16;
|
|
34421
|
+
var MAX_BATCH_VARIABLES = 64;
|
|
34422
|
+
var BatchAnalysisError = class extends Error {
|
|
34423
|
+
constructor(message, statementIndex) {
|
|
34424
|
+
super(message);
|
|
34425
|
+
this.statementIndex = statementIndex;
|
|
34426
|
+
}
|
|
34427
|
+
};
|
|
34428
|
+
function collectRefs(node, tempRefs, appIds) {
|
|
34429
|
+
if (Array.isArray(node)) {
|
|
34430
|
+
for (const v of node) collectRefs(v, tempRefs, appIds);
|
|
34431
|
+
return;
|
|
34432
|
+
}
|
|
34433
|
+
if (node !== null && typeof node === "object") {
|
|
34434
|
+
const obj = node;
|
|
34435
|
+
const cte = obj["cteName"];
|
|
34436
|
+
if (typeof cte === "string" && cte.startsWith("#")) tempRefs.add(cte);
|
|
34437
|
+
const appId = obj["appId"];
|
|
34438
|
+
if (typeof appId === "number" && appId > 0) appIds.add(appId);
|
|
34439
|
+
for (const v of Object.values(obj)) collectRefs(v, tempRefs, appIds);
|
|
34440
|
+
}
|
|
34441
|
+
}
|
|
34442
|
+
function collectVariableRefs(node, refs) {
|
|
34443
|
+
if (Array.isArray(node)) {
|
|
34444
|
+
for (const v of node) collectVariableRefs(v, refs);
|
|
34445
|
+
return;
|
|
34446
|
+
}
|
|
34447
|
+
if (node !== null && typeof node === "object") {
|
|
34448
|
+
const obj = node;
|
|
34449
|
+
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
|
|
34450
|
+
refs.add(obj["name"]);
|
|
34451
|
+
return;
|
|
34452
|
+
}
|
|
34453
|
+
for (const v of Object.values(obj)) collectVariableRefs(v, refs);
|
|
34454
|
+
}
|
|
34455
|
+
}
|
|
34456
|
+
function analyzeBatch(statements) {
|
|
34457
|
+
if (statements.length === 0) {
|
|
34458
|
+
throw new BatchAnalysisError("ArgumentError: SQL is empty.", 0);
|
|
34459
|
+
}
|
|
34460
|
+
statements.forEach((stmt, index) => {
|
|
34461
|
+
try {
|
|
34462
|
+
validateKlikeStatement(stmt);
|
|
34463
|
+
} catch (error51) {
|
|
34464
|
+
if (error51 instanceof KlikeValidationError) {
|
|
34465
|
+
throw new BatchAnalysisError(error51.message, index);
|
|
34466
|
+
}
|
|
34467
|
+
throw error51;
|
|
34468
|
+
}
|
|
34469
|
+
});
|
|
34470
|
+
if (statements.length === 1) {
|
|
34471
|
+
const t = statements[0].type;
|
|
34472
|
+
if (t === "SET_VARIABLE" || t === "DECLARE_VARIABLE") {
|
|
34473
|
+
const verb = t === "SET_VARIABLE" ? "SET" : "DECLARE";
|
|
34474
|
+
throw new BatchAnalysisError(`ArgumentError: ${verb} variable requires a batch.`, 0);
|
|
34475
|
+
}
|
|
34476
|
+
if (t === "CREATE_TEMP_TABLE" || t === "DROP_TEMP_TABLE") {
|
|
34477
|
+
const verb = t === "CREATE_TEMP_TABLE" ? "CREATE TEMP TABLE" : "DROP TEMP TABLE";
|
|
34478
|
+
throw new BatchAnalysisError(
|
|
34479
|
+
`ArgumentError: ${verb} requires a batch (temp tables are batch-scoped).`,
|
|
34480
|
+
0
|
|
34481
|
+
);
|
|
34482
|
+
}
|
|
34483
|
+
}
|
|
34484
|
+
const defined = /* @__PURE__ */ new Map();
|
|
34485
|
+
const createdOrder = [];
|
|
34486
|
+
const results = [];
|
|
34487
|
+
const variableDefs = /* @__PURE__ */ new Map();
|
|
34488
|
+
const variableOrder = [];
|
|
34489
|
+
statements.forEach((stmt, index) => {
|
|
34490
|
+
const statementType = getStatementType(stmt);
|
|
34491
|
+
const created = [];
|
|
34492
|
+
const dropped = [];
|
|
34493
|
+
const refs = /* @__PURE__ */ new Set();
|
|
34494
|
+
const stmtAppIds = /* @__PURE__ */ new Set();
|
|
34495
|
+
const dependsOn = /* @__PURE__ */ new Set();
|
|
34496
|
+
const variableRefs = /* @__PURE__ */ new Set();
|
|
34497
|
+
collectVariableRefs(stmt, variableRefs);
|
|
34498
|
+
for (const name of variableRefs) {
|
|
34499
|
+
const def = variableDefs.get(name);
|
|
34500
|
+
if (def === void 0) {
|
|
34501
|
+
throw new BatchAnalysisError(
|
|
34502
|
+
`ParseError: variable @${name} is not defined before statement ${index + 1}.`,
|
|
34503
|
+
index
|
|
34504
|
+
);
|
|
34505
|
+
}
|
|
34506
|
+
def.referencedBy.push(index);
|
|
34507
|
+
}
|
|
34508
|
+
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
34509
|
+
if (variableDefs.has(stmt.name)) {
|
|
34510
|
+
throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
|
|
34511
|
+
}
|
|
34512
|
+
variableDefs.set(stmt.name, { index, referencedBy: [] });
|
|
34513
|
+
variableOrder.push(stmt.name);
|
|
34514
|
+
if (variableOrder.length > MAX_BATCH_VARIABLES) {
|
|
34515
|
+
throw new BatchAnalysisError(
|
|
34516
|
+
`ParseError: batch exceeds ${MAX_BATCH_VARIABLES} variables.`,
|
|
34517
|
+
index
|
|
34518
|
+
);
|
|
34519
|
+
}
|
|
34520
|
+
}
|
|
34521
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
34522
|
+
collectRefs(stmt.query, refs, stmtAppIds);
|
|
34523
|
+
} else if (stmt.type === "DROP_TEMP_TABLE") {
|
|
34524
|
+
} else {
|
|
34525
|
+
collectRefs(stmt, refs, stmtAppIds);
|
|
34526
|
+
}
|
|
34527
|
+
let tempOnlySource = false;
|
|
34528
|
+
if (stmt.type === "INSERT_SELECT" || stmt.type === "UPSERT_SELECT") {
|
|
34529
|
+
const srcTemp = /* @__PURE__ */ new Set();
|
|
34530
|
+
const srcApps = /* @__PURE__ */ new Set();
|
|
34531
|
+
collectRefs(stmt.select, srcTemp, srcApps);
|
|
34532
|
+
tempOnlySource = srcTemp.size > 0 && srcApps.size === 0;
|
|
34533
|
+
}
|
|
34534
|
+
for (const name of refs) {
|
|
34535
|
+
const at = defined.get(name);
|
|
34536
|
+
if (at === void 0) {
|
|
34537
|
+
throw new BatchAnalysisError(
|
|
34538
|
+
`ParseError: temp table ${name} is not defined in this batch.`,
|
|
34539
|
+
index
|
|
34540
|
+
);
|
|
34541
|
+
}
|
|
34542
|
+
dependsOn.add(at);
|
|
34543
|
+
}
|
|
34544
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
34545
|
+
if (defined.has(stmt.name)) {
|
|
34546
|
+
throw new BatchAnalysisError(
|
|
34547
|
+
`ParseError: temp table ${stmt.name} is already defined.`,
|
|
34548
|
+
index
|
|
34549
|
+
);
|
|
34550
|
+
}
|
|
34551
|
+
defined.set(stmt.name, index);
|
|
34552
|
+
createdOrder.push(stmt.name);
|
|
34553
|
+
created.push(stmt.name);
|
|
34554
|
+
if (defined.size > MAX_TEMP_TABLES) {
|
|
34555
|
+
throw new BatchAnalysisError(
|
|
34556
|
+
`ParseError: batch exceeds ${MAX_TEMP_TABLES} temp tables.`,
|
|
34557
|
+
index
|
|
34558
|
+
);
|
|
34559
|
+
}
|
|
34560
|
+
}
|
|
34561
|
+
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
34562
|
+
const at = defined.get(stmt.name);
|
|
34563
|
+
if (at === void 0) {
|
|
34564
|
+
throw new BatchAnalysisError(
|
|
34565
|
+
`ParseError: temp table ${stmt.name} is not defined in this batch.`,
|
|
34566
|
+
index
|
|
34567
|
+
);
|
|
34568
|
+
}
|
|
34569
|
+
dependsOn.add(at);
|
|
34570
|
+
dropped.push(stmt.name);
|
|
34571
|
+
defined.delete(stmt.name);
|
|
34572
|
+
}
|
|
34573
|
+
results.push({
|
|
34574
|
+
index,
|
|
34575
|
+
statementType,
|
|
34576
|
+
isDml: isDmlType(statementType),
|
|
34577
|
+
isReadOnly: isReadOnlyType(statementType),
|
|
34578
|
+
hasWhere: hasWhereClause(stmt),
|
|
34579
|
+
insertValuesCount: getInsertValuesCount(stmt),
|
|
34580
|
+
appIds: [...stmtAppIds].sort((a, b) => a - b),
|
|
34581
|
+
tempTablesCreated: created,
|
|
34582
|
+
tempTablesReferenced: [...refs],
|
|
34583
|
+
tempTablesDropped: dropped,
|
|
34584
|
+
dependsOn: [...dependsOn].sort((a, b) => a - b),
|
|
34585
|
+
tempOnlySource,
|
|
34586
|
+
targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null
|
|
34587
|
+
});
|
|
34588
|
+
});
|
|
34589
|
+
const containsDml = results.some((r) => r.isDml);
|
|
34590
|
+
const variables = variableOrder.map((name) => ({
|
|
34591
|
+
name,
|
|
34592
|
+
referencedBy: [...variableDefs.get(name).referencedBy]
|
|
34593
|
+
}));
|
|
34594
|
+
return {
|
|
34595
|
+
statementCount: statements.length,
|
|
34596
|
+
isReadOnlyBatch: !containsDml && results.every((r) => r.isReadOnly),
|
|
34597
|
+
containsDml,
|
|
34598
|
+
tempTables: createdOrder,
|
|
34599
|
+
variables,
|
|
34600
|
+
warnings: variables.filter((v) => v.referencedBy.length === 0).map((v) => `variable @${v.name} is never used.`),
|
|
34601
|
+
statements: results
|
|
34602
|
+
};
|
|
34603
|
+
}
|
|
34604
|
+
|
|
34605
|
+
// src/core/batchVariables.ts
|
|
34606
|
+
var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
34607
|
+
function normalizeBatchVariableName(name) {
|
|
34608
|
+
if (!VARIABLE_NAME_RE.test(name)) {
|
|
34609
|
+
throw new Error(
|
|
34610
|
+
`ArgumentError: invalid variable name "${name}". Use a name without @ matching [A-Za-z_][A-Za-z0-9_]{0,63}.`
|
|
34611
|
+
);
|
|
34612
|
+
}
|
|
34613
|
+
return name.toLowerCase();
|
|
34614
|
+
}
|
|
34615
|
+
function normalizeBatchVariables(input) {
|
|
34616
|
+
const normalized = /* @__PURE__ */ Object.create(null);
|
|
34617
|
+
for (const [rawName, value] of Object.entries(input ?? {})) {
|
|
34618
|
+
const name = normalizeBatchVariableName(rawName);
|
|
34619
|
+
if (Object.prototype.hasOwnProperty.call(normalized, name)) {
|
|
34620
|
+
throw new Error(`ArgumentError: variable "${rawName}" is specified more than once.`);
|
|
34621
|
+
}
|
|
34622
|
+
normalized[name] = value;
|
|
34623
|
+
}
|
|
34624
|
+
return normalized;
|
|
34625
|
+
}
|
|
34626
|
+
function validateDeclaredBatchVariables(statements, input) {
|
|
34627
|
+
const normalized = normalizeBatchVariables(input);
|
|
34628
|
+
const declared = new Set(
|
|
34629
|
+
statements.filter((stmt) => stmt.type === "DECLARE_VARIABLE").map((stmt) => stmt.name)
|
|
34630
|
+
);
|
|
34631
|
+
for (const name of Object.keys(normalized)) {
|
|
34632
|
+
if (!declared.has(name)) {
|
|
34633
|
+
throw new Error(`ArgumentError: injected variable @${name} is not declared.`);
|
|
34634
|
+
}
|
|
34635
|
+
}
|
|
34636
|
+
return normalized;
|
|
34637
|
+
}
|
|
34638
|
+
|
|
34639
|
+
// src/core/scalarCompare.ts
|
|
34640
|
+
function compareScalarValues(op, leftStr, rightStr) {
|
|
34641
|
+
if (op === "=") return leftStr === rightStr;
|
|
34642
|
+
if (op === "!=" || op === "<>") return leftStr !== rightStr;
|
|
34643
|
+
const rightNum = Number(rightStr);
|
|
34644
|
+
if (leftStr === "" && rightStr !== "" && Number.isFinite(rightNum)) {
|
|
34645
|
+
return op === "<" || op === "<=";
|
|
34646
|
+
}
|
|
34647
|
+
const leftNum = Number(leftStr);
|
|
34648
|
+
const numeric = !Number.isNaN(leftNum) && !Number.isNaN(rightNum);
|
|
34649
|
+
switch (op) {
|
|
34650
|
+
case ">":
|
|
34651
|
+
return numeric ? leftNum > rightNum : leftStr > rightStr;
|
|
34652
|
+
case "<":
|
|
34653
|
+
return numeric ? leftNum < rightNum : leftStr < rightStr;
|
|
34654
|
+
case ">=":
|
|
34655
|
+
return numeric ? leftNum >= rightNum : leftStr >= rightStr;
|
|
34656
|
+
case "<=":
|
|
34657
|
+
return numeric ? leftNum <= rightNum : leftStr <= rightStr;
|
|
34658
|
+
}
|
|
34659
|
+
}
|
|
34660
|
+
|
|
34201
34661
|
// src/engine/evalFunc.ts
|
|
34202
34662
|
function evalArithExpr(expr, row) {
|
|
34203
34663
|
if (expr.type === "NUMBER") return expr.value;
|
|
@@ -34409,25 +34869,29 @@ function resolveFieldRef(row, field) {
|
|
|
34409
34869
|
}
|
|
34410
34870
|
|
|
34411
34871
|
// src/engine/evalWhere.ts
|
|
34412
|
-
function evalWhere(expr, row, resolveFieldType) {
|
|
34872
|
+
function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
|
|
34413
34873
|
switch (expr.type) {
|
|
34414
34874
|
case "BINARY":
|
|
34415
|
-
return evalBinary(expr, row, resolveFieldType);
|
|
34875
|
+
return evalBinary(expr, row, resolveFieldType, appliedKlikes);
|
|
34416
34876
|
case "NULL_CHECK":
|
|
34417
34877
|
return evalNullCheck(expr, row);
|
|
34418
34878
|
case "LOGICAL":
|
|
34419
|
-
return evalLogical(expr, row, resolveFieldType);
|
|
34879
|
+
return evalLogical(expr, row, resolveFieldType, appliedKlikes);
|
|
34420
34880
|
case "NOT":
|
|
34421
|
-
return !evalWhere(expr.expr, row, resolveFieldType);
|
|
34881
|
+
return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
34422
34882
|
case "GROUP":
|
|
34423
|
-
return evalWhere(expr.expr, row, resolveFieldType);
|
|
34883
|
+
return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
|
|
34424
34884
|
case "EXISTS": {
|
|
34425
34885
|
const exists = expr.resolved;
|
|
34426
34886
|
return expr.not ? !exists : exists;
|
|
34427
34887
|
}
|
|
34428
34888
|
}
|
|
34429
34889
|
}
|
|
34430
|
-
function evalBinary(expr, row, resolveFieldType) {
|
|
34890
|
+
function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
|
|
34891
|
+
if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
|
|
34892
|
+
if (appliedKlikes?.has(expr)) return true;
|
|
34893
|
+
throw new Error("KLIKE / NOT KLIKE \u306F\u62BC\u3057\u4E0B\u3052\u6E08\u307F\u96C6\u5408\u306B\u542B\u307E\u308C\u306A\u3044\u305F\u3081 JavaScript \u5074\u3067\u306F\u8A55\u4FA1\u3067\u304D\u307E\u305B\u3093");
|
|
34894
|
+
}
|
|
34431
34895
|
const left = resolveField(expr.left, row, resolveFieldType);
|
|
34432
34896
|
const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
|
|
34433
34897
|
return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
|
|
@@ -34454,6 +34918,9 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
|
|
|
34454
34918
|
const pattern = resolveValue(right, row, resolveFieldType);
|
|
34455
34919
|
return !matchLike(leftStr, pattern);
|
|
34456
34920
|
}
|
|
34921
|
+
if (op === "KLIKE" || op === "NOT_KLIKE") {
|
|
34922
|
+
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");
|
|
34923
|
+
}
|
|
34457
34924
|
const rightStr = resolveValue(right, row, resolveFieldType);
|
|
34458
34925
|
return compareScalarValues(op, leftStr, rightStr);
|
|
34459
34926
|
}
|
|
@@ -34506,11 +34973,11 @@ function evalNullCheck(expr, row) {
|
|
|
34506
34973
|
const val = resolveField(expr.field, row);
|
|
34507
34974
|
return expr.not ? val !== "" : val === "";
|
|
34508
34975
|
}
|
|
34509
|
-
function evalLogical(expr, row, resolveFieldType) {
|
|
34976
|
+
function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
|
|
34510
34977
|
if (expr.op === "AND") {
|
|
34511
|
-
return evalWhere(expr.left, row, resolveFieldType) && evalWhere(expr.right, row, resolveFieldType);
|
|
34978
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
34512
34979
|
}
|
|
34513
|
-
return evalWhere(expr.left, row, resolveFieldType) || evalWhere(expr.right, row, resolveFieldType);
|
|
34980
|
+
return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
|
|
34514
34981
|
}
|
|
34515
34982
|
function resolveField(field, row, resolveFieldType) {
|
|
34516
34983
|
if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
|
|
@@ -34609,6 +35076,11 @@ function matchLike(value, pattern) {
|
|
|
34609
35076
|
|
|
34610
35077
|
// src/converter/dmlToKintone.ts
|
|
34611
35078
|
function assertDmlWhereIsSafe(where) {
|
|
35079
|
+
if (whereHasKlike(where)) {
|
|
35080
|
+
throw new DmlConvertError(
|
|
35081
|
+
"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\u3001\u5168 DML \u3067\u5B89\u5168\u4E0A\u62D2\u5426\u3057\u3066\u3044\u307E\u3059\u3002"
|
|
35082
|
+
);
|
|
35083
|
+
}
|
|
34612
35084
|
if (!whereHasLike(where)) return;
|
|
34613
35085
|
throw new DmlConvertError(
|
|
34614
35086
|
"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"
|
|
@@ -35096,92 +35568,6 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
35096
35568
|
};
|
|
35097
35569
|
}
|
|
35098
35570
|
|
|
35099
|
-
// src/core/optimization/wherePredicatePushdown.ts
|
|
35100
|
-
function extractSafePushdownLeaves(where, options = {}) {
|
|
35101
|
-
return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
|
|
35102
|
-
}
|
|
35103
|
-
function extractTypedPushdownCandidates(where, options = {}) {
|
|
35104
|
-
return extractAndLeaves(
|
|
35105
|
-
where,
|
|
35106
|
-
(expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
|
|
35107
|
-
);
|
|
35108
|
-
}
|
|
35109
|
-
function extractAndLeaves(where, accept) {
|
|
35110
|
-
switch (where.type) {
|
|
35111
|
-
case "BINARY":
|
|
35112
|
-
return accept(where) ? where : null;
|
|
35113
|
-
case "LOGICAL":
|
|
35114
|
-
if (where.op !== "AND") return null;
|
|
35115
|
-
{
|
|
35116
|
-
const left = extractAndLeaves(where.left, accept);
|
|
35117
|
-
const right = extractAndLeaves(where.right, accept);
|
|
35118
|
-
if (left && right) return { ...where, left, right };
|
|
35119
|
-
return left ?? right ?? null;
|
|
35120
|
-
}
|
|
35121
|
-
case "GROUP":
|
|
35122
|
-
return extractAndLeaves(where.expr, accept);
|
|
35123
|
-
case "NULL_CHECK":
|
|
35124
|
-
case "NOT":
|
|
35125
|
-
case "EXISTS":
|
|
35126
|
-
return null;
|
|
35127
|
-
}
|
|
35128
|
-
}
|
|
35129
|
-
function isSafeComparison(expr, options) {
|
|
35130
|
-
if (isSafeIdComparison(expr, options)) return true;
|
|
35131
|
-
if (isNumericCandidate(expr, options)) {
|
|
35132
|
-
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
35133
|
-
}
|
|
35134
|
-
return isSelectionInComparison(expr, options);
|
|
35135
|
-
}
|
|
35136
|
-
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
35137
|
-
"DROP_DOWN",
|
|
35138
|
-
"RADIO_BUTTON",
|
|
35139
|
-
"CHECK_BOX",
|
|
35140
|
-
"MULTI_SELECT",
|
|
35141
|
-
"STATUS"
|
|
35142
|
-
]);
|
|
35143
|
-
function isSelectionInComparison(expr, options) {
|
|
35144
|
-
if (!isSelectionInCandidate(expr, options)) return false;
|
|
35145
|
-
if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
|
|
35146
|
-
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
35147
|
-
if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
|
|
35148
|
-
const validOptions = options.fieldOptions?.get(expr.left.field);
|
|
35149
|
-
if (validOptions === void 0) return false;
|
|
35150
|
-
return expr.right.values.every(
|
|
35151
|
-
(value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
|
|
35152
|
-
);
|
|
35153
|
-
}
|
|
35154
|
-
function isSafeIdComparison(expr, options) {
|
|
35155
|
-
if (!isTargetIdField(expr.left, options)) return false;
|
|
35156
|
-
if (expr.right.type !== "NUMBER") return false;
|
|
35157
|
-
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
35158
|
-
}
|
|
35159
|
-
function isTargetIdField(field, options) {
|
|
35160
|
-
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
35161
|
-
const targetAlias = options.tableAlias ?? null;
|
|
35162
|
-
if (field.tableAlias === targetAlias) return true;
|
|
35163
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
35164
|
-
}
|
|
35165
|
-
function isNumericCandidate(expr, options) {
|
|
35166
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
35167
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
35168
|
-
if (expr.right.type !== "NUMBER") return false;
|
|
35169
|
-
if (expr.op === "=") return true;
|
|
35170
|
-
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
35171
|
-
}
|
|
35172
|
-
function isSelectionInCandidate(expr, options) {
|
|
35173
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
35174
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
35175
|
-
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
35176
|
-
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
35177
|
-
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
35178
|
-
}
|
|
35179
|
-
function isTargetField(field, options) {
|
|
35180
|
-
const targetAlias = options.tableAlias ?? null;
|
|
35181
|
-
if (field.tableAlias === targetAlias) return true;
|
|
35182
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
35183
|
-
}
|
|
35184
|
-
|
|
35185
35571
|
// src/engine/process.ts
|
|
35186
35572
|
function flatten(record2, alias) {
|
|
35187
35573
|
const row = {};
|
|
@@ -35246,9 +35632,9 @@ function applyJoin(leftRows, rightRows, join) {
|
|
|
35246
35632
|
}
|
|
35247
35633
|
return result;
|
|
35248
35634
|
}
|
|
35249
|
-
function applyFilter(rows, where, resolveFieldType) {
|
|
35635
|
+
function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
|
|
35250
35636
|
if (where === null) return rows;
|
|
35251
|
-
return rows.filter((row) => evalWhere(where, row, resolveFieldType));
|
|
35637
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
|
|
35252
35638
|
}
|
|
35253
35639
|
function hasAggregateColumns(columns) {
|
|
35254
35640
|
return columns.some(
|
|
@@ -35705,7 +36091,8 @@ function runFullScan(input) {
|
|
|
35705
36091
|
optionOrders,
|
|
35706
36092
|
sortKinds,
|
|
35707
36093
|
fieldTypeResolver,
|
|
35708
|
-
havingFieldTypeResolver
|
|
36094
|
+
havingFieldTypeResolver,
|
|
36095
|
+
appliedKlikes
|
|
35709
36096
|
} = input;
|
|
35710
36097
|
let rows = [];
|
|
35711
36098
|
const mainAlias = stmt.from.alias;
|
|
@@ -35717,7 +36104,7 @@ function runFullScan(input) {
|
|
|
35717
36104
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
35718
36105
|
rows = applyJoin(rows, rightRows, join);
|
|
35719
36106
|
}
|
|
35720
|
-
rows = applyFilter(rows, stmt.where, fieldTypeResolver);
|
|
36107
|
+
rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
|
|
35721
36108
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
35722
36109
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
|
|
35723
36110
|
}
|
|
@@ -35838,6 +36225,7 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
35838
36225
|
if (unresolved !== null) {
|
|
35839
36226
|
throw new Error(`ParseError: variable @${unresolved} is not defined in a batch.`);
|
|
35840
36227
|
}
|
|
36228
|
+
validateKlikeStatement(stmt);
|
|
35841
36229
|
switch (stmt.type) {
|
|
35842
36230
|
case "SELECT":
|
|
35843
36231
|
return executeSelect(stmt, client, options, cacheContext);
|
|
@@ -35974,6 +36362,7 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
35974
36362
|
async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables) {
|
|
35975
36363
|
if (stmt.type === "SET_VARIABLE") {
|
|
35976
36364
|
const resolvedStmt2 = resolveVariableRefs(stmt, variables);
|
|
36365
|
+
validateKlikeStatement(resolvedStmt2);
|
|
35977
36366
|
if (resolvedStmt2.expr.type === "SCALAR_SUBQUERY") {
|
|
35978
36367
|
try {
|
|
35979
36368
|
const value = await evaluateScalarSubquery(
|
|
@@ -36006,6 +36395,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
36006
36395
|
return {};
|
|
36007
36396
|
}
|
|
36008
36397
|
const resolvedStmt = resolveVariableRefs(stmt, variables);
|
|
36398
|
+
validateKlikeStatement(resolvedStmt);
|
|
36009
36399
|
if (resolvedStmt.type === "CREATE_TEMP_TABLE") {
|
|
36010
36400
|
const materializeOptions = {
|
|
36011
36401
|
...options,
|
|
@@ -36402,23 +36792,6 @@ async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
|
|
|
36402
36792
|
}
|
|
36403
36793
|
}
|
|
36404
36794
|
}
|
|
36405
|
-
function extractMainSafePushdown(stmt, fieldTypes, fieldOptions) {
|
|
36406
|
-
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
36407
|
-
if (stmt.joins.length === 0) {
|
|
36408
|
-
return extractSafePushdownLeaves(stmt.where, {
|
|
36409
|
-
tableAlias: stmt.from.alias ?? void 0,
|
|
36410
|
-
allowUnqualifiedFields: true,
|
|
36411
|
-
fieldTypes,
|
|
36412
|
-
fieldOptions
|
|
36413
|
-
});
|
|
36414
|
-
}
|
|
36415
|
-
if (!stmt.from.alias) return null;
|
|
36416
|
-
return extractSafePushdownLeaves(stmt.where, {
|
|
36417
|
-
tableAlias: stmt.from.alias,
|
|
36418
|
-
fieldTypes,
|
|
36419
|
-
fieldOptions
|
|
36420
|
-
});
|
|
36421
|
-
}
|
|
36422
36795
|
function extractMainTypedPushdownCandidate(stmt) {
|
|
36423
36796
|
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
36424
36797
|
if (stmt.joins.length === 0) {
|
|
@@ -36600,23 +36973,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
36600
36973
|
loadTypedInFieldTypes(stmt, client, cacheContext)
|
|
36601
36974
|
]);
|
|
36602
36975
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
36603
|
-
const
|
|
36604
|
-
|
|
36605
|
-
|
|
36606
|
-
|
|
36607
|
-
);
|
|
36608
|
-
const tableConditions = /* @__PURE__ */ new Map();
|
|
36609
|
-
if (stmt.where !== null) {
|
|
36610
|
-
for (const join of stmt.joins) {
|
|
36611
|
-
if (!join.table.alias || join.table.subtableCode || join.table.cteName !== null) continue;
|
|
36612
|
-
const cond = extractSafePushdownLeaves(stmt.where, {
|
|
36613
|
-
tableAlias: join.table.alias,
|
|
36614
|
-
fieldTypes: pushdownMeta.fieldTypesByApp.get(join.table.appId),
|
|
36615
|
-
fieldOptions: pushdownMeta.fieldOptionsByApp.get(join.table.appId)
|
|
36616
|
-
});
|
|
36617
|
-
if (cond) tableConditions.set(join.table.alias, cond);
|
|
36618
|
-
}
|
|
36619
|
-
}
|
|
36976
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
36977
|
+
validateKlikePushdownPlan(pushdownPlan);
|
|
36978
|
+
const mainPushDown = pushdownPlan.mainCondition;
|
|
36979
|
+
const tableConditions = pushdownPlan.joinConditions;
|
|
36620
36980
|
const mainFetch = fetchTableRecordsForFullScan(
|
|
36621
36981
|
stmt,
|
|
36622
36982
|
stmt.from,
|
|
@@ -36697,7 +37057,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
36697
37057
|
optionOrders,
|
|
36698
37058
|
sortKinds,
|
|
36699
37059
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
36700
|
-
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
37060
|
+
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
37061
|
+
appliedKlikes: pushdownPlan.appliedKlikes
|
|
36701
37062
|
});
|
|
36702
37063
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
36703
37064
|
}
|
|
@@ -36746,83 +37107,6 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
|
|
|
36746
37107
|
}
|
|
36747
37108
|
return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
|
|
36748
37109
|
}
|
|
36749
|
-
function canInlineSingleCte(stmt) {
|
|
36750
|
-
if (stmt.ctes.length !== 1) return false;
|
|
36751
|
-
const cteDef = stmt.ctes[0];
|
|
36752
|
-
if (cteDef.query.type !== "SELECT") return false;
|
|
36753
|
-
if (resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
|
|
36754
|
-
const finalQuery = stmt.query;
|
|
36755
|
-
if (finalQuery.type !== "SELECT") return false;
|
|
36756
|
-
if (finalQuery.from.cteName !== cteDef.name) return false;
|
|
36757
|
-
if (finalQuery.joins.length > 0) return false;
|
|
36758
|
-
if (finalQuery.groupBy.length > 0) return false;
|
|
36759
|
-
if (finalQuery.distinct) return false;
|
|
36760
|
-
if (finalQuery.columns.some(
|
|
36761
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"
|
|
36762
|
-
)) return false;
|
|
36763
|
-
return true;
|
|
36764
|
-
}
|
|
36765
|
-
function buildInlinedQuery(stmt) {
|
|
36766
|
-
const cteBody = stmt.ctes[0].query;
|
|
36767
|
-
const final = stmt.query;
|
|
36768
|
-
const cteAlias = final.from.alias;
|
|
36769
|
-
const finalWhere = stripCteAlias(final.where, cteAlias);
|
|
36770
|
-
let mergedWhere;
|
|
36771
|
-
if (cteBody.where === null) mergedWhere = finalWhere;
|
|
36772
|
-
else if (finalWhere === null) mergedWhere = cteBody.where;
|
|
36773
|
-
else mergedWhere = { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
|
|
36774
|
-
const columns = final.columns.every((c) => c.type === "WILDCARD") ? cteBody.columns : final.columns;
|
|
36775
|
-
return {
|
|
36776
|
-
type: "SELECT",
|
|
36777
|
-
from: cteBody.from,
|
|
36778
|
-
joins: [],
|
|
36779
|
-
columns,
|
|
36780
|
-
where: mergedWhere,
|
|
36781
|
-
groupBy: [],
|
|
36782
|
-
having: null,
|
|
36783
|
-
orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
|
|
36784
|
-
limit: final.limit ?? cteBody.limit,
|
|
36785
|
-
offset: final.offset ?? cteBody.offset,
|
|
36786
|
-
distinct: false
|
|
36787
|
-
};
|
|
36788
|
-
}
|
|
36789
|
-
function stripCteAlias(where, alias) {
|
|
36790
|
-
if (where === null || alias === null) return where;
|
|
36791
|
-
switch (where.type) {
|
|
36792
|
-
case "BINARY":
|
|
36793
|
-
return {
|
|
36794
|
-
type: "BINARY",
|
|
36795
|
-
op: where.op,
|
|
36796
|
-
left: stripCteAliasFromFieldValue(where.left, alias),
|
|
36797
|
-
right: where.right
|
|
36798
|
-
};
|
|
36799
|
-
case "NULL_CHECK":
|
|
36800
|
-
return {
|
|
36801
|
-
type: "NULL_CHECK",
|
|
36802
|
-
not: where.not,
|
|
36803
|
-
field: stripCteAliasFromFieldValue(where.field, alias)
|
|
36804
|
-
};
|
|
36805
|
-
case "LOGICAL":
|
|
36806
|
-
return {
|
|
36807
|
-
type: "LOGICAL",
|
|
36808
|
-
op: where.op,
|
|
36809
|
-
left: stripCteAlias(where.left, alias),
|
|
36810
|
-
right: stripCteAlias(where.right, alias)
|
|
36811
|
-
};
|
|
36812
|
-
case "NOT":
|
|
36813
|
-
return { type: "NOT", expr: stripCteAlias(where.expr, alias) };
|
|
36814
|
-
case "GROUP":
|
|
36815
|
-
return { type: "GROUP", expr: stripCteAlias(where.expr, alias) };
|
|
36816
|
-
case "EXISTS":
|
|
36817
|
-
return where;
|
|
36818
|
-
}
|
|
36819
|
-
}
|
|
36820
|
-
function stripCteAliasFromFieldValue(fv, alias) {
|
|
36821
|
-
if (fv.type === "FIELD" && fv.tableAlias === alias) {
|
|
36822
|
-
return { type: "FIELD", field: fv.field, tableAlias: null };
|
|
36823
|
-
}
|
|
36824
|
-
return fv;
|
|
36825
|
-
}
|
|
36826
37110
|
async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
|
|
36827
37111
|
if (query.type === "UNION") {
|
|
36828
37112
|
const [leftResult, rightResult] = await Promise.all([
|
|
@@ -36857,8 +37141,13 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
36857
37141
|
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
|
|
36858
37142
|
resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
|
|
36859
37143
|
]);
|
|
36860
|
-
const typedInFieldTypes = await
|
|
37144
|
+
const [pushdownMeta, typedInFieldTypes] = await Promise.all([
|
|
37145
|
+
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
37146
|
+
loadTypedInFieldTypes(stmt, client, cacheContext)
|
|
37147
|
+
]);
|
|
36861
37148
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
37149
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
37150
|
+
validateKlikePushdownPlan(pushdownPlan);
|
|
36862
37151
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
36863
37152
|
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
36864
37153
|
scalarCachePromise.catch(() => {
|
|
@@ -36878,7 +37167,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
36878
37167
|
parallel,
|
|
36879
37168
|
true,
|
|
36880
37169
|
options.onLimitReached ?? "error",
|
|
36881
|
-
warnings
|
|
37170
|
+
warnings,
|
|
37171
|
+
pushdownPlan.mainCondition
|
|
36882
37172
|
);
|
|
36883
37173
|
tables.set(stmt.from.alias, mainRecords);
|
|
36884
37174
|
}
|
|
@@ -36887,6 +37177,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
36887
37177
|
const rows2 = cteCache.get(join.table.cteName) ?? [];
|
|
36888
37178
|
tables.set(join.table.alias, rows2.map(processRowToKintoneRecord));
|
|
36889
37179
|
} else {
|
|
37180
|
+
const pushDownCond = join.table.alias ? pushdownPlan.joinConditions.get(join.table.alias) ?? null : null;
|
|
36890
37181
|
const optimized = await tryFetchJoinRecordsBySourceKeys(
|
|
36891
37182
|
stmt,
|
|
36892
37183
|
join,
|
|
@@ -36895,7 +37186,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
36895
37186
|
maxRecords2,
|
|
36896
37187
|
parallel,
|
|
36897
37188
|
options.onLimitReached ?? "error",
|
|
36898
|
-
warnings
|
|
37189
|
+
warnings,
|
|
37190
|
+
pushDownCond
|
|
36899
37191
|
);
|
|
36900
37192
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
36901
37193
|
stmt,
|
|
@@ -36905,7 +37197,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
36905
37197
|
parallel,
|
|
36906
37198
|
false,
|
|
36907
37199
|
options.onLimitReached ?? "error",
|
|
36908
|
-
warnings
|
|
37200
|
+
warnings,
|
|
37201
|
+
pushDownCond
|
|
36909
37202
|
);
|
|
36910
37203
|
tables.set(join.table.alias, joinRecords);
|
|
36911
37204
|
}
|
|
@@ -36920,7 +37213,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
36920
37213
|
optionOrders,
|
|
36921
37214
|
sortKinds,
|
|
36922
37215
|
fieldTypeResolver: fieldTypeResolvers.row,
|
|
36923
|
-
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
37216
|
+
havingFieldTypeResolver: fieldTypeResolvers.having,
|
|
37217
|
+
appliedKlikes: pushdownPlan.appliedKlikes
|
|
36924
37218
|
});
|
|
36925
37219
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
36926
37220
|
}
|
|
@@ -37874,7 +38168,9 @@ async function executeDescribe(stmt, client, cacheContext) {
|
|
|
37874
38168
|
function parseSql(sql) {
|
|
37875
38169
|
try {
|
|
37876
38170
|
const tokens = new Lexer(sql).tokenize();
|
|
37877
|
-
|
|
38171
|
+
const stmt = new Parser(tokens).parse();
|
|
38172
|
+
validateKlikeStatement(stmt);
|
|
38173
|
+
return stmt;
|
|
37878
38174
|
} catch (e) {
|
|
37879
38175
|
if (e instanceof LexError || e instanceof ParseError) {
|
|
37880
38176
|
throw e;
|
|
@@ -37985,6 +38281,7 @@ function buildBatchExplainPlans(sql, injectedVariables) {
|
|
|
37985
38281
|
statementCount: statements.length,
|
|
37986
38282
|
statements: statements.map((stmt, i) => {
|
|
37987
38283
|
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
|
|
38284
|
+
validateKlikeStatement(planStmt);
|
|
37988
38285
|
const result = {
|
|
37989
38286
|
index: i,
|
|
37990
38287
|
type: analysis.statements[i].statementType,
|
|
@@ -38126,9 +38423,10 @@ function buildSelectPlan(stmt, label) {
|
|
|
38126
38423
|
lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
|
|
38127
38424
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
38128
38425
|
} else {
|
|
38426
|
+
const pushdownPlan = buildKlikePushdownPlan(stmt);
|
|
38129
38427
|
const mainFields = selectToFetchAllFields(stmt, stmt.from);
|
|
38130
38428
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
38131
|
-
const mainPushDown =
|
|
38429
|
+
const mainPushDown = pushdownPlan.mainCondition;
|
|
38132
38430
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
38133
38431
|
const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
38134
38432
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
@@ -38141,7 +38439,7 @@ function buildSelectPlan(stmt, label) {
|
|
|
38141
38439
|
const joinFields = selectToFetchAllFields(stmt, join.table);
|
|
38142
38440
|
const joinAliasStr = join.table.alias ? ` AS ${join.table.alias}` : "";
|
|
38143
38441
|
const joinType = join.type === "INNER" ? "JOIN" : `${join.type} JOIN`;
|
|
38144
|
-
const joinPushDown = join.table.alias
|
|
38442
|
+
const joinPushDown = join.table.alias ? pushdownPlan.joinConditions.get(join.table.alias) ?? null : null;
|
|
38145
38443
|
const joinCandidate = join.table.alias && !join.table.subtableCode && join.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join.table.alias }) : null;
|
|
38146
38444
|
const joinQ = joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
38147
38445
|
lines.push(` ${joinType}: APP${join.table.appId}${joinAliasStr} (${join.table.appId})`);
|
|
@@ -38184,6 +38482,10 @@ function buildWithPlan(stmt) {
|
|
|
38184
38482
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
38185
38483
|
lines.push(...buildExplainPlan(stmt.query, "[main]"));
|
|
38186
38484
|
}
|
|
38485
|
+
if (canInlineSingleCte(stmt)) {
|
|
38486
|
+
lines.push("");
|
|
38487
|
+
lines.push(...buildSelectPlan(buildInlinedQuery(stmt), "[effective: inlined CTE]"));
|
|
38488
|
+
}
|
|
38187
38489
|
return lines;
|
|
38188
38490
|
}
|
|
38189
38491
|
function collectFullScanReasons(stmt) {
|
|
@@ -38418,11 +38720,15 @@ var OperationCancelledError = class extends Error {
|
|
|
38418
38720
|
// src/core/sql.ts
|
|
38419
38721
|
function parseSqlStatement(sql) {
|
|
38420
38722
|
const tokens = new Lexer(sql).tokenize();
|
|
38421
|
-
|
|
38723
|
+
const stmt = new Parser(tokens).parse();
|
|
38724
|
+
validateKlikeStatement(stmt);
|
|
38725
|
+
return stmt;
|
|
38422
38726
|
}
|
|
38423
38727
|
function parseSqlStatements(sql) {
|
|
38424
38728
|
const tokens = new Lexer(sql).tokenize();
|
|
38425
|
-
|
|
38729
|
+
const statements = new Parser(tokens).parseStatements();
|
|
38730
|
+
statements.forEach(validateKlikeStatement);
|
|
38731
|
+
return statements;
|
|
38426
38732
|
}
|
|
38427
38733
|
|
|
38428
38734
|
// src/output/batchEnvelope.ts
|
|
@@ -40516,7 +40822,7 @@ Options:
|
|
|
40516
40822
|
-h, --help Show help
|
|
40517
40823
|
`);
|
|
40518
40824
|
}
|
|
40519
|
-
var SERVER_VERSION = true ? "2.
|
|
40825
|
+
var SERVER_VERSION = true ? "2.9.0" : "0.0.0-dev";
|
|
40520
40826
|
function createServer(args) {
|
|
40521
40827
|
const server = new McpServer({
|
|
40522
40828
|
name: "ksql-mcp",
|