@rex0220/kintone-sql-tools 3.4.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.
@@ -31656,13 +31656,14 @@ var Parser = class {
31656
31656
  if (upper === "CREATE") return this.parseCreateTempTable();
31657
31657
  if (upper === "DROP") return this.parseDropTempTable();
31658
31658
  if (upper === "DECLARE") return this.parseDeclareVariable();
31659
+ if (upper === "VALIDATE") return this.parseValidate();
31659
31660
  break;
31660
31661
  }
31661
31662
  default:
31662
31663
  break;
31663
31664
  }
31664
31665
  throw new ParseError(
31665
- "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",
31666
31667
  tok
31667
31668
  );
31668
31669
  }
@@ -31673,7 +31674,7 @@ var Parser = class {
31673
31674
  this.expect("SET" /* SET */);
31674
31675
  const variable = this.expect("VARIABLE" /* VARIABLE */, "SET \u306E\u5F8C\u306B\u306F\u5909\u6570\u540D\uFF08\u4F8B: @name\uFF09\u304C\u5FC5\u8981\u3067\u3059");
31675
31676
  this.expect("=" /* EQ */);
31676
- const expr = this.parseScalarExpr("SET", true);
31677
+ const expr = this.peek().kind === "[" /* LBRACKET */ ? this.parseArrayLiteral() : this.parseScalarExpr("SET", true);
31677
31678
  return { type: "SET_VARIABLE", name: variable.value.slice(1).toLowerCase(), expr };
31678
31679
  }
31679
31680
  parseDeclareVariable() {
@@ -31834,11 +31835,63 @@ var Parser = class {
31834
31835
  query = this.parseDelete();
31835
31836
  } else if (tok.kind === "REORDER" /* REORDER */) {
31836
31837
  query = this.parseReorder();
31838
+ } else if (tok.kind === "IDENT" /* IDENT */ && tok.value.toUpperCase() === "VALIDATE") {
31839
+ query = this.parseValidate();
31837
31840
  } else {
31838
- 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);
31839
31842
  }
31840
31843
  return { type: "EXPLAIN", query };
31841
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
+ }
31842
31895
  // ----------------------------------------------------------
31843
31896
  // ASSERT
31844
31897
  //
@@ -32124,6 +32177,17 @@ var Parser = class {
32124
32177
  const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
32125
32178
  return { type: "SCALAR_VALUE_COL", expr, alias: alias2 };
32126
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
+ }
32127
32191
  const windowFunc = this.tryWindowFunc();
