@rex0220/kintone-sql-tools 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist-cli/ksql.js +1331 -153
- package/dist-mcp/ksql-mcp.js +1332 -155
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -31219,6 +31219,10 @@ var Lexer = class {
|
|
|
31219
31219
|
this.pos += 2;
|
|
31220
31220
|
return this.makeToken("<=" /* LTE */, "<=", start);
|
|
31221
31221
|
}
|
|
31222
|
+
if (ch === "|" && ch2 === "|") {
|
|
31223
|
+
this.pos += 2;
|
|
31224
|
+
return this.makeToken("||" /* CONCAT_OP */, "||", start);
|
|
31225
|
+
}
|
|
31222
31226
|
switch (ch) {
|
|
31223
31227
|
case "=":
|
|
31224
31228
|
this.pos++;
|
|
@@ -31562,6 +31566,8 @@ var Parser = class {
|
|
|
31562
31566
|
constructor(tokens) {
|
|
31563
31567
|
this.tokens = tokens;
|
|
31564
31568
|
this.allowUnaryPlusNumber = false;
|
|
31569
|
+
this.scalarAllowsAggregateArgs = true;
|
|
31570
|
+
this.scalarAllowsCase = true;
|
|
31565
31571
|
this.pos = 0;
|
|
31566
31572
|
/** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
|
|
31567
31573
|
this.cteNames = /* @__PURE__ */ new Set();
|
|
@@ -31650,13 +31656,14 @@ var Parser = class {
|
|
|
31650
31656
|
if (upper === "CREATE") return this.parseCreateTempTable();
|
|
31651
31657
|
if (upper === "DROP") return this.parseDropTempTable();
|
|
31652
31658
|
if (upper === "DECLARE") return this.parseDeclareVariable();
|
|
31659
|
+
if (upper === "VALIDATE") return this.parseValidate();
|
|
31653
31660
|
break;
|
|
31654
31661
|
}
|
|
31655
31662
|
default:
|
|
31656
31663
|
break;
|
|
31657
31664
|
}
|
|
31658
31665
|
throw new ParseError(
|
|
31659
|
-
"SELECT / INSERT / UPDATE / DELETE / REORDER / WITH / SHOW / DESCRIBE / EXPLAIN / CREATE TEMP TABLE / DROP TEMP TABLE / SET / DECLARE / ASSERT \u306E\u3044\u305A\u308C\u304B\u3067\u59CB\u307E\u308B SQL \u6587\u304C\u5FC5\u8981\u3067\u3059",
|
|
31666
|
+
"SELECT / INSERT / UPDATE / DELETE / REORDER / VALIDATE / WITH / SHOW / DESCRIBE / EXPLAIN / CREATE TEMP TABLE / DROP TEMP TABLE / SET / DECLARE / ASSERT \u306E\u3044\u305A\u308C\u304B\u3067\u59CB\u307E\u308B SQL \u6587\u304C\u5FC5\u8981\u3067\u3059",
|
|
31660
31667
|
tok
|
|
31661
31668
|
);
|
|
31662
31669
|
}
|
|
@@ -31667,7 +31674,7 @@ var Parser = class {
|
|
|
31667
31674
|
this.expect("SET" /* SET */);
|
|
31668
31675
|
const variable = this.expect("VARIABLE" /* VARIABLE */, "SET \u306E\u5F8C\u306B\u306F\u5909\u6570\u540D\uFF08\u4F8B: @name\uFF09\u304C\u5FC5\u8981\u3067\u3059");
|
|
31669
31676
|
this.expect("=" /* EQ */);
|
|
31670
|
-
const expr = this.parseScalarExpr("SET", true);
|
|
31677
|
+
const expr = this.peek().kind === "[" /* LBRACKET */ ? this.parseArrayLiteral() : this.parseScalarExpr("SET", true);
|
|
31671
31678
|
return { type: "SET_VARIABLE", name: variable.value.slice(1).toLowerCase(), expr };
|
|
31672
31679
|
}
|
|
31673
31680
|
parseDeclareVariable() {
|
|
@@ -31730,17 +31737,19 @@ var Parser = class {
|
|
|
31730
31737
|
}
|
|
31731
31738
|
rejectNonScalarExpr(node, tok, context) {
|
|
31732
31739
|
if (node.type === "STRING" || node.type === "NUMBER") return;
|
|
31733
|
-
if (node.type === "FIELD_REF" || node.type === "AGG_REF") {
|
|
31740
|
+
if (node.type === "FIELD_REF" || node.type === "FIELD" || node.type === "VARIABLE" || node.type === "AGG_REF") {
|
|
31734
31741
|
throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u30FB\u96C6\u8A08\u95A2\u6570\u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`, tok);
|
|
31735
31742
|
}
|
|
31736
|
-
if (node.type === "ARITH" || node.type === "AGG_ARITH") {
|
|
31743
|
+
if (node.type === "ARITH" || node.type === "SCALAR_ARITH" || node.type === "CONCAT_OP" || node.type === "AGG_ARITH") {
|
|
31737
31744
|
this.rejectNonScalarExpr(node.left, tok, context);
|
|
31738
31745
|
this.rejectNonScalarExpr(node.right, tok, context);
|
|
31739
31746
|
return;
|
|
31740
31747
|
}
|
|
31741
31748
|
if (node.type === "STRING_FUNC") {
|
|
31742
31749
|
for (const arg of node.args) this.rejectNonScalarExpr(arg, tok, context);
|
|
31750
|
+
return;
|
|
31743
31751
|
}
|
|
31752
|
+
throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F CASE \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`, tok);
|
|
31744
31753
|
}
|
|
31745
31754
|
// ----------------------------------------------------------
|
|
31746
31755
|
// CREATE TEMP TABLE / DROP TEMP TABLE(バッチ内一時テーブル)
|
|
@@ -31826,11 +31835,63 @@ var Parser = class {
|
|
|
31826
31835
|
query = this.parseDelete();
|
|
31827
31836
|
} else if (tok.kind === "REORDER" /* REORDER */) {
|
|
31828
31837
|
query = this.parseReorder();
|
|
31838
|
+
} else if (tok.kind === "IDENT" /* IDENT */ && tok.value.toUpperCase() === "VALIDATE") {
|
|
31839
|
+
query = this.parseValidate();
|
|
31829
31840
|
} else {
|
|
31830
|
-
throw new ParseError("EXPLAIN \u306E\u5F8C\u306B\u306F SELECT / WITH / INSERT / UPSERT / UPDATE / DELETE / REORDER \u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
31841
|
+
throw new ParseError("EXPLAIN \u306E\u5F8C\u306B\u306F SELECT / WITH / INSERT / UPSERT / UPDATE / DELETE / REORDER / VALIDATE \u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
31831
31842
|
}
|
|
31832
31843
|
return { type: "EXPLAIN", query };
|
|
31833
31844
|
}
|
|
31845
|
+
/** VALIDATE APP100 [(fields)] [WHERE ...] [CHECK ...] [INTO #err]. */
|
|
31846
|
+
parseValidate() {
|
|
31847
|
+
const validateTok = this.advance();
|
|
31848
|
+
const name = this.parseIdentifier();
|
|
31849
|
+
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
31850
|
+
if (subtableCode) {
|
|
31851
|
+
throw new ParseError("VALIDATE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u3092\u5BFE\u8C61\u306B\u3067\u304D\u307E\u305B\u3093", this.prev());
|
|
31852
|
+
}
|
|
31853
|
+
let fields;
|
|
31854
|
+
if (this.consume("(" /* LPAREN */)) {
|
|
31855
|
+
fields = this.parseIdentList();
|
|
31856
|
+
this.expect(")" /* RPAREN */);
|
|
31857
|
+
}
|
|
31858
|
+
const where = this.consume("WHERE" /* WHERE */) ? this.parseWhereExpr() : null;
|
|
31859
|
+
const checks = this.parseCheckGroups();
|
|
31860
|
+
let errorTable;
|
|
31861
|
+
if (this.consume("INTO" /* INTO */)) {
|
|
31862
|
+
const tableTok = this.peek();
|
|
31863
|
+
if (tableTok.kind !== "IDENT" /* IDENT */ || !tableTok.value.startsWith("#")) {
|
|
31864
|
+
throw new ParseError("VALIDATE INTO \u306B\u306F # \u3067\u59CB\u307E\u308B\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u540D\u304C\u5FC5\u8981\u3067\u3059", tableTok);
|
|
31865
|
+
}
|
|
31866
|
+
errorTable = this.parseTableName();
|
|
31867
|
+
}
|
|
31868
|
+
const stmt = { type: "VALIDATE", appId, fields, where, ...checks, ...errorTable ? { errorTable } : {} };
|
|
31869
|
+
this.assertValidateExpressions(stmt, validateTok);
|
|
31870
|
+
return stmt;
|
|
31871
|
+
}
|
|
31872
|
+
/** v1 VALIDATE is single-app/local: subqueries and qualified references are rejected. */
|
|
31873
|
+
assertValidateExpressions(stmt, tok) {
|
|
31874
|
+
const visit = (node) => {
|
|
31875
|
+
if (Array.isArray(node)) {
|
|
31876
|
+
node.forEach(visit);
|
|
31877
|
+
return;
|
|
31878
|
+
}
|
|
31879
|
+
if (node === null || typeof node !== "object") return;
|
|
31880
|
+
const obj = node;
|
|
31881
|
+
if (obj.type === "EXISTS" || obj.type === "SUBQUERY_IN_LIST" || obj.type === "SCALAR_SUBQUERY") {
|
|
31882
|
+
throw new ParseError("VALIDATE \u306E WHERE / CHECK \u306B\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
31883
|
+
}
|
|
31884
|
+
if (obj.type === "FIELD" && obj.tableAlias !== null && obj.tableAlias !== void 0) {
|
|
31885
|
+
throw new ParseError("VALIDATE \u306E WHERE / CHECK \u3067\u306F\u4FEE\u98FE\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
31886
|
+
}
|
|
31887
|
+
if (obj.type === "FIELD_REF" && typeof obj.field === "string" && obj.field.includes(".")) {
|
|
31888
|
+
throw new ParseError("VALIDATE \u306E WHERE / CHECK \u3067\u306F\u4FEE\u98FE\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
31889
|
+
}
|
|
31890
|
+
Object.values(obj).forEach(visit);
|
|
31891
|
+
};
|
|
31892
|
+
visit(stmt.where);
|
|
31893
|
+
visit(stmt.checkGroups);
|
|
31894
|
+
}
|
|
31834
31895
|
// ----------------------------------------------------------
|
|
31835
31896
|
// ASSERT
|
|
31836
31897
|
//
|
|
@@ -32111,6 +32172,22 @@ var Parser = class {
|
|
|
32111
32172
|
if (this.consume("*" /* STAR */)) {
|
|
32112
32173
|
return { type: "WILDCARD" };
|
|
32113
32174
|
}
|
|
32175
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
32176
|
+
const expr = this.parseScalarValueExpr({ allowAggregateArgs: true });
|
|
32177
|
+
const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
32178
|
+
return { type: "SCALAR_VALUE_COL", expr, alias: alias2 };
|
|
32179
|
+
}
|
|
32180
|
+
if (this.peek().kind === "VARIABLE" /* VARIABLE */) {
|
|
32181
|
+
const variable = this.advance();
|
|
32182
|
+
if (!this.consume("AS" /* AS */)) {
|
|
32183
|
+
throw new ParseError("SELECT \u5217\u306E\u30D0\u30C3\u30C1\u5909\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
|
|
32184
|
+
}
|
|
32185
|
+
return {
|
|
32186
|
+
type: "VARIABLE_COL",
|
|
32187
|
+
name: variable.value.slice(1).toLowerCase(),
|
|
32188
|
+
alias: this.parseAliasName()
|
|
32189
|
+
};
|
|
32190
|
+
}
|
|
32114
32191
|
const windowFunc = this.tryWindowFunc();
|
|
32115
32192
|
if (windowFunc !== null) {
|
|
32116
32193
|
return this.parseWindowColumn(windowFunc);
|
|
@@ -32226,13 +32303,25 @@ var Parser = class {
|
|
|
32226
32303
|
}
|
|
32227
32304
|
selectColumnHasAggregate(column) {
|
|
32228
32305
|
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
32229
|
-
if (column.type
|
|
32230
|
-
|
|
32306
|
+
if (column.type === "STRFUNC_COL") return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
32307
|
+
if (column.type === "SCALAR_VALUE_COL") return this.scalarValueHasAggregate(column.expr);
|
|
32308
|
+
return false;
|
|
32231
32309
|
}
|
|
32232
32310
|
stringFuncArgHasAggregate(arg) {
|
|
32233
32311
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
32234
|
-
|
|
32235
|
-
|
|
32312
|
+
return this.scalarValueHasAggregate(arg);
|
|
32313
|
+
}
|
|
32314
|
+
scalarValueHasAggregate(expr) {
|
|
32315
|
+
if (expr.type === "STRING_FUNC") return expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
32316
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
32317
|
+
return this.scalarValueHasAggregate(expr.left) || this.scalarValueHasAggregate(expr.right);
|
|
32318
|
+
}
|
|
32319
|
+
if (expr.type === "CASE_WHEN") {
|
|
32320
|
+
const results = [...expr.branches.map((b) => b.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
32321
|
+
return results.some((result) => {
|
|
32322
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
32323
|
+
return this.scalarValueHasAggregate(result);
|
|
32324
|
+
});
|
|
32236
32325
|
}
|
|
32237
32326
|
return false;
|
|
32238
32327
|
}
|
|
@@ -32290,6 +32379,105 @@ var Parser = class {
|
|
|
32290
32379
|
throw new ParseError("\u96C6\u8A08\u7B97\u8853\u5F0F\u306B\u306F\u96C6\u8A08\u95A2\u6570\u307E\u305F\u306F\u6570\u5024\u304C\u5FC5\u8981\u3067\u3059", this.peek());
|
|
32291
32380
|
}
|
|
32292
32381
|
// ──────────────────────────────────────────────────
|
|
32382
|
+
// 汎用スカラー値式パーサー(B38)
|
|
32383
|
+
// ──────────────────────────────────────────────────
|
|
32384
|
+
/** 比較・述語・集約・サブクエリを含まない値式の公開入口。 */
|
|
32385
|
+
parseScalarValueExpr(options = {}) {
|
|
32386
|
+
const previousAggregateArgs = this.scalarAllowsAggregateArgs;
|
|
32387
|
+
const previousCase = this.scalarAllowsCase;
|
|
32388
|
+
this.scalarAllowsAggregateArgs = options.allowAggregateArgs === true;
|
|
32389
|
+
this.scalarAllowsCase = options.allowCase !== false;
|
|
32390
|
+
let expr;
|
|
32391
|
+
try {
|
|
32392
|
+
expr = this.parseScalarAddSubConcat(this.scalarAllowsCase);
|
|
32393
|
+
} finally {
|
|
32394
|
+
this.scalarAllowsAggregateArgs = previousAggregateArgs;
|
|
32395
|
+
this.scalarAllowsCase = previousCase;
|
|
32396
|
+
}
|
|
32397
|
+
const next = this.peek();
|
|
32398
|
+
if (next.kind === "IS" /* IS */ || next.kind === "=" /* EQ */ || next.kind === "!=" /* NEQ */ || next.kind === "<>" /* LT_GT */ || next.kind === ">" /* GT */ || next.kind === "<" /* LT */ || next.kind === ">=" /* GTE */ || next.kind === "<=" /* LTE */ || next.kind === "LIKE" /* LIKE */ || next.kind === "KLIKE" /* KLIKE */ || next.kind === "IN" /* IN */ || next.kind === "BETWEEN" /* BETWEEN */) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u6BD4\u8F03\u30FB\u8FF0\u8A9E\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", next);
|
|
32399
|
+
return expr;
|
|
32400
|
+
}
|
|
32401
|
+
parseScalarAddSubConcat(allowCase) {
|
|
32402
|
+
let left = this.parseScalarMulDiv(allowCase);
|
|
32403
|
+
while (this.peek().kind === "+" /* PLUS */ || this.peek().kind === "-" /* MINUS */ || this.peek().kind === "||" /* CONCAT_OP */) {
|
|
32404
|
+
const token = this.advance();
|
|
32405
|
+
const right = this.parseScalarMulDiv(allowCase);
|
|
32406
|
+
left = token.kind === "||" /* CONCAT_OP */ ? { type: "CONCAT_OP", left, right } : { type: "SCALAR_ARITH", left, op: token.kind === "+" /* PLUS */ ? "+" : "-", right };
|
|
32407
|
+
}
|
|
32408
|
+
return left;
|
|
32409
|
+
}
|
|
32410
|
+
parseScalarMulDiv(allowCase) {
|
|
32411
|
+
let left = this.parseScalarPrimary(allowCase);
|
|
32412
|
+
while (this.peek().kind === "*" /* STAR */ || this.peek().kind === "/" /* SLASH */ || this.peek().kind === "%" /* PERCENT */) {
|
|
32413
|
+
const token = this.advance();
|
|
32414
|
+
const op = token.kind === "*" /* STAR */ ? "*" : token.kind === "/" /* SLASH */ ? "/" : "%";
|
|
32415
|
+
left = { type: "SCALAR_ARITH", left, op, right: this.parseScalarPrimary(allowCase) };
|
|
32416
|
+
}
|
|
32417
|
+
return left;
|
|
32418
|
+
}
|
|
32419
|
+
parseScalarPrimary(allowCase) {
|
|
32420
|
+
const tok = this.peek();
|
|
32421
|
+
if (tok.kind === "(" /* LPAREN */) {
|
|
32422
|
+
if (this.peekAt(1).kind === "SELECT" /* SELECT */) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
32423
|
+
this.advance();
|
|
32424
|
+
const expr = this.parseScalarAddSubConcat(allowCase);
|
|
32425
|
+
this.expect(")" /* RPAREN */);
|
|
32426
|
+
return expr;
|
|
32427
|
+
}
|
|
32428
|
+
if (tok.kind === "+" /* PLUS */ || tok.kind === "-" /* MINUS */) {
|
|
32429
|
+
this.advance();
|
|
32430
|
+
if (this.peek().kind === "+" /* PLUS */ || this.peek().kind === "-" /* MINUS */) {
|
|
32431
|
+
throw new ParseError("\u5358\u9805\u7B26\u53F7\u3092\u91CD\u306D\u3066\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
32432
|
+
}
|
|
32433
|
+
const operand = this.parseScalarPrimary(allowCase);
|
|
32434
|
+
if (operand.type === "NUMBER") {
|
|
32435
|
+
return makeNumberLiteral(`${tok.kind === "-" /* MINUS */ ? "-" : "+"}${numberLiteralText(operand)}`);
|
|
32436
|
+
}
|
|
32437
|
+
if (tok.kind === "+" /* PLUS */) throw new ParseError("\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
32438
|
+
return { type: "SCALAR_ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
32439
|
+
}
|
|
32440
|
+
if (tok.kind === "STRING" /* STRING */) {
|
|
32441
|
+
this.advance();
|
|
32442
|
+
return { type: "STRING", value: tok.value };
|
|
32443
|
+
}
|
|
32444
|
+
if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
32445
|
+
this.advance();
|
|
32446
|
+
return makeNumberLiteral(tok.value);
|
|
32447
|
+
}
|
|
32448
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
32449
|
+
this.advance();
|
|
32450
|
+
return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
|
|
32451
|
+
}
|
|
32452
|
+
if (tok.kind === "CASE" /* CASE */) {
|
|
32453
|
+
if (!allowCase) throw new ParseError("\u3053\u306E\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u3067\u306F CASE \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
32454
|
+
return this.parseCaseWhenExpr();
|
|
32455
|
+
}
|
|
32456
|
+
if (this.tryAggregateFunc() !== null) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
32457
|
+
if (this.tryStringFuncName() !== null) return this.parseStringFuncExpr();
|
|
32458
|
+
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
32459
|
+
this.advance();
|
|
32460
|
+
if (this.consume("." /* DOT */)) return { type: "FIELD", tableAlias: tok.value, field: this.parseIdentifier() };
|
|
32461
|
+
return { type: "FIELD", tableAlias: null, field: tok.value };
|
|
32462
|
+
}
|
|
32463
|
+
throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306E\u30AA\u30DA\u30E9\u30F3\u30C9\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
32464
|
+
}
|
|
32465
|
+
/** 現在の値の終端までに指定トークンがあるか(括弧内も対象)。 */
|
|
32466
|
+
hasTopLevelTokenBeforeValueEnd(target) {
|
|
32467
|
+
let depth = 0;
|
|
32468
|
+
for (let i = this.pos; i < this.tokens.length; i++) {
|
|
32469
|
+
const kind = this.tokens[i].kind;
|
|
32470
|
+
if (kind === "(" /* LPAREN */ || kind === "[" /* LBRACKET */) depth++;
|
|
32471
|
+
else if (kind === ")" /* RPAREN */ || kind === "]" /* RBRACKET */) {
|
|
32472
|
+
if (depth === 0) break;
|
|
32473
|
+
depth--;
|
|
32474
|
+
}
|
|
32475
|
+
if (kind === target) return true;
|
|
32476
|
+
if (depth === 0 && (kind === "," /* COMMA */ || kind === "AS" /* AS */ || kind === "FROM" /* FROM */ || kind === "WHERE" /* WHERE */ || kind === "WHEN" /* WHEN */ || kind === "THEN" /* THEN */ || kind === "ELSE" /* ELSE */ || kind === "END" /* END */ || kind === ";" /* SEMICOLON */ || kind === "EOF" /* EOF */)) break;
|
|
32477
|
+
}
|
|
32478
|
+
return false;
|
|
32479
|
+
}
|
|
32480
|
+
// ──────────────────────────────────────────────────
|
|
32293
32481
|
// 算術式パーサー(演算子優先順位: * / > + -)
|
|
32294
32482
|
//
|
|
32295
32483
|
// parseArithAddSub : + -(左結合・低優先度)
|
|
@@ -32411,12 +32599,15 @@ var Parser = class {
|
|
|
32411
32599
|
this.expect("END" /* END */);
|
|
32412
32600
|
return { type: "CASE_WHEN", branches, elseResult };
|
|
32413
32601
|
}
|
|
32414
|
-
/** THEN / ELSE
|
|
32602
|
+
/** THEN / ELSE の結果値。`||` を含む場合だけ新スカラー文法へ渡す。 */
|
|
32415
32603
|
parseCaseResult() {
|
|
32416
32604
|
const tok = this.peek();
|
|
32417
32605
|
if (tok.kind === "[" /* LBRACKET */) {
|
|
32418
32606
|
return this.parseArrayLiteral();
|
|
32419
32607
|
}
|
|
32608
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
32609
|
+
return this.parseScalarValueExpr({ allowAggregateArgs: true });
|
|
32610
|
+
}
|
|
32420
32611
|
if (tok.kind === "STRING" /* STRING */) {
|
|
32421
32612
|
this.advance();
|
|
32422
32613
|
return { type: "STRING", value: tok.value };
|
|
@@ -32564,25 +32755,19 @@ var Parser = class {
|
|
|
32564
32755
|
}
|
|
32565
32756
|
return { type: "STRING", value: normalized };
|
|
32566
32757
|
}
|
|
32567
|
-
/** 文字列関数の引数:
|
|
32758
|
+
/** 文字列関数の引数: ScalarValueExpr / 集計算術式 */
|
|
32568
32759
|
parseStringFuncArg() {
|
|
32569
|
-
|
|
32570
|
-
|
|
32571
|
-
|
|
32572
|
-
|
|
32573
|
-
|
|
32574
|
-
|
|
32575
|
-
|
|
32576
|
-
|
|
32577
|
-
|
|
32578
|
-
try {
|
|
32579
|
-
const left = this.parseAggPrimary();
|
|
32580
|
-
const expr = this.continueAggArith(left);
|
|
32581
|
-
if (this.hasAggregateOperand(expr)) return expr;
|
|
32582
|
-
} catch {
|
|
32760
|
+
if (this.scalarAllowsAggregateArgs) {
|
|
32761
|
+
const startPos = this.pos;
|
|
32762
|
+
try {
|
|
32763
|
+
const left = this.parseAggPrimary();
|
|
32764
|
+
const expr = this.continueAggArith(left);
|
|
32765
|
+
if (this.hasAggregateOperand(expr)) return expr;
|
|
32766
|
+
} catch {
|
|
32767
|
+
}
|
|
32768
|
+
this.pos = startPos;
|
|
32583
32769
|
}
|
|
32584
|
-
this.
|
|
32585
|
-
return this.parseArithAddSub();
|
|
32770
|
+
return this.parseScalarAddSubConcat(this.scalarAllowsCase);
|
|
32586
32771
|
}
|
|
32587
32772
|
hasAggregateOperand(node) {
|
|
32588
32773
|
if (node.type === "AGG_REF") return true;
|
|
@@ -32670,6 +32855,7 @@ var Parser = class {
|
|
|
32670
32855
|
const k = this.peek().kind;
|
|
32671
32856
|
if (k === "IDENT" /* IDENT */ || k === "BIDENT" /* BIDENT */) {
|
|
32672
32857
|
if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "VALIDATE" && this.peekAt(1).kind === "IDENT" /* IDENT */ && this.peekAt(1).value.toUpperCase() === "ONLY") return null;
|
|
32858
|
+
if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "CHECK" && this.peekAt(1).kind === "WHEN" /* WHEN */) return null;
|
|
32673
32859
|
return this.parseTableAliasName();
|
|
32674
32860
|
}
|
|
32675
32861
|
return null;
|
|
@@ -32817,9 +33003,7 @@ var Parser = class {
|
|
|
32817
33003
|
}
|
|
32818
33004
|
if (this.consume("NOT" /* NOT */)) {
|
|
32819
33005
|
if (this.consume("IN" /* IN */)) {
|
|
32820
|
-
this.
|
|
32821
|
-
const right2 = this.parseInListOrSubquery();
|
|
32822
|
-
this.expect(")" /* RPAREN */);
|
|
33006
|
+
const right2 = this.parseInRight();
|
|
32823
33007
|
return { type: "BINARY", op: "NOT_IN", left: field, right: right2 };
|
|
32824
33008
|
}
|
|
32825
33009
|
if (this.consume("LIKE" /* LIKE */)) {
|
|
@@ -32836,9 +33020,7 @@ var Parser = class {
|
|
|
32836
33020
|
);
|
|
32837
33021
|
}
|
|
32838
33022
|
if (this.consume("IN" /* IN */)) {
|
|
32839
|
-
this.
|
|
32840
|
-
const right2 = this.parseInListOrSubquery();
|
|
32841
|
-
this.expect(")" /* RPAREN */);
|
|
33023
|
+
const right2 = this.parseInRight();
|
|
32842
33024
|
return { type: "BINARY", op: "IN", left: field, right: right2 };
|
|
32843
33025
|
}
|
|
32844
33026
|
if (this.consume("KLIKE" /* KLIKE */)) {
|
|
@@ -33005,6 +33187,18 @@ var Parser = class {
|
|
|
33005
33187
|
);
|
|
33006
33188
|
}
|
|
33007
33189
|
// IN (...) — 値リストまたはサブクエリ
|
|
33190
|
+
parseInRight() {
|
|
33191
|
+
if (this.consume("(" /* LPAREN */)) {
|
|
33192
|
+
const right = this.parseInListOrSubquery();
|
|
33193
|
+
this.expect(")" /* RPAREN */);
|
|
33194
|
+
return right;
|
|
33195
|
+
}
|
|
33196
|
+
const variable = this.expect(
|
|
33197
|
+
"VARIABLE" /* VARIABLE */,
|
|
33198
|
+
"IN / NOT IN \u306E\u5F8C\u306B\u306F (\u5024\u30EA\u30B9\u30C8\u307E\u305F\u306F SELECT) \u304B\u914D\u5217\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
33199
|
+
);
|
|
33200
|
+
return { type: "VARIABLE_IN_LIST", name: variable.value.slice(1).toLowerCase() };
|
|
33201
|
+
}
|
|
33008
33202
|
parseInListOrSubquery() {
|
|
33009
33203
|
if (this.peek().kind === "SELECT" /* SELECT */) {
|
|
33010
33204
|
const query = this.parseSelect();
|
|
@@ -33135,8 +33329,9 @@ var Parser = class {
|
|
|
33135
33329
|
if (subtableCode) {
|
|
33136
33330
|
throw new ParseError("INSERT INTO ... SELECT \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u3067\u306F\u672A\u5BFE\u5FDC\u3067\u3059", this.prev());
|
|
33137
33331
|
}
|
|
33332
|
+
const checkGroups2 = this.parseCheckGroups();
|
|
33138
33333
|
const validation2 = this.parseDmlControlSuffix();
|
|
33139
|
-
return { type: "INSERT_SELECT", appId, fields, select, ...validation2 };
|
|
33334
|
+
return { type: "INSERT_SELECT", appId, fields, select, ...checkGroups2, ...validation2 };
|
|
33140
33335
|
}
|
|
33141
33336
|
this.expect("VALUES" /* VALUES */);
|
|
33142
33337
|
const values = [];
|
|
@@ -33146,11 +33341,15 @@ var Parser = class {
|
|
|
33146
33341
|
this.expect(")" /* RPAREN */);
|
|
33147
33342
|
values.push(row);
|
|
33148
33343
|
} while (this.consume("," /* COMMA */));
|
|
33344
|
+
const checkGroups = this.parseCheckGroups();
|
|
33149
33345
|
const validation = this.parseDmlControlSuffix();
|
|
33346
|
+
if (subtableCode && checkGroups.checkGroups) {
|
|
33347
|
+
throw new ParseError("CHECK \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB INSERT \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
33348
|
+
}
|
|
33150
33349
|
if (subtableCode && (validation.validateOnly || validation.onErrorSkip)) {
|
|
33151
33350
|
throw new ParseError("VALIDATE ONLY / ON ERROR SKIP \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB INSERT \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
33152
33351
|
}
|
|
33153
|
-
return subtableCode ? { type: "INSERT", appId, subtableCode, fields, values, ...validation } : { type: "INSERT", appId, fields, values, ...validation };
|
|
33352
|
+
return subtableCode ? { type: "INSERT", appId, subtableCode, fields, values, ...checkGroups, ...validation } : { type: "INSERT", appId, fields, values, ...checkGroups, ...validation };
|
|
33154
33353
|
}
|
|
33155
33354
|
parseUpsert() {
|
|
33156
33355
|
this.expect("UPSERT" /* UPSERT */);
|
|
@@ -33167,8 +33366,9 @@ var Parser = class {
|
|
|
33167
33366
|
if (this.peek().kind === "SELECT" /* SELECT */) {
|
|
33168
33367
|
const select = this.parseSelect();
|
|
33169
33368
|
const keyFields2 = this.parseOnDuplicate();
|
|
33369
|
+
const checkGroups2 = this.parseCheckGroups();
|
|
33170
33370
|
const validation2 = this.parseDmlControlSuffix();
|
|
33171
|
-
return { type: "UPSERT_SELECT", appId, fields, select, keyFields: keyFields2, ...validation2 };
|
|
33371
|
+
return { type: "UPSERT_SELECT", appId, fields, select, keyFields: keyFields2, ...checkGroups2, ...validation2 };
|
|
33172
33372
|
}
|
|
33173
33373
|
this.expect("VALUES" /* VALUES */);
|
|
33174
33374
|
const values = [];
|
|
@@ -33178,8 +33378,9 @@ var Parser = class {
|
|
|
33178
33378
|
this.expect(")" /* RPAREN */);
|
|
33179
33379
|
} while (this.consume("," /* COMMA */));
|
|
33180
33380
|
const keyFields = this.parseOnDuplicate();
|
|
33381
|
+
const checkGroups = this.parseCheckGroups();
|
|
33181
33382
|
const validation = this.parseDmlControlSuffix();
|
|
33182
|
-
return { type: "UPSERT", appId, fields, values, keyFields, ...validation };
|
|
33383
|
+
return { type: "UPSERT", appId, fields, values, keyFields, ...checkGroups, ...validation };
|
|
33183
33384
|
}
|
|
33184
33385
|
parseOnDuplicate() {
|
|
33185
33386
|
this.expectKeyword("ON" /* ON */, "UPSERT \u306B\u306F ON DUPLICATE (\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9) \u304C\u5FC5\u8981\u3067\u3059");
|
|
@@ -33311,12 +33512,33 @@ var Parser = class {
|
|
|
33311
33512
|
whereTok
|
|
33312
33513
|
);
|
|
33313
33514
|
}
|
|
33515
|
+
const checkGroups = this.parseCheckGroups();
|
|
33314
33516
|
const validation = this.parseDmlControlSuffix();
|
|
33517
|
+
if (subtableCode && checkGroups.checkGroups) {
|
|
33518
|
+
throw new ParseError("CHECK \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
33519
|
+
}
|
|
33315
33520
|
if (subtableCode && (validation.validateOnly || validation.onErrorSkip)) {
|
|
33316
33521
|
throw new ParseError("VALIDATE ONLY / ON ERROR SKIP \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
33317
33522
|
}
|
|
33318
|
-
if (from !== null) return { type: "UPDATE", appId, assignments, where, from, ...validation };
|
|
33319
|
-
return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where, ...validation } : { type: "UPDATE", appId, assignments, where, ...validation };
|
|
33523
|
+
if (from !== null) return { type: "UPDATE", appId, assignments, where, from, ...checkGroups, ...validation };
|
|
33524
|
+
return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where, ...checkGroups, ...validation } : { type: "UPDATE", appId, assignments, where, ...checkGroups, ...validation };
|
|
33525
|
+
}
|
|
33526
|
+
/** CHECK WHEN ... THEN ... blocks. CHECK is a soft keyword. */
|
|
33527
|
+
parseCheckGroups() {
|
|
33528
|
+
const groups = [];
|
|
33529
|
+
while (this.isSoftKeyword("CHECK") && this.peekAt(1).kind === "WHEN" /* WHEN */) {
|
|
33530
|
+
const check2 = this.advance();
|
|
33531
|
+
const rules = [];
|
|
33532
|
+
while (this.consume("WHEN" /* WHEN */)) {
|
|
33533
|
+
const condition = this.parseWhereExpr();
|
|
33534
|
+
this.expect("THEN" /* THEN */, "CHECK WHEN \u306E\u6761\u4EF6\u306E\u5F8C\u306B\u306F THEN \u304C\u5FC5\u8981\u3067\u3059");
|
|
33535
|
+
const message = this.parseScalarValueExpr({ allowCase: false });
|
|
33536
|
+
rules.push({ condition, message });
|
|
33537
|
+
}
|
|
33538
|
+
if (rules.length === 0) throw new ParseError("CHECK \u306E\u5F8C\u306B\u306F WHEN \u304C\u6700\u4F4E 1 \u3064\u5FC5\u8981\u3067\u3059", check2);
|
|
33539
|
+
groups.push({ rules });
|
|
33540
|
+
}
|
|
33541
|
+
return groups.length > 0 ? { checkGroups: groups } : {};
|
|
33320
33542
|
}
|
|
33321
33543
|
/** DML末尾の VALIDATE ONLY または ON ERROR SKIP。各語はsoft keyword。 */
|
|
33322
33544
|
parseDmlControlSuffix() {
|
|
@@ -33505,6 +33727,12 @@ var Parser = class {
|
|
|
33505
33727
|
*/
|
|
33506
33728
|
parseAssignmentValue() {
|
|
33507
33729
|
const tok = this.peek();
|
|
33730
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
33731
|
+
const expr = this.parseScalarValueExpr();
|
|
33732
|
+
if (expr.type === "CONCAT_OP" || expr.type === "SCALAR_ARITH" || expr.type === "STRING_FUNC") return expr;
|
|
33733
|
+
if (expr.type === "CASE_WHEN") return { type: "CASE_VALUE", expr };
|
|
33734
|
+
throw new ParseError("SET \u306E\u5024\u306B\u306F\u9023\u7D50\u3092\u542B\u3080\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
33735
|
+
}
|
|
33508
33736
|
if (tok.kind === "VARIABLE" /* VARIABLE */) return this.parseSqlValue();
|
|
33509
33737
|
if (tok.kind === "STRING" /* STRING */) return this.parseSqlValue();
|
|
33510
33738
|
if (tok.kind === "TODAY" /* TODAY */ || tok.kind === "NOW" /* NOW */ || tok.kind === "LOGINUSER" /* LOGINUSER */) return this.parseSqlValue();
|
|
@@ -33781,7 +34009,7 @@ function isDmlType(type) {
|
|
|
33781
34009
|
return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
|
|
33782
34010
|
}
|
|
33783
34011
|
function isReadOnlyType(type) {
|
|
33784
|
-
return type === "SELECT" || type === "UNION" || type === "WITH" || type === "EXPLAIN" || type === "SHOW_APPS" || type === "DESCRIBE" || type === "CREATE_TEMP_TABLE" || type === "DROP_TEMP_TABLE" || type === "SET_VARIABLE" || type === "DECLARE_VARIABLE" || type === "ASSERT";
|
|
34012
|
+
return type === "SELECT" || type === "VALIDATE" || type === "UNION" || type === "WITH" || type === "EXPLAIN" || type === "SHOW_APPS" || type === "DESCRIBE" || type === "CREATE_TEMP_TABLE" || type === "DROP_TEMP_TABLE" || type === "SET_VARIABLE" || type === "DECLARE_VARIABLE" || type === "ASSERT";
|
|
33785
34013
|
}
|
|
33786
34014
|
function writesKintone(stmt) {
|
|
33787
34015
|
return isDmlType(stmt.type) && !("validateOnly" in stmt && stmt.validateOnly === true);
|
|
@@ -33792,6 +34020,8 @@ function isReadOnlyStatement(stmt) {
|
|
|
33792
34020
|
function requiresCompleteInput(stmt) {
|
|
33793
34021
|
if (isDmlType(stmt.type)) return true;
|
|
33794
34022
|
switch (stmt.type) {
|
|
34023
|
+
case "VALIDATE":
|
|
34024
|
+
return true;
|
|
33795
34025
|
case "SELECT":
|
|
33796
34026
|
return selectRequiresCompleteInput(stmt);
|
|
33797
34027
|
case "UNION":
|
|
@@ -33832,6 +34062,7 @@ function whereRequiresCompleteInput(where) {
|
|
|
33832
34062
|
case "EXISTS":
|
|
33833
34063
|
return selectRequiresCompleteInput(where.query);
|
|
33834
34064
|
case "NULL_CHECK":
|
|
34065
|
+
case "BOOLEAN":
|
|
33835
34066
|
return false;
|
|
33836
34067
|
}
|
|
33837
34068
|
}
|
|
@@ -33855,6 +34086,8 @@ function getInsertValuesCount(stmt) {
|
|
|
33855
34086
|
// src/engine/pushDownNot.ts
|
|
33856
34087
|
function pushDownNot(expr) {
|
|
33857
34088
|
switch (expr.type) {
|
|
34089
|
+
case "BOOLEAN":
|
|
34090
|
+
return { type: "BOOLEAN", value: !expr.value };
|
|
33858
34091
|
case "BINARY": {
|
|
33859
34092
|
const negated = negateOp(expr.op);
|
|
33860
34093
|
if (negated === null) {
|
|
@@ -33934,6 +34167,7 @@ function whereHasLike(where) {
|
|
|
33934
34167
|
case "BINARY":
|
|
33935
34168
|
case "NULL_CHECK":
|
|
33936
34169
|
case "EXISTS":
|
|
34170
|
+
case "BOOLEAN":
|
|
33937
34171
|
return false;
|
|
33938
34172
|
}
|
|
33939
34173
|
}
|
|
@@ -33949,6 +34183,7 @@ function whereHasKlike(where) {
|
|
|
33949
34183
|
case "BINARY":
|
|
33950
34184
|
case "NULL_CHECK":
|
|
33951
34185
|
case "EXISTS":
|
|
34186
|
+
case "BOOLEAN":
|
|
33952
34187
|
return false;
|
|
33953
34188
|
}
|
|
33954
34189
|
}
|
|
@@ -33968,6 +34203,8 @@ function whereToKintone(expr) {
|
|
|
33968
34203
|
return convertGroup(expr);
|
|
33969
34204
|
case "EXISTS":
|
|
33970
34205
|
throw new KintoneQueryError("EXISTS \u306F kintone \u30AF\u30A8\u30EA\u306B\u5909\u63DB\u3067\u304D\u307E\u305B\u3093");
|
|
34206
|
+
case "BOOLEAN":
|
|
34207
|
+
throw new KintoneQueryError("internal error: BOOLEAN predicate reached kintone query conversion");
|
|
33971
34208
|
}
|
|
33972
34209
|
}
|
|
33973
34210
|
function convertBinary(expr) {
|
|
@@ -34046,6 +34283,8 @@ function convertValue(value, op) {
|
|
|
34046
34283
|
switch (value.type) {
|
|
34047
34284
|
case "VARIABLE":
|
|
34048
34285
|
throw new KintoneQueryError(`\u672A\u89E3\u6C7A\u306E\u30D0\u30C3\u30C1\u5909\u6570 @${value.name} \u304C\u3042\u308A\u307E\u3059`);
|
|
34286
|
+
case "VARIABLE_IN_LIST":
|
|
34287
|
+
throw new KintoneQueryError(`\u672A\u89E3\u6C7A\u306E\u914D\u5217\u5909\u6570 @${value.name} \u304C\u3042\u308A\u307E\u3059`);
|
|
34049
34288
|
case "STRING":
|
|
34050
34289
|
return convertString(value);
|
|
34051
34290
|
case "NUMBER":
|
|
@@ -34116,7 +34355,7 @@ function resolveSelectMode(stmt) {
|
|
|
34116
34355
|
if (stmt.distinct) return "FULL_SCAN";
|
|
34117
34356
|
if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
|
|
34118
34357
|
if (stmt.columns.some(
|
|
34119
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
|
|
34358
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate(c.expr)
|
|
34120
34359
|
)) return "FULL_SCAN";
|
|
34121
34360
|
if (whereRequiresJsEval(stmt.where)) return "FULL_SCAN";
|
|
34122
34361
|
if (stmt.orderBy.some((o) => o.key.type !== "FIELD_NAME")) return "FULL_SCAN";
|
|
@@ -34125,6 +34364,8 @@ function resolveSelectMode(stmt) {
|
|
|
34125
34364
|
function whereRequiresJsEval(where) {
|
|
34126
34365
|
if (where === null) return false;
|
|
34127
34366
|
switch (where.type) {
|
|
34367
|
+
case "BOOLEAN":
|
|
34368
|
+
return true;
|
|
34128
34369
|
case "BINARY":
|
|
34129
34370
|
return isFunc(where.left) || where.right.type === "ARITH_VALUE" || where.right.type === "CASE_VALUE" || where.right.type === "SUBQUERY_IN_LIST" || where.right.type === "SCALAR_SUBQUERY" || isLike(where);
|
|
34130
34371
|
case "NULL_CHECK":
|
|
@@ -34211,6 +34452,8 @@ function extractFields(columns) {
|
|
|
34211
34452
|
collectArithNode(col.expr, fields);
|
|
34212
34453
|
} else if (col.type === "STRFUNC_COL") {
|
|
34213
34454
|
collectStringFuncFields(col.expr, fields);
|
|
34455
|
+
} else if (col.type === "SCALAR_VALUE_COL") {
|
|
34456
|
+
collectScalarValueFields(col.expr, fields);
|
|
34214
34457
|
}
|
|
34215
34458
|
}
|
|
34216
34459
|
return [...new Set(fields)];
|
|
@@ -34236,16 +34479,38 @@ function collectStringFuncFields(expr, out) {
|
|
|
34236
34479
|
}
|
|
34237
34480
|
}
|
|
34238
34481
|
function collectStringFuncArgFields(arg, out) {
|
|
34239
|
-
if (arg.type === "STRING") return;
|
|
34240
|
-
if (arg.type === "STRING_FUNC") {
|
|
34241
|
-
collectStringFuncFields(arg, out);
|
|
34242
|
-
return;
|
|
34243
|
-
}
|
|
34244
34482
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
34245
34483
|
collectAggOperandFields(arg, out);
|
|
34246
34484
|
return;
|
|
34247
34485
|
}
|
|
34248
|
-
|
|
34486
|
+
collectScalarValueFields(arg, out);
|
|
34487
|
+
}
|
|
34488
|
+
function collectScalarValueFields(expr, out) {
|
|
34489
|
+
if (expr.type === "FIELD") {
|
|
34490
|
+
out.push(normalizeSimpleFieldRef(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field));
|
|
34491
|
+
return;
|
|
34492
|
+
}
|
|
34493
|
+
if (expr.type === "STRING_FUNC") {
|
|
34494
|
+
collectStringFuncFields(expr, out);
|
|
34495
|
+
return;
|
|
34496
|
+
}
|
|
34497
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
34498
|
+
collectScalarValueFields(expr.left, out);
|
|
34499
|
+
collectScalarValueFields(expr.right, out);
|
|
34500
|
+
return;
|
|
34501
|
+
}
|
|
34502
|
+
if (expr.type === "CASE_WHEN") {
|
|
34503
|
+
for (const branch of expr.branches) collectCaseResultScalarFields(branch.result, out);
|
|
34504
|
+
if (expr.elseResult) collectCaseResultScalarFields(expr.elseResult, out);
|
|
34505
|
+
}
|
|
34506
|
+
}
|
|
34507
|
+
function collectCaseResultScalarFields(result, out) {
|
|
34508
|
+
if (result.type === "ARRAY") return;
|
|
34509
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
34510
|
+
collectArithNode(result, out);
|
|
34511
|
+
return;
|
|
34512
|
+
}
|
|
34513
|
+
collectScalarValueFields(result, out);
|
|
34249
34514
|
}
|
|
34250
34515
|
function collectAggOperandFields(node, out) {
|
|
34251
34516
|
if (node.type === "AGG_REF") {
|
|
@@ -34260,10 +34525,23 @@ function collectAggOperandFields(node, out) {
|
|
|
34260
34525
|
function hasAggregateInStringFuncExpr(expr) {
|
|
34261
34526
|
return expr.args.some((arg) => {
|
|
34262
34527
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
34263
|
-
|
|
34264
|
-
return false;
|
|
34528
|
+
return scalarValueHasAggregate(arg);
|
|
34265
34529
|
});
|
|
34266
34530
|
}
|
|
34531
|
+
function scalarValueHasAggregate(expr) {
|
|
34532
|
+
if (expr.type === "STRING_FUNC") return hasAggregateInStringFuncExpr(expr);
|
|
34533
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
34534
|
+
return scalarValueHasAggregate(expr.left) || scalarValueHasAggregate(expr.right);
|
|
34535
|
+
}
|
|
34536
|
+
if (expr.type === "CASE_WHEN") {
|
|
34537
|
+
return expr.branches.some((b) => caseResultHasAggregate(b.result)) || expr.elseResult !== null && caseResultHasAggregate(expr.elseResult);
|
|
34538
|
+
}
|
|
34539
|
+
return false;
|
|
34540
|
+
}
|
|
34541
|
+
function caseResultHasAggregate(result) {
|
|
34542
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
34543
|
+
return scalarValueHasAggregate(result);
|
|
34544
|
+
}
|
|
34267
34545
|
function collectRequiredFieldsByTable(stmt) {
|
|
34268
34546
|
const physicalTables = [stmt.from, ...stmt.joins.map((j) => j.table)].filter((t) => t.cteName === null);
|
|
34269
34547
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -34398,28 +34676,38 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
34398
34676
|
}
|
|
34399
34677
|
};
|
|
34400
34678
|
const walkStringArg = (arg, phase = "select") => {
|
|
34401
|
-
if (arg.type === "STRING") return;
|
|
34402
|
-
if (arg.type === "STRING_FUNC") {
|
|
34403
|
-
walkStringFunc(arg, phase);
|
|
34404
|
-
return;
|
|
34405
|
-
}
|
|
34406
34679
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
34407
34680
|
walkAgg(arg, phase);
|
|
34408
34681
|
return;
|
|
34409
34682
|
}
|
|
34410
|
-
|
|
34683
|
+
walkScalar(arg, phase);
|
|
34411
34684
|
};
|
|
34412
34685
|
const walkStringFunc = (expr, phase = "select") => {
|
|
34413
34686
|
for (const arg of expr.args) walkStringArg(arg, phase);
|
|
34414
34687
|
};
|
|
34688
|
+
const walkScalar = (expr, phase = "select") => {
|
|
34689
|
+
if (expr.type === "FIELD") {
|
|
34690
|
+
addFieldRef(expr.field, expr.tableAlias, phase);
|
|
34691
|
+
return;
|
|
34692
|
+
}
|
|
34693
|
+
if (expr.type === "STRING_FUNC") {
|
|
34694
|
+
walkStringFunc(expr, phase);
|
|
34695
|
+
return;
|
|
34696
|
+
}
|
|
34697
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
34698
|
+
walkScalar(expr.left, phase);
|
|
34699
|
+
walkScalar(expr.right, phase);
|
|
34700
|
+
return;
|
|
34701
|
+
}
|
|
34702
|
+
if (expr.type === "CASE_WHEN") walkCase(expr, phase);
|
|
34703
|
+
};
|
|
34415
34704
|
const walkCaseResult = (result, phase = "select") => {
|
|
34416
|
-
if (result.type === "STRING") return;
|
|
34417
34705
|
if (result.type === "ARRAY") return;
|
|
34418
|
-
if (result.type === "
|
|
34419
|
-
|
|
34706
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
34707
|
+
walkArith(result, phase);
|
|
34420
34708
|
return;
|
|
34421
34709
|
}
|
|
34422
|
-
|
|
34710
|
+
walkScalar(result, phase);
|
|
34423
34711
|
};
|
|
34424
34712
|
const walkCase = (expr, phase = "select") => {
|
|
34425
34713
|
for (const b of expr.branches) {
|
|
@@ -34472,6 +34760,7 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
34472
34760
|
walkWhere(where.expr, phase);
|
|
34473
34761
|
return;
|
|
34474
34762
|
case "EXISTS":
|
|
34763
|
+
case "BOOLEAN":
|
|
34475
34764
|
return;
|
|
34476
34765
|
}
|
|
34477
34766
|
};
|
|
@@ -34510,6 +34799,8 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
34510
34799
|
break;
|
|
34511
34800
|
case "LITERAL_COL":
|
|
34512
34801
|
break;
|
|
34802
|
+
case "VARIABLE_COL":
|
|
34803
|
+
throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
|
|
34513
34804
|
case "AGGREGATE":
|
|
34514
34805
|
if (col.arg.type !== "WILDCARD") walkArith(col.arg, "select");
|
|
34515
34806
|
break;
|
|
@@ -34525,6 +34816,9 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
34525
34816
|
case "STRFUNC_COL":
|
|
34526
34817
|
walkStringFunc(col.expr, "select");
|
|
34527
34818
|
break;
|
|
34819
|
+
case "SCALAR_VALUE_COL":
|
|
34820
|
+
walkScalar(col.expr, "select");
|
|
34821
|
+
break;
|
|
34528
34822
|
case "SCALAR_SUBQUERY_COL":
|
|
34529
34823
|
break;
|
|
34530
34824
|
case "WINDOW_COL":
|
|
@@ -34575,6 +34869,10 @@ function collectSelectOutputNames(columns) {
|
|
|
34575
34869
|
if (col.alias) names.add(col.alias);
|
|
34576
34870
|
continue;
|
|
34577
34871
|
}
|
|
34872
|
+
if (col.type === "SCALAR_VALUE_COL") {
|
|
34873
|
+
if (col.alias) names.add(col.alias);
|
|
34874
|
+
continue;
|
|
34875
|
+
}
|
|
34578
34876
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
34579
34877
|
names.add(col.alias ?? "(subquery)");
|
|
34580
34878
|
continue;
|
|
@@ -34597,14 +34895,32 @@ function arithNodeLabel(node) {
|
|
|
34597
34895
|
}
|
|
34598
34896
|
function stringFuncLabel(expr) {
|
|
34599
34897
|
const args = expr.args.map((a) => {
|
|
34600
|
-
if (a.type === "STRING") return `'${a.value}'`;
|
|
34601
|
-
if (a.type === "STRING_FUNC") return stringFuncLabel(a);
|
|
34602
34898
|
if (a.type === "AGG_REF") return aggregateSyntheticName(a.func, a.distinct, a.arg);
|
|
34603
34899
|
if (a.type === "AGG_ARITH") return "agg_arith";
|
|
34604
|
-
return
|
|
34900
|
+
return scalarValueLabel(a);
|
|
34605
34901
|
});
|
|
34606
34902
|
return `${expr.func}(${args.join(",")})`;
|
|
34607
34903
|
}
|
|
34904
|
+
function scalarValueLabel(expr) {
|
|
34905
|
+
switch (expr.type) {
|
|
34906
|
+
case "STRING":
|
|
34907
|
+
return `'${expr.value}'`;
|
|
34908
|
+
case "NUMBER":
|
|
34909
|
+
return numberLiteralText(expr);
|
|
34910
|
+
case "VARIABLE":
|
|
34911
|
+
return `@${expr.name}`;
|
|
34912
|
+
case "FIELD":
|
|
34913
|
+
return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
|
|
34914
|
+
case "STRING_FUNC":
|
|
34915
|
+
return stringFuncLabel(expr);
|
|
34916
|
+
case "CASE_WHEN":
|
|
34917
|
+
return "case";
|
|
34918
|
+
case "SCALAR_ARITH":
|
|
34919
|
+
return `(${scalarValueLabel(expr.left)}${expr.op}${scalarValueLabel(expr.right)})`;
|
|
34920
|
+
case "CONCAT_OP":
|
|
34921
|
+
return `(${scalarValueLabel(expr.left)}||${scalarValueLabel(expr.right)})`;
|
|
34922
|
+
}
|
|
34923
|
+
}
|
|
34608
34924
|
function isAggregateSyntheticName(name) {
|
|
34609
34925
|
return /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT)\(/i.test(name);
|
|
34610
34926
|
}
|
|
@@ -34660,6 +34976,7 @@ function stripCteAlias(where, alias) {
|
|
|
34660
34976
|
case "GROUP":
|
|
34661
34977
|
return { ...where, expr: stripCteAlias(where.expr, alias) };
|
|
34662
34978
|
case "EXISTS":
|
|
34979
|
+
case "BOOLEAN":
|
|
34663
34980
|
return where;
|
|
34664
34981
|
}
|
|
34665
34982
|
}
|
|
@@ -34697,6 +35014,7 @@ function extractAndLeaves(where, accept) {
|
|
|
34697
35014
|
case "NULL_CHECK":
|
|
34698
35015
|
case "NOT":
|
|
34699
35016
|
case "EXISTS":
|
|
35017
|
+
case "BOOLEAN":
|
|
34700
35018
|
return null;
|
|
34701
35019
|
}
|
|
34702
35020
|
}
|
|
@@ -34835,6 +35153,7 @@ function collectKlikes(where, out) {
|
|
|
34835
35153
|
case "BINARY":
|
|
34836
35154
|
case "NULL_CHECK":
|
|
34837
35155
|
case "EXISTS":
|
|
35156
|
+
case "BOOLEAN":
|
|
34838
35157
|
return;
|
|
34839
35158
|
}
|
|
34840
35159
|
}
|
|
@@ -34891,6 +35210,11 @@ function validateStatement(stmt) {
|
|
|
34891
35210
|
);
|
|
34892
35211
|
}
|
|
34893
35212
|
return;
|
|
35213
|
+
case "VALIDATE":
|
|
35214
|
+
if (containsKlike(stmt)) {
|
|
35215
|
+
throw new KlikeValidationError("KLIKE / NOT KLIKE \u306F VALIDATE \u306E WHERE / CHECK \u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
35216
|
+
}
|
|
35217
|
+
return;
|
|
34894
35218
|
case "SHOW_APPS":
|
|
34895
35219
|
case "DESCRIBE":
|
|
34896
35220
|
case "DROP_TEMP_TABLE":
|
|
@@ -34978,6 +35302,8 @@ function isDescendantOf(root, target) {
|
|
|
34978
35302
|
case "NULL_CHECK":
|
|
34979
35303
|
case "EXISTS":
|
|
34980
35304
|
return false;
|
|
35305
|
+
case "BOOLEAN":
|
|
35306
|
+
return false;
|
|
34981
35307
|
}
|
|
34982
35308
|
}
|
|
34983
35309
|
function walkWithoutNestedSelects(node, visitWhere) {
|
|
@@ -35039,8 +35365,12 @@ function collectVariableRefs(node, refs) {
|
|
|
35039
35365
|
}
|
|
35040
35366
|
if (node !== null && typeof node === "object") {
|
|
35041
35367
|
const obj = node;
|
|
35042
|
-
|
|
35043
|
-
|
|
35368
|
+
const type = obj["type"];
|
|
35369
|
+
if ((type === "VARIABLE" || type === "VARIABLE_COL" || type === "VARIABLE_IN_LIST") && typeof obj["name"] === "string") {
|
|
35370
|
+
refs.push({
|
|
35371
|
+
name: obj["name"],
|
|
35372
|
+
kind: type === "VARIABLE" ? "scalar" : type === "VARIABLE_COL" ? "select-column" : "array-in-list"
|
|
35373
|
+
});
|
|
35044
35374
|
return;
|
|
35045
35375
|
}
|
|
35046
35376
|
for (const v of Object.values(obj)) collectVariableRefs(v, refs);
|
|
@@ -35081,9 +35411,9 @@ function analyzeBatch(statements) {
|
|
|
35081
35411
|
const variableDefs = /* @__PURE__ */ new Map();
|
|
35082
35412
|
const variableOrder = [];
|
|
35083
35413
|
statements.forEach((stmt, index) => {
|
|
35084
|
-
const validationTable = "validationErrorTable" in stmt && stmt.validationErrorTable ? stmt.validationErrorTable : "onErrorSkip" in stmt && stmt.onErrorSkip ? stmt.errorTable ?? null : null;
|
|
35414
|
+
const validationTable = stmt.type === "VALIDATE" && stmt.errorTable ? stmt.errorTable : "validationErrorTable" in stmt && stmt.validationErrorTable ? stmt.validationErrorTable : "onErrorSkip" in stmt && stmt.onErrorSkip ? stmt.errorTable ?? null : null;
|
|
35085
35415
|
if (statements.length === 1 && validationTable) {
|
|
35086
|
-
const message = "onErrorSkip" in stmt && stmt.onErrorSkip ? "ArgumentError: ON ERROR SKIP requires a batch." : "ArgumentError: VALIDATE ONLY INTO requires a batch.";
|
|
35416
|
+
const message = stmt.type === "VALIDATE" ? "ArgumentError: VALIDATE INTO requires a batch." : "onErrorSkip" in stmt && stmt.onErrorSkip ? "ArgumentError: ON ERROR SKIP requires a batch." : "ArgumentError: VALIDATE ONLY INTO requires a batch.";
|
|
35087
35417
|
throw new BatchAnalysisError(message, index);
|
|
35088
35418
|
}
|
|
35089
35419
|
const statementType = getStatementType(stmt);
|
|
@@ -35092,23 +35422,43 @@ function analyzeBatch(statements) {
|
|
|
35092
35422
|
const refs = /* @__PURE__ */ new Set();
|
|
35093
35423
|
const stmtAppIds = /* @__PURE__ */ new Set();
|
|
35094
35424
|
const dependsOn = /* @__PURE__ */ new Set();
|
|
35095
|
-
const variableRefs =
|
|
35425
|
+
const variableRefs = [];
|
|
35096
35426
|
collectVariableRefs(stmt, variableRefs);
|
|
35097
|
-
|
|
35098
|
-
|
|
35427
|
+
const referencedThisStatement = /* @__PURE__ */ new Set();
|
|
35428
|
+
for (const use of variableRefs) {
|
|
35429
|
+
const def = variableDefs.get(use.name);
|
|
35099
35430
|
if (def === void 0) {
|
|
35100
35431
|
throw new BatchAnalysisError(
|
|
35101
|
-
`ParseError: variable @${name} is not defined before statement ${index + 1}.`,
|
|
35432
|
+
`ParseError: variable @${use.name} is not defined before statement ${index + 1}.`,
|
|
35433
|
+
index
|
|
35434
|
+
);
|
|
35435
|
+
}
|
|
35436
|
+
if (def.kind === "scalar" && use.kind === "array-in-list") {
|
|
35437
|
+
throw new BatchAnalysisError(
|
|
35438
|
+
`ParseError: scalar variable @${use.name} cannot be used as IN @${use.name}; use IN (@${use.name}) instead.`,
|
|
35439
|
+
index
|
|
35440
|
+
);
|
|
35441
|
+
}
|
|
35442
|
+
if (def.kind === "array" && use.kind !== "array-in-list") {
|
|
35443
|
+
throw new BatchAnalysisError(
|
|
35444
|
+
`ParseError: array variable @${use.name} can only be used as IN @${use.name}.`,
|
|
35102
35445
|
index
|
|
35103
35446
|
);
|
|
35104
35447
|
}
|
|
35105
|
-
|
|
35448
|
+
if (!referencedThisStatement.has(use.name)) {
|
|
35449
|
+
def.referencedBy.push(index);
|
|
35450
|
+
referencedThisStatement.add(use.name);
|
|
35451
|
+
}
|
|
35106
35452
|
}
|
|
35107
35453
|
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
35108
35454
|
if (variableDefs.has(stmt.name)) {
|
|
35109
35455
|
throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
|
|
35110
35456
|
}
|
|
35111
|
-
variableDefs.set(stmt.name, {
|
|
35457
|
+
variableDefs.set(stmt.name, {
|
|
35458
|
+
index,
|
|
35459
|
+
kind: stmt.type === "SET_VARIABLE" && stmt.expr.type === "ARRAY" ? "array" : "scalar",
|
|
35460
|
+
referencedBy: []
|
|
35461
|
+
});
|
|
35112
35462
|
variableOrder.push(stmt.name);
|
|
35113
35463
|
if (variableOrder.length > MAX_BATCH_VARIABLES) {
|
|
35114
35464
|
throw new BatchAnalysisError(
|
|
@@ -35141,7 +35491,7 @@ function analyzeBatch(statements) {
|
|
|
35141
35491
|
dependsOn.add(at);
|
|
35142
35492
|
}
|
|
35143
35493
|
if (validationTable) {
|
|
35144
|
-
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : "fields" in stmt ? stmt.fields : [];
|
|
35494
|
+
const payloadFields = stmt.type === "VALIDATE" ? ["$id", "$err_field", "$err_code", "$err_message", "$err_value"] : stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : "fields" in stmt ? stmt.fields : [];
|
|
35145
35495
|
const signature = JSON.stringify(payloadFields);
|
|
35146
35496
|
const at = defined.get(validationTable);
|
|
35147
35497
|
if (at === void 0) {
|
|
@@ -35216,6 +35566,7 @@ function analyzeBatch(statements) {
|
|
|
35216
35566
|
const needsCompleteInput = results.some((r) => r.requiresCompleteInput);
|
|
35217
35567
|
const variables = variableOrder.map((name) => ({
|
|
35218
35568
|
name,
|
|
35569
|
+
kind: variableDefs.get(name).kind,
|
|
35219
35570
|
referencedBy: [...variableDefs.get(name).referencedBy]
|
|
35220
35571
|
}));
|
|
35221
35572
|
return {
|
|
@@ -35492,6 +35843,7 @@ function whereNeedsFieldMetadata(where) {
|
|
|
35492
35843
|
case "GROUP":
|
|
35493
35844
|
return whereNeedsFieldMetadata(where.expr);
|
|
35494
35845
|
case "EXISTS":
|
|
35846
|
+
case "BOOLEAN":
|
|
35495
35847
|
return false;
|
|
35496
35848
|
}
|
|
35497
35849
|
}
|
|
@@ -35516,6 +35868,7 @@ function explainNeedsAppMetadata(statement) {
|
|
|
35516
35868
|
seen.add(node);
|
|
35517
35869
|
if (Array.isArray(node)) return node.some(visit);
|
|
35518
35870
|
const item = node;
|
|
35871
|
+
if (item["type"] === "VALIDATE") return true;
|
|
35519
35872
|
if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
|
|
35520
35873
|
return true;
|
|
35521
35874
|
}
|
|
@@ -35547,6 +35900,45 @@ function evalArithExpr(expr, row) {
|
|
|
35547
35900
|
return r !== 0 ? l % r : NaN;
|
|
35548
35901
|
}
|
|
35549
35902
|
}
|
|
35903
|
+
function evalScalarValueExpr(expr, row) {
|
|
35904
|
+
switch (expr.type) {
|
|
35905
|
+
case "STRING":
|
|
35906
|
+
return expr.value;
|
|
35907
|
+
case "NUMBER":
|
|
35908
|
+
return expr.value;
|
|
35909
|
+
case "FIELD":
|
|
35910
|
+
return resolveFieldRef(row, expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field);
|
|
35911
|
+
case "VARIABLE":
|
|
35912
|
+
throw new Error(`ArgumentError: unresolved variable @${expr.name} reached scalar evaluator.`);
|
|
35913
|
+
case "STRING_FUNC":
|
|
35914
|
+
return evalStringFunc(expr, row);
|
|
35915
|
+
case "CASE_WHEN":
|
|
35916
|
+
return evalCaseWhen(expr, row);
|
|
35917
|
+
case "CONCAT_OP": {
|
|
35918
|
+
return evalStringFunc({
|
|
35919
|
+
type: "STRING_FUNC",
|
|
35920
|
+
func: "CONCAT",
|
|
35921
|
+
args: [expr.left, expr.right]
|
|
35922
|
+
}, row);
|
|
35923
|
+
}
|
|
35924
|
+
case "SCALAR_ARITH": {
|
|
35925
|
+
const left = Number(evalScalarValueExpr(expr.left, row));
|
|
35926
|
+
const right = Number(evalScalarValueExpr(expr.right, row));
|
|
35927
|
+
switch (expr.op) {
|
|
35928
|
+
case "+":
|
|
35929
|
+
return left + right;
|
|
35930
|
+
case "-":
|
|
35931
|
+
return left - right;
|
|
35932
|
+
case "*":
|
|
35933
|
+
return left * right;
|
|
35934
|
+
case "/":
|
|
35935
|
+
return right !== 0 ? left / right : NaN;
|
|
35936
|
+
case "%":
|
|
35937
|
+
return right !== 0 ? left % right : NaN;
|
|
35938
|
+
}
|
|
35939
|
+
}
|
|
35940
|
+
}
|
|
35941
|
+
}
|
|
35550
35942
|
function applyRoundOp(op, num, digits) {
|
|
35551
35943
|
const factor = Math.pow(10, digits);
|
|
35552
35944
|
const raw = Math[op](num * factor) / factor;
|
|
@@ -35949,12 +36341,9 @@ function formatWithComma(num, digits) {
|
|
|
35949
36341
|
return decStr ? `${intFmt}.${decStr}` : intFmt;
|
|
35950
36342
|
}
|
|
35951
36343
|
function evalStringFuncArg(arg, row) {
|
|
35952
|
-
if (arg.type === "STRING") return arg.value;
|
|
35953
|
-
if (arg.type === "STRING_FUNC") return evalStringFunc(arg, row);
|
|
35954
|
-
if (arg.type === "FIELD_REF") return resolveFieldRef(row, arg.field);
|
|
35955
|
-
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
35956
36344
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return "";
|
|
35957
|
-
|
|
36345
|
+
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
36346
|
+
return String(evalScalarValueExpr(arg, row));
|
|
35958
36347
|
}
|
|
35959
36348
|
function resolveFieldRef(row, field) {
|
|
35960
36349
|
const direct = row[field];
|
|
@@ -35970,6 +36359,8 @@ function resolveFieldRef(row, field) {
|
|
|
35970
36359
|
// src/engine/evalWhere.ts
|
|
35971
36360
|
function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
35972
36361
|
switch (expr.type) {
|
|
36362
|
+
case "BOOLEAN":
|
|
36363
|
+
return expr.value;
|
|
35973
36364
|
case "BINARY":
|
|
35974
36365
|
return evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
|
|
35975
36366
|
case "NULL_CHECK":
|
|
@@ -36140,6 +36531,8 @@ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
36140
36531
|
switch (value.type) {
|
|
36141
36532
|
case "VARIABLE":
|
|
36142
36533
|
throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
|
|
36534
|
+
case "VARIABLE_IN_LIST":
|
|
36535
|
+
throw new Error(`ParseError: unresolved batch array variable @${value.name}.`);
|
|
36143
36536
|
case "STRING":
|
|
36144
36537
|
return value.value;
|
|
36145
36538
|
case "NUMBER":
|
|
@@ -36177,10 +36570,13 @@ function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
36177
36570
|
}
|
|
36178
36571
|
function evalCaseResult(result, row) {
|
|
36179
36572
|
if (result.type === "ARRAY") return result.elements.map((e) => e.value).join(",");
|
|
36180
|
-
if (result.type === "
|
|
36181
|
-
|
|
36182
|
-
|
|
36183
|
-
|
|
36573
|
+
if (result.type === "FIELD_REF") {
|
|
36574
|
+
return row[result.field] ?? "";
|
|
36575
|
+
}
|
|
36576
|
+
if (result.type === "ARITH") {
|
|
36577
|
+
return String(evalArithExpr(result, row));
|
|
36578
|
+
}
|
|
36579
|
+
return String(evalScalarValueExpr(result, row));
|
|
36184
36580
|
}
|
|
36185
36581
|
function resolveKintoneFunc(name) {
|
|
36186
36582
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -36224,6 +36620,63 @@ function matchLike(value, pattern) {
|
|
|
36224
36620
|
return regex.test(value);
|
|
36225
36621
|
}
|
|
36226
36622
|
|
|
36623
|
+
// src/core/dmlCustomCheck.ts
|
|
36624
|
+
function collectCheckFieldRefs(groups) {
|
|
36625
|
+
return collectRefs2(groups);
|
|
36626
|
+
}
|
|
36627
|
+
function collectCheckComparisonFieldRefs(groups) {
|
|
36628
|
+
return collectRefs2(groups.flatMap((group) => group.rules.map((rule) => rule.condition)));
|
|
36629
|
+
}
|
|
36630
|
+
function collectRefs2(root) {
|
|
36631
|
+
const refs = [];
|
|
36632
|
+
const seen = /* @__PURE__ */ new Set();
|
|
36633
|
+
const visit = (node) => {
|
|
36634
|
+
if (Array.isArray(node)) {
|
|
36635
|
+
node.forEach(visit);
|
|
36636
|
+
return;
|
|
36637
|
+
}
|
|
36638
|
+
if (node === null || typeof node !== "object") return;
|
|
36639
|
+
const obj = node;
|
|
36640
|
+
if (obj.type === "EXISTS" || obj.type === "SUBQUERY_IN_LIST" || obj.type === "SCALAR_SUBQUERY") {
|
|
36641
|
+
throw customCheckParseError("CHECK \u306B\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
36642
|
+
}
|
|
36643
|
+
if (obj.type === "FIELD" && typeof obj.field === "string") {
|
|
36644
|
+
add(typeof obj.tableAlias === "string" ? obj.tableAlias : null, obj.field);
|
|
36645
|
+
} else if (obj.type === "FIELD_REF" && typeof obj.field === "string") {
|
|
36646
|
+
const dot = obj.field.indexOf(".");
|
|
36647
|
+
add(dot > 0 ? obj.field.slice(0, dot) : null, dot > 0 ? obj.field.slice(dot + 1) : obj.field);
|
|
36648
|
+
}
|
|
36649
|
+
for (const value of Object.values(obj)) visit(value);
|
|
36650
|
+
};
|
|
36651
|
+
const add = (tableAlias, field) => {
|
|
36652
|
+
if (/^(COUNT|SUM|AVG|MIN|MAX|GROUP_CONCAT)\(/i.test(field)) {
|
|
36653
|
+
throw customCheckParseError("CHECK \u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
36654
|
+
}
|
|
36655
|
+
const key = `${tableAlias ?? ""}\0${field}`;
|
|
36656
|
+
if (!seen.has(key)) {
|
|
36657
|
+
seen.add(key);
|
|
36658
|
+
refs.push({ tableAlias, field });
|
|
36659
|
+
}
|
|
36660
|
+
};
|
|
36661
|
+
visit(root);
|
|
36662
|
+
return refs;
|
|
36663
|
+
}
|
|
36664
|
+
function customCheckParseError(message) {
|
|
36665
|
+
return new ParseError(message, { kind: "EOF" /* EOF */, value: "CHECK", pos: 0 });
|
|
36666
|
+
}
|
|
36667
|
+
function evaluateCustomChecks(groups, row, resolveFieldType) {
|
|
36668
|
+
const errors = [];
|
|
36669
|
+
groups.forEach((group, groupIndex) => {
|
|
36670
|
+
for (const rule of group.rules) {
|
|
36671
|
+
if (!evalWhere(rule.condition, row, resolveFieldType)) continue;
|
|
36672
|
+
const value = evalScalarValueExpr(rule.message, row);
|
|
36673
|
+
errors.push({ groupIndex, message: value == null ? "" : String(value) });
|
|
36674
|
+
break;
|
|
36675
|
+
}
|
|
36676
|
+
});
|
|
36677
|
+
return errors;
|
|
36678
|
+
}
|
|
36679
|
+
|
|
36227
36680
|
// src/converter/dmlToKintone.ts
|
|
36228
36681
|
function assertDmlWhereIsSafe(where) {
|
|
36229
36682
|
if (whereHasKlike(where)) {
|
|
@@ -36261,10 +36714,11 @@ function buildInsertRecord(fields, row, fieldTypes) {
|
|
|
36261
36714
|
}
|
|
36262
36715
|
function updateToGetQuery(stmt) {
|
|
36263
36716
|
assertDmlWhereIsSafe(stmt.where);
|
|
36717
|
+
const checkFields = collectUpdateCheckTargetFields(stmt);
|
|
36264
36718
|
return {
|
|
36265
36719
|
app: stmt.appId,
|
|
36266
36720
|
query: whereToKintone(stmt.where),
|
|
36267
|
-
fields: ["$id"],
|
|
36721
|
+
fields: ["$id", ...checkFields],
|
|
36268
36722
|
totalCount: false
|
|
36269
36723
|
};
|
|
36270
36724
|
}
|
|
@@ -36278,19 +36732,19 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
|
36278
36732
|
function buildUpdateRecord(assignments, fieldTypes) {
|
|
36279
36733
|
const record2 = {};
|
|
36280
36734
|
for (const { field, value } of assignments) {
|
|
36281
|
-
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
36735
|
+
if (value.type === "ARITH" || value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
36282
36736
|
record2[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
|
|
36283
36737
|
}
|
|
36284
36738
|
return record2;
|
|
36285
36739
|
}
|
|
36286
36740
|
function hasArithAssignment(stmt) {
|
|
36287
36741
|
return stmt.assignments.some(
|
|
36288
|
-
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
|
|
36742
|
+
(a) => a.value.type === "ARITH" || a.value.type === "SCALAR_ARITH" || a.value.type === "CONCAT_OP" || a.value.type === "CASE_VALUE"
|
|
36289
36743
|
);
|
|
36290
36744
|
}
|
|
36291
36745
|
function hasRowDependentAssignment(stmt) {
|
|
36292
36746
|
return stmt.assignments.some(
|
|
36293
|
-
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
36747
|
+
(a) => a.value.type === "ARITH" || a.value.type === "SCALAR_ARITH" || a.value.type === "CONCAT_OP" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
36294
36748
|
);
|
|
36295
36749
|
}
|
|
36296
36750
|
function updateToGetQueryForArith(stmt) {
|
|
@@ -36299,12 +36753,15 @@ function updateToGetQueryForArith(stmt) {
|
|
|
36299
36753
|
for (const { value } of stmt.assignments) {
|
|
36300
36754
|
if (value.type === "ARITH") {
|
|
36301
36755
|
collectArithFields2(value, refFields);
|
|
36756
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
36757
|
+
collectScalarValueFields2(value, refFields);
|
|
36302
36758
|
} else if (value.type === "STRING_FUNC") {
|
|
36303
36759
|
collectStringFuncFields2(value, refFields);
|
|
36304
36760
|
} else if (value.type === "CASE_VALUE") {
|
|
36305
36761
|
collectCaseFields(value.expr, refFields);
|
|
36306
36762
|
}
|
|
36307
36763
|
}
|
|
36764
|
+
collectUpdateCheckTargetFields(stmt).forEach((field) => refFields.add(field));
|
|
36308
36765
|
return {
|
|
36309
36766
|
app: stmt.appId,
|
|
36310
36767
|
query: whereToKintone(stmt.where),
|
|
@@ -36325,16 +36782,27 @@ function collectStringFuncFields2(expr, out) {
|
|
|
36325
36782
|
for (const arg of expr.args) collectStringFuncArgFields2(arg, out);
|
|
36326
36783
|
}
|
|
36327
36784
|
function collectStringFuncArgFields2(arg, out) {
|
|
36328
|
-
if (arg.type === "STRING") return;
|
|
36329
|
-
if (arg.type === "STRING_FUNC") {
|
|
36330
|
-
collectStringFuncFields2(arg, out);
|
|
36331
|
-
return;
|
|
36332
|
-
}
|
|
36333
36785
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
36334
36786
|
collectAggOperandFields2(arg, out);
|
|
36335
36787
|
return;
|
|
36336
36788
|
}
|
|
36337
|
-
|
|
36789
|
+
collectScalarValueFields2(arg, out);
|
|
36790
|
+
}
|
|
36791
|
+
function collectScalarValueFields2(expr, out) {
|
|
36792
|
+
if (expr.type === "FIELD") {
|
|
36793
|
+
out.add(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field);
|
|
36794
|
+
return;
|
|
36795
|
+
}
|
|
36796
|
+
if (expr.type === "STRING_FUNC") {
|
|
36797
|
+
collectStringFuncFields2(expr, out);
|
|
36798
|
+
return;
|
|
36799
|
+
}
|
|
36800
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
36801
|
+
collectScalarValueFields2(expr.left, out);
|
|
36802
|
+
collectScalarValueFields2(expr.right, out);
|
|
36803
|
+
return;
|
|
36804
|
+
}
|
|
36805
|
+
if (expr.type === "CASE_WHEN") collectCaseFields(expr, out);
|
|
36338
36806
|
}
|
|
36339
36807
|
function collectAggOperandFields2(node, out) {
|
|
36340
36808
|
if (node.type === "AGG_REF") {
|
|
@@ -36347,9 +36815,12 @@ function collectAggOperandFields2(node, out) {
|
|
|
36347
36815
|
}
|
|
36348
36816
|
}
|
|
36349
36817
|
function collectCaseResultFields(result, out) {
|
|
36350
|
-
if (result.type === "STRING") return;
|
|
36351
36818
|
if (result.type === "ARRAY") return;
|
|
36352
|
-
|
|
36819
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
36820
|
+
collectArithNode2(result, out);
|
|
36821
|
+
return;
|
|
36822
|
+
}
|
|
36823
|
+
collectScalarValueFields2(result, out);
|
|
36353
36824
|
}
|
|
36354
36825
|
function collectCaseFields(expr, out) {
|
|
36355
36826
|
for (const branch of expr.branches) {
|
|
@@ -36377,6 +36848,9 @@ function collectConditionFields(expr, out) {
|
|
|
36377
36848
|
case "GROUP":
|
|
36378
36849
|
collectConditionFields(expr.expr, out);
|
|
36379
36850
|
break;
|
|
36851
|
+
case "EXISTS":
|
|
36852
|
+
case "BOOLEAN":
|
|
36853
|
+
break;
|
|
36380
36854
|
}
|
|
36381
36855
|
}
|
|
36382
36856
|
function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
@@ -36387,6 +36861,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
|
|
|
36387
36861
|
for (const { field, value } of stmt.assignments) {
|
|
36388
36862
|
if (value.type === "ARITH") {
|
|
36389
36863
|
record2[field] = { value: String(evalArith(value, raw)) };
|
|
36864
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
36865
|
+
record2[field] = { value: String(evalScalarValueExpr(value, row)) };
|
|
36390
36866
|
} else if (value.type === "STRING_FUNC") {
|
|
36391
36867
|
record2[field] = { value: evalStringFunc(value, row) };
|
|
36392
36868
|
} else if (value.type === "CASE_VALUE") {
|
|
@@ -36441,6 +36917,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
|
|
|
36441
36917
|
throw new DmlConvertError("UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
36442
36918
|
} else if (value.type === "ARITH") {
|
|
36443
36919
|
record2[field] = { value: String(evalArith(value, target)) };
|
|
36920
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
36921
|
+
record2[field] = { value: String(evalScalarValueExpr(value, targetRow)) };
|
|
36444
36922
|
} else if (value.type === "CASE_VALUE") {
|
|
36445
36923
|
record2[field] = { value: evalCaseWhenValue(value.expr, targetRow, fieldType) };
|
|
36446
36924
|
} else {
|
|
@@ -36551,7 +37029,15 @@ function evalCaseResultValue(result, row, fieldType) {
|
|
|
36551
37029
|
if (result.type === "STRING_FUNC") {
|
|
36552
37030
|
return evalStringFunc(result, row);
|
|
36553
37031
|
}
|
|
36554
|
-
|
|
37032
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
37033
|
+
return String(evalArithExpr(result, row));
|
|
37034
|
+
}
|
|
37035
|
+
return String(evalScalarValueExpr(result, row));
|
|
37036
|
+
}
|
|
37037
|
+
function collectUpdateCheckTargetFields(stmt) {
|
|
37038
|
+
if (!stmt.checkGroups) return [];
|
|
37039
|
+
const targetAlias = `app${stmt.appId}`.toLowerCase();
|
|
37040
|
+
return [...new Set(collectCheckFieldRefs(stmt.checkGroups).filter((ref) => ref.tableAlias === null || ref.tableAlias.toLowerCase() === targetAlias).map((ref) => ref.field).filter((field) => field !== "$id"))];
|
|
36555
37041
|
}
|
|
36556
37042
|
function evalCaseWhenValue(expr, row, fieldType) {
|
|
36557
37043
|
for (const branch of expr.branches) {
|
|
@@ -36580,6 +37066,8 @@ function convertDmlSqlValue(value, fieldType) {
|
|
|
36580
37066
|
switch (value.type) {
|
|
36581
37067
|
case "VARIABLE":
|
|
36582
37068
|
throw new DmlConvertError(`\u672A\u89E3\u6C7A\u306E\u30D0\u30C3\u30C1\u5909\u6570 @${value.name} \u304C\u3042\u308A\u307E\u3059`);
|
|
37069
|
+
case "VARIABLE_IN_LIST":
|
|
37070
|
+
throw new DmlConvertError(`\u672A\u89E3\u6C7A\u306E\u914D\u5217\u5909\u6570 @${value.name} \u304C\u3042\u308A\u307E\u3059`);
|
|
36583
37071
|
case "STRING":
|
|
36584
37072
|
return convertString2(value.value, fieldType);
|
|
36585
37073
|
case "NUMBER":
|
|
@@ -37104,7 +37592,7 @@ function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldS
|
|
|
37104
37592
|
}
|
|
37105
37593
|
function hasAggregateColumns(columns) {
|
|
37106
37594
|
return columns.some(
|
|
37107
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
|
|
37595
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(c.expr)
|
|
37108
37596
|
);
|
|
37109
37597
|
}
|
|
37110
37598
|
function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
@@ -37141,6 +37629,10 @@ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
|
37141
37629
|
const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
|
|
37142
37630
|
const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
|
|
37143
37631
|
outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
|
|
37632
|
+
} else if (col.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(col.expr)) {
|
|
37633
|
+
const outputKey = col.alias ?? scalarValueDefaultKey(col.expr);
|
|
37634
|
+
const resolvedExpr = resolveAggInScalarValue(col.expr, groupRows, resolveAggSortKind);
|
|
37635
|
+
outRow[outputKey] = String(evalScalarValueExpr(resolvedExpr, outRow));
|
|
37144
37636
|
}
|
|
37145
37637
|
}
|
|
37146
37638
|
result.push(outRow);
|
|
@@ -37411,6 +37903,8 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
|
|
|
37411
37903
|
const out = {};
|
|
37412
37904
|
for (const [colIdx, col] of columns.entries()) {
|
|
37413
37905
|
switch (col.type) {
|
|
37906
|
+
case "VARIABLE_COL":
|
|
37907
|
+
throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
|
|
37414
37908
|
case "WILDCARD":
|
|
37415
37909
|
Object.assign(out, stripParentShortcutColumns(row));
|
|
37416
37910
|
break;
|
|
@@ -37471,6 +37965,13 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
|
|
|
37471
37965
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
37472
37966
|
break;
|
|
37473
37967
|
}
|
|
37968
|
+
case "SCALAR_VALUE_COL": {
|
|
37969
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? scalarValueDefaultKey(col.expr);
|
|
37970
|
+
const srcKey = scalarValueDefaultKey(col.expr);
|
|
37971
|
+
out[key] = scalarValueHasAggregate2(col.expr) ? row[col.alias ?? srcKey] ?? row[srcKey] ?? "" : String(evalScalarValueExpr(col.expr, row));
|
|
37972
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
37973
|
+
break;
|
|
37974
|
+
}
|
|
37474
37975
|
case "SCALAR_SUBQUERY_COL": {
|
|
37475
37976
|
const key = outputKeys?.[colIdx] ?? col.alias ?? "(subquery)";
|
|
37476
37977
|
out[key] = scalarCache?.get(colIdx) ?? "";
|
|
@@ -37507,6 +38008,8 @@ function computeExplicitOutputKeys(columns, defaultFieldKeys) {
|
|
|
37507
38008
|
}
|
|
37508
38009
|
function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
37509
38010
|
switch (col.type) {
|
|
38011
|
+
case "VARIABLE_COL":
|
|
38012
|
+
throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
|
|
37510
38013
|
case "FIELD":
|
|
37511
38014
|
return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
37512
38015
|
case "LITERAL_COL":
|
|
@@ -37521,6 +38024,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
37521
38024
|
return col.alias ?? "case";
|
|
37522
38025
|
case "STRFUNC_COL":
|
|
37523
38026
|
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
38027
|
+
case "SCALAR_VALUE_COL":
|
|
38028
|
+
return col.alias ?? scalarValueDefaultKey(col.expr);
|
|
37524
38029
|
case "SCALAR_SUBQUERY_COL":
|
|
37525
38030
|
return col.alias ?? "(subquery)";
|
|
37526
38031
|
case "WINDOW_COL":
|
|
@@ -37576,18 +38081,49 @@ function arithColDefaultKey(expr) {
|
|
|
37576
38081
|
}
|
|
37577
38082
|
function stringFuncDefaultKey(expr) {
|
|
37578
38083
|
const argStrs = expr.args.map((a) => {
|
|
37579
|
-
if (a.type === "STRING") return `'${a.value}'`;
|
|
37580
|
-
if (a.type === "STRING_FUNC") return stringFuncDefaultKey(a);
|
|
37581
38084
|
if (a.type === "AGG_REF" || a.type === "AGG_ARITH") return aggArithDefaultKey(a);
|
|
37582
|
-
return
|
|
38085
|
+
return scalarValueDefaultKey(a);
|
|
37583
38086
|
});
|
|
37584
38087
|
return `${expr.func}(${argStrs.join(",")})`;
|
|
37585
38088
|
}
|
|
38089
|
+
function scalarValueDefaultKey(expr) {
|
|
38090
|
+
switch (expr.type) {
|
|
38091
|
+
case "STRING":
|
|
38092
|
+
return `'${expr.value}'`;
|
|
38093
|
+
case "NUMBER":
|
|
38094
|
+
return numberLiteralText(expr);
|
|
38095
|
+
case "VARIABLE":
|
|
38096
|
+
return `@${expr.name}`;
|
|
38097
|
+
case "FIELD":
|
|
38098
|
+
return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
|
|
38099
|
+
case "STRING_FUNC":
|
|
38100
|
+
return stringFuncDefaultKey(expr);
|
|
38101
|
+
case "CASE_WHEN":
|
|
38102
|
+
return "case";
|
|
38103
|
+
case "SCALAR_ARITH":
|
|
38104
|
+
return `${scalarValueDefaultKey(expr.left)}${expr.op}${scalarValueDefaultKey(expr.right)}`;
|
|
38105
|
+
case "CONCAT_OP":
|
|
38106
|
+
return `${scalarValueDefaultKey(expr.left)}||${scalarValueDefaultKey(expr.right)}`;
|
|
38107
|
+
}
|
|
38108
|
+
}
|
|
37586
38109
|
function hasAggregateInStringFuncArg(arg) {
|
|
37587
38110
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
37588
|
-
|
|
38111
|
+
return scalarValueHasAggregate2(arg);
|
|
38112
|
+
}
|
|
38113
|
+
function scalarValueHasAggregate2(expr) {
|
|
38114
|
+
if (expr.type === "STRING_FUNC") return hasAggregateInStringFuncExpr2(expr);
|
|
38115
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
38116
|
+
return scalarValueHasAggregate2(expr.left) || scalarValueHasAggregate2(expr.right);
|
|
38117
|
+
}
|
|
38118
|
+
if (expr.type === "CASE_WHEN") {
|
|
38119
|
+
return expr.branches.some((branch) => caseResultHasAggregate2(branch.result)) || expr.elseResult !== null && caseResultHasAggregate2(expr.elseResult);
|
|
38120
|
+
}
|
|
37589
38121
|
return false;
|
|
37590
38122
|
}
|
|
38123
|
+
function caseResultHasAggregate2(result) {
|
|
38124
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
38125
|
+
return scalarValueHasAggregate2(result);
|
|
38126
|
+
}
|
|
37591
38127
|
function hasAggregateInStringFuncExpr2(expr) {
|
|
37592
38128
|
return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
|
|
37593
38129
|
}
|
|
@@ -37603,7 +38139,18 @@ function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
|
37603
38139
|
if (arg.type === "STRING_FUNC") {
|
|
37604
38140
|
return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
|
|
37605
38141
|
}
|
|
37606
|
-
return arg;
|
|
38142
|
+
return resolveAggInScalarValue(arg, rows, resolveAggSortKind);
|
|
38143
|
+
}
|
|
38144
|
+
function resolveAggInScalarValue(expr, rows, resolveAggSortKind) {
|
|
38145
|
+
if (expr.type === "STRING_FUNC") return resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind);
|
|
38146
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
38147
|
+
return {
|
|
38148
|
+
...expr,
|
|
38149
|
+
left: resolveAggInScalarValue(expr.left, rows, resolveAggSortKind),
|
|
38150
|
+
right: resolveAggInScalarValue(expr.right, rows, resolveAggSortKind)
|
|
38151
|
+
};
|
|
38152
|
+
}
|
|
38153
|
+
return expr;
|
|
37607
38154
|
}
|
|
37608
38155
|
function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
37609
38156
|
return {
|
|
@@ -37624,7 +38171,7 @@ function deriveOutputOrderSemantics(columns) {
|
|
|
37624
38171
|
} else if (column.func === "GROUP_CONCAT") {
|
|
37625
38172
|
result.set(column.alias, syntheticSemantics("string"));
|
|
37626
38173
|
}
|
|
37627
|
-
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
|
|
38174
|
+
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
37628
38175
|
result.set(column.alias, syntheticSemantics("string"));
|
|
37629
38176
|
} else if (column.type === "STRFUNC_COL") {
|
|
37630
38177
|
result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
|
|
@@ -37897,19 +38444,20 @@ var VALIDATION_META_COLUMNS = [
|
|
|
37897
38444
|
"$err_code",
|
|
37898
38445
|
"$err_message"
|
|
37899
38446
|
];
|
|
37900
|
-
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision) {
|
|
38447
|
+
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision, checkGroups = [], validateMissingCreateFields = true, includePreErrors = true) {
|
|
37901
38448
|
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
37902
38449
|
const errors = [];
|
|
37903
38450
|
const invalid = /* @__PURE__ */ new Set();
|
|
38451
|
+
let firstEvaluationError;
|
|
37904
38452
|
for (const candidate of candidates) {
|
|
37905
38453
|
candidate.record ??= {};
|
|
37906
|
-
const rowErrors = [...candidate.preErrors];
|
|
38454
|
+
const rowErrors = includePreErrors ? [...candidate.preErrors] : [];
|
|
37907
38455
|
for (const code of targetFields) {
|
|
37908
38456
|
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
|
|
37909
38457
|
if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
|
|
37910
38458
|
else candidate.record[code] = { value: result.value };
|
|
37911
38459
|
}
|
|
37912
|
-
if (candidate.mode === "create") {
|
|
38460
|
+
if (validateMissingCreateFields && candidate.mode === "create") {
|
|
37913
38461
|
for (const info of fieldInfos) {
|
|
37914
38462
|
if (info.inSubtable) continue;
|
|
37915
38463
|
if (candidate.payload.has(info.code)) continue;
|
|
@@ -37931,6 +38479,23 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
37931
38479
|
}
|
|
37932
38480
|
}
|
|
37933
38481
|
}
|
|
38482
|
+
if (checkGroups.length > 0) {
|
|
38483
|
+
const row = candidate.evaluationRow ?? Object.fromEntries(
|
|
38484
|
+
[...candidate.payload].map(([field, value]) => [field, renderValidationValue(value)])
|
|
38485
|
+
);
|
|
38486
|
+
const types = candidate.evaluationFieldTypes;
|
|
38487
|
+
const resolveType = (field) => {
|
|
38488
|
+
const qualified = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
38489
|
+
return types?.get(qualified) ?? types?.get(field.field);
|
|
38490
|
+
};
|
|
38491
|
+
try {
|
|
38492
|
+
for (const custom2 of evaluateCustomChecks(checkGroups, row, resolveType)) {
|
|
38493
|
+
rowErrors.push({ field: "", code: "ERR_CHECK", message: custom2.message });
|
|
38494
|
+
}
|
|
38495
|
+
} catch (error51) {
|
|
38496
|
+
firstEvaluationError ??= error51;
|
|
38497
|
+
}
|
|
38498
|
+
}
|
|
37934
38499
|
if (rowErrors.length > 0) invalid.add(candidate.rowNumber);
|
|
37935
38500
|
for (const error51 of rowErrors) {
|
|
37936
38501
|
const row = {};
|
|
@@ -37944,6 +38509,7 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
37944
38509
|
errors.push(row);
|
|
37945
38510
|
}
|
|
37946
38511
|
}
|
|
38512
|
+
if (firstEvaluationError !== void 0) throw firstEvaluationError;
|
|
37947
38513
|
return { errors, invalidRows: invalid.size, invalidRowNumbers: invalid };
|
|
37948
38514
|
}
|
|
37949
38515
|
function renderValidationValue(value) {
|
|
@@ -37958,6 +38524,11 @@ function renderValidationValue(value) {
|
|
|
37958
38524
|
return String(value);
|
|
37959
38525
|
}
|
|
37960
38526
|
|
|
38527
|
+
// src/core/existingRecordValidation.ts
|
|
38528
|
+
function renderExistingValidationValue(raw, fieldType) {
|
|
38529
|
+
return isEmptyDmlValue(raw) ? "" : renderValidationValue(normalizeRaw(raw, fieldType));
|
|
38530
|
+
}
|
|
38531
|
+
|
|
37961
38532
|
// src/core/optimization/whereCapability.ts
|
|
37962
38533
|
var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
|
|
37963
38534
|
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
@@ -38032,6 +38603,8 @@ function classifyWhereCapability(where, resolveField2) {
|
|
|
38032
38603
|
}
|
|
38033
38604
|
function classifyNode(where, resolveField2) {
|
|
38034
38605
|
switch (where.type) {
|
|
38606
|
+
case "BOOLEAN":
|
|
38607
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
38035
38608
|
case "BINARY":
|
|
38036
38609
|
return classifyBinary(where.op, where.left, where.right.type, resolveField2);
|
|
38037
38610
|
case "NULL_CHECK":
|
|
@@ -38358,6 +38931,8 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
38358
38931
|
throw new Error("ArgumentError: ON ERROR SKIP requires a batch.");
|
|
38359
38932
|
}
|
|
38360
38933
|
switch (stmt.type) {
|
|
38934
|
+
case "VALIDATE":
|
|
38935
|
+
return executeExistingRecordValidation(stmt, client, options, cacheContext);
|
|
38361
38936
|
case "SELECT":
|
|
38362
38937
|
return executeSelect(stmt, client, options, cacheContext);
|
|
38363
38938
|
case "UNION":
|
|
@@ -38403,6 +38978,144 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
38403
38978
|
return executeAssert(stmt, client, options, cacheContext);
|
|
38404
38979
|
}
|
|
38405
38980
|
}
|
|
38981
|
+
var EXISTING_VALIDATION_COLUMNS = ["$id", "$err_field", "$err_code", "$err_message", "$err_value"];
|
|
38982
|
+
function hasAuditableConstraint(field) {
|
|
38983
|
+
return field.required === true || field.minValue !== void 0 || field.maxValue !== void 0 || field.minLength !== void 0 || field.maxLength !== void 0 || field.optionOrder !== void 0;
|
|
38984
|
+
}
|
|
38985
|
+
function resolveExistingValidationTargets(stmt, fieldInfos) {
|
|
38986
|
+
const byCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
38987
|
+
const auditable = (field) => !field.inSubtable && (field.fieldType === "NUMBER" || hasAuditableConstraint(field));
|
|
38988
|
+
if (stmt.fields === void 0) return fieldInfos.filter(auditable);
|
|
38989
|
+
const seen = /* @__PURE__ */ new Set();
|
|
38990
|
+
return stmt.fields.map((code) => {
|
|
38991
|
+
if (seen.has(code)) throw new Error(`ArgumentError: VALIDATE field ${code} is duplicated.`);
|
|
38992
|
+
seen.add(code);
|
|
38993
|
+
if (code === "$id") throw new Error("ArgumentError: VALIDATE cannot audit system field $id.");
|
|
38994
|
+
const info = byCode.get(code);
|
|
38995
|
+
if (!info) throw new Error(`ArgumentError: VALIDATE field ${code} does not exist.`);
|
|
38996
|
+
if (info.inSubtable) throw new Error(`ArgumentError: VALIDATE field ${code} is a subtable child field.`);
|
|
38997
|
+
if (!auditable(info)) throw new Error(`ArgumentError: VALIDATE field ${code} has no auditable constraint.`);
|
|
38998
|
+
return info;
|
|
38999
|
+
});
|
|
39000
|
+
}
|
|
39001
|
+
function collectValidateWhereFields(where) {
|
|
39002
|
+
const fields = [];
|
|
39003
|
+
const seen = /* @__PURE__ */ new Set();
|
|
39004
|
+
const add = (field) => {
|
|
39005
|
+
if (!seen.has(field)) {
|
|
39006
|
+
seen.add(field);
|
|
39007
|
+
fields.push(field);
|
|
39008
|
+
}
|
|
39009
|
+
};
|
|
39010
|
+
const visit = (node) => {
|
|
39011
|
+
if (Array.isArray(node)) {
|
|
39012
|
+
node.forEach(visit);
|
|
39013
|
+
return;
|
|
39014
|
+
}
|
|
39015
|
+
if (node === null || typeof node !== "object") return;
|
|
39016
|
+
const obj = node;
|
|
39017
|
+
if (obj.type === "FIELD" && typeof obj.field === "string") add(obj.field);
|
|
39018
|
+
if (obj.type === "FIELD_REF" && typeof obj.field === "string") add(obj.field);
|
|
39019
|
+
Object.values(obj).forEach(visit);
|
|
39020
|
+
};
|
|
39021
|
+
visit(where);
|
|
39022
|
+
return fields;
|
|
39023
|
+
}
|
|
39024
|
+
function existingValidationColumnMeta() {
|
|
39025
|
+
return new Map(EXISTING_VALIDATION_COLUMNS.map((column) => [column, {
|
|
39026
|
+
fieldType: column === "$id" ? "KSQL_NUMBER" : "KSQL_STRING",
|
|
39027
|
+
sortKind: column === "$id" ? "number" : "string",
|
|
39028
|
+
semantics: syntheticSemantics(column === "$id" ? "number" : "string")
|
|
39029
|
+
}]));
|
|
39030
|
+
}
|
|
39031
|
+
async function executeExistingRecordValidation(stmt, client, options, cacheContext) {
|
|
39032
|
+
if (stmt.errorTable) throw new Error("ArgumentError: VALIDATE INTO requires a batch.");
|
|
39033
|
+
return executeExistingRecordValidationCore(stmt, client, options, cacheContext);
|
|
39034
|
+
}
|
|
39035
|
+
async function executeExistingRecordValidationCore(stmt, client, options, cacheContext) {
|
|
39036
|
+
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
39037
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
39038
|
+
const targets = resolveExistingValidationTargets(stmt, fieldInfos);
|
|
39039
|
+
const checkGroups = stmt.checkGroups ?? [];
|
|
39040
|
+
const checkRefs2 = collectCheckFieldRefs(checkGroups);
|
|
39041
|
+
for (const ref of checkRefs2) {
|
|
39042
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
|
|
39043
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${stmt.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
39044
|
+
}
|
|
39045
|
+
}
|
|
39046
|
+
const evaluationTypes = new Map(fieldInfos.map((field) => [field.code, field.fieldType]));
|
|
39047
|
+
evaluationTypes.set("$id", "RECORD_NUMBER");
|
|
39048
|
+
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
39049
|
+
const whereFields = collectValidateWhereFields(stmt.where);
|
|
39050
|
+
const requiredFields = [.../* @__PURE__ */ new Set([
|
|
39051
|
+
"$id",
|
|
39052
|
+
...targets.map((field) => field.code),
|
|
39053
|
+
...whereFields,
|
|
39054
|
+
...checkRefs2.map((ref) => ref.field)
|
|
39055
|
+
])];
|
|
39056
|
+
for (const field of whereFields) {
|
|
39057
|
+
if (field !== "$id" && !infoByCode.has(field)) {
|
|
39058
|
+
throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${stmt.appId}.`);
|
|
39059
|
+
}
|
|
39060
|
+
}
|
|
39061
|
+
const numberPrecision = targets.some((field) => field.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
|
|
39062
|
+
const semantics = (field) => field.field === "$id" ? resolveFieldSemantics({ fieldType: "__ID__" }) : infoByCode.get(field.field)?.semantics ?? (infoByCode.has(field.field) ? resolveFieldSemantics(infoByCode.get(field.field)) : void 0);
|
|
39063
|
+
const capability = classifyWhereCapability(stmt.where, semantics);
|
|
39064
|
+
if (capability.capability === "UNSUPPORTED") {
|
|
39065
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
39066
|
+
}
|
|
39067
|
+
const fieldTypes = new Map(fieldInfos.map((field) => [field.code, field.fieldType]));
|
|
39068
|
+
const fieldOptions = new Map(fieldInfos.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
39069
|
+
const prefilter = stmt.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? stmt.where : extractSafePushdownLeaves(stmt.where, {
|
|
39070
|
+
allowUnqualifiedFields: true,
|
|
39071
|
+
fieldTypes,
|
|
39072
|
+
fieldOptions,
|
|
39073
|
+
allowKlike: false
|
|
39074
|
+
});
|
|
39075
|
+
const query = prefilter === null ? "" : whereToKintone(prefilter);
|
|
39076
|
+
const records = await fetchAll(client.getRecords, stmt.appId, query, requiredFields, {
|
|
39077
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
39078
|
+
parallel: options.fetchParallel ?? 1,
|
|
39079
|
+
onLimit: "error"
|
|
39080
|
+
});
|
|
39081
|
+
const validationRows = records.map((record2) => ({
|
|
39082
|
+
id: String(record2["$id"]?.value ?? ""),
|
|
39083
|
+
record: record2,
|
|
39084
|
+
flat: flatten(record2, null)
|
|
39085
|
+
})).filter((row) => stmt.where === null || evalWhere(stmt.where, row.flat, (field) => evaluationTypes.get(field.field)));
|
|
39086
|
+
const rows = [];
|
|
39087
|
+
for (const row of validationRows) {
|
|
39088
|
+
for (const field of targets) {
|
|
39089
|
+
const raw = row.record[field.code]?.value;
|
|
39090
|
+
const validation = validateAndNormalizeDmlValue(raw, field, numberPrecision);
|
|
39091
|
+
if (validation.ok) continue;
|
|
39092
|
+
rows.push({
|
|
39093
|
+
"$id": row.id,
|
|
39094
|
+
"$err_field": field.code,
|
|
39095
|
+
"$err_code": validation.code,
|
|
39096
|
+
"$err_message": validation.message,
|
|
39097
|
+
"$err_value": renderExistingValidationValue(raw, field.fieldType)
|
|
39098
|
+
});
|
|
39099
|
+
}
|
|
39100
|
+
for (const check2 of evaluateCustomChecks(checkGroups, row.flat, (field) => evaluationTypes.get(field.field))) {
|
|
39101
|
+
rows.push({
|
|
39102
|
+
"$id": row.id,
|
|
39103
|
+
"$err_field": "",
|
|
39104
|
+
"$err_code": "ERR_CHECK",
|
|
39105
|
+
"$err_message": check2.message,
|
|
39106
|
+
"$err_value": ""
|
|
39107
|
+
});
|
|
39108
|
+
}
|
|
39109
|
+
}
|
|
39110
|
+
const result = {
|
|
39111
|
+
type: "SELECT",
|
|
39112
|
+
columns: [...EXISTING_VALIDATION_COLUMNS],
|
|
39113
|
+
rows,
|
|
39114
|
+
rowCount: rows.length
|
|
39115
|
+
};
|
|
39116
|
+
materializedMetaBySelectResult.set(result, existingValidationColumnMeta());
|
|
39117
|
+
return result;
|
|
39118
|
+
}
|
|
38406
39119
|
var TEMP_TABLE_MAX_ROWS = 1e4;
|
|
38407
39120
|
function appendValidationErrors(tempTables, name, columns, rows, maxRows, columnMeta) {
|
|
38408
39121
|
const current = tempTables.get(name);
|
|
@@ -38536,9 +39249,14 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
38536
39249
|
}
|
|
38537
39250
|
async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables) {
|
|
38538
39251
|
if (stmt.type === "SET_VARIABLE") {
|
|
38539
|
-
const resolvedStmt2 =
|
|
39252
|
+
const resolvedStmt2 = resolveBatchVariableReferences(stmt, variables);
|
|
38540
39253
|
validateKlikeStatement(resolvedStmt2);
|
|
38541
|
-
if (resolvedStmt2.expr.type === "
|
|
39254
|
+
if (resolvedStmt2.expr.type === "ARRAY") {
|
|
39255
|
+
variables.set(stmt.name, {
|
|
39256
|
+
type: "array",
|
|
39257
|
+
elements: resolvedStmt2.expr.elements.map((element) => ({ type: "string", value: element.value }))
|
|
39258
|
+
});
|
|
39259
|
+
} else if (resolvedStmt2.expr.type === "SCALAR_SUBQUERY") {
|
|
38542
39260
|
try {
|
|
38543
39261
|
const value = await evaluateScalarSubquery(
|
|
38544
39262
|
resolvedStmt2.expr.query,
|
|
@@ -38575,8 +39293,27 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
38575
39293
|
}
|
|
38576
39294
|
return {};
|
|
38577
39295
|
}
|
|
38578
|
-
const resolvedStmt =
|
|
39296
|
+
const resolvedStmt = resolveBatchVariableReferences(stmt, variables);
|
|
38579
39297
|
validateKlikeStatement(resolvedStmt);
|
|
39298
|
+
if (resolvedStmt.type === "VALIDATE") {
|
|
39299
|
+
const result = await executeExistingRecordValidationCore(
|
|
39300
|
+
resolvedStmt,
|
|
39301
|
+
client,
|
|
39302
|
+
{ ...options, onLimitReached: "error" },
|
|
39303
|
+
cacheContext
|
|
39304
|
+
);
|
|
39305
|
+
if (resolvedStmt.errorTable) {
|
|
39306
|
+
appendValidationErrors(
|
|
39307
|
+
tempTables,
|
|
39308
|
+
resolvedStmt.errorTable,
|
|
39309
|
+
result.columns,
|
|
39310
|
+
result.rows,
|
|
39311
|
+
options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
|
|
39312
|
+
materializedMetaBySelectResult.get(result) ?? existingValidationColumnMeta()
|
|
39313
|
+
);
|
|
39314
|
+
}
|
|
39315
|
+
return { result };
|
|
39316
|
+
}
|
|
38580
39317
|
if ("validateOnly" in resolvedStmt && resolvedStmt.validateOnly === true) {
|
|
38581
39318
|
const result = await executeDmlValidation(
|
|
38582
39319
|
resolvedStmt,
|
|
@@ -38761,9 +39498,9 @@ function evaluateScalarExpr(expr) {
|
|
|
38761
39498
|
}
|
|
38762
39499
|
}
|
|
38763
39500
|
}
|
|
38764
|
-
function
|
|
39501
|
+
function resolveBatchVariableReferences(node, variables) {
|
|
38765
39502
|
if (Array.isArray(node)) {
|
|
38766
|
-
return node.map((v) =>
|
|
39503
|
+
return node.map((v) => resolveBatchVariableReferences(v, variables));
|
|
38767
39504
|
}
|
|
38768
39505
|
if (node !== null && typeof node === "object") {
|
|
38769
39506
|
const obj = node;
|
|
@@ -38772,14 +39509,72 @@ function resolveVariableRefs(node, variables) {
|
|
|
38772
39509
|
if (value === void 0) {
|
|
38773
39510
|
throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
|
|
38774
39511
|
}
|
|
39512
|
+
if (value.type === "array") {
|
|
39513
|
+
throw new Error(`ParseError: array variable @${obj["name"]} can only be used as IN @${obj["name"]}.`);
|
|
39514
|
+
}
|
|
38775
39515
|
return value.type === "number" ? { type: "NUMBER", value: value.value, raw: value.raw ?? String(value.value) } : { type: "STRING", value: value.value };
|
|
38776
39516
|
}
|
|
38777
|
-
|
|
38778
|
-
|
|
39517
|
+
if (obj["type"] === "VARIABLE_COL" && typeof obj["name"] === "string" && typeof obj["alias"] === "string") {
|
|
39518
|
+
const value = variables.get(obj["name"]);
|
|
39519
|
+
if (value === void 0) throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
|
|
39520
|
+
if (value.type === "array") throw new Error(`ParseError: array variable @${obj["name"]} cannot be used as a SELECT column.`);
|
|
39521
|
+
return value.type === "number" ? { type: "ARITH_COL", expr: { type: "NUMBER", value: value.value, raw: value.raw ?? String(value.value) }, alias: obj["alias"] } : { type: "LITERAL_COL", value: value.value, alias: obj["alias"] };
|
|
39522
|
+
}
|
|
39523
|
+
if (obj["type"] === "VARIABLE_IN_LIST") return obj;
|
|
39524
|
+
const resolved = Object.fromEntries(
|
|
39525
|
+
Object.entries(obj).map(([key, value]) => [key, resolveBatchVariableReferences(value, variables)])
|
|
38779
39526
|
);
|
|
39527
|
+
if (resolved["type"] === "BINARY") {
|
|
39528
|
+
const right = resolved["right"];
|
|
39529
|
+
if (right?.["type"] === "VARIABLE_IN_LIST" && typeof right["name"] === "string") {
|
|
39530
|
+
const value = variables.get(right["name"]);
|
|
39531
|
+
if (value === void 0) throw new Error(`ParseError: variable @${right["name"]} is not defined in this batch.`);
|
|
39532
|
+
if (value.type !== "array") {
|
|
39533
|
+
throw new Error(`ParseError: scalar variable @${right["name"]} cannot be used as IN @${right["name"]}; use IN (@${right["name"]}) instead.`);
|
|
39534
|
+
}
|
|
39535
|
+
if (value.elements.length === 0) {
|
|
39536
|
+
return { type: "BOOLEAN", value: resolved["op"] === "NOT_IN" };
|
|
39537
|
+
}
|
|
39538
|
+
resolved["right"] = {
|
|
39539
|
+
type: "IN_LIST",
|
|
39540
|
+
values: value.elements.map((element) => ({ type: "STRING", value: element.value }))
|
|
39541
|
+
};
|
|
39542
|
+
}
|
|
39543
|
+
}
|
|
39544
|
+
const simplified = simplifyBooleanWhere(resolved);
|
|
39545
|
+
if (simplified["type"] === "SELECT" && isBooleanNode(simplified["where"], true)) {
|
|
39546
|
+
simplified["where"] = null;
|
|
39547
|
+
}
|
|
39548
|
+
if ((simplified["type"] === "UPDATE" || simplified["type"] === "DELETE" || simplified["type"] === "REORDER") && isBooleanNode(simplified["where"], true)) {
|
|
39549
|
+
throw new Error("ArgumentError: empty-array simplification makes the target WHERE always true; use an explicit safe target condition.");
|
|
39550
|
+
}
|
|
39551
|
+
return simplified;
|
|
38780
39552
|
}
|
|
38781
39553
|
return node;
|
|
38782
39554
|
}
|
|
39555
|
+
function isBooleanNode(value, expected) {
|
|
39556
|
+
return value !== null && typeof value === "object" && value.type === "BOOLEAN" && (expected === void 0 || value.value === expected);
|
|
39557
|
+
}
|
|
39558
|
+
function simplifyBooleanWhere(obj) {
|
|
39559
|
+
if (obj["type"] === "NOT" && isBooleanNode(obj["expr"])) {
|
|
39560
|
+
return { type: "BOOLEAN", value: !obj["expr"].value };
|
|
39561
|
+
}
|
|
39562
|
+
if (obj["type"] === "GROUP" && isBooleanNode(obj["expr"])) return obj["expr"];
|
|
39563
|
+
if (obj["type"] === "LOGICAL") {
|
|
39564
|
+
const left = obj["left"];
|
|
39565
|
+
const right = obj["right"];
|
|
39566
|
+
if (obj["op"] === "AND") {
|
|
39567
|
+
if (isBooleanNode(left, false) || isBooleanNode(right, false)) return { type: "BOOLEAN", value: false };
|
|
39568
|
+
if (isBooleanNode(left, true)) return right;
|
|
39569
|
+
if (isBooleanNode(right, true)) return left;
|
|
39570
|
+
} else if (obj["op"] === "OR") {
|
|
39571
|
+
if (isBooleanNode(left, true) || isBooleanNode(right, true)) return { type: "BOOLEAN", value: true };
|
|
39572
|
+
if (isBooleanNode(left, false)) return right;
|
|
39573
|
+
if (isBooleanNode(right, false)) return left;
|
|
39574
|
+
}
|
|
39575
|
+
}
|
|
39576
|
+
return obj;
|
|
39577
|
+
}
|
|
38783
39578
|
function findVariableRef(node) {
|
|
38784
39579
|
if (Array.isArray(node)) {
|
|
38785
39580
|
for (const value of node) {
|
|
@@ -38790,7 +39585,7 @@ function findVariableRef(node) {
|
|
|
38790
39585
|
}
|
|
38791
39586
|
if (node !== null && typeof node === "object") {
|
|
38792
39587
|
const obj = node;
|
|
38793
|
-
if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") return obj["name"];
|
|
39588
|
+
if ((obj["type"] === "VARIABLE" || obj["type"] === "VARIABLE_COL" || obj["type"] === "VARIABLE_IN_LIST") && typeof obj["name"] === "string") return obj["name"];
|
|
38794
39589
|
for (const value of Object.values(obj)) {
|
|
38795
39590
|
const found = findVariableRef(value);
|
|
38796
39591
|
if (found !== null) return found;
|
|
@@ -39000,7 +39795,7 @@ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
|
|
|
39000
39795
|
}
|
|
39001
39796
|
} else if (column.type === "STRFUNC_COL") {
|
|
39002
39797
|
semantics = stringFunctionColumnMeta(column.expr).semantics;
|
|
39003
|
-
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
|
|
39798
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
39004
39799
|
semantics = syntheticSemantics("string");
|
|
39005
39800
|
}
|
|
39006
39801
|
if (semantics) aliases.set(column.alias, semantics);
|
|
@@ -39033,6 +39828,7 @@ async function assertDmlWhereCapability(stmt, client, cacheContext) {
|
|
|
39033
39828
|
if (stmt.subtableCode || stmt.type === "UPDATE" && stmt.from != null) return;
|
|
39034
39829
|
const fields = whereNeedsFieldMetadata(stmt.where) ? await getFieldsCached(stmt.appId, client, cacheContext) : [];
|
|
39035
39830
|
const byCode = new Map(fields.map((field) => [field.code, field]));
|
|
39831
|
+
if (stmt.where.type === "BOOLEAN" && stmt.where.value === false) return;
|
|
39036
39832
|
const result = classifyWhereCapability(stmt.where, (field) => {
|
|
39037
39833
|
if (field.field === "$id") return resolveFieldSemantics({ fieldType: "__ID__" });
|
|
39038
39834
|
const info = byCode.get(field.field);
|
|
@@ -39105,6 +39901,9 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
39105
39901
|
}
|
|
39106
39902
|
return result;
|
|
39107
39903
|
}
|
|
39904
|
+
function isConstantFalseWhere(where) {
|
|
39905
|
+
return where?.type === "BOOLEAN" && where.value === false;
|
|
39906
|
+
}
|
|
39108
39907
|
function isNoFromSelect(stmt) {
|
|
39109
39908
|
return stmt.from.appId === 0 && stmt.from.cteName === NO_FROM_CTE_NAME;
|
|
39110
39909
|
}
|
|
@@ -39115,10 +39914,19 @@ function arithHasFieldRef(node) {
|
|
|
39115
39914
|
return false;
|
|
39116
39915
|
}
|
|
39117
39916
|
function stringFuncArgHasFieldRef(arg) {
|
|
39118
|
-
if (arg.type === "FIELD_REF") return true;
|
|
39119
|
-
if (arg.type === "ARITH") return arithHasFieldRef(arg);
|
|
39120
|
-
if (arg.type === "STRING_FUNC") return stringFuncHasFieldRef(arg);
|
|
39121
39917
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
39918
|
+
return scalarValueHasFieldRef(arg);
|
|
39919
|
+
}
|
|
39920
|
+
function scalarValueHasFieldRef(expr) {
|
|
39921
|
+
if (expr.type === "FIELD") return true;
|
|
39922
|
+
if (expr.type === "STRING_FUNC") return stringFuncHasFieldRef(expr);
|
|
39923
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
39924
|
+
return scalarValueHasFieldRef(expr.left) || scalarValueHasFieldRef(expr.right);
|
|
39925
|
+
}
|
|
39926
|
+
if (expr.type === "CASE_WHEN") {
|
|
39927
|
+
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
39928
|
+
return results.some((result) => result.type !== "ARRAY" && (result.type === "FIELD_REF" || result.type === "ARITH" ? arithHasFieldRef(result) : scalarValueHasFieldRef(result)));
|
|
39929
|
+
}
|
|
39122
39930
|
return false;
|
|
39123
39931
|
}
|
|
39124
39932
|
function stringFuncHasFieldRef(expr) {
|
|
@@ -39127,6 +39935,8 @@ function stringFuncHasFieldRef(expr) {
|
|
|
39127
39935
|
function validateNoFromColumns(stmt) {
|
|
39128
39936
|
for (const col of stmt.columns) {
|
|
39129
39937
|
switch (col.type) {
|
|
39938
|
+
case "VARIABLE_COL":
|
|
39939
|
+
throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
|
|
39130
39940
|
case "LITERAL_COL":
|
|
39131
39941
|
break;
|
|
39132
39942
|
case "ARITH_COL":
|
|
@@ -39139,6 +39949,11 @@ function validateNoFromColumns(stmt) {
|
|
|
39139
39949
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
39140
39950
|
}
|
|
39141
39951
|
break;
|
|
39952
|
+
case "SCALAR_VALUE_COL":
|
|
39953
|
+
if (scalarValueHasFieldRef(col.expr)) {
|
|
39954
|
+
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
39955
|
+
}
|
|
39956
|
+
break;
|
|
39142
39957
|
case "WINDOW_COL":
|
|
39143
39958
|
if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
|
|
39144
39959
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
@@ -39357,6 +40172,7 @@ function collectTypedInFieldRefs(expr, out) {
|
|
|
39357
40172
|
return;
|
|
39358
40173
|
case "NULL_CHECK":
|
|
39359
40174
|
case "EXISTS":
|
|
40175
|
+
case "BOOLEAN":
|
|
39360
40176
|
return;
|
|
39361
40177
|
}
|
|
39362
40178
|
}
|
|
@@ -39425,8 +40241,26 @@ function collectStringFuncAggregateRefs(expr, out) {
|
|
|
39425
40241
|
for (const arg of expr.args) {
|
|
39426
40242
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
39427
40243
|
collectAggregateOperandRefs(arg, out);
|
|
39428
|
-
} else
|
|
39429
|
-
|
|
40244
|
+
} else {
|
|
40245
|
+
collectScalarAggregateRefs(arg, out);
|
|
40246
|
+
}
|
|
40247
|
+
}
|
|
40248
|
+
}
|
|
40249
|
+
function collectScalarAggregateRefs(expr, out) {
|
|
40250
|
+
if (expr.type === "STRING_FUNC") {
|
|
40251
|
+
collectStringFuncAggregateRefs(expr, out);
|
|
40252
|
+
return;
|
|
40253
|
+
}
|
|
40254
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
40255
|
+
collectScalarAggregateRefs(expr.left, out);
|
|
40256
|
+
collectScalarAggregateRefs(expr.right, out);
|
|
40257
|
+
return;
|
|
40258
|
+
}
|
|
40259
|
+
if (expr.type === "CASE_WHEN") {
|
|
40260
|
+
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
40261
|
+
for (const result of results) {
|
|
40262
|
+
if (result.type === "STRING_FUNC") collectStringFuncAggregateRefs(result, out);
|
|
40263
|
+
else if (result.type !== "ARRAY" && result.type !== "FIELD_REF" && result.type !== "ARITH") collectScalarAggregateRefs(result, out);
|
|
39430
40264
|
}
|
|
39431
40265
|
}
|
|
39432
40266
|
}
|
|
@@ -39439,6 +40273,8 @@ function collectSelectAggregateSortRefs(columns) {
|
|
|
39439
40273
|
collectAggregateOperandRefs(column.expr, refs);
|
|
39440
40274
|
} else if (column.type === "STRFUNC_COL") {
|
|
39441
40275
|
collectStringFuncAggregateRefs(column.expr, refs);
|
|
40276
|
+
} else if (column.type === "SCALAR_VALUE_COL") {
|
|
40277
|
+
collectScalarAggregateRefs(column.expr, refs);
|
|
39442
40278
|
}
|
|
39443
40279
|
}
|
|
39444
40280
|
return refs;
|
|
@@ -39604,10 +40440,11 @@ function stringFunctionColumnMeta(expr) {
|
|
|
39604
40440
|
function caseResultColumnMeta(result, resolveField2) {
|
|
39605
40441
|
if (result.type === "STRING") return syntheticColumnMeta("string");
|
|
39606
40442
|
if (result.type === "ARRAY") return unsupportedColumnMeta();
|
|
39607
|
-
if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
|
|
40443
|
+
if (result.type === "NUMBER" || result.type === "ARITH" || result.type === "SCALAR_ARITH") return syntheticColumnMeta("number");
|
|
39608
40444
|
if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
|
|
39609
|
-
|
|
39610
|
-
return
|
|
40445
|
+
if (result.type === "FIELD_REF") return resolveField2(aggregateFieldRef(result.field)) ?? unknownStringColumnMeta();
|
|
40446
|
+
if (result.type === "FIELD") return resolveField2(result) ?? unknownStringColumnMeta();
|
|
40447
|
+
return unknownStringColumnMeta();
|
|
39611
40448
|
}
|
|
39612
40449
|
function mergeExpressionColumnMeta(candidates) {
|
|
39613
40450
|
if (candidates.length === 0) return unknownStringColumnMeta();
|
|
@@ -39709,7 +40546,7 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
39709
40546
|
}
|
|
39710
40547
|
} else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
|
|
39711
40548
|
meta3 = syntheticColumnMeta("number");
|
|
39712
|
-
} else if (column.type === "LITERAL_COL") {
|
|
40549
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
39713
40550
|
meta3 = syntheticColumnMeta("string");
|
|
39714
40551
|
} else if (column.type === "STRFUNC_COL") {
|
|
39715
40552
|
meta3 = stringFunctionColumnMeta(column.expr);
|
|
@@ -39798,7 +40635,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
39798
40635
|
validateKlikePushdownPlan(pushdownPlan);
|
|
39799
40636
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
39800
40637
|
const tableConditions = pushdownPlan.joinConditions;
|
|
39801
|
-
const
|
|
40638
|
+
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
40639
|
+
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
39802
40640
|
stmt,
|
|
39803
40641
|
stmt.from,
|
|
39804
40642
|
client,
|
|
@@ -39813,6 +40651,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
39813
40651
|
const parallelJoins = [];
|
|
39814
40652
|
const onOptJoins = [];
|
|
39815
40653
|
for (const join of stmt.joins) {
|
|
40654
|
+
if (constantFalse) {
|
|
40655
|
+
parallelJoins.push({ join, promise: Promise.resolve([]) });
|
|
40656
|
+
continue;
|
|
40657
|
+
}
|
|
39816
40658
|
const jCond = join.table.alias ? tableConditions.get(join.table.alias) ?? null : null;
|
|
39817
40659
|
if (jCond !== null) {
|
|
39818
40660
|
parallelJoins.push({
|
|
@@ -40416,7 +41258,7 @@ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, material
|
|
|
40416
41258
|
if (column.type === "FIELD") meta3 = resolveField2(aggregateFieldRef(column.field));
|
|
40417
41259
|
else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
40418
41260
|
meta3 = syntheticColumnMeta("number");
|
|
40419
|
-
} else if (column.type === "LITERAL_COL") meta3 = syntheticColumnMeta("string");
|
|
41261
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_VALUE_COL") meta3 = syntheticColumnMeta("string");
|
|
40420
41262
|
else if (column.type === "STRFUNC_COL") meta3 = stringFunctionColumnMeta(column.expr);
|
|
40421
41263
|
else if (column.type === "SCALAR_SUBQUERY_COL") meta3 = unknownStringColumnMeta();
|
|
40422
41264
|
else if (column.type === "CASE_COL") {
|
|
@@ -40604,7 +41446,7 @@ var RejectLimitExceededError = class extends Error {
|
|
|
40604
41446
|
this.name = "RejectLimitExceededError";
|
|
40605
41447
|
}
|
|
40606
41448
|
};
|
|
40607
|
-
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
41449
|
+
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber, validateMissingCreateFields = true, includePreErrors = true) {
|
|
40608
41450
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
40609
41451
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
40610
41452
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
@@ -40636,7 +41478,10 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
40636
41478
|
targetFields,
|
|
40637
41479
|
fieldInfos,
|
|
40638
41480
|
statementNumber,
|
|
40639
|
-
numberPrecision
|
|
41481
|
+
numberPrecision,
|
|
41482
|
+
stmt.checkGroups ?? [],
|
|
41483
|
+
validateMissingCreateFields,
|
|
41484
|
+
includePreErrors
|
|
40640
41485
|
);
|
|
40641
41486
|
const columns = [...payloadFields, ...VALIDATION_META_COLUMNS];
|
|
40642
41487
|
const result = {
|
|
@@ -40746,15 +41591,33 @@ async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTable
|
|
|
40746
41591
|
async function materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode) {
|
|
40747
41592
|
if (stmt.type === "UPDATE") return materializeUpdateValidationCandidates(stmt, client, options, cacheContext, tempTables);
|
|
40748
41593
|
let rows;
|
|
41594
|
+
let sourceRows;
|
|
41595
|
+
let evaluationTypes;
|
|
40749
41596
|
if (stmt.type === "INSERT" || stmt.type === "UPSERT") {
|
|
41597
|
+
assertInsertCheckRefs(stmt, stmt.fields);
|
|
41598
|
+
evaluationTypes = new Map(stmt.fields.map((field) => [field, infoByCode.get(field)?.fieldType ?? ""]));
|
|
41599
|
+
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
40750
41600
|
rows = stmt.values.map((row) => row.map(
|
|
40751
41601
|
(value, i) => value.type === "CASE_VALUE" ? evalCaseWhenValue(value.expr, {}, infoByCode.get(stmt.fields[i])?.fieldType) : value
|
|
40752
41602
|
));
|
|
40753
41603
|
} else {
|
|
40754
|
-
const selectResult = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, { ...options, onLimitReached: "error" }, tempTables, cacheContext) : await executeSelect(stmt.select, client, { ...options, onLimitReached: "error" }, cacheContext);
|
|
40755
|
-
|
|
41604
|
+
const selectResult = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, { ...options, onLimitReached: "error" }, tempTables, cacheContext) : await executeSelect(stmt.select, client, { ...options, onLimitReached: "error" }, cacheContext, void 0, true);
|
|
41605
|
+
const hasChecks = (stmt.checkGroups?.length ?? 0) > 0;
|
|
41606
|
+
if (selectResult.columns.length < stmt.fields.length || !hasChecks && selectResult.columns.length !== stmt.fields.length) {
|
|
40756
41607
|
throw new Error(`SELECT \u306E\u5217\u6570\uFF08${selectResult.columns.length}\uFF09\u3068 DML \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`);
|
|
40757
41608
|
}
|
|
41609
|
+
if (hasChecks && new Set(selectResult.columns).size !== selectResult.columns.length) {
|
|
41610
|
+
throw customCheckParseError("CHECK \u4ED8\u304D DML \u30BD\u30FC\u30B9 SELECT \u306E\u51FA\u529B\u540D\u306F\u4E00\u610F\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
|
|
41611
|
+
}
|
|
41612
|
+
assertInsertCheckRefs(stmt, selectResult.columns);
|
|
41613
|
+
sourceRows = selectResult.rows;
|
|
41614
|
+
const meta3 = materializedMetaBySelectResult.get(selectResult);
|
|
41615
|
+
evaluationTypes = new Map(selectResult.columns.map((column) => {
|
|
41616
|
+
const columnMeta = meta3?.get(column);
|
|
41617
|
+
const type = columnMeta?.fieldType ?? (columnMeta?.semantics?.compareMode === "number" || columnMeta?.sortKind === "number" ? "NUMBER" : "SINGLE_LINE_TEXT");
|
|
41618
|
+
return [column, type];
|
|
41619
|
+
}));
|
|
41620
|
+
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
40758
41621
|
rows = selectResult.rows.map((row) => selectResult.columns.map((column) => row[column] ?? ""));
|
|
40759
41622
|
}
|
|
40760
41623
|
const candidates = rows.map((values, index) => ({
|
|
@@ -40763,7 +41626,11 @@ async function materializeValidationCandidates(stmt, operation, client, options,
|
|
|
40763
41626
|
mode: "create",
|
|
40764
41627
|
payload: new Map(stmt.fields.map((field, i) => [field, values[i]])),
|
|
40765
41628
|
preErrors: [],
|
|
40766
|
-
record: {}
|
|
41629
|
+
record: {},
|
|
41630
|
+
evaluationRow: sourceRows?.[index] ?? Object.fromEntries(
|
|
41631
|
+
stmt.fields.map((field, i) => [field, renderValidationValue(values[i])])
|
|
41632
|
+
),
|
|
41633
|
+
evaluationFieldTypes: evaluationTypes
|
|
40767
41634
|
}));
|
|
40768
41635
|
if (stmt.type !== "UPSERT" && stmt.type !== "UPSERT_SELECT") return candidates;
|
|
40769
41636
|
for (const key of stmt.keyFields) {
|
|
@@ -40792,26 +41659,74 @@ async function materializeValidationCandidates(stmt, operation, client, options,
|
|
|
40792
41659
|
});
|
|
40793
41660
|
return candidates;
|
|
40794
41661
|
}
|
|
41662
|
+
function checkRefs(stmt) {
|
|
41663
|
+
return stmt.checkGroups ? collectCheckFieldRefs(stmt.checkGroups) : [];
|
|
41664
|
+
}
|
|
41665
|
+
function assertInsertCheckRefs(stmt, available) {
|
|
41666
|
+
const names = new Set(available);
|
|
41667
|
+
for (const ref of checkRefs(stmt)) {
|
|
41668
|
+
if (ref.tableAlias !== null) {
|
|
41669
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.tableAlias}.${ref.field} \u306F\u3053\u306E\u8A55\u4FA1\u884C\u3067\u306F\u4FEE\u98FE\u3067\u304D\u307E\u305B\u3093`);
|
|
41670
|
+
}
|
|
41671
|
+
if (!names.has(ref.field)) {
|
|
41672
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u8A55\u4FA1\u884C\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
41673
|
+
}
|
|
41674
|
+
}
|
|
41675
|
+
}
|
|
41676
|
+
var CHECK_UNSUPPORTED_COMPARISON_TYPES = /* @__PURE__ */ new Set([
|
|
41677
|
+
"CHECK_BOX",
|
|
41678
|
+
"MULTI_SELECT",
|
|
41679
|
+
"USER_SELECT",
|
|
41680
|
+
"ORGANIZATION_SELECT",
|
|
41681
|
+
"GROUP_SELECT",
|
|
41682
|
+
"FILE",
|
|
41683
|
+
"KSQL_ARRAY"
|
|
41684
|
+
]);
|
|
41685
|
+
function assertCheckComparisonTypes(stmt, types) {
|
|
41686
|
+
if (!stmt.checkGroups) return;
|
|
41687
|
+
for (const ref of collectCheckComparisonFieldRefs(stmt.checkGroups)) {
|
|
41688
|
+
const key = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
|
|
41689
|
+
const type = types.get(key) ?? types.get(ref.field);
|
|
41690
|
+
if (CHECK_UNSUPPORTED_COMPARISON_TYPES.has(type ?? "")) {
|
|
41691
|
+
throw customCheckParseError(`CHECK \u306E\u6BD4\u8F03\u3067\u306F ${type} \u30D5\u30A3\u30FC\u30EB\u30C9 ${key} \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
|
|
41692
|
+
}
|
|
41693
|
+
}
|
|
41694
|
+
}
|
|
40795
41695
|
async function materializeUpdateValidationCandidates(stmt, client, options, cacheContext, tempTables) {
|
|
40796
41696
|
if (stmt.from) return materializeUpdateFromValidationCandidates(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
40797
41697
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
40798
41698
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
41699
|
+
const checkTargetFields = assertUpdateCheckRefs(stmt, fieldTypes);
|
|
41700
|
+
assertCheckComparisonTypes(stmt, updateEvaluationTypes(fieldTypes, stmt.appId));
|
|
40799
41701
|
let records;
|
|
41702
|
+
let evaluationById = /* @__PURE__ */ new Map();
|
|
40800
41703
|
if (hasRowDependentAssignment(stmt)) {
|
|
40801
41704
|
const getParams = updateToGetQueryForArith(stmt);
|
|
40802
|
-
const
|
|
41705
|
+
const fields = [.../* @__PURE__ */ new Set([...getParams.fields, ...checkTargetFields])];
|
|
41706
|
+
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, fields, {
|
|
40803
41707
|
maxRecords: options.maxRecords ?? 1e4,
|
|
40804
41708
|
parallel: options.fetchParallel ?? 1,
|
|
40805
41709
|
onLimit: "error"
|
|
40806
41710
|
});
|
|
41711
|
+
evaluationById = new Map(resolved.records.map((record2) => [Number(record2["$id"]?.value), record2]));
|
|
40807
41712
|
records = updateToPutBatchesArith(stmt, resolved.records, fieldTypes).flatMap((batch) => batch.records);
|
|
40808
41713
|
} else {
|
|
40809
41714
|
const getParams = updateToGetQuery(stmt);
|
|
40810
|
-
|
|
40811
|
-
|
|
40812
|
-
|
|
40813
|
-
|
|
40814
|
-
|
|
41715
|
+
if (checkTargetFields.length > 0) {
|
|
41716
|
+
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [.../* @__PURE__ */ new Set(["$id", ...checkTargetFields])], {
|
|
41717
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
41718
|
+
parallel: options.fetchParallel ?? 1,
|
|
41719
|
+
onLimit: "error"
|
|
41720
|
+
});
|
|
41721
|
+
evaluationById = new Map(resolved.records.map((record2) => [Number(record2["$id"]?.value), record2]));
|
|
41722
|
+
records = updateToPutBatches(stmt, [...evaluationById.keys()], fieldTypes).flatMap((batch) => batch.records);
|
|
41723
|
+
} else {
|
|
41724
|
+
const resolved = await resolveDmlTargetIds(client.getRecords, getParams.app, getParams.query, {
|
|
41725
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
41726
|
+
parallel: options.fetchParallel ?? 1
|
|
41727
|
+
});
|
|
41728
|
+
records = updateToPutBatches(stmt, resolved.ids, fieldTypes).flatMap((batch) => batch.records);
|
|
41729
|
+
}
|
|
40815
41730
|
}
|
|
40816
41731
|
return records.sort((a, b) => a.id - b.id).map((entry, index) => ({
|
|
40817
41732
|
rowNumber: index + 1,
|
|
@@ -40820,13 +41735,48 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
|
|
|
40820
41735
|
payload: new Map([["$id", String(entry.id)], ...stmt.assignments.map((a) => [a.field, entry.record[a.field]?.value ?? ""])]),
|
|
40821
41736
|
preErrors: [],
|
|
40822
41737
|
record: entry.record,
|
|
40823
|
-
targetId: entry.id
|
|
41738
|
+
targetId: entry.id,
|
|
41739
|
+
evaluationRow: updateEvaluationRow(evaluationById.get(entry.id), stmt.appId),
|
|
41740
|
+
evaluationFieldTypes: updateEvaluationTypes(fieldTypes, stmt.appId)
|
|
40824
41741
|
}));
|
|
40825
41742
|
}
|
|
41743
|
+
function assertUpdateCheckRefs(stmt, targetTypes) {
|
|
41744
|
+
if (stmt.from) return [];
|
|
41745
|
+
const fields = /* @__PURE__ */ new Set();
|
|
41746
|
+
for (const ref of checkRefs(stmt)) {
|
|
41747
|
+
if (ref.tableAlias !== null && ref.tableAlias.toLowerCase() !== `app${stmt.appId}`.toLowerCase()) {
|
|
41748
|
+
throw customCheckParseError(`CHECK \u306E\u4FEE\u98FE\u5B50 ${ref.tableAlias} \u306F\u66F4\u65B0\u5148 APP${stmt.appId} \u3067\u306F\u3042\u308A\u307E\u305B\u3093`);
|
|
41749
|
+
}
|
|
41750
|
+
if (ref.field !== "$id" && !targetTypes.has(ref.field)) {
|
|
41751
|
+
throw customCheckParseError(`CHECK \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
41752
|
+
}
|
|
41753
|
+
fields.add(ref.field);
|
|
41754
|
+
}
|
|
41755
|
+
return [...fields];
|
|
41756
|
+
}
|
|
41757
|
+
function updateEvaluationRow(record2, appId) {
|
|
41758
|
+
if (!record2) return {};
|
|
41759
|
+
const plain = flatten(record2, null);
|
|
41760
|
+
return Object.fromEntries([
|
|
41761
|
+
...Object.entries(plain),
|
|
41762
|
+
...Object.entries(plain).map(([field, value]) => [`APP${appId}.${field}`, value])
|
|
41763
|
+
]);
|
|
41764
|
+
}
|
|
41765
|
+
function updateEvaluationTypes(types, appId) {
|
|
41766
|
+
return new Map([
|
|
41767
|
+
...types,
|
|
41768
|
+
...[...types].map(([field, type]) => [`APP${appId}.${field}`, type]),
|
|
41769
|
+
["$id", "RECORD_NUMBER"],
|
|
41770
|
+
[`APP${appId}.$id`, "RECORD_NUMBER"]
|
|
41771
|
+
]);
|
|
41772
|
+
}
|
|
40826
41773
|
async function materializeUpdateFromValidationCandidates(stmt, from, client, options, cacheContext, tempTables) {
|
|
41774
|
+
const scope = await resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables);
|
|
41775
|
+
assertCheckComparisonTypes(stmt, scope.evaluationTypes);
|
|
40827
41776
|
const matched = await resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables);
|
|
40828
41777
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
40829
41778
|
const records = updateFromToPutBatches(stmt, matched, fieldTypes).flatMap((batch) => batch.records);
|
|
41779
|
+
const matchedById = new Map(matched.map((pair) => [Number(pair.target["$id"]?.value), pair]));
|
|
40830
41780
|
return records.sort((a, b) => a.id - b.id).map((entry, index) => ({
|
|
40831
41781
|
rowNumber: index + 1,
|
|
40832
41782
|
operation: "UPDATE",
|
|
@@ -40834,7 +41784,9 @@ async function materializeUpdateFromValidationCandidates(stmt, from, client, opt
|
|
|
40834
41784
|
payload: new Map([["$id", String(entry.id)], ...stmt.assignments.map((a) => [a.field, entry.record[a.field]?.value ?? ""])]),
|
|
40835
41785
|
preErrors: [],
|
|
40836
41786
|
record: entry.record,
|
|
40837
|
-
targetId: entry.id
|
|
41787
|
+
targetId: entry.id,
|
|
41788
|
+
evaluationRow: updateFromEvaluationRow(matchedById.get(entry.id), stmt.appId, from.alias),
|
|
41789
|
+
evaluationFieldTypes: scope.evaluationTypes
|
|
40838
41790
|
}));
|
|
40839
41791
|
}
|
|
40840
41792
|
var UPDATE_FROM_KEY_CHUNK_SIZE = UPSERT_IN_CHUNK_SIZE;
|
|
@@ -40848,7 +41800,8 @@ var UPDATE_FROM_UNSUPPORTED_SOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
|
40848
41800
|
]);
|
|
40849
41801
|
async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables) {
|
|
40850
41802
|
const joinKind = await resolveUpdateFromTargetJoinKind(stmt, from, client, cacheContext);
|
|
40851
|
-
const
|
|
41803
|
+
const checkScope = await resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables);
|
|
41804
|
+
const sourceFields = [...new Set(stmt.assignments.filter((a) => a.value.type === "SOURCE_FIELD").map((a) => a.value.type === "SOURCE_FIELD" ? a.value.field : "").concat(checkScope.sourceFields))];
|
|
40852
41805
|
const requiredSourceFields = [.../* @__PURE__ */ new Set([from.joinKeyField, ...sourceFields])];
|
|
40853
41806
|
const sourceRows = await loadUpdateFromSourceRows(
|
|
40854
41807
|
from,
|
|
@@ -40874,8 +41827,8 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
40874
41827
|
}
|
|
40875
41828
|
if (sourceByKey.size === 0) return [];
|
|
40876
41829
|
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
40877
|
-
const targetFields = collectUpdateFromTargetFields(stmt);
|
|
40878
|
-
const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter }).query;
|
|
41830
|
+
const targetFields = [.../* @__PURE__ */ new Set([...collectUpdateFromTargetFields(stmt), ...checkScope.targetFields])];
|
|
41831
|
+
const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter, checkGroups: void 0 }).query;
|
|
40879
41832
|
const targetRecords = [];
|
|
40880
41833
|
const seenTargetIds = /* @__PURE__ */ new Set();
|
|
40881
41834
|
let fetchedTargetCount = 0;
|
|
@@ -40993,7 +41946,54 @@ function normalizeUpdateFromJoinKey(raw, kind, side) {
|
|
|
40993
41946
|
}
|
|
40994
41947
|
return JSON.stringify(decimal);
|
|
40995
41948
|
}
|
|
41949
|
+
async function executeCheckedPlainDml(stmt, client, options, cacheContext, tempTables) {
|
|
41950
|
+
const prepared = await prepareDmlValidation(
|
|
41951
|
+
stmt,
|
|
41952
|
+
client,
|
|
41953
|
+
options,
|
|
41954
|
+
cacheContext,
|
|
41955
|
+
tempTables,
|
|
41956
|
+
1,
|
|
41957
|
+
false,
|
|
41958
|
+
false
|
|
41959
|
+
);
|
|
41960
|
+
if (prepared.result.errors.length > 0) {
|
|
41961
|
+
const first = prepared.result.errors[0];
|
|
41962
|
+
throw new Error(
|
|
41963
|
+
`DmlValidationError: ${first["$err_code"]} ${first["$err_message"]} (row=${first["$err_row"]}, field=${first["$err_field"]})`
|
|
41964
|
+
);
|
|
41965
|
+
}
|
|
41966
|
+
const candidates = prepared.candidates;
|
|
41967
|
+
const confirmOperation = stmt.type.startsWith("INSERT") ? "INSERT" : "UPDATE";
|
|
41968
|
+
if (options.confirm && candidates.length > 0) {
|
|
41969
|
+
const ok = await options.confirm(candidates.length, confirmOperation);
|
|
41970
|
+
if (!ok) throw new OperationCancelledError(confirmOperation, candidates.length);
|
|
41971
|
+
}
|
|
41972
|
+
if (stmt.type === "INSERT" || stmt.type === "INSERT_SELECT") {
|
|
41973
|
+
const createdIds = [];
|
|
41974
|
+
for (let i = 0; i < candidates.length; i += 100) {
|
|
41975
|
+
const response = await client.postRecords({ app: stmt.appId, records: candidates.slice(i, i + 100).map((c) => c.record) });
|
|
41976
|
+
createdIds.push(response.ids);
|
|
41977
|
+
}
|
|
41978
|
+
return { type: "INSERT", createdIds, insertedCount: createdIds.flat().length };
|
|
41979
|
+
}
|
|
41980
|
+
if (stmt.type === "UPDATE") {
|
|
41981
|
+
const updates2 = candidates.map((candidate) => ({ id: candidate.targetId, record: candidate.record }));
|
|
41982
|
+
for (let i = 0; i < updates2.length; i += 100) await client.putRecords({ app: stmt.appId, records: updates2.slice(i, i + 100) });
|
|
41983
|
+
return { type: "UPDATE", updatedCount: updates2.length };
|
|
41984
|
+
}
|
|
41985
|
+
const inserts = candidates.filter((candidate) => candidate.mode === "create");
|
|
41986
|
+
const updates = candidates.filter((candidate) => candidate.mode === "update").map((candidate) => ({ id: candidate.targetId, record: candidate.record }));
|
|
41987
|
+
let insertedCount = 0;
|
|
41988
|
+
for (let i = 0; i < inserts.length; i += 100) {
|
|
41989
|
+
const response = await client.postRecords({ app: stmt.appId, records: inserts.slice(i, i + 100).map((c) => c.record) });
|
|
41990
|
+
insertedCount += response.ids.length;
|
|
41991
|
+
}
|
|
41992
|
+
for (let i = 0; i < updates.length; i += 100) await client.putRecords({ app: stmt.appId, records: updates.slice(i, i + 100) });
|
|
41993
|
+
return { type: "UPSERT", insertedCount, updatedCount: updates.length };
|
|
41994
|
+
}
|
|
40996
41995
|
async function executeInsert(stmt, client, options, cacheContext) {
|
|
41996
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext);
|
|
40997
41997
|
if (stmt.subtableCode) {
|
|
40998
41998
|
return executeInsertSubtable(stmt, client, options, cacheContext);
|
|
40999
41999
|
}
|
|
@@ -41014,6 +42014,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
|
|
|
41014
42014
|
};
|
|
41015
42015
|
}
|
|
41016
42016
|
async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
42017
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
|
|
41017
42018
|
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41018
42019
|
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
41019
42020
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
@@ -41051,6 +42052,27 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
41051
42052
|
};
|
|
41052
42053
|
}
|
|
41053
42054
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
42055
|
+
if (stmt.checkGroups?.length && isConstantFalseWhere(stmt.where)) {
|
|
42056
|
+
const fieldInfos2 = await loadWritableTopLevelDmlFields(
|
|
42057
|
+
stmt.appId,
|
|
42058
|
+
stmt.assignments.map((assignment) => assignment.field),
|
|
42059
|
+
client,
|
|
42060
|
+
cacheContext
|
|
42061
|
+
);
|
|
42062
|
+
await loadNumberPrecisionForTargets(
|
|
42063
|
+
stmt.appId,
|
|
42064
|
+
stmt.assignments.map((assignment) => assignment.field),
|
|
42065
|
+
fieldInfos2,
|
|
42066
|
+
client,
|
|
42067
|
+
cacheContext
|
|
42068
|
+
);
|
|
42069
|
+
const fieldTypes2 = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
42070
|
+
assertUpdateCheckRefs(stmt, fieldTypes2);
|
|
42071
|
+
assertCheckComparisonTypes(stmt, updateEvaluationTypes(fieldTypes2, stmt.appId));
|
|
42072
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
42073
|
+
return { type: "UPDATE", updatedCount: 0 };
|
|
42074
|
+
}
|
|
42075
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, tempTables);
|
|
41054
42076
|
if (stmt.subtableCode) {
|
|
41055
42077
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
41056
42078
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
@@ -41070,6 +42092,7 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
41070
42092
|
cacheContext
|
|
41071
42093
|
);
|
|
41072
42094
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
42095
|
+
if (isConstantFalseWhere(stmt.where)) return { type: "UPDATE", updatedCount: 0 };
|
|
41073
42096
|
if (stmt.from != null) {
|
|
41074
42097
|
return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
41075
42098
|
}
|
|
@@ -41151,6 +42174,7 @@ function collectUpdateFromTargetFields(stmt) {
|
|
|
41151
42174
|
}
|
|
41152
42175
|
async function executeDelete(stmt, client, options, cacheContext) {
|
|
41153
42176
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
42177
|
+
if (isConstantFalseWhere(stmt.where)) return { type: "DELETE", deletedCount: 0 };
|
|
41154
42178
|
if (stmt.subtableCode) {
|
|
41155
42179
|
return executeDeleteSubtable(stmt, client, options, cacheContext);
|
|
41156
42180
|
}
|
|
@@ -41176,6 +42200,7 @@ async function executeDelete(stmt, client, options, cacheContext) {
|
|
|
41176
42200
|
return { type: "DELETE", deletedCount: ids.length };
|
|
41177
42201
|
}
|
|
41178
42202
|
async function executeUpsert(stmt, client, options, cacheContext) {
|
|
42203
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext);
|
|
41179
42204
|
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41180
42205
|
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
41181
42206
|
const toInsert = [];
|
|
@@ -41532,6 +42557,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
|
|
|
41532
42557
|
cacheContext
|
|
41533
42558
|
);
|
|
41534
42559
|
const reorderFields = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
42560
|
+
if (isConstantFalseWhere(stmt.where)) return { type: "REORDER", reorderedParentCount: 0 };
|
|
41535
42561
|
const reorderSemanticsByCode = new Map(reorderFields.map((field) => [
|
|
41536
42562
|
field.code,
|
|
41537
42563
|
field.semantics ?? resolveFieldSemantics(field)
|
|
@@ -41610,6 +42636,7 @@ function evalOrderKeyForRow(key, row) {
|
|
|
41610
42636
|
}
|
|
41611
42637
|
}
|
|
41612
42638
|
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
42639
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
|
|
41613
42640
|
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41614
42641
|
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
41615
42642
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
@@ -41757,6 +42784,8 @@ function collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCa
|
|
|
41757
42784
|
}));
|
|
41758
42785
|
break;
|
|
41759
42786
|
}
|
|
42787
|
+
case "BOOLEAN":
|
|
42788
|
+
break;
|
|
41760
42789
|
}
|
|
41761
42790
|
}
|
|
41762
42791
|
async function resolveSetSubqueries(assignments, client, options, cacheContext) {
|
|
@@ -41794,9 +42823,11 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
41794
42823
|
pending.forEach(([i], idx) => cache.set(i, values[idx]));
|
|
41795
42824
|
return cache;
|
|
41796
42825
|
}
|
|
42826
|
+
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
41797
42827
|
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords2 = 1e4) {
|
|
41798
42828
|
const fieldApps = /* @__PURE__ */ new Set();
|
|
41799
42829
|
const processStatusApps = /* @__PURE__ */ new Set();
|
|
42830
|
+
const numberPrecisionApps = /* @__PURE__ */ new Set();
|
|
41800
42831
|
const tracedClient = {
|
|
41801
42832
|
...client,
|
|
41802
42833
|
getFields: async (appId) => {
|
|
@@ -41806,6 +42837,10 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
41806
42837
|
getProcessStatuses: async (appId) => {
|
|
41807
42838
|
processStatusApps.add(appId);
|
|
41808
42839
|
return client.getProcessStatuses(appId);
|
|
42840
|
+
},
|
|
42841
|
+
getNumberPrecision: async (appId) => {
|
|
42842
|
+
numberPrecisionApps.add(appId);
|
|
42843
|
+
return client.getNumberPrecision(appId);
|
|
41809
42844
|
}
|
|
41810
42845
|
};
|
|
41811
42846
|
const capabilities = /* @__PURE__ */ new Map();
|
|
@@ -41854,6 +42889,51 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
41854
42889
|
}));
|
|
41855
42890
|
}
|
|
41856
42891
|
}
|
|
42892
|
+
} else if (typed["type"] === "VALIDATE") {
|
|
42893
|
+
const validate = node;
|
|
42894
|
+
fieldApps.add(validate.appId);
|
|
42895
|
+
const fields = await getFieldsCached(validate.appId, tracedClient, cacheContext);
|
|
42896
|
+
const infoByCode = new Map(fields.map((field) => [field.code, field]));
|
|
42897
|
+
const targets = resolveExistingValidationTargets(validate, fields);
|
|
42898
|
+
const checks = collectCheckFieldRefs(validate.checkGroups ?? []);
|
|
42899
|
+
const whereFields = collectValidateWhereFields(validate.where);
|
|
42900
|
+
for (const ref of checks) {
|
|
42901
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
|
|
42902
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${validate.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
42903
|
+
}
|
|
42904
|
+
}
|
|
42905
|
+
for (const field of whereFields) {
|
|
42906
|
+
if (field !== "$id" && !infoByCode.has(field)) {
|
|
42907
|
+
throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${validate.appId}.`);
|
|
42908
|
+
}
|
|
42909
|
+
}
|
|
42910
|
+
const types = new Map(fields.map((field) => [field.code, field.fieldType]));
|
|
42911
|
+
types.set("$id", "RECORD_NUMBER");
|
|
42912
|
+
assertCheckComparisonTypes(validate, types);
|
|
42913
|
+
const capability = classifyWhereCapability(validate.where, (field) => field.field === "$id" ? resolveFieldSemantics({ fieldType: "__ID__" }) : infoByCode.get(field.field)?.semantics ?? (infoByCode.has(field.field) ? resolveFieldSemantics(infoByCode.get(field.field)) : void 0));
|
|
42914
|
+
if (capability.capability === "UNSUPPORTED") {
|
|
42915
|
+
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
42916
|
+
}
|
|
42917
|
+
const fieldTypes = new Map(fields.map((field) => [field.code, field.fieldType]));
|
|
42918
|
+
const fieldOptions = new Map(fields.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
42919
|
+
const prefilter = validate.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? validate.where : extractSafePushdownLeaves(validate.where, {
|
|
42920
|
+
allowUnqualifiedFields: true,
|
|
42921
|
+
fieldTypes,
|
|
42922
|
+
fieldOptions,
|
|
42923
|
+
allowKlike: false
|
|
42924
|
+
});
|
|
42925
|
+
const needsPrecision = targets.some((field) => field.fieldType === "NUMBER");
|
|
42926
|
+
if (needsPrecision) {
|
|
42927
|
+
numberPrecisionApps.add(validate.appId);
|
|
42928
|
+
await getNumberPrecisionCached(validate.appId, tracedClient, cacheContext);
|
|
42929
|
+
}
|
|
42930
|
+
validateExplainInfo.set(validate, {
|
|
42931
|
+
targetFields: targets.map((field) => field.code),
|
|
42932
|
+
fetchFields: [.../* @__PURE__ */ new Set(["$id", ...targets.map((field) => field.code), ...whereFields, ...checks.map((ref) => ref.field)])],
|
|
42933
|
+
capability,
|
|
42934
|
+
prefilter,
|
|
42935
|
+
numberPrecision: needsPrecision
|
|
42936
|
+
});
|
|
41857
42937
|
} else if (typed["type"] === "UPDATE" || typed["type"] === "DELETE") {
|
|
41858
42938
|
fieldApps.add(node.appId);
|
|
41859
42939
|
await assertDmlWhereCapability(
|
|
@@ -41884,12 +42964,13 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
41884
42964
|
}));
|
|
41885
42965
|
}
|
|
41886
42966
|
}
|
|
41887
|
-
return { capabilities, orderPlans, fieldApps, processStatusApps };
|
|
42967
|
+
return { capabilities, orderPlans, fieldApps, processStatusApps, numberPrecisionApps };
|
|
41888
42968
|
}
|
|
41889
42969
|
function explainMetadataLines(analysis) {
|
|
41890
42970
|
return [
|
|
41891
42971
|
...[...analysis.fieldApps].sort((a, b) => a - b).map((appId) => ` metadata API: form definition APP${appId}`),
|
|
41892
|
-
...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`)
|
|
42972
|
+
...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`),
|
|
42973
|
+
...[...analysis.numberPrecisionApps].sort((a, b) => a - b).map((appId) => ` metadata API: number precision APP${appId}`)
|
|
41893
42974
|
];
|
|
41894
42975
|
}
|
|
41895
42976
|
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords2 = 1e4, cursorMaxActive2 = 2) {
|
|
@@ -41900,7 +42981,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
41900
42981
|
const plans = [];
|
|
41901
42982
|
for (let i = 0; i < statements.length; i++) {
|
|
41902
42983
|
const stmt = statements[i];
|
|
41903
|
-
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr:
|
|
42984
|
+
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveBatchVariableReferences(stmt.expr, variables) } : stmt : resolveBatchVariableReferences(stmt, variables);
|
|
41904
42985
|
validateKlikeStatement(planStmt);
|
|
41905
42986
|
const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords2);
|
|
41906
42987
|
const statementPlan = addCursorConcurrency(buildBatchStatementPlan(
|
|
@@ -41916,7 +42997,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
41916
42997
|
plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
|
|
41917
42998
|
});
|
|
41918
42999
|
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
41919
|
-
variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
|
|
43000
|
+
variables.set(stmt.name, stmt.type === "SET_VARIABLE" && stmt.expr.type === "ARRAY" ? { type: "array", elements: stmt.expr.elements.map((element) => ({ type: "string", value: element.value })) } : { type: "string", value: `@${stmt.name}` });
|
|
41920
43001
|
}
|
|
41921
43002
|
}
|
|
41922
43003
|
return { statementCount: statements.length, statements: plans };
|
|
@@ -42051,8 +43132,30 @@ function buildExplainPlan(query, label, capabilities, orderPlans) {
|
|
|
42051
43132
|
if (query.type === "UPDATE") return buildUpdatePlan(query, label, capabilities, orderPlans);
|
|
42052
43133
|
if (query.type === "DELETE") return buildDeletePlan(query, label);
|
|
42053
43134
|
if (query.type === "REORDER") return buildReorderPlan(query, label);
|
|
43135
|
+
if (query.type === "VALIDATE") return buildValidatePlan(query, label);
|
|
42054
43136
|
return buildSelectPlan(query, label, capabilities, orderPlans);
|
|
42055
43137
|
}
|
|
43138
|
+
function buildValidatePlan(stmt, label) {
|
|
43139
|
+
const info = validateExplainInfo.get(stmt);
|
|
43140
|
+
const lines = [];
|
|
43141
|
+
if (label) lines.push(label);
|
|
43142
|
+
lines.push(`VALIDATE APP${stmt.appId}`);
|
|
43143
|
+
lines.push(" operation: read-only existing-record constraint audit (writesKintone=false)");
|
|
43144
|
+
lines.push(" fetch API: GET records via offset + $id keyset paging (Cursor API unused)");
|
|
43145
|
+
lines.push(" complete input: required (onLimit=truncate disabled)");
|
|
43146
|
+
if (!info) {
|
|
43147
|
+
lines.push(" metadata: form definition required; number precision required for NUMBER targets");
|
|
43148
|
+
return lines;
|
|
43149
|
+
}
|
|
43150
|
+
lines.push(` WHERE capability: ${info.capability.capability}`);
|
|
43151
|
+
lines.push(` kintone query: ${info.prefilter === null ? "(\u5168\u4EF6\u53D6\u5F97)" : whereToKintone(info.prefilter)}`);
|
|
43152
|
+
lines.push(` audit fields: ${info.targetFields.length === 0 ? "(\u306A\u3057)" : info.targetFields.join(", ")}`);
|
|
43153
|
+
lines.push(` fetch fields: ${info.fetchFields.join(", ")}`);
|
|
43154
|
+
lines.push(` number precision: ${info.numberPrecision ? "required" : "not required"}`);
|
|
43155
|
+
lines.push(" local checks: original WHERE re-evaluation + built-in constraints + CHECK groups");
|
|
43156
|
+
lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
|
|
43157
|
+
return lines;
|
|
43158
|
+
}
|
|
42056
43159
|
function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
42057
43160
|
const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
42058
43161
|
const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
@@ -42064,6 +43167,12 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
42064
43167
|
const lines = [];
|
|
42065
43168
|
if (label) lines.push(label);
|
|
42066
43169
|
lines.push(` mode: ${mode}`);
|
|
43170
|
+
if (isConstantFalseWhere(stmt.where)) {
|
|
43171
|
+
lines.push(" predicate: constant false");
|
|
43172
|
+
lines.push(" records API access: none");
|
|
43173
|
+
lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
|
|
43174
|
+
return lines;
|
|
43175
|
+
}
|
|
42067
43176
|
if (orderPlan) {
|
|
42068
43177
|
lines.push(` order plan: ${orderPlan.kind}`);
|
|
42069
43178
|
if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
|
|
@@ -42211,6 +43320,8 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
|
42211
43320
|
break;
|
|
42212
43321
|
case "NULL_CHECK":
|
|
42213
43322
|
break;
|
|
43323
|
+
case "BOOLEAN":
|
|
43324
|
+
break;
|
|
42214
43325
|
}
|
|
42215
43326
|
};
|
|
42216
43327
|
visitWhere(stmt.where);
|
|
@@ -42263,7 +43374,7 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
|
42263
43374
|
} else {
|
|
42264
43375
|
lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
|
|
42265
43376
|
}
|
|
42266
|
-
lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
|
|
43377
|
+
lines.push(isConstantFalseWhere(stmt.where) ? " api: metadata validation only (records API access: none)" : ` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
|
|
42267
43378
|
const setTypes = [];
|
|
42268
43379
|
if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
|
|
42269
43380
|
if (isStringFunc) setTypes.push("\u6587\u5B57\u5217\u95A2\u6570 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A55\u4FA1\uFF09");
|
|
@@ -42294,7 +43405,7 @@ function buildDeletePlan(stmt, label) {
|
|
|
42294
43405
|
lines.push(` [DELETE]`);
|
|
42295
43406
|
lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
|
|
42296
43407
|
lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
|
|
42297
|
-
lines.push(` api: GET /k/v1/records.json \u2192 DELETE /k/v1/records.json`);
|
|
43408
|
+
lines.push(isConstantFalseWhere(stmt.where) ? " api: metadata validation only (records API access: none)" : ` api: GET /k/v1/records.json \u2192 DELETE /k/v1/records.json`);
|
|
42298
43409
|
return lines;
|
|
42299
43410
|
}
|
|
42300
43411
|
function buildUpsertPlan(stmt, label) {
|
|
@@ -42334,7 +43445,7 @@ function buildReorderPlan(stmt, label) {
|
|
|
42334
43445
|
` table: ${target}`,
|
|
42335
43446
|
` scope: ${scope}`,
|
|
42336
43447
|
` by: ${byStr}`,
|
|
42337
|
-
` api: GET /k/v1/records.json\uFF08\u884C ID \u53D6\u5F97\uFF09\u2192 PUT /k/v1/records.json\uFF08id \u914D\u5217\u306E\u307F\u9001\u4FE1\uFF09`
|
|
43448
|
+
isConstantFalseWhere(stmt.where) ? ` api: metadata validation only (records API access: none)` : ` api: GET /k/v1/records.json\uFF08\u884C ID \u53D6\u5F97\uFF09\u2192 PUT /k/v1/records.json\uFF08id \u914D\u5217\u306E\u307F\u9001\u4FE1\uFF09`
|
|
42338
43449
|
];
|
|
42339
43450
|
if (!stmt.all && stmt.where) {
|
|
42340
43451
|
lines.splice(5, 0, ` where: ${safeWhereToKintone(stmt.where)}`);
|
|
@@ -42346,6 +43457,7 @@ function formatOrderByItem(item) {
|
|
|
42346
43457
|
return `${key} ${item.direction}`;
|
|
42347
43458
|
}
|
|
42348
43459
|
function safeWhereToKintone(where) {
|
|
43460
|
+
if (where.type === "BOOLEAN") return where.value ? "TRUE" : "FALSE (constant)";
|
|
42349
43461
|
try {
|
|
42350
43462
|
return whereToKintone(where);
|
|
42351
43463
|
} catch {
|
|
@@ -42357,6 +43469,7 @@ function collectArithRefFields(stmt) {
|
|
|
42357
43469
|
for (const { value } of stmt.assignments) {
|
|
42358
43470
|
if (value.type === "ARITH") collectArithNodeRefs(value, refs);
|
|
42359
43471
|
if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
|
|
43472
|
+
if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") collectScalarNodeRefs(value, refs);
|
|
42360
43473
|
}
|
|
42361
43474
|
return [...refs];
|
|
42362
43475
|
}
|
|
@@ -42371,10 +43484,74 @@ function collectArithNodeRefs(node, out) {
|
|
|
42371
43484
|
}
|
|
42372
43485
|
if (node.type === "STRING_FUNC") {
|
|
42373
43486
|
for (const arg of node.args) {
|
|
42374
|
-
if (arg.type !== "
|
|
42375
|
-
|
|
43487
|
+
if (arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") collectScalarNodeRefs(arg, out);
|
|
43488
|
+
}
|
|
43489
|
+
}
|
|
43490
|
+
}
|
|
43491
|
+
async function resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables) {
|
|
43492
|
+
const refs = checkRefs(stmt);
|
|
43493
|
+
const targetTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
43494
|
+
const sourceTableName = from.cteName;
|
|
43495
|
+
const sourceTypes = sourceTableName !== null ? new Map((tempTables?.get(sourceTableName)?.columns ?? []).map((column) => [
|
|
43496
|
+
column,
|
|
43497
|
+
tempTables?.get(sourceTableName)?.columnMeta?.get(column)?.fieldType ?? (tempTables?.get(sourceTableName)?.columnMeta?.get(column)?.semantics?.compareMode === "number" ? "NUMBER" : "SINGLE_LINE_TEXT")
|
|
43498
|
+
])) : await getFieldTypeMap(from.appId, client, cacheContext);
|
|
43499
|
+
const targetFields = /* @__PURE__ */ new Set();
|
|
43500
|
+
const sourceFields = /* @__PURE__ */ new Set();
|
|
43501
|
+
for (const ref of refs) {
|
|
43502
|
+
if (ref.tableAlias !== null) {
|
|
43503
|
+
if (ref.tableAlias.toLowerCase() === `app${stmt.appId}`.toLowerCase()) {
|
|
43504
|
+
if (ref.field !== "$id" && !targetTypes.has(ref.field)) throw customCheckParseError(`CHECK \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
43505
|
+
targetFields.add(ref.field);
|
|
43506
|
+
} else if (ref.tableAlias.toLowerCase() === from.alias.toLowerCase()) {
|
|
43507
|
+
if (ref.field !== "$id" && !sourceTypes.has(ref.field)) throw customCheckParseError(`CHECK \u306E\u30BD\u30FC\u30B9\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
43508
|
+
sourceFields.add(ref.field);
|
|
43509
|
+
} else {
|
|
43510
|
+
throw customCheckParseError(`CHECK \u306E\u4FEE\u98FE\u5B50 ${ref.tableAlias} \u306F\u66F4\u65B0\u5148\u307E\u305F\u306F FROM alias \u3067\u306F\u3042\u308A\u307E\u305B\u3093`);
|
|
42376
43511
|
}
|
|
43512
|
+
continue;
|
|
42377
43513
|
}
|
|
43514
|
+
const inTarget = ref.field === "$id" || targetTypes.has(ref.field);
|
|
43515
|
+
const inSource = ref.field === "$id" || sourceTypes.has(ref.field);
|
|
43516
|
+
if (!inTarget) {
|
|
43517
|
+
throw customCheckParseError(`UPDATE FROM \u306E CHECK \u3067\u306F\u30BD\u30FC\u30B9\u5217 ${ref.field} \u3092\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`);
|
|
43518
|
+
}
|
|
43519
|
+
if (inSource) {
|
|
43520
|
+
throw customCheckParseError(`UPDATE FROM \u306E CHECK \u306E\u975E\u4FEE\u98FE\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u66D6\u6627\u3067\u3059`);
|
|
43521
|
+
}
|
|
43522
|
+
targetFields.add(ref.field);
|
|
43523
|
+
}
|
|
43524
|
+
const evaluationTypes = /* @__PURE__ */ new Map();
|
|
43525
|
+
for (const [field, type] of targetTypes) {
|
|
43526
|
+
evaluationTypes.set(field, type);
|
|
43527
|
+
evaluationTypes.set(`APP${stmt.appId}.${field}`, type);
|
|
43528
|
+
}
|
|
43529
|
+
evaluationTypes.set("$id", "RECORD_NUMBER");
|
|
43530
|
+
evaluationTypes.set(`APP${stmt.appId}.$id`, "RECORD_NUMBER");
|
|
43531
|
+
for (const [field, type] of sourceTypes) evaluationTypes.set(`${from.alias}.${field}`, type);
|
|
43532
|
+
return { targetFields: [...targetFields], sourceFields: [...sourceFields], evaluationTypes };
|
|
43533
|
+
}
|
|
43534
|
+
function updateFromEvaluationRow(pair, appId, sourceAlias) {
|
|
43535
|
+
if (!pair) return {};
|
|
43536
|
+
const target = flatten(pair.target, null);
|
|
43537
|
+
return Object.fromEntries([
|
|
43538
|
+
...Object.entries(target),
|
|
43539
|
+
...Object.entries(target).map(([field, value]) => [`APP${appId}.${field}`, value]),
|
|
43540
|
+
...Object.entries(pair.source).map(([field, value]) => [`${sourceAlias}.${field}`, value])
|
|
43541
|
+
]);
|
|
43542
|
+
}
|
|
43543
|
+
function collectScalarNodeRefs(node, out) {
|
|
43544
|
+
if (node.type === "FIELD") {
|
|
43545
|
+
out.add(node.tableAlias ? `${node.tableAlias}.${node.field}` : node.field);
|
|
43546
|
+
return;
|
|
43547
|
+
}
|
|
43548
|
+
if (node.type === "STRING_FUNC") {
|
|
43549
|
+
for (const arg of node.args) if (arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") collectScalarNodeRefs(arg, out);
|
|
43550
|
+
return;
|
|
43551
|
+
}
|
|
43552
|
+
if (node.type === "SCALAR_ARITH" || node.type === "CONCAT_OP") {
|
|
43553
|
+
collectScalarNodeRefs(node.left, out);
|
|
43554
|
+
collectScalarNodeRefs(node.right, out);
|
|
42378
43555
|
}
|
|
42379
43556
|
}
|
|
42380
43557
|
function formatAssignment(a) {
|
|
@@ -44520,7 +45697,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
44520
45697
|
profile: input.profile,
|
|
44521
45698
|
maxRecords: input.maxRecords,
|
|
44522
45699
|
fetchParallel: input.fetchParallel,
|
|
44523
|
-
onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
|
|
45700
|
+
onLimit: validation.containsValidationOnly || validation.statements.some((s) => s.statementType === "VALIDATE") ? "error" : input.onLimit,
|
|
44524
45701
|
timeout: input.timeout,
|
|
44525
45702
|
tempTableMaxRows: input.tempTableMaxRows,
|
|
44526
45703
|
cursorMaxActive: input.cursorMaxActive
|
|
@@ -44567,7 +45744,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
44567
45744
|
profile: input.profile,
|
|
44568
45745
|
maxRecords: input.maxRecords,
|
|
44569
45746
|
fetchParallel: input.fetchParallel,
|
|
44570
|
-
onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
|
|
45747
|
+
onLimit: validation.containsValidationOnly || validation.statements.some((s) => s.statementType === "VALIDATE") ? "error" : input.onLimit,
|
|
44571
45748
|
timeout: input.timeout,
|
|
44572
45749
|
cursorMaxActive: input.cursorMaxActive
|
|
44573
45750
|
});
|
|
@@ -44869,7 +46046,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
44869
46046
|
var profile = external_exports.string().min(1).describe("kintone connection profile name from ksql.config.json (default: the server's default profile).").optional();
|
|
44870
46047
|
var maxRecords = external_exports.number().int().positive().describe("Maximum records fetched per SELECT (default 500).").optional();
|
|
44871
46048
|
var fetchParallel = external_exports.number().int().min(1).max(10).describe("Number of parallel kintone record-fetch requests (1-10).").optional();
|
|
44872
|
-
var onLimit = external_exports.enum(["error", "truncate"]).describe("Behavior when maxRecords is exceeded: 'error' rejects, 'truncate' returns the first maxRecords rows (default 'error'). Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. VALIDATE ONLY always
|
|
46049
|
+
var onLimit = external_exports.enum(["error", "truncate"]).describe("Behavior when maxRecords is exceeded: 'error' rejects, 'truncate' returns the first maxRecords rows (default 'error'). Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. Leading VALIDATE and DML VALIDATE ONLY always override 'truncate' to 'error'.").optional();
|
|
44873
46050
|
var tempTableMaxRows = external_exports.number().int().positive().describe("Per-temp-table cap on materialized rows for CREATE TEMP TABLE ... AS SELECT (default 10000). Overflow always errors \u2014 'truncate' never applies to temp tables, so downstream statements never see silently truncated data. Raising this increases memory use (up to 16 temp tables per batch); prefer narrowing the SELECT with WHERE.").optional();
|
|
44874
46051
|
var timeout = external_exports.number().int().positive().describe("Request timeout in milliseconds. For multi-statement batches this also acts as the total batch deadline.").optional();
|
|
44875
46052
|
var cursorMaxActive = external_exports.number().int().min(1).max(5).describe("Maximum active Cursor API handles per kintone host in this process (1-5, default 2). Later calls update the host limit; lowering it keeps existing cursors and delays new ones until active usage falls below the new limit. Create/Get are never automatically retried; capacity waits up to 30 seconds.").optional();
|
|
@@ -44998,7 +46175,7 @@ Options:
|
|
|
44998
46175
|
-h, --help Show help
|
|
44999
46176
|
`);
|
|
45000
46177
|
}
|
|
45001
|
-
var SERVER_VERSION = true ? "3.
|
|
46178
|
+
var SERVER_VERSION = true ? "3.5.0" : "0.0.0-dev";
|
|
45002
46179
|
function createServer(args) {
|
|
45003
46180
|
const server = new McpServer({
|
|
45004
46181
|
name: "ksql-mcp",
|
|
@@ -45020,7 +46197,7 @@ function createServer(args) {
|
|
|
45020
46197
|
}, tools.explainTool);
|
|
45021
46198
|
server.registerTool("ksql_query", {
|
|
45022
46199
|
title: "Run read-only kSQL",
|
|
45023
|
-
description: "Execute read-only kSQL: SELECT, WITH, UNION, EXPLAIN, SHOW APPS, DESCRIBE, ASSERT, and INSERT/UPSERT/UPDATE ... VALIDATE ONLY. ASSERT failure always stops the batch. Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. VALIDATE ONLY always
|
|
46200
|
+
description: "Execute read-only kSQL: SELECT, WITH, UNION, EXPLAIN, SHOW APPS, DESCRIBE, ASSERT, leading VALIDATE app existing-record audits, and INSERT/UPSERT/UPDATE ... VALIDATE ONLY. ASSERT failure always stops the batch. Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. VALIDATE and VALIDATE ONLY always treat onLimit=truncate as error and perform zero write API calls. Existing-record VALIDATE applies built-in form constraints plus optional CHECK groups and can materialize its fixed five diagnostic columns with INTO #err in a batch. NUMBER targets use the app numberPrecision settings for integer-digit validation and fail closed if settings cannot be read. Excess fractional digits pass through for kintone to round automatically. Supports multi-statement batches with temp tables, including VALIDATE ONLY INTO #err for later SELECT. Mutating DML is rejected.",
|
|
45024
46201
|
inputSchema: queryInputShape
|
|
45025
46202
|
}, tools.queryTool);
|
|
45026
46203
|
server.registerTool("ksql_mutate", {
|