@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.
@@ -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 !== "STRFUNC_COL") return false;
32230
- return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
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
- if (arg.type === "STRING_FUNC") {
32235
- return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
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
- const tok = this.peek();
32570
- if (tok.kind === "STRING" /* STRING */) {
32571
- this.advance();
32572
- return { type: "STRING", value: tok.value };
32573
- }
32574
- if (this.tryStringFuncName() !== null) {
32575
- return this.parseStringFuncExpr();
32576
- }
32577
- const startPos = this.pos;
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.pos = startPos;
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.expect("(" /* LPAREN */);
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.expect("(" /* LPAREN */);
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
- collectArithNode(arg, out);
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
- if (arg.type === "STRING_FUNC") return hasAggregateInStringFuncExpr(arg);
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
- walkArith(arg, phase);
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 === "STRING_FUNC") {
34419
- walkStringFunc(result, phase);
34706
+ if (result.type === "FIELD_REF" || result.type === "ARITH") {
34707
+ walkArith(result, phase);
34420
34708
  return;
34421
34709
  }
34422
- walkArith(result, phase);
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 arithNodeLabel(a);
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
- if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
35043
- refs.add(obj["name"]);
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 = /* @__PURE__ */ new Set();
35425
+ const variableRefs = [];
35096
35426
  collectVariableRefs(stmt, variableRefs);
35097
- for (const name of variableRefs) {
35098
- const def = variableDefs.get(name);
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
- def.referencedBy.push(index);
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, { index, referencedBy: [] });
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
- return String(evalArithExpr(arg, row));
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 === "STRING") return result.value;
36181
- if (result.type === "STRING_FUNC") return evalStringFunc(result, row);
36182
- if (result.type === "FIELD_REF") return row[result.field] ?? "";
36183
- return String(evalArithExpr(result, row));
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
- collectArithNode2(arg, out);
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
- collectArithNode2(result, out);
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
- return String(evalArithExpr(result, row));
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 arithColDefaultKey(a);
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
- if (arg.type === "STRING_FUNC") return hasAggregateInStringFuncExpr2(arg);
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 = resolveVariableRefs(stmt, variables);
39252
+ const resolvedStmt2 = resolveBatchVariableReferences(stmt, variables);
38540
39253
  validateKlikeStatement(resolvedStmt2);
38541
- if (resolvedStmt2.expr.type === "SCALAR_SUBQUERY") {
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 = resolveVariableRefs(stmt, variables);
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 resolveVariableRefs(node, variables) {
39501
+ function resolveBatchVariableReferences(node, variables) {
38765
39502
  if (Array.isArray(node)) {
38766
- return node.map((v) => resolveVariableRefs(v, variables));
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
- return Object.fromEntries(
38778
- Object.entries(obj).map(([key, value]) => [key, resolveVariableRefs(value, variables)])
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 if (arg.type === "STRING_FUNC") {
39429
- collectStringFuncAggregateRefs(arg, out);
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
- const source = resolveField2(aggregateFieldRef(result.field));
39610
- return source ?? unknownStringColumnMeta();
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 mainFetch = fetchTableRecordsForFullScan(
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
- if (selectResult.columns.length !== stmt.fields.length) {
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 resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [...getParams.fields], {
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
- const resolved = await resolveDmlTargetIds(client.getRecords, getParams.app, getParams.query, {
40811
- maxRecords: options.maxRecords ?? 1e4,
40812
- parallel: options.fetchParallel ?? 1
40813
- });
40814
- records = updateToPutBatches(stmt, resolved.ids, fieldTypes).flatMap((batch) => batch.records);
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 sourceFields = [...new Set(stmt.assignments.filter((a) => a.value.type === "SOURCE_FIELD").map((a) => a.value.type === "SOURCE_FIELD" ? a.value.field : ""))];
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: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
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 !== "STRING" && arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") {
42375
- collectArithNodeRefs(arg, out);
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 overrides 'truncate' to 'error'.").optional();
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.3.0" : "0.0.0-dev";
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 treats onLimit=truncate as error. VALIDATE ONLY performs local Tier-0 validation with zero write API calls; 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.",
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", {