32128
32192
  if (windowFunc !== null) {
32129
32193
  return this.parseWindowColumn(windowFunc);
@@ -32939,9 +33003,7 @@ var Parser = class {
32939
33003
  }
32940
33004
  if (this.consume("NOT" /* NOT */)) {
32941
33005
  if (this.consume("IN" /* IN */)) {
32942
- this.expect("(" /* LPAREN */);
32943
- const right2 = this.parseInListOrSubquery();
32944
- this.expect(")" /* RPAREN */);
33006
+ const right2 = this.parseInRight();
32945
33007
  return { type: "BINARY", op: "NOT_IN", left: field, right: right2 };
32946
33008
  }
32947
33009
  if (this.consume("LIKE" /* LIKE */)) {
@@ -32958,9 +33020,7 @@ var Parser = class {
32958
33020
  );
32959
33021
  }
32960
33022
  if (this.consume("IN" /* IN */)) {
32961
- this.expect("(" /* LPAREN */);
32962
- const right2 = this.parseInListOrSubquery();
32963
- this.expect(")" /* RPAREN */);
33023
+ const right2 = this.parseInRight();
32964
33024
  return { type: "BINARY", op: "IN", left: field, right: right2 };
32965
33025
  }
32966
33026
  if (this.consume("KLIKE" /* KLIKE */)) {
@@ -33127,6 +33187,18 @@ var Parser = class {
33127
33187
  );
33128
33188
  }
33129
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
+ }
33130
33202
  parseInListOrSubquery() {
33131
33203
  if (this.peek().kind === "SELECT" /* SELECT */) {
33132
33204
  const query = this.parseSelect();
@@ -33937,7 +34009,7 @@ function isDmlType(type) {
33937
34009
  return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
33938
34010
  }
33939
34011
  function isReadOnlyType(type) {
33940
- 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";
33941
34013
  }
33942
34014
  function writesKintone(stmt) {
33943
34015
  return isDmlType(stmt.type) && !("validateOnly" in stmt && stmt.validateOnly === true);
@@ -33948,6 +34020,8 @@ function isReadOnlyStatement(stmt) {
33948
34020
  function requiresCompleteInput(stmt) {
33949
34021
  if (isDmlType(stmt.type)) return true;
33950
34022
  switch (stmt.type) {
34023
+ case "VALIDATE":
34024
+ return true;
33951
34025
  case "SELECT":
33952
34026
  return selectRequiresCompleteInput(stmt);
33953
34027
  case "UNION":
@@ -33988,6 +34062,7 @@ function whereRequiresCompleteInput(where) {
33988
34062
  case "EXISTS":
33989
34063
  return selectRequiresCompleteInput(where.query);
33990
34064
  case "NULL_CHECK":
34065
+ case "BOOLEAN":
33991
34066
  return false;
33992
34067
  }
33993
34068
  }
@@ -34011,6 +34086,8 @@ function getInsertValuesCount(stmt) {
34011
34086
  // src/engine/pushDownNot.ts
34012
34087
  function pushDownNot(expr) {
34013
34088
  switch (expr.type) {
34089
+ case "BOOLEAN":
34090
+ return { type: "BOOLEAN", value: !expr.value };
34014
34091
  case "BINARY": {
34015
34092
  const negated = negateOp(expr.op);
34016
34093
  if (negated === null) {
@@ -34090,6 +34167,7 @@ function whereHasLike(where) {
34090
34167
  case "BINARY":
34091
34168
  case "NULL_CHECK":
34092
34169
  case "EXISTS":
34170
+ case "BOOLEAN":
34093
34171
  return false;
34094
34172
  }
34095
34173
  }
@@ -34105,6 +34183,7 @@ function whereHasKlike(where) {
34105
34183
  case "BINARY":
34106
34184
  case "NULL_CHECK":
34107
34185
  case "EXISTS":
34186
+ case "BOOLEAN":
34108
34187
  return false;
34109
34188
  }
34110
34189
  }
@@ -34124,6 +34203,8 @@ function whereToKintone(expr) {
34124
34203
  return convertGroup(expr);
34125
34204
  case "EXISTS":
34126
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");
34127
34208
  }
34128
34209
  }
34129
34210
  function convertBinary(expr) {
@@ -34202,6 +34283,8 @@ function convertValue(value, op) {
34202
34283
  switch (value.type) {
34203
34284
  case "VARIABLE":
34204
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`);
34205
34288
  case "STRING":
34206
34289
  return convertString(value);
34207
34290
  case "NUMBER":
@@ -34281,6 +34364,8 @@ function resolveSelectMode(stmt) {
34281
34364
  function whereRequiresJsEval(where) {
34282
34365
  if (where === null) return false;
34283
34366
  switch (where.type) {
34367
+ case "BOOLEAN":
34368
+ return true;
34284
34369
  case "BINARY":
34285
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);
34286
34371
  case "NULL_CHECK":
@@ -34675,6 +34760,7 @@ function collectRequiredFieldsByTable(stmt) {
34675
34760
  walkWhere(where.expr, phase);
34676
34761
  return;
34677
34762
  case "EXISTS":
34763
+ case "BOOLEAN":
34678
34764
  return;
34679
34765
  }
34680
34766
  };
@@ -34713,6 +34799,8 @@ function collectRequiredFieldsByTable(stmt) {
34713
34799
  break;
34714
34800
  case "LITERAL_COL":
34715
34801
  break;
34802
+ case "VARIABLE_COL":
34803
+ throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
34716
34804
  case "AGGREGATE":
34717
34805
  if (col.arg.type !== "WILDCARD") walkArith(col.arg, "select");
34718
34806
  break;
@@ -34888,6 +34976,7 @@ function stripCteAlias(where, alias) {
34888
34976
  case "GROUP":
34889
34977
  return { ...where, expr: stripCteAlias(where.expr, alias) };
34890
34978
  case "EXISTS":
34979
+ case "BOOLEAN":
34891
34980
  return where;
34892
34981
  }
34893
34982
  }
@@ -34925,6 +35014,7 @@ function extractAndLeaves(where, accept) {
34925
35014
  case "NULL_CHECK":
34926
35015
  case "NOT":
34927
35016
  case "EXISTS":
35017
+ case "BOOLEAN":
34928
35018
  return null;
34929
35019
  }
34930
35020
  }
@@ -35063,6 +35153,7 @@ function collectKlikes(where, out) {
35063
35153
  case "BINARY":
35064
35154
  case "NULL_CHECK":
35065
35155
  case "EXISTS":
35156
+ case "BOOLEAN":
35066
35157
  return;
35067
35158
  }
35068
35159
  }
@@ -35119,6 +35210,11 @@ function validateStatement(stmt) {
35119
35210
  );
35120
35211
  }
35121
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;
35122
35218
  case "SHOW_APPS":
35123
35219
  case "DESCRIBE":
35124
35220
  case "DROP_TEMP_TABLE":
@@ -35206,6 +35302,8 @@ function isDescendantOf(root, target) {
35206
35302
  case "NULL_CHECK":
35207
35303
  case "EXISTS":
35208
35304
  return false;
35305
+ case "BOOLEAN":
35306
+ return false;
35209
35307
  }
35210
35308
  }
35211
35309
  function walkWithoutNestedSelects(node, visitWhere) {
@@ -35267,8 +35365,12 @@ function collectVariableRefs(node, refs) {
35267
35365
  }
35268
35366
  if (node !== null && typeof node === "object") {
35269
35367
  const obj = node;
35270
- if (obj["type"] === "VARIABLE" && typeof obj["name"] === "string") {
35271
- 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
+ });
35272
35374
  return;
35273
35375
  }
35274
35376
  for (const v of Object.values(obj)) collectVariableRefs(v, refs);
@@ -35309,9 +35411,9 @@ function analyzeBatch(statements) {
35309
35411
  const variableDefs = /* @__PURE__ */ new Map();
35310
35412
  const variableOrder = [];
35311
35413
  statements.forEach((stmt, index) => {
35312
- 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;
35313
35415
  if (statements.length === 1 && validationTable) {
35314
- 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.";
35315
35417
  throw new BatchAnalysisError(message, index);
35316
35418
  }
35317
35419
  const statementType = getStatementType(stmt);
@@ -35320,23 +35422,43 @@ function analyzeBatch(statements) {
35320
35422
  const refs = /* @__PURE__ */ new Set();
35321
35423
  const stmtAppIds = /* @__PURE__ */ new Set();
35322
35424
  const dependsOn = /* @__PURE__ */ new Set();
35323
- const variableRefs = /* @__PURE__ */ new Set();
35425
+ const variableRefs = [];
35324
35426
  collectVariableRefs(stmt, variableRefs);
35325
- for (const name of variableRefs) {
35326
- const def = variableDefs.get(name);
35427
+ const referencedThisStatement = /* @__PURE__ */ new Set();
35428
+ for (const use of variableRefs) {
35429
+ const def = variableDefs.get(use.name);
35327
35430
  if (def === void 0) {
35328
35431
  throw new BatchAnalysisError(
35329
- `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.`,
35330
35439
  index
35331
35440
  );
35332
35441
  }
35333
- def.referencedBy.push(index);
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}.`,
35445
+ index
35446
+ );
35447
+ }
35448
+ if (!referencedThisStatement.has(use.name)) {
35449
+ def.referencedBy.push(index);
35450
+ referencedThisStatement.add(use.name);
35451
+ }
35334
35452
  }
35335
35453
  if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
35336
35454
  if (variableDefs.has(stmt.name)) {
35337
35455
  throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
35338
35456
  }
35339
- 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
+ });
35340
35462
  variableOrder.push(stmt.name);
35341
35463
  if (variableOrder.length > MAX_BATCH_VARIABLES) {
35342
35464
  throw new BatchAnalysisError(
@@ -35369,7 +35491,7 @@ function analyzeBatch(statements) {
35369
35491
  dependsOn.add(at);
35370
35492
  }
35371
35493
  if (validationTable) {
35372
- 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 : [];
35373
35495
  const signature = JSON.stringify(payloadFields);
35374
35496
  const at = defined.get(validationTable);
35375
35497
  if (at === void 0) {
@@ -35444,6 +35566,7 @@ function analyzeBatch(statements) {
35444
35566
  const needsCompleteInput = results.some((r) => r.requiresCompleteInput);
35445
35567
  const variables = variableOrder.map((name) => ({
35446
35568
  name,
35569
+ kind: variableDefs.get(name).kind,
35447
35570
  referencedBy: [...variableDefs.get(name).referencedBy]
35448
35571
  }));
35449
35572
  return {
@@ -35720,6 +35843,7 @@ function whereNeedsFieldMetadata(where) {
35720
35843
  case "GROUP":
35721
35844
  return whereNeedsFieldMetadata(where.expr);
35722
35845
  case "EXISTS":
35846
+ case "BOOLEAN":
35723
35847
  return false;
35724
35848
  }
35725
35849
  }
@@ -35744,6 +35868,7 @@ function explainNeedsAppMetadata(statement) {
35744
35868
  seen.add(node);
35745
35869
  if (Array.isArray(node)) return node.some(visit);
35746
35870
  const item = node;
35871
+ if (item["type"] === "VALIDATE") return true;
35747
35872
  if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
35748
35873
  return true;
35749
35874
  }
@@ -36234,6 +36359,8 @@ function resolveFieldRef(row, field) {
36234
36359
  // src/engine/evalWhere.ts
36235
36360
  function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
36236
36361
  switch (expr.type) {
36362
+ case "BOOLEAN":
36363
+ return expr.value;
36237
36364
  case "BINARY":
36238
36365
  return evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
36239
36366
  case "NULL_CHECK":
@@ -36404,6 +36531,8 @@ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
36404
36531
  switch (value.type) {
36405
36532
  case "VARIABLE":
36406
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}.`);
36407
36536
  case "STRING":
36408
36537
  return value.value;
36409
36538
  case "NUMBER":
@@ -36719,6 +36848,9 @@ function collectConditionFields(expr, out) {
36719
36848
  case "GROUP":
36720
36849
  collectConditionFields(expr.expr, out);
36721
36850
  break;
36851
+ case "EXISTS":
36852
+ case "BOOLEAN":
36853
+ break;
36722
36854
  }
36723
36855
  }
36724
36856
  function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new Map()) {
@@ -36934,6 +37066,8 @@ function convertDmlSqlValue(value, fieldType) {
36934
37066
  switch (value.type) {
36935
37067
  case "VARIABLE":
36936
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`);
36937
37071
  case "STRING":
36938
37072
  return convertString2(value.value, fieldType);
36939
37073
  case "NUMBER":
@@ -37769,6 +37903,8 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
37769
37903
  const out = {};
37770
37904
  for (const [colIdx, col] of columns.entries()) {
37771
37905
  switch (col.type) {
37906
+ case "VARIABLE_COL":
37907
+ throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
37772
37908
  case "WILDCARD":
37773
37909
  Object.assign(out, stripParentShortcutColumns(row));
37774
37910
  break;
@@ -37872,6 +38008,8 @@ function computeExplicitOutputKeys(columns, defaultFieldKeys) {
37872
38008
  }
37873
38009
  function computeOutputKey(col, colIdx, defaultFieldKeys) {
37874
38010
  switch (col.type) {
38011
+ case "VARIABLE_COL":
38012
+ throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
37875
38013
  case "FIELD":
37876
38014
  return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
37877
38015
  case "LITERAL_COL":
@@ -38386,6 +38524,11 @@ function renderValidationValue(value) {
38386
38524
  return String(value);
38387
38525
  }
38388
38526
 
38527
+ // src/core/existingRecordValidation.ts
38528
+ function renderExistingValidationValue(raw, fieldType) {
38529
+ return isEmptyDmlValue(raw) ? "" : renderValidationValue(normalizeRaw(raw, fieldType));
38530
+ }
38531
+
38389
38532
  // src/core/optimization/whereCapability.ts
38390
38533
  var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
38391
38534
  var EQUALITY_IN = ["=", "!=", "in", "not in"];
@@ -38460,6 +38603,8 @@ function classifyWhereCapability(where, resolveField2) {
38460
38603
  }
38461
38604
  function classifyNode(where, resolveField2) {
38462
38605
  switch (where.type) {
38606
+ case "BOOLEAN":
38607
+ return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
38463
38608
  case "BINARY":
38464
38609
  return classifyBinary(where.op, where.left, where.right.type, resolveField2);
38465
38610
  case "NULL_CHECK":
@@ -38786,6 +38931,8 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
38786
38931
  throw new Error("ArgumentError: ON ERROR SKIP requires a batch.");
38787
38932
  }
38788
38933
  switch (stmt.type) {
38934
+ case "VALIDATE":
38935
+ return executeExistingRecordValidation(stmt, client, options, cacheContext);
38789
38936
  case "SELECT":
38790
38937
  return executeSelect(stmt, client, options, cacheContext);
38791
38938
  case "UNION":
@@ -38831,6 +38978,144 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
38831
38978
  return executeAssert(stmt, client, options, cacheContext);
38832
38979
  }
38833
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
+ }
38834
39119
  var TEMP_TABLE_MAX_ROWS = 1e4;
38835
39120
  function appendValidationErrors(tempTables, name, columns, rows, maxRows, columnMeta) {
38836
39121
  const current = tempTables.get(name);
@@ -38964,9 +39249,14 @@ async function executeBatch(sql, client, options = {}) {
38964
39249
  }
38965
39250
  async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables) {
38966
39251
  if (stmt.type === "SET_VARIABLE") {
38967
- const resolvedStmt2 = resolveVariableRefs(stmt, variables);
39252
+ const resolvedStmt2 = resolveBatchVariableReferences(stmt, variables);
38968
39253
  validateKlikeStatement(resolvedStmt2);
38969
- 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") {
38970
39260
  try {
38971
39261
  const value = await evaluateScalarSubquery(
38972
39262
  resolvedStmt2.expr.query,
@@ -39003,8 +39293,27 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
39003
39293
  }
39004
39294
  return {};
39005
39295
  }
39006
- const resolvedStmt = resolveVariableRefs(stmt, variables);
39296
+ const resolvedStmt = resolveBatchVariableReferences(stmt, variables);
39007
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
+ }
39008
39317
  if ("validateOnly" in resolvedStmt && resolvedStmt.validateOnly === true) {
39009
39318
  const result = await executeDmlValidation(
39010
39319
  resolvedStmt,
@@ -39189,9 +39498,9 @@ function evaluateScalarExpr(expr) {
39189
39498
  }
39190
39499
  }
39191
39500
  }
39192
- function resolveVariableRefs(node, variables) {
39501
+ function resolveBatchVariableReferences(node, variables) {
39193
39502
  if (Array.isArray(node)) {
39194
- return node.map((v) => resolveVariableRefs(v, variables));
39503
+ return node.map((v) => resolveBatchVariableReferences(v, variables));
39195
39504
  }
39196
39505
  if (node !== null && typeof node === "object") {
39197
39506
  const obj = node;
@@ -39200,14 +39509,72 @@ function resolveVariableRefs(node, variables) {
39200
39509
  if (value === void 0) {
39201
39510
  throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
39202
39511
  }
39512
+ if (value.type === "array") {
39513
+ throw new Error(`ParseError: array variable @${obj["name"]} can only be used as IN @${obj["name"]}.`);
39514
+ }
39203
39515
  return value.type === "number" ? { type: "NUMBER", value: value.value, raw: value.raw ?? String(value.value) } : { type: "STRING", value: value.value };
39204
39516
  }
39205
- return Object.fromEntries(
39206
- 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)])
39207
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;
39208
39552
  }
39209
39553
  return node;
39210
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
+ }
39211
39578
  function findVariableRef(node) {
39212
39579
  if (Array.isArray(node)) {
39213
39580
  for (const value of node) {
@@ -39218,7 +39585,7 @@ function findVariableRef(node) {
39218
39585
  }
39219
39586
  if (node !== null && typeof node === "object") {
39220
39587
  const obj = node;
39221
- 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"];
39222
39589
  for (const value of Object.values(obj)) {
39223
39590
  const found = findVariableRef(value);
39224
39591
  if (found !== null) return found;
@@ -39461,6 +39828,7 @@ async function assertDmlWhereCapability(stmt, client, cacheContext) {
39461
39828
  if (stmt.subtableCode || stmt.type === "UPDATE" && stmt.from != null) return;
39462
39829
  const fields = whereNeedsFieldMetadata(stmt.where) ? await getFieldsCached(stmt.appId, client, cacheContext) : [];
39463
39830
  const byCode = new Map(fields.map((field) => [field.code, field]));
39831
+ if (stmt.where.type === "BOOLEAN" && stmt.where.value === false) return;
39464
39832
  const result = classifyWhereCapability(stmt.where, (field) => {
39465
39833
  if (field.field === "$id") return resolveFieldSemantics({ fieldType: "__ID__" });
39466
39834
  const info = byCode.get(field.field);
@@ -39533,6 +39901,9 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
39533
39901
  }
39534
39902
  return result;
39535
39903
  }
39904
+ function isConstantFalseWhere(where) {
39905
+ return where?.type === "BOOLEAN" && where.value === false;
39906
+ }
39536
39907
  function isNoFromSelect(stmt) {
39537
39908
  return stmt.from.appId === 0 && stmt.from.cteName === NO_FROM_CTE_NAME;
39538
39909
  }
@@ -39564,6 +39935,8 @@ function stringFuncHasFieldRef(expr) {
39564
39935
  function validateNoFromColumns(stmt) {
39565
39936
  for (const col of stmt.columns) {
39566
39937
  switch (col.type) {
39938
+ case "VARIABLE_COL":
39939
+ throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
39567
39940
  case "LITERAL_COL":
39568
39941
  break;
39569
39942
  case "ARITH_COL":
@@ -39799,6 +40172,7 @@ function collectTypedInFieldRefs(expr, out) {
39799
40172
  return;
39800
40173
  case "NULL_CHECK":
39801
40174
  case "EXISTS":
40175
+ case "BOOLEAN":
39802
40176
  return;
39803
40177
  }
39804
40178
  }
@@ -40261,7 +40635,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
40261
40635
  validateKlikePushdownPlan(pushdownPlan);
40262
40636
  const mainPushDown = pushdownPlan.mainCondition;
40263
40637
  const tableConditions = pushdownPlan.joinConditions;
40264
- const mainFetch = fetchTableRecordsForFullScan(
40638
+ const constantFalse = isConstantFalseWhere(stmt.where);
40639
+ const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
40265
40640
  stmt,
40266
40641
  stmt.from,
40267
40642
  client,
@@ -40276,6 +40651,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
40276
40651
  const parallelJoins = [];
40277
40652
  const onOptJoins = [];
40278
40653
  for (const join of stmt.joins) {
40654
+ if (constantFalse) {
40655
+ parallelJoins.push({ join, promise: Promise.resolve([]) });
40656
+ continue;
40657
+ }
40279
40658
  const jCond = join.table.alias ? tableConditions.get(join.table.alias) ?? null : null;
40280
40659
  if (jCond !== null) {
40281
40660
  parallelJoins.push({
@@ -41673,6 +42052,26 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
41673
42052
  };
41674
42053
  }
41675
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
+ }
41676
42075
  if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, tempTables);
41677
42076
  if (stmt.subtableCode) {
41678
42077
  await assertDmlWhereCapability(stmt, client, cacheContext);
@@ -41693,6 +42092,7 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
41693
42092
  cacheContext
41694
42093
  );
41695
42094
  await assertDmlWhereCapability(stmt, client, cacheContext);
42095
+ if (isConstantFalseWhere(stmt.where)) return { type: "UPDATE", updatedCount: 0 };
41696
42096
  if (stmt.from != null) {
41697
42097
  return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
41698
42098
  }
@@ -41774,6 +42174,7 @@ function collectUpdateFromTargetFields(stmt) {
41774
42174
  }
41775
42175
  async function executeDelete(stmt, client, options, cacheContext) {
41776
42176
  await assertDmlWhereCapability(stmt, client, cacheContext);
42177
+ if (isConstantFalseWhere(stmt.where)) return { type: "DELETE", deletedCount: 0 };
41777
42178
  if (stmt.subtableCode) {
41778
42179
  return executeDeleteSubtable(stmt, client, options, cacheContext);
41779
42180
  }
@@ -42156,6 +42557,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
42156
42557
  cacheContext
42157
42558
  );
42158
42559
  const reorderFields = await getFieldsCached(stmt.appId, client, cacheContext);
42560
+ if (isConstantFalseWhere(stmt.where)) return { type: "REORDER", reorderedParentCount: 0 };
42159
42561
  const reorderSemanticsByCode = new Map(reorderFields.map((field) => [
42160
42562
  field.code,
42161
42563
  field.semantics ?? resolveFieldSemantics(field)
@@ -42382,6 +42784,8 @@ function collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCa
42382
42784
  }));
42383
42785
  break;
42384
42786
  }
42787
+ case "BOOLEAN":
42788
+ break;
42385
42789
  }
42386
42790
  }
42387
42791
  async function resolveSetSubqueries(assignments, client, options, cacheContext) {
@@ -42419,9 +42823,11 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
42419
42823
  pending.forEach(([i], idx) => cache.set(i, values[idx]));
42420
42824
  return cache;
42421
42825
  }
42826
+ var validateExplainInfo = /* @__PURE__ */ new WeakMap();
42422
42827
  async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords2 = 1e4) {
42423
42828
  const fieldApps = /* @__PURE__ */ new Set();
42424
42829
  const processStatusApps = /* @__PURE__ */ new Set();
42830
+ const numberPrecisionApps = /* @__PURE__ */ new Set();
42425
42831
  const tracedClient = {
42426
42832
  ...client,
42427
42833
  getFields: async (appId) => {
@@ -42431,6 +42837,10 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
42431
42837
  getProcessStatuses: async (appId) => {
42432
42838
  processStatusApps.add(appId);
42433
42839
  return client.getProcessStatuses(appId);
42840
+ },
42841
+ getNumberPrecision: async (appId) => {
42842
+ numberPrecisionApps.add(appId);
42843
+ return client.getNumberPrecision(appId);
42434
42844
  }
42435
42845
  };
42436
42846
  const capabilities = /* @__PURE__ */ new Map();
@@ -42479,6 +42889,51 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
42479
42889
  }));
42480
42890
  }
42481
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
+ });
42482
42937
  } else if (typed["type"] === "UPDATE" || typed["type"] === "DELETE") {
42483
42938
  fieldApps.add(node.appId);
42484
42939
  await assertDmlWhereCapability(
@@ -42509,12 +42964,13 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
42509
42964
  }));
42510
42965
  }
42511
42966
  }
42512
- return { capabilities, orderPlans, fieldApps, processStatusApps };
42967
+ return { capabilities, orderPlans, fieldApps, processStatusApps, numberPrecisionApps };
42513
42968
  }
42514
42969
  function explainMetadataLines(analysis) {
42515
42970
  return [
42516
42971
  ...[...analysis.fieldApps].sort((a, b) => a - b).map((appId) => ` metadata API: form definition APP${appId}`),
42517
- ...[...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}`)
42518
42974
  ];
42519
42975
  }
42520
42976
  async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords2 = 1e4, cursorMaxActive2 = 2) {
@@ -42525,7 +42981,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
42525
42981
  const plans = [];
42526
42982
  for (let i = 0; i < statements.length; i++) {
42527
42983
  const stmt = statements[i];
42528
- 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);
42529
42985
  validateKlikeStatement(planStmt);
42530
42986
  const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords2);
42531
42987
  const statementPlan = addCursorConcurrency(buildBatchStatementPlan(
@@ -42541,7 +42997,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
42541
42997
  plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
42542
42998
  });
42543
42999
  if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
42544
- 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}` });
42545
43001
  }
42546
43002
  }
42547
43003
  return { statementCount: statements.length, statements: plans };
@@ -42676,8 +43132,30 @@ function buildExplainPlan(query, label, capabilities, orderPlans) {
42676
43132
  if (query.type === "UPDATE") return buildUpdatePlan(query, label, capabilities, orderPlans);
42677
43133
  if (query.type === "DELETE") return buildDeletePlan(query, label);
42678
43134
  if (query.type === "REORDER") return buildReorderPlan(query, label);
43135
+ if (query.type === "VALIDATE") return buildValidatePlan(query, label);
42679
43136
  return buildSelectPlan(query, label, capabilities, orderPlans);
42680
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
+ }
42681
43159
  function buildSelectPlan(stmt, label, capabilities, orderPlans) {
42682
43160
  const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
42683
43161
  const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
@@ -42689,6 +43167,12 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
42689
43167
  const lines = [];
42690
43168
  if (label) lines.push(label);
42691
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
+ }
42692
43176
  if (orderPlan) {
42693
43177
  lines.push(` order plan: ${orderPlan.kind}`);
42694
43178
  if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
@@ -42836,6 +43320,8 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
42836
43320
  break;
42837
43321
  case "NULL_CHECK":
42838
43322
  break;
43323
+ case "BOOLEAN":
43324
+ break;
42839
43325
  }
42840
43326
  };
42841
43327
  visitWhere(stmt.where);
@@ -42888,7 +43374,7 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
42888
43374
  } else {
42889
43375
  lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
42890
43376
  }
42891
- 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`);
42892
43378
  const setTypes = [];
42893
43379
  if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
42894
43380
  if (isStringFunc) setTypes.push("\u6587\u5B57\u5217\u95A2\u6570 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A55\u4FA1\uFF09");
@@ -42919,7 +43405,7 @@ function buildDeletePlan(stmt, label) {
42919
43405
  lines.push(` [DELETE]`);
42920
43406
  lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
42921
43407
  lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
42922
- 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`);
42923
43409
  return lines;
42924
43410
  }
42925
43411
  function buildUpsertPlan(stmt, label) {
@@ -42959,7 +43445,7 @@ function buildReorderPlan(stmt, label) {
42959
43445
  ` table: ${target}`,
42960
43446
  ` scope: ${scope}`,
42961
43447
  ` by: ${byStr}`,
42962
- ` 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`
42963
43449
  ];
42964
43450
  if (!stmt.all && stmt.where) {
42965
43451
  lines.splice(5, 0, ` where: ${safeWhereToKintone(stmt.where)}`);
@@ -42971,6 +43457,7 @@ function formatOrderByItem(item) {
42971
43457
  return `${key} ${item.direction}`;
42972
43458
  }
42973
43459
  function safeWhereToKintone(where) {
43460
+ if (where.type === "BOOLEAN") return where.value ? "TRUE" : "FALSE (constant)";
42974
43461
  try {
42975
43462
  return whereToKintone(where);
42976
43463
  } catch {
@@ -45210,7 +45697,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45210
45697
  profile: input.profile,
45211
45698
  maxRecords: input.maxRecords,
45212
45699
  fetchParallel: input.fetchParallel,
45213
- onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
45700
+ onLimit: validation.containsValidationOnly || validation.statements.some((s) => s.statementType === "VALIDATE") ? "error" : input.onLimit,
45214
45701
  timeout: input.timeout,
45215
45702
  tempTableMaxRows: input.tempTableMaxRows,
45216
45703
  cursorMaxActive: input.cursorMaxActive
@@ -45257,7 +45744,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45257
45744
  profile: input.profile,
45258
45745
  maxRecords: input.maxRecords,
45259
45746
  fetchParallel: input.fetchParallel,
45260
- onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
45747
+ onLimit: validation.containsValidationOnly || validation.statements.some((s) => s.statementType === "VALIDATE") ? "error" : input.onLimit,
45261
45748
  timeout: input.timeout,
45262
45749
  cursorMaxActive: input.cursorMaxActive
45263
45750
  });
@@ -45559,7 +46046,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45559
46046
  var profile = external_exports.string().min(1).describe("kintone connection profile name from ksql.config.json (default: the server's default profile).").optional();
45560
46047
  var maxRecords = external_exports.number().int().positive().describe("Maximum records fetched per SELECT (default 500).").optional();
45561
46048
  var fetchParallel = external_exports.number().int().min(1).max(10).describe("Number of parallel kintone record-fetch requests (1-10).").optional();
45562
- 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();
45563
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();
45564
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();
45565
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();
@@ -45688,7 +46175,7 @@ Options:
45688
46175
  -h, --help Show help
45689
46176
  `);
45690
46177
  }
45691
- var SERVER_VERSION = true ? "3.4.0" : "0.0.0-dev";
46178
+ var SERVER_VERSION = true ? "3.5.0" : "0.0.0-dev";
45692
46179
  function createServer(args) {
45693
46180
  const server = new McpServer({
45694
46181
  name: "ksql-mcp",
@@ -45710,7 +46197,7 @@ function createServer(args) {
45710
46197
  }, tools.explainTool);
45711
46198
  server.registerTool("ksql_query", {
45712
46199
  title: "Run read-only kSQL",
45713
- 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.",
45714
46201
  inputSchema: queryInputShape
45715
46202
  }, tools.queryTool);
45716
46203
  server.registerTool("ksql_mutate", {