@rex0220/kintone-sql-tools 2.3.0 → 2.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.
@@ -31512,13 +31512,14 @@ var Parser = class {
31512
31512
  const upper = tok.value.toUpperCase();
31513
31513
  if (upper === "CREATE") return this.parseCreateTempTable();
31514
31514
  if (upper === "DROP") return this.parseDropTempTable();
31515
+ if (upper === "DECLARE") return this.parseDeclareVariable();
31515
31516
  break;
31516
31517
  }
31517
31518
  default:
31518
31519
  break;
31519
31520
  }
31520
31521
  throw new ParseError(
31521
- "SELECT / INSERT / UPDATE / DELETE / REORDER / WITH / SHOW / DESCRIBE / EXPLAIN / CREATE TEMP TABLE / DROP TEMP TABLE / SET / ASSERT \u306E\u3044\u305A\u308C\u304B\u3067\u59CB\u307E\u308B SQL \u6587\u304C\u5FC5\u8981\u3067\u3059",
31522
+ "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",
31522
31523
  tok
31523
31524
  );
31524
31525
  }
@@ -31529,19 +31530,32 @@ var Parser = class {
31529
31530
  this.expect("SET" /* SET */);
31530
31531
  const variable = this.expect("VARIABLE" /* VARIABLE */, "SET \u306E\u5F8C\u306B\u306F\u5909\u6570\u540D\uFF08\u4F8B: @name\uFF09\u304C\u5FC5\u8981\u3067\u3059");
31531
31532
  this.expect("=" /* EQ */);
31532
- const expr = this.parseScalarExpr();
31533
+ const expr = this.parseScalarExpr("SET", true);
31533
31534
  return { type: "SET_VARIABLE", name: variable.value.slice(1).toLowerCase(), expr };
31534
31535
  }
31535
- /** SET RHS 専用。既存式パーサーで構文を読み、フィールド参照を明示的に拒否する。 */
31536
- parseScalarExpr() {
31536
+ parseDeclareVariable() {
31537
+ this.advance();
31538
+ const variable = this.expect("VARIABLE" /* VARIABLE */, "DECLARE \u306E\u5F8C\u306B\u306F\u5909\u6570\u540D\uFF08\u4F8B: @name\uFF09\u304C\u5FC5\u8981\u3067\u3059");
31539
+ this.expect("=" /* EQ */);
31540
+ const expr = this.parseScalarExpr("DECLARE", false);
31541
+ if (expr.type === "SCALAR_SUBQUERY") {
31542
+ throw new ParseError("DECLARE \u306E\u65E2\u5B9A\u5024\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
31543
+ }
31544
+ return { type: "DECLARE_VARIABLE", name: variable.value.slice(1).toLowerCase(), default: expr };
31545
+ }
31546
+ /** SET / DECLARE RHS 専用。既存式パーサーで構文を読み、フィールド参照を明示的に拒否する。 */
31547
+ parseScalarExpr(context, allowScalarSubquery) {
31537
31548
  const tok = this.peek();
31538
31549
  if (tok.kind === "VARIABLE" /* VARIABLE */) {
31539
- throw new ParseError("SET \u306E\u53F3\u8FBA\u3067\u306F\u4ED6\u306E\u5909\u6570\u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\uFF08Phase 1a\uFF09", tok);
31550
+ throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F\u4ED6\u306E\u5909\u6570\u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093`, tok);
31540
31551
  }
31541
31552
  if (tok.kind === "NULL" /* NULL */) {
31542
- throw new ParseError("SET \u306E\u53F3\u8FBA\u3067 NULL \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\uFF08Phase 1a\uFF09", tok);
31553
+ throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067 NULL \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`, tok);
31543
31554
  }
31544
31555
  if (tok.kind === "(" /* LPAREN */ && this.peekAt(1).kind === "SELECT" /* SELECT */) {
31556
+ if (!allowScalarSubquery) {
31557
+ throw new ParseError("DECLARE \u306E\u65E2\u5B9A\u5024\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
31558
+ }
31545
31559
  this.advance();
31546
31560
  const query = this.parseSelect();
31547
31561
  this.expect(")" /* RPAREN */);
@@ -31565,7 +31579,7 @@ var Parser = class {
31565
31579
  }
31566
31580
  if (tok.kind === "LOGINUSER" /* LOGINUSER */) {
31567
31581
  throw new ParseError(
31568
- "SET \u306E\u53F3\u8FBA\u3067 LOGINUSER() \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\uFF08\u5B9F\u884C\u74B0\u5883\u5171\u901A\u306E\u30ED\u30B0\u30A4\u30F3\u30E6\u30FC\u30B6\u30FC\u89E3\u6C7A\u306F\u672A\u5BFE\u5FDC\u3067\u3059\uFF09",
31582
+ `${context} \u306E\u53F3\u8FBA\u3067 LOGINUSER() \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\uFF08\u5B9F\u884C\u74B0\u5883\u5171\u901A\u306E\u30ED\u30B0\u30A4\u30F3\u30E6\u30FC\u30B6\u30FC\u89E3\u6C7A\u306F\u672A\u5BFE\u5FDC\u3067\u3059\uFF09`,
31569
31583
  tok
31570
31584
  );
31571
31585
  }
@@ -31573,22 +31587,22 @@ var Parser = class {
31573
31587
  return this.parseSqlValue();
31574
31588
  }
31575
31589
  const expr = this.parseArithAddSub();
31576
- this.rejectNonScalarExpr(expr, tok);
31590
+ this.rejectNonScalarExpr(expr, tok, context);
31577
31591
  if (expr.type === "NUMBER" || expr.type === "STRING_FUNC" || expr.type === "ARITH") return expr;
31578
- throw new ParseError("SET \u306E\u53F3\u8FBA\u306B\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u542B\u307E\u306A\u3044\u30B9\u30AB\u30E9\u30FC\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", tok);
31592
+ throw new ParseError(`${context} \u306E\u53F3\u8FBA\u306B\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u542B\u307E\u306A\u3044\u30B9\u30AB\u30E9\u30FC\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044`, tok);
31579
31593
  }
31580
- rejectNonScalarExpr(node, tok) {
31594
+ rejectNonScalarExpr(node, tok, context) {
31581
31595
  if (node.type === "STRING" || node.type === "NUMBER") return;
31582
31596
  if (node.type === "FIELD_REF" || node.type === "AGG_REF") {
31583
- throw new ParseError("SET \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);
31597
+ 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);
31584
31598
  }
31585
31599
  if (node.type === "ARITH" || node.type === "AGG_ARITH") {
31586
- this.rejectNonScalarExpr(node.left, tok);
31587
- this.rejectNonScalarExpr(node.right, tok);
31600
+ this.rejectNonScalarExpr(node.left, tok, context);
31601
+ this.rejectNonScalarExpr(node.right, tok, context);
31588
31602
  return;
31589
31603
  }
31590
31604
  if (node.type === "STRING_FUNC") {
31591
- for (const arg of node.args) this.rejectNonScalarExpr(arg, tok);
31605
+ for (const arg of node.args) this.rejectNonScalarExpr(arg, tok, context);
31592
31606
  }
31593
31607
  }
31594
31608
  // ----------------------------------------------------------
@@ -33214,7 +33228,7 @@ function isDmlType(type) {
33214
33228
  return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
33215
33229
  }
33216
33230
  function isReadOnlyType(type) {
33217
- 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 === "ASSERT";
33231
+ 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";
33218
33232
  }
33219
33233
  function hasWhereClause(stmt) {
33220
33234
  if (!stmt || typeof stmt !== "object") return false;
@@ -33276,8 +33290,9 @@ function analyzeBatch(statements) {
33276
33290
  }
33277
33291
  if (statements.length === 1) {
33278
33292
  const t = statements[0].type;
33279
- if (t === "SET_VARIABLE") {
33280
- throw new BatchAnalysisError("ArgumentError: SET variable requires a batch.", 0);
33293
+ if (t === "SET_VARIABLE" || t === "DECLARE_VARIABLE") {
33294
+ const verb = t === "SET_VARIABLE" ? "SET" : "DECLARE";
33295
+ throw new BatchAnalysisError(`ArgumentError: ${verb} variable requires a batch.`, 0);
33281
33296
  }
33282
33297
  if (t === "CREATE_TEMP_TABLE" || t === "DROP_TEMP_TABLE") {
33283
33298
  const verb = t === "CREATE_TEMP_TABLE" ? "CREATE TEMP TABLE" : "DROP TEMP TABLE";
@@ -33311,7 +33326,7 @@ function analyzeBatch(statements) {
33311
33326
  }
33312
33327
  def.referencedBy.push(index);
33313
33328
  }
33314
- if (stmt.type === "SET_VARIABLE") {
33329
+ if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
33315
33330
  if (variableDefs.has(stmt.name)) {
33316
33331
  throw new BatchAnalysisError(`ParseError: variable @${stmt.name} is already defined.`, index);
33317
33332
  }
@@ -33408,6 +33423,40 @@ function analyzeBatch(statements) {
33408
33423
  };
33409
33424
  }
33410
33425
 
33426
+ // src/core/batchVariables.ts
33427
+ var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
33428
+ function normalizeBatchVariableName(name) {
33429
+ if (!VARIABLE_NAME_RE.test(name)) {
33430
+ throw new Error(
33431
+ `ArgumentError: invalid variable name "${name}". Use a name without @ matching [A-Za-z_][A-Za-z0-9_]{0,63}.`
33432
+ );
33433
+ }
33434
+ return name.toLowerCase();
33435
+ }
33436
+ function normalizeBatchVariables(input) {
33437
+ const normalized = /* @__PURE__ */ Object.create(null);
33438
+ for (const [rawName, value] of Object.entries(input ?? {})) {
33439
+ const name = normalizeBatchVariableName(rawName);
33440
+ if (Object.prototype.hasOwnProperty.call(normalized, name)) {
33441
+ throw new Error(`ArgumentError: variable "${rawName}" is specified more than once.`);
33442
+ }
33443
+ normalized[name] = value;
33444
+ }
33445
+ return normalized;
33446
+ }
33447
+ function validateDeclaredBatchVariables(statements, input) {
33448
+ const normalized = normalizeBatchVariables(input);
33449
+ const declared = new Set(
33450
+ statements.filter((stmt) => stmt.type === "DECLARE_VARIABLE").map((stmt) => stmt.name)
33451
+ );
33452
+ for (const name of Object.keys(normalized)) {
33453
+ if (!declared.has(name)) {
33454
+ throw new Error(`ArgumentError: injected variable @${name} is not declared.`);
33455
+ }
33456
+ }
33457
+ return normalized;
33458
+ }
33459
+
33411
33460
  // src/core/scalarCompare.ts
33412
33461
  function compareScalarValues(op, leftStr, rightStr) {
33413
33462
  if (op === "=") return leftStr === rightStr;
@@ -34360,52 +34409,91 @@ function resolveFieldRef(row, field) {
34360
34409
  }
34361
34410
 
34362
34411
  // src/engine/evalWhere.ts
34363
- function evalWhere(expr, row) {
34412
+ function evalWhere(expr, row, resolveFieldType) {
34364
34413
  switch (expr.type) {
34365
34414
  case "BINARY":
34366
- return evalBinary(expr, row);
34415
+ return evalBinary(expr, row, resolveFieldType);
34367
34416
  case "NULL_CHECK":
34368
34417
  return evalNullCheck(expr, row);
34369
34418
  case "LOGICAL":
34370
- return evalLogical(expr, row);
34419
+ return evalLogical(expr, row, resolveFieldType);
34371
34420
  case "NOT":
34372
- return !evalWhere(expr.expr, row);
34421
+ return !evalWhere(expr.expr, row, resolveFieldType);
34373
34422
  case "GROUP":
34374
- return evalWhere(expr.expr, row);
34423
+ return evalWhere(expr.expr, row, resolveFieldType);
34375
34424
  case "EXISTS": {
34376
34425
  const exists = expr.resolved;
34377
34426
  return expr.not ? !exists : exists;
34378
34427
  }
34379
34428
  }
34380
34429
  }
34381
- function evalBinary(expr, row) {
34382
- const left = resolveField(expr.left, row);
34383
- return evalOp(expr.op, left, expr.right, row);
34430
+ function evalBinary(expr, row, resolveFieldType) {
34431
+ const left = resolveField(expr.left, row, resolveFieldType);
34432
+ const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
34433
+ return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
34384
34434
  }
34385
- function evalOp(op, leftStr, right, row) {
34435
+ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
34386
34436
  if (op === "IN" || op === "NOT_IN") {
34437
+ let values = null;
34387
34438
  if (right.type === "IN_LIST") {
34388
34439
  assertResolvedInListValues2(right.values);
34389
- const contains = right.values.some((v) => leftStr === String(v.value));
34390
- return op === "IN" ? contains : !contains;
34440
+ values = new Set(right.values.map((v) => String(v.value)));
34391
34441
  }
34392
34442
  if (right.type === "SUBQUERY_IN_LIST") {
34393
- const contains = right.resolved.has(leftStr);
34394
- return op === "IN" ? contains : !contains;
34443
+ values = right.resolved;
34395
34444
  }
34396
- return op === "NOT_IN";
34445
+ if (values === null) return op === "NOT_IN";
34446
+ const contains = typedInContains(leftStr, values, fieldType);
34447
+ return op === "IN" ? contains : !contains;
34397
34448
  }
34398
34449
  if (op === "LIKE") {
34399
- const pattern = resolveValue(right, row);
34450
+ const pattern = resolveValue(right, row, resolveFieldType);
34400
34451
  return matchLike(leftStr, pattern);
34401
34452
  }
34402
34453
  if (op === "NOT_LIKE") {
34403
- const pattern = resolveValue(right, row);
34454
+ const pattern = resolveValue(right, row, resolveFieldType);
34404
34455
  return !matchLike(leftStr, pattern);
34405
34456
  }
34406
- const rightStr = resolveValue(right, row);
34457
+ const rightStr = resolveValue(right, row, resolveFieldType);
34407
34458
  return compareScalarValues(op, leftStr, rightStr);
34408
34459
  }
34460
+ var STRING_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
34461
+ var OBJECT_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([
34462
+ "USER_SELECT",
34463
+ "ORGANIZATION_SELECT",
34464
+ "GROUP_SELECT",
34465
+ "STATUS_ASSIGNEE"
34466
+ ]);
34467
+ var SINGLE_OBJECT_FIELD_TYPES = /* @__PURE__ */ new Set(["CREATOR", "MODIFIER"]);
34468
+ function typedInContains(leftStr, values, fieldType) {
34469
+ const fallback = () => values.has(leftStr);
34470
+ if (fieldType === void 0) return fallback();
34471
+ let parsed;
34472
+ if (STRING_ARRAY_FIELD_TYPES.has(fieldType) || OBJECT_ARRAY_FIELD_TYPES.has(fieldType) || SINGLE_OBJECT_FIELD_TYPES.has(fieldType)) {
34473
+ try {
34474
+ parsed = JSON.parse(leftStr);
34475
+ } catch {
34476
+ return fallback();
34477
+ }
34478
+ } else {
34479
+ return fallback();
34480
+ }
34481
+ if (STRING_ARRAY_FIELD_TYPES.has(fieldType)) {
34482
+ if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === "string")) {
34483
+ return fallback();
34484
+ }
34485
+ return parsed.some((item) => values.has(item));
34486
+ }
34487
+ if (OBJECT_ARRAY_FIELD_TYPES.has(fieldType)) {
34488
+ if (!Array.isArray(parsed) || !parsed.every(hasStringCode)) return fallback();
34489
+ return parsed.some((item) => values.has(item.code));
34490
+ }
34491
+ if (!hasStringCode(parsed)) return fallback();
34492
+ return values.has(parsed.code);
34493
+ }
34494
+ function hasStringCode(value) {
34495
+ return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.code === "string";
34496
+ }
34409
34497
  function assertResolvedInListValues2(values) {
34410
34498
  const unresolved = values.find((item) => item.type === "VARIABLE");
34411
34499
  if (unresolved?.type === "VARIABLE") {
@@ -34416,20 +34504,20 @@ function evalNullCheck(expr, row) {
34416
34504
  const val = resolveField(expr.field, row);
34417
34505
  return expr.not ? val !== "" : val === "";
34418
34506
  }
34419
- function evalLogical(expr, row) {
34507
+ function evalLogical(expr, row, resolveFieldType) {
34420
34508
  if (expr.op === "AND") {
34421
- return evalWhere(expr.left, row) && evalWhere(expr.right, row);
34509
+ return evalWhere(expr.left, row, resolveFieldType) && evalWhere(expr.right, row, resolveFieldType);
34422
34510
  }
34423
- return evalWhere(expr.left, row) || evalWhere(expr.right, row);
34511
+ return evalWhere(expr.left, row, resolveFieldType) || evalWhere(expr.right, row, resolveFieldType);
34424
34512
  }
34425
- function resolveField(field, row) {
34513
+ function resolveField(field, row, resolveFieldType) {
34426
34514
  if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
34427
34515
  if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
34428
- if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row);
34516
+ if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType);
34429
34517
  const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
34430
34518
  return resolveFieldRef(row, key);
34431
34519
  }
34432
- function resolveValue(value, row) {
34520
+ function resolveValue(value, row, resolveFieldType) {
34433
34521
  switch (value.type) {
34434
34522
  case "VARIABLE":
34435
34523
  throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
@@ -34452,14 +34540,14 @@ function resolveValue(value, row) {
34452
34540
  if (value.expr.type === "STRING_FUNC") return evalStringFunc(value.expr, row);
34453
34541
  return String(evalArithExpr(value.expr, row));
34454
34542
  case "CASE_VALUE":
34455
- return evalCaseWhen(value.expr, row);
34543
+ return evalCaseWhen(value.expr, row, resolveFieldType);
34456
34544
  case "ARRAY":
34457
34545
  return value.elements.map((e) => e.value).join(",");
34458
34546
  }
34459
34547
  }
34460
- function evalCaseWhen(expr, row) {
34548
+ function evalCaseWhen(expr, row, resolveFieldType) {
34461
34549
  for (const branch of expr.branches) {
34462
- if (evalWhere(branch.condition, row)) {
34550
+ if (evalWhere(branch.condition, row, resolveFieldType)) {
34463
34551
  return evalCaseResult(branch.result, row);
34464
34552
  }
34465
34553
  }
@@ -35126,9 +35214,9 @@ function applyJoin(leftRows, rightRows, join) {
35126
35214
  }
35127
35215
  return result;
35128
35216
  }
35129
- function applyFilter(rows, where) {
35217
+ function applyFilter(rows, where, resolveFieldType) {
35130
35218
  if (where === null) return rows;
35131
- return rows.filter((row) => evalWhere(where, row));
35219
+ return rows.filter((row) => evalWhere(where, row, resolveFieldType));
35132
35220
  }
35133
35221
  function hasAggregateColumns(columns) {
35134
35222
  return columns.some(
@@ -35254,9 +35342,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
35254
35342
  const argStr = aggregateArgLabel(arg);
35255
35343
  return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
35256
35344
  }
35257
- function applyHaving(rows, having) {
35345
+ function applyHaving(rows, having, resolveFieldType) {
35258
35346
  if (having === null) return rows;
35259
- return rows.filter((row) => evalWhere(having, row));
35347
+ return rows.filter((row) => evalWhere(having, row, resolveFieldType));
35260
35348
  }
35261
35349
  function applyDistinct(rows, columns) {
35262
35350
  if (rows.length === 0) return rows;
@@ -35381,7 +35469,7 @@ function applyLimit(rows, limit, offset) {
35381
35469
  if (limit === null) return rows.slice(start);
35382
35470
  return rows.slice(start, start + limit);
35383
35471
  }
35384
- function project(rows, columns, scalarCache) {
35472
+ function project(rows, columns, scalarCache, resolveFieldType) {
35385
35473
  if (columns.length === 1 && columns[0].type === "WILDCARD") {
35386
35474
  const projected2 = rows.map((row) => stripParentShortcutColumns(row));
35387
35475
  const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [];
@@ -35442,7 +35530,7 @@ function project(rows, columns, scalarCache) {
35442
35530
  }
35443
35531
  case "CASE_COL": {
35444
35532
  const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
35445
- out[key] = evalCaseWhen(col.expr, row);
35533
+ out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
35446
35534
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
35447
35535
  break;
35448
35536
  }
@@ -35578,7 +35666,15 @@ function resolveAggInStringFuncExpr(expr, rows) {
35578
35666
  };
35579
35667
  }
35580
35668
  function runFullScan(input) {
35581
- const { stmt, tables, scalarCache, optionOrders, sortKinds } = input;
35669
+ const {
35670
+ stmt,
35671
+ tables,
35672
+ scalarCache,
35673
+ optionOrders,
35674
+ sortKinds,
35675
+ fieldTypeResolver,
35676
+ havingFieldTypeResolver
35677
+ } = input;
35582
35678
  let rows = [];
35583
35679
  const mainAlias = stmt.from.alias;
35584
35680
  const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
@@ -35589,17 +35685,17 @@ function runFullScan(input) {
35589
35685
  const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
35590
35686
  rows = applyJoin(rows, rightRows, join);
35591
35687
  }
35592
- rows = applyFilter(rows, stmt.where);
35688
+ rows = applyFilter(rows, stmt.where, fieldTypeResolver);
35593
35689
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
35594
35690
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
35595
35691
  }
35596
- rows = applyHaving(rows, stmt.having);
35692
+ rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
35597
35693
  if (stmt.distinct) {
35598
35694
  rows = applyDistinct(rows, stmt.columns);
35599
35695
  }
35600
35696
  rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
35601
35697
  rows = applyLimit(rows, stmt.limit, stmt.offset);
35602
- return project(rows, stmt.columns, scalarCache);
35698
+ return project(rows, stmt.columns, scalarCache, fieldTypeResolver);
35603
35699
  }
35604
35700
 
35605
35701
  // src/converter/subtableAdapter.ts
@@ -35739,6 +35835,8 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
35739
35835
  throw new Error("ArgumentError: DROP TEMP TABLE requires a batch (temp tables are batch-scoped).");
35740
35836
  case "SET_VARIABLE":
35741
35837
  throw new Error("ArgumentError: SET variable requires a batch.");
35838
+ case "DECLARE_VARIABLE":
35839
+ throw new Error("ArgumentError: DECLARE variable requires a batch.");
35742
35840
  case "ASSERT":
35743
35841
  return executeAssert(stmt, client, options, cacheContext);
35744
35842
  }
@@ -35752,6 +35850,8 @@ var BatchTimeoutError = class extends Error {
35752
35850
  async function executeBatch(sql, client, options = {}) {
35753
35851
  const statements = parseSqlBatch(sql);
35754
35852
  const analysis = analyzeBatch(statements);
35853
+ const injectedVariables = validateDeclaredBatchVariables(statements, options.variables);
35854
+ const batchOptions = { ...options, variables: injectedVariables };
35755
35855
  if (options.continueOnError && analysis.containsDml) {
35756
35856
  throw new Error("ArgumentError: continueOnError is not allowed for batches containing DML.");
35757
35857
  }
@@ -35796,16 +35896,16 @@ async function executeBatch(sql, client, options = {}) {
35796
35896
  }
35797
35897
  try {
35798
35898
  const remaining = deadline !== null ? deadline - Date.now() : null;
35799
- const userConfirm = options.confirm;
35899
+ const userConfirm = batchOptions.confirm;
35800
35900
  const stmtOptions = userConfirm ? {
35801
- ...options,
35901
+ ...batchOptions,
35802
35902
  confirm: (count, operation) => userConfirm(count, operation, {
35803
35903
  statementIndex: i,
35804
35904
  statementCount: statements.length,
35805
35905
  statementType: info.statementType,
35806
35906
  targetAppId: info.targetAppId
35807
35907
  })
35808
- } : options;
35908
+ } : batchOptions;
35809
35909
  const outcome = await runWithDeadline(
35810
35910
  executeBatchStatement(statements[i], info, countedClient, stmtOptions, cacheContext, tempTables, variables),
35811
35911
  remaining
@@ -35818,7 +35918,7 @@ async function executeBatch(sql, client, options = {}) {
35818
35918
  aborted2 = "timeout";
35819
35919
  } else if (e instanceof AssertError) {
35820
35920
  aborted2 = "assertion";
35821
- } else if (info.statementType === "SET_VARIABLE") {
35921
+ } else if (info.statementType === "SET_VARIABLE" || info.statementType === "DECLARE_VARIABLE") {
35822
35922
  aborted2 = "fail-fast";
35823
35923
  } else if (!options.continueOnError) {
35824
35924
  aborted2 = "fail-fast";
@@ -35858,6 +35958,16 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
35858
35958
  }
35859
35959
  return {};
35860
35960
  }
35961
+ if (stmt.type === "DECLARE_VARIABLE") {
35962
+ const injected = options.variables ?? {};
35963
+ if (Object.prototype.hasOwnProperty.call(injected, stmt.name)) {
35964
+ variables.set(stmt.name, { type: "string", value: injected[stmt.name] });
35965
+ } else {
35966
+ const value = evaluateScalarExpr(stmt.default);
35967
+ variables.set(stmt.name, { type: "string", value: String(value.value) });
35968
+ }
35969
+ return {};
35970
+ }
35861
35971
  const resolvedStmt = resolveVariableRefs(stmt, variables);
35862
35972
  if (resolvedStmt.type === "CREATE_TEMP_TABLE") {
35863
35973
  const materializeOptions = {
@@ -36118,6 +36228,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache) {
36118
36228
  if (isNoFromSelect(stmt)) {
36119
36229
  return executeNoFromSelect(stmt);
36120
36230
  }
36231
+ await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
36121
36232
  const mode = resolveSelectMode(stmt);
36122
36233
  await validateSelectFieldCodes(stmt, mode, client, cacheContext);
36123
36234
  if (mode === "SIMPLE") {
@@ -36176,6 +36287,8 @@ function executeNoFromSelect(stmt) {
36176
36287
  }
36177
36288
  async function executeSimpleSelect(stmt, client, options, cacheContext) {
36178
36289
  const params = selectToKintoneParams(stmt);
36290
+ const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
36291
+ const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
36179
36292
  const maxRecords2 = options.maxRecords ?? 1e4;
36180
36293
  const warnings = /* @__PURE__ */ new Set();
36181
36294
  const onLimit2 = options.onLimitReached ?? "error";
@@ -36212,7 +36325,12 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
36212
36325
  rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
36213
36326
  rows = applyLimit(rows, stmt.limit, stmt.offset);
36214
36327
  }
36215
- const { rows: projected, columns } = project(rows, stmt.columns);
36328
+ const { rows: projected, columns } = project(
36329
+ rows,
36330
+ stmt.columns,
36331
+ void 0,
36332
+ fieldTypeResolvers.row
36333
+ );
36216
36334
  return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
36217
36335
  }
36218
36336
  async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
@@ -36285,6 +36403,108 @@ async function loadNumericPushdownFieldTypes(stmt, client, cacheContext) {
36285
36403
  const entries = await Promise.all([...appIds].map(async (appId) => [appId, await getFieldTypeMap(appId, client, cacheContext)]));
36286
36404
  return new Map(entries);
36287
36405
  }
36406
+ function collectTypedInFieldRefs(expr, out) {
36407
+ if (expr === null) return;
36408
+ switch (expr.type) {
36409
+ case "BINARY":
36410
+ if ((expr.op === "IN" || expr.op === "NOT_IN") && expr.left.type === "FIELD") {
36411
+ out.push(expr.left);
36412
+ }
36413
+ if (expr.left.type === "CASE_FIELD") collectCaseTypedInFieldRefs(expr.left.expr, out);
36414
+ if (expr.right.type === "CASE_VALUE") collectCaseTypedInFieldRefs(expr.right.expr, out);
36415
+ return;
36416
+ case "LOGICAL":
36417
+ collectTypedInFieldRefs(expr.left, out);
36418
+ collectTypedInFieldRefs(expr.right, out);
36419
+ return;
36420
+ case "NOT":
36421
+ case "GROUP":
36422
+ collectTypedInFieldRefs(expr.expr, out);
36423
+ return;
36424
+ case "NULL_CHECK":
36425
+ case "EXISTS":
36426
+ return;
36427
+ }
36428
+ }
36429
+ function collectCaseTypedInFieldRefs(expr, out) {
36430
+ for (const branch of expr.branches) collectTypedInFieldRefs(branch.condition, out);
36431
+ }
36432
+ function collectSelectTypedInFieldRefs(stmt) {
36433
+ const refs = [];
36434
+ collectTypedInFieldRefs(stmt.where, refs);
36435
+ collectTypedInFieldRefs(stmt.having, refs);
36436
+ for (const column of stmt.columns) {
36437
+ if (column.type === "CASE_COL") collectCaseTypedInFieldRefs(column.expr, refs);
36438
+ }
36439
+ return refs;
36440
+ }
36441
+ function findTableForAlias(stmt, alias) {
36442
+ return [stmt.from, ...stmt.joins.map((join) => join.table)].find((table) => table.alias === alias);
36443
+ }
36444
+ function physicalSelectTables(stmt) {
36445
+ return [stmt.from, ...stmt.joins.map((join) => join.table)].filter((table) => table.cteName === null);
36446
+ }
36447
+ async function loadTypedInFieldTypes(stmt, client, cacheContext) {
36448
+ const refs = collectSelectTypedInFieldRefs(stmt);
36449
+ if (refs.length === 0) return /* @__PURE__ */ new Map();
36450
+ const appIds = /* @__PURE__ */ new Set();
36451
+ const physicalTables = physicalSelectTables(stmt);
36452
+ for (const ref of refs) {
36453
+ if (ref.tableAlias !== null) {
36454
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
36455
+ appIds.add(stmt.from.appId);
36456
+ continue;
36457
+ }
36458
+ const table = findTableForAlias(stmt, ref.tableAlias);
36459
+ if (table && table.cteName === null) appIds.add(table.appId);
36460
+ continue;
36461
+ }
36462
+ if (stmt.joins.length === 0) {
36463
+ if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
36464
+ continue;
36465
+ }
36466
+ for (const table of physicalTables) appIds.add(table.appId);
36467
+ }
36468
+ const entries = await Promise.all([...appIds].map(async (appId) => [appId, await getFieldTypeMap(appId, client, cacheContext)]));
36469
+ return new Map(entries);
36470
+ }
36471
+ function fieldCodeForTypeLookup(table, field) {
36472
+ if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
36473
+ return field;
36474
+ }
36475
+ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
36476
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
36477
+ const physicalTables = tables.filter((table) => table.cteName === null);
36478
+ const outputAliases = new Set(
36479
+ stmt.columns.map((column) => "alias" in column ? column.alias : null).filter((alias) => alias !== null)
36480
+ );
36481
+ const row = (field) => {
36482
+ if (field.tableAlias !== null) {
36483
+ if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
36484
+ return fieldTypesByApp.get(stmt.from.appId)?.get(field.field);
36485
+ }
36486
+ const table2 = tables.find((candidate) => candidate.alias === field.tableAlias);
36487
+ if (!table2 || table2.cteName !== null) return void 0;
36488
+ return fieldTypesByApp.get(table2.appId)?.get(fieldCodeForTypeLookup(table2, field.field));
36489
+ }
36490
+ if (stmt.joins.length === 0) {
36491
+ if (stmt.from.cteName !== null) return void 0;
36492
+ return fieldTypesByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, field.field));
36493
+ }
36494
+ if (tables.some((table2) => table2.cteName !== null)) return void 0;
36495
+ const matches = physicalTables.filter(
36496
+ (table2) => fieldTypesByApp.get(table2.appId)?.has(fieldCodeForTypeLookup(table2, field.field))
36497
+ );
36498
+ if (matches.length !== 1) return void 0;
36499
+ const table = matches[0];
36500
+ return fieldTypesByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field.field));
36501
+ };
36502
+ const having = (field) => {
36503
+ if (field.tableAlias === null && outputAliases.has(field.field)) return void 0;
36504
+ return row(field);
36505
+ };
36506
+ return { row, having };
36507
+ }
36288
36508
  async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
36289
36509
  const maxRecords2 = options.maxRecords ?? 1e4;
36290
36510
  const warnings = /* @__PURE__ */ new Set();
@@ -36293,7 +36513,11 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
36293
36513
  resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
36294
36514
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
36295
36515
  ]);
36296
- const pushdownFieldTypes = await loadNumericPushdownFieldTypes(stmt, client, cacheContext);
36516
+ const [pushdownFieldTypes, typedInFieldTypes] = await Promise.all([
36517
+ loadNumericPushdownFieldTypes(stmt, client, cacheContext),
36518
+ loadTypedInFieldTypes(stmt, client, cacheContext)
36519
+ ]);
36520
+ const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
36297
36521
  const mainPushDown = extractMainSafePushdown(
36298
36522
  stmt,
36299
36523
  pushdownFieldTypes.get(stmt.from.appId)
@@ -36382,7 +36606,15 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
36382
36606
  }));
36383
36607
  const scalarCache = await scalarCachePromise;
36384
36608
  const { optionOrders, sortKinds } = await orderByMetaPromise;
36385
- const { rows, columns } = runFullScan({ tables, stmt, scalarCache, optionOrders, sortKinds });
36609
+ const { rows, columns } = runFullScan({
36610
+ tables,
36611
+ stmt,
36612
+ scalarCache,
36613
+ optionOrders,
36614
+ sortKinds,
36615
+ fieldTypeResolver: fieldTypeResolvers.row,
36616
+ havingFieldTypeResolver: fieldTypeResolvers.having
36617
+ });
36386
36618
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
36387
36619
  }
36388
36620
  async function executeUnion(stmt, client, options, cacheContext) {
@@ -36538,8 +36770,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
36538
36770
  const parallel = options.fetchParallel ?? 1;
36539
36771
  await Promise.all([
36540
36772
  resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
36541
- resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
36773
+ resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
36774
+ resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
36542
36775
  ]);
36776
+ const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
36777
+ const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
36543
36778
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
36544
36779
  const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
36545
36780
  scalarCachePromise.catch(() => {
@@ -36594,7 +36829,15 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
36594
36829
  await Promise.all(joinFetches);
36595
36830
  const scalarCache = await scalarCachePromise;
36596
36831
  const { optionOrders, sortKinds } = await orderByMetaPromise;
36597
- const { rows, columns } = runFullScan({ tables, stmt, scalarCache, optionOrders, sortKinds });
36832
+ const { rows, columns } = runFullScan({
36833
+ tables,
36834
+ stmt,
36835
+ scalarCache,
36836
+ optionOrders,
36837
+ sortKinds,
36838
+ fieldTypeResolver: fieldTypeResolvers.row,
36839
+ havingFieldTypeResolver: fieldTypeResolvers.having
36840
+ });
36598
36841
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
36599
36842
  }
36600
36843
  function processRowToKintoneRecord(row) {
@@ -37091,6 +37334,16 @@ async function executeUpsert(stmt, client, options, cacheContext) {
37091
37334
  updatedCount: toUpdate.length
37092
37335
  };
37093
37336
  }
37337
+ async function buildSubtableFieldTypeResolver(appId, typedInRefs, client, cacheContext) {
37338
+ if (typedInRefs.length === 0) return void 0;
37339
+ const fieldTypes = await getFieldTypeMap(appId, client, cacheContext);
37340
+ return (field) => {
37341
+ if (field.tableAlias !== null && field.tableAlias !== "_p") return void 0;
37342
+ if (field.tableAlias === "_p") return fieldTypes.get(field.field);
37343
+ const code = field.field.startsWith("_p.") ? field.field.slice(3) : field.field;
37344
+ return fieldTypes.get(code);
37345
+ };
37346
+ }
37094
37347
  async function executeInsertSubtable(stmt, client, options, _cacheContext) {
37095
37348
  const subtableCode = stmt.subtableCode;
37096
37349
  const pidIndex = stmt.fields.indexOf("_pid");
@@ -37131,11 +37384,24 @@ async function executeInsertSubtable(stmt, client, options, _cacheContext) {
37131
37384
  }
37132
37385
  return { type: "INSERT", createdIds: [], insertedCount: stmt.values.length };
37133
37386
  }
37134
- async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
37387
+ async function executeUpdateSubtable(stmt, client, options, cacheContext) {
37135
37388
  const subtableCode = stmt.subtableCode;
37136
37389
  if (!hasRidCondition(stmt.where)) {
37137
37390
  throw new Error("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u306F _rid \u6761\u4EF6\u304C\u5FC5\u9808\u3067\u3059");
37138
37391
  }
37392
+ const typedInRefs = [];
37393
+ collectTypedInFieldRefs(stmt.where, typedInRefs);
37394
+ for (const assignment of stmt.assignments) {
37395
+ if (assignment.value.type === "CASE_VALUE") {
37396
+ collectCaseTypedInFieldRefs(assignment.value.expr, typedInRefs);
37397
+ }
37398
+ }
37399
+ const resolveFieldType = await buildSubtableFieldTypeResolver(
37400
+ stmt.appId,
37401
+ typedInRefs,
37402
+ client,
37403
+ cacheContext
37404
+ );
37139
37405
  const parents = await fetchAll(
37140
37406
  client.getRecords,
37141
37407
  stmt.appId,
@@ -37144,7 +37410,7 @@ async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
37144
37410
  { maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
37145
37411
  );
37146
37412
  const expanded = expandRowsForSubtableDml(parents, subtableCode);
37147
- const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat));
37413
+ const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat, resolveFieldType));
37148
37414
  if (options.confirm) {
37149
37415
  const ok = await options.confirm(targets.length, "UPDATE");
37150
37416
  if (!ok) throw new OperationCancelledError("UPDATE", targets.length);
@@ -37164,7 +37430,7 @@ async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
37164
37430
  if (a.field.startsWith("_")) {
37165
37431
  throw new Error(`\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u3067\u30B7\u30B9\u30C6\u30E0\u5217\u300C${a.field}\u300D\u306F\u66F4\u65B0\u3067\u304D\u307E\u305B\u3093`);
37166
37432
  }
37167
- updates[a.field] = { value: evalAssignmentValueForSubtable(a.value, t.flat) };
37433
+ updates[a.field] = { value: evalAssignmentValueForSubtable(a.value, t.flat, resolveFieldType) };
37168
37434
  }
37169
37435
  byRid.set(t.rowId, updates);
37170
37436
  }
@@ -37186,11 +37452,19 @@ async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
37186
37452
  }
37187
37453
  return { type: "UPDATE", updatedCount: targets.length };
37188
37454
  }
37189
- async function executeDeleteSubtable(stmt, client, options, _cacheContext) {
37455
+ async function executeDeleteSubtable(stmt, client, options, cacheContext) {
37190
37456
  const subtableCode = stmt.subtableCode;
37191
37457
  if (!hasRidCondition(stmt.where)) {
37192
37458
  throw new Error("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB DELETE \u306B\u306F _rid \u6761\u4EF6\u304C\u5FC5\u9808\u3067\u3059");
37193
37459
  }
37460
+ const typedInRefs = [];
37461
+ collectTypedInFieldRefs(stmt.where, typedInRefs);
37462
+ const resolveFieldType = await buildSubtableFieldTypeResolver(
37463
+ stmt.appId,
37464
+ typedInRefs,
37465
+ client,
37466
+ cacheContext
37467
+ );
37194
37468
  const parents = await fetchAll(
37195
37469
  client.getRecords,
37196
37470
  stmt.appId,
@@ -37199,7 +37473,7 @@ async function executeDeleteSubtable(stmt, client, options, _cacheContext) {
37199
37473
  { maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
37200
37474
  );
37201
37475
  const expanded = expandRowsForSubtableDml(parents, subtableCode);
37202
- const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat));
37476
+ const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat, resolveFieldType));
37203
37477
  if (options.confirm) {
37204
37478
  const ok = await options.confirm(targets.length, "DELETE");
37205
37479
  if (!ok) throw new OperationCancelledError("DELETE", targets.length);
@@ -37314,11 +37588,11 @@ function buildSubtableReorderPutParams(appId, parentId, revision, subtableCode,
37314
37588
  ]
37315
37589
  };
37316
37590
  }
37317
- function evalAssignmentValueForSubtable(value, row) {
37591
+ function evalAssignmentValueForSubtable(value, row, resolveFieldType) {
37318
37592
  if (value.type === "STRING") return value.value;
37319
37593
  if (value.type === "NUMBER") return String(value.value);
37320
37594
  if (value.type === "ARITH") return String(evalArithExpr(value, row));
37321
- if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row);
37595
+ if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row, resolveFieldType);
37322
37596
  throw new Error(`${value.type} \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E\u5024\u3068\u3057\u3066\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
37323
37597
  }
37324
37598
  function valueToString(value) {
@@ -37350,7 +37624,15 @@ function hasRidCondition(where) {
37350
37624
  return false;
37351
37625
  }
37352
37626
  }
37353
- async function executeReorder(stmt, client, options, _cacheContext) {
37627
+ async function executeReorder(stmt, client, options, cacheContext) {
37628
+ const typedInRefs = [];
37629
+ collectTypedInFieldRefs(stmt.where, typedInRefs);
37630
+ const resolveFieldType = await buildSubtableFieldTypeResolver(
37631
+ stmt.appId,
37632
+ typedInRefs,
37633
+ client,
37634
+ cacheContext
37635
+ );
37354
37636
  const parents = await fetchAll(
37355
37637
  client.getRecords,
37356
37638
  stmt.appId,
@@ -37359,7 +37641,7 @@ async function executeReorder(stmt, client, options, _cacheContext) {
37359
37641
  { maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
37360
37642
  );
37361
37643
  const expanded = expandRowsForSubtableDml(parents, stmt.subtableCode);
37362
- const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(stmt.where, r.flat)).map((r) => r.parentId));
37644
+ const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(stmt.where, r.flat, resolveFieldType)).map((r) => r.parentId));
37363
37645
  if (options.confirm) {
37364
37646
  const ok = await options.confirm(targetParentIds.size, "UPDATE");
37365
37647
  if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
@@ -37504,6 +37786,16 @@ async function resolveSubqueries(where, client, options, cacheContext, cteCache)
37504
37786
  collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache);
37505
37787
  await Promise.all(tasks);
37506
37788
  }
37789
+ async function resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache) {
37790
+ const tasks = [];
37791
+ for (const column of stmt.columns) {
37792
+ if (column.type !== "CASE_COL") continue;
37793
+ for (const branch of column.expr.branches) {
37794
+ tasks.push(resolveSubqueries(branch.condition, client, options, cacheContext, cteCache));
37795
+ }
37796
+ }
37797
+ await Promise.all(tasks);
37798
+ }
37507
37799
  function runSubquery(query, client, options, cacheContext, cteCache) {
37508
37800
  if (cteCache !== void 0 && cteCache.size > 0) {
37509
37801
  return executeQueryWithCte(query, client, options, cteCache, cacheContext);
@@ -37583,9 +37875,10 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
37583
37875
  pending.forEach(([i], idx) => cache.set(i, values[idx]));
37584
37876
  return cache;
37585
37877
  }
37586
- function buildBatchExplainPlans(sql) {
37878
+ function buildBatchExplainPlans(sql, injectedVariables) {
37587
37879
  const statements = parseSqlBatch(sql);
37588
37880
  const analysis = analyzeBatch(statements);
37881
+ validateDeclaredBatchVariables(statements, injectedVariables);
37589
37882
  const variables = /* @__PURE__ */ new Map();
37590
37883
  return {
37591
37884
  statementCount: statements.length,
@@ -37596,7 +37889,7 @@ function buildBatchExplainPlans(sql) {
37596
37889
  type: analysis.statements[i].statementType,
37597
37890
  plan: buildBatchStatementPlan(planStmt, analysis.statements[i])
37598
37891
  };
37599
- if (stmt.type === "SET_VARIABLE") {
37892
+ if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
37600
37893
  variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
37601
37894
  }
37602
37895
  return result;
@@ -37633,6 +37926,12 @@ function buildBatchStatementPlan(stmt, info) {
37633
37926
  " value: \u5B9F\u884C\u6642\u306B1\u56DE\u8A55\u4FA1\uFF08\u30D0\u30C3\u30C1\u5185\u5B9A\u6570\u30FB\u7D50\u679C\u30E1\u30BF\u30C7\u30FC\u30BF\u306B\u306F\u975E\u516C\u958B\uFF09"
37634
37927
  ];
37635
37928
  }
37929
+ if (stmt.type === "DECLARE_VARIABLE") {
37930
+ return [
37931
+ `DECLARE @${stmt.name} = <default scalar expression>`,
37932
+ " value: \u5916\u90E8\u6CE8\u5165\u304C\u3042\u308C\u3070\u63A1\u7528\u3001\u306A\u3051\u308C\u3070\u65E2\u5B9A\u5024\u3092\u5B9F\u884C\u6642\u306B1\u56DE\u8A55\u4FA1\uFF08\u5024\u306F\u975E\u516C\u958B\uFF09"
37933
+ ];
37934
+ }
37636
37935
  if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
37637
37936
  if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
37638
37937
  if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
@@ -38419,6 +38718,42 @@ function clampInt(v, min, max) {
38419
38718
  return Math.max(min, Math.min(max, Math.trunc(v)));
38420
38719
  }
38421
38720
 
38721
+ // src/core/formFieldInfo.ts
38722
+ function flattenFormFieldProperties(properties) {
38723
+ const out = [];
38724
+ for (const field of Object.values(properties)) {
38725
+ out.push({
38726
+ code: field.code,
38727
+ label: field.label,
38728
+ fieldType: field.type,
38729
+ optionOrder: toOptionOrderMap(field.options),
38730
+ sortKind: detectSortKind(field.type, field.format)
38731
+ });
38732
+ if (field.fields) out.push(...flattenFormFieldProperties(field.fields));
38733
+ }
38734
+ return out;
38735
+ }
38736
+ function toOptionOrderMap(options) {
38737
+ if (!options || typeof options !== "object") return void 0;
38738
+ const order = {};
38739
+ let hasAny = false;
38740
+ for (const [label, meta3] of Object.entries(options)) {
38741
+ const n = Number(meta3?.index);
38742
+ if (!Number.isFinite(n)) continue;
38743
+ order[label] = n;
38744
+ hasAny = true;
38745
+ }
38746
+ return hasAny ? order : void 0;
38747
+ }
38748
+ function detectSortKind(fieldType, calcFormat) {
38749
+ if (fieldType === "NUMBER" || fieldType === "RECORD_NUMBER") return "number";
38750
+ if (fieldType === "CALC") {
38751
+ if (calcFormat === "NUMBER" || calcFormat === "NUMBER_DIGIT") return "number";
38752
+ return "string";
38753
+ }
38754
+ return void 0;
38755
+ }
38756
+
38422
38757
  // src/cli/nodeKintoneClient.ts
38423
38758
  function createNodeKintoneClient(baseUrl, tokenResolver) {
38424
38759
  const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
@@ -38597,36 +38932,10 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
38597
38932
  { method: "GET" },
38598
38933
  appId
38599
38934
  );
38600
- return Object.values(res.properties).map((f) => ({
38601
- code: f.code,
38602
- label: f.label,
38603
- fieldType: f.type,
38604
- optionOrder: toOptionOrderMap(f.options),
38605
- sortKind: detectSortKind(f.type, f.format)
38606
- }));
38935
+ return flattenFormFieldProperties(res.properties);
38607
38936
  }
38608
38937
  };
38609
38938
  }
38610
- function toOptionOrderMap(options) {
38611
- if (!options || typeof options !== "object") return void 0;
38612
- const order = {};
38613
- let hasAny = false;
38614
- for (const [label, meta3] of Object.entries(options)) {
38615
- const n = Number(meta3?.index);
38616
- if (!Number.isFinite(n)) continue;
38617
- order[label] = n;
38618
- hasAny = true;
38619
- }
38620
- return hasAny ? order : void 0;
38621
- }
38622
- function detectSortKind(fieldType, calcFormat) {
38623
- if (fieldType === "NUMBER" || fieldType === "RECORD_NUMBER") return "number";
38624
- if (fieldType === "CALC") {
38625
- if (calcFormat === "NUMBER" || calcFormat === "NUMBER_DIGIT") return "number";
38626
- return "string";
38627
- }
38628
- return void 0;
38629
- }
38630
38939
 
38631
38940
  // src/node/appProfiles.ts
38632
38941
  function parseTokenMap(raw) {
@@ -39611,6 +39920,9 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
39611
39920
  }
39612
39921
  async function query(input, validated) {
39613
39922
  const validation = validated ?? await validate(input);
39923
+ if (!validation.batch && input.variables && Object.keys(input.variables).length > 0) {
39924
+ throw new Error("ArgumentError: variables require a batch containing DECLARE.");
39925
+ }
39614
39926
  if (validation.batch) {
39615
39927
  if (validation.containsDml) {
39616
39928
  throw new Error("ArgumentError: batch contains DML statements. Use ksql_mutate.");
@@ -39637,7 +39949,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
39637
39949
  // バッチでは timeout を合計タイムアウトとして扱う(仕様 §5.7)。
39638
39950
  // runtime.timeout は env / profile / 既定 30000ms を解決済みの値で、
39639
39951
  // HTTP クライアント側の per-request タイムアウトと同値になる
39640
- timeoutMs: runtime2.timeout
39952
+ timeoutMs: runtime2.timeout,
39953
+ variables: input.variables
39641
39954
  });
39642
39955
  return { ...buildBatchEnvelope(batchResult, { maxTotalRecords: input.maxTotalRecords }) };
39643
39956
  }
@@ -39726,6 +40039,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
39726
40039
  tempTableMaxRows: runtime.tempTableMaxRows,
39727
40040
  // 合計タイムアウト(解決済みの runtime.timeout。per-request と同値)
39728
40041
  timeoutMs: runtime.timeout,
40042
+ variables: input.variables,
39729
40043
  confirm: async (count, operation) => {
39730
40044
  if (count > dmlMaxRows) {
39731
40045
  throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows}).`);
@@ -39754,6 +40068,9 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
39754
40068
  async function mutate(input, validated) {
39755
40069
  const dmlMaxRows = requireDmlApproval(input, "ksql_mutate");
39756
40070
  const validation = validated ?? await validate(input);
40071
+ if (!validation.batch && input.variables && Object.keys(input.variables).length > 0) {
40072
+ throw new Error("ArgumentError: variables require a batch containing DECLARE.");
40073
+ }
39757
40074
  if (validation.batch) {
39758
40075
  return mutateBatch(input, validation, dmlMaxRows);
39759
40076
  }
@@ -39976,7 +40293,8 @@ var queryInputSchema = external_exports.object({
39976
40293
  tempTableMaxRows,
39977
40294
  timeout,
39978
40295
  continueOnError: external_exports.boolean().describe("Batch (multi-statement) only: keep executing subsequent statements after a runtime error (default false = fail-fast).").optional(),
39979
- maxTotalRecords: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total rows returned across all result sets (default: unlimited).").optional()
40296
+ maxTotalRecords: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total rows returned across all result sets (default: unlimited).").optional(),
40297
+ variables: external_exports.record(external_exports.string(), external_exports.string()).describe("Batch only: string values for variables declared with DECLARE. Keys omit @ and are case-insensitive.").optional()
39980
40298
  });
39981
40299
  var mutateInputSchema = external_exports.object({
39982
40300
  sql: external_exports.string().min(1).describe("DML kSQL text. May contain multiple ;-separated statements (batch) with temp tables, e.g. CREATE TEMP TABLE #t AS SELECT ...; INSERT INTO APPx (...) SELECT ... FROM #t;"),
@@ -39987,7 +40305,8 @@ var mutateInputSchema = external_exports.object({
39987
40305
  fetchParallel,
39988
40306
  tempTableMaxRows,
39989
40307
  timeout,
39990
- dmlTotalMaxRows: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total affected rows across the whole batch (default: per-statement dmlMaxRows only). DML batches always run fail-fast.").optional()
40308
+ dmlTotalMaxRows: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total affected rows across the whole batch (default: per-statement dmlMaxRows only). DML batches always run fail-fast.").optional(),
40309
+ variables: external_exports.record(external_exports.string(), external_exports.string()).describe("Batch only: string values for variables declared with DECLARE. Keys omit @ and are case-insensitive.").optional()
39991
40310
  });
39992
40311
  var describeAppInputSchema = external_exports.object({
39993
40312
  app: external_exports.number().int().positive().describe("kintone app ID to describe."),
@@ -40076,7 +40395,7 @@ Options:
40076
40395
  -h, --help Show help
40077
40396
  `);
40078
40397
  }
40079
- var SERVER_VERSION = true ? "2.3.0" : "0.0.0-dev";
40398
+ var SERVER_VERSION = true ? "2.5.0" : "0.0.0-dev";
40080
40399
  function createServer(args) {
40081
40400
  const server = new McpServer({
40082
40401
  name: "ksql-mcp",