@rex0220/kintone-sql-tools 3.67.0 → 3.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cli/ksql.js CHANGED
@@ -475,7 +475,7 @@ var Lexer = class {
475
475
  // ヘルパー
476
476
  // ----------------------------------------------------------
477
477
  makeToken(kind, value, pos) {
478
- return { kind, value, pos };
478
+ return { kind, value, pos, end: this.pos };
479
479
  }
480
480
  };
481
481
  function isIdentStart(ch) {
@@ -677,6 +677,75 @@ function resolveGroupingSpec(stmt, resolve2) {
677
677
  };
678
678
  }
679
679
 
680
+ // src/core/asOfClock.ts
681
+ var AS_OF_FUNCTION_NAMES = [
682
+ "NOW",
683
+ "TODAY",
684
+ "MONTH_START",
685
+ "NEXT_MONTH_START"
686
+ ];
687
+ var AS_OF_VARIABLE_PREFIX = "\0as-of:";
688
+ function asOfVariableName(name) {
689
+ return `${AS_OF_VARIABLE_PREFIX}${name}`;
690
+ }
691
+ function asOfFunctionNameFromVariable(name) {
692
+ if (!name.startsWith(AS_OF_VARIABLE_PREFIX)) return null;
693
+ const candidate = name.slice(AS_OF_VARIABLE_PREFIX.length);
694
+ return isAsOfFunctionName(candidate) ? candidate : null;
695
+ }
696
+ function isAsOfFunctionName(name) {
697
+ return AS_OF_FUNCTION_NAMES.includes(name);
698
+ }
699
+ function createAsOfClock(asOf = /* @__PURE__ */ new Date(), timezone) {
700
+ if (!(asOf instanceof Date) || !Number.isFinite(asOf.getTime())) {
701
+ throw new Error("ArgumentError: asOf must be a valid Date.");
702
+ }
703
+ let formatter;
704
+ try {
705
+ formatter = new Intl.DateTimeFormat("en-CA", {
706
+ ...timezone === void 0 ? {} : { timeZone: timezone },
707
+ year: "numeric",
708
+ month: "2-digit",
709
+ day: "2-digit"
710
+ });
711
+ formatter.format(asOf);
712
+ } catch {
713
+ throw new Error(`ArgumentError: invalid IANA timezone: ${timezone ?? ""}.`);
714
+ }
715
+ const parts = formatter.formatToParts(asOf);
716
+ const year = partNumber(parts, "year");
717
+ const month = partNumber(parts, "month");
718
+ const day = partNumber(parts, "day");
719
+ const today = `${pad4(year)}-${pad2(month)}-${pad2(day)}`;
720
+ const monthStart = `${pad4(year)}-${pad2(month)}-01`;
721
+ const nextYear = month === 12 ? year + 1 : year;
722
+ const nextMonth = month === 12 ? 1 : month + 1;
723
+ return {
724
+ asOf: new Date(asOf.getTime()),
725
+ ...timezone === void 0 ? {} : { timezone },
726
+ values: {
727
+ NOW: asOf.toISOString(),
728
+ TODAY: today,
729
+ MONTH_START: monthStart,
730
+ NEXT_MONTH_START: `${pad4(nextYear)}-${pad2(nextMonth)}-01`
731
+ }
732
+ };
733
+ }
734
+ function partNumber(parts, type) {
735
+ const value = parts.find((part) => part.type === type)?.value;
736
+ const parsed = Number(value);
737
+ if (!Number.isInteger(parsed)) {
738
+ throw new Error(`InternalError: Intl.DateTimeFormat did not return ${type}.`);
739
+ }
740
+ return parsed;
741
+ }
742
+ function pad2(value) {
743
+ return String(value).padStart(2, "0");
744
+ }
745
+ function pad4(value) {
746
+ return String(value).padStart(4, "0");
747
+ }
748
+
680
749
  // src/core/aggregateExpression.ts
681
750
  function quote(value) {
682
751
  return `'${value.replace(/'/g, "''")}'`;
@@ -1082,6 +1151,8 @@ var Parser = class {
1082
1151
  this.allowRelativeDateFunctions = false;
1083
1152
  /** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
1084
1153
  this.cteNames = /* @__PURE__ */ new Set();
1154
+ /** dialect 1 の裸名で宣言された一時テーブル名(参照時に # 付きへ正規化) */
1155
+ this.bareTempTableNames = /* @__PURE__ */ new Set();
1085
1156
  /** パース中に出現した一時テーブル参照(#name)のトークン。単文 API での拒否に使う */
1086
1157
  this.tempTableRefs = [];
1087
1158
  /** GROUP BY を読む前に作る B124 候補 leaf の診断位置。AST 公開型へ位置情報を足さない。 */
@@ -1093,6 +1164,19 @@ var Parser = class {
1093
1164
  this.activeCteDefinition = null;
1094
1165
  this.provisionalRecursiveCte = null;
1095
1166
  this.allowSelectArithVariable = false;
1167
+ if (capabilities.dialect1) {
1168
+ for (let index = 0; index + 1 < tokens.length; index++) {
1169
+ const token = tokens[index];
1170
+ if (token.kind !== "VARIABLE" /* VARIABLE */ || tokens[index + 1].kind !== "(" /* LPAREN */) continue;
1171
+ const name = token.value.slice(1).toUpperCase();
1172
+ if (!isAsOfFunctionName(name)) {
1173
+ throw new ParseError(
1174
+ `\u4F7F\u7528\u53EF\u80FD\u306A as-of \u95A2\u6570\u306F ${AS_OF_FUNCTION_NAMES.map((item) => `@${item}`).join("/")} \u3067\u3059`,
1175
+ token
1176
+ );
1177
+ }
1178
+ }
1179
+ }
1096
1180
  }
1097
1181
  // ----------------------------------------------------------
1098
1182
  // 公開 API
@@ -1120,7 +1204,12 @@ var Parser = class {
1120
1204
  }
1121
1205
  /** 複文(`;` 区切り)をパースする。空文はスキップする */
1122
1206
  parseStatements() {
1207
+ return this.parseStatementsWithRanges().statements;
1208
+ }
1209
+ /** 複文と、原文上の文ごとの文字範囲を同時に返す。 */
1210
+ parseStatementsWithRanges() {
1123
1211
  const stmts = [];
1212
+ const statementRanges = [];
1124
1213
  while (true) {
1125
1214
  while (this.peek().kind === ";" /* SEMICOLON */) this.advance();
1126
1215
  if (this.peek().kind === "EOF" /* EOF */) break;
@@ -1136,9 +1225,11 @@ var Parser = class {
1136
1225
  if (after.kind !== ";" /* SEMICOLON */ && after.kind !== "EOF" /* EOF */) {
1137
1226
  throw new ParseError("\u6587\u306E\u533A\u5207\u308A\u306B\u306F ; \u304C\u5FC5\u8981\u3067\u3059", after);
1138
1227
  }
1228
+ const lastTok = this.prev();
1229
+ statementRanges.push({ start: startTok.pos, end: lastTok.end ?? after.pos });
1139
1230
  }
1140
1231
  this.expect("EOF" /* EOF */);
1141
- return stmts;
1232
+ return { statements: stmts, statementRanges };
1142
1233
  }
1143
1234
  // ----------------------------------------------------------
1144
1235
  // Statement ディスパッチ
@@ -1177,6 +1268,13 @@ var Parser = class {
1177
1268
  if (upper === "DROP") return this.parseDropTempTable();
1178
1269
  if (upper === "DECLARE") return this.parseDeclareVariable();
1179
1270
  if (upper === "VALIDATE") return this.parseValidate();
1271
+ if (upper === "EXIT") return this.parseExit();
1272
+ if (upper === "MERGE") {
1273
+ if (!this.capabilities.dialect1) {
1274
+ throw new ParseError("MERGE \u306B\u306F -- @ksql dialect: 1 \u306E\u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059", tok);
1275
+ }
1276
+ return this.parseMergeAsUpsert();
1277
+ }
1180
1278
  if (upper === "GENERATE_SERIES") {
1181
1279
  throw new ParseError(
1182
1280
  "GENERATE_SERIES \u306F WITH \u306E CTE \u672C\u4F53\u306B\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B: WITH s AS (GENERATE_SERIES(1, 5)) SELECT generate_series FROM s",
@@ -1195,7 +1293,7 @@ var Parser = class {
1195
1293
  break;
1196
1294
  }
1197
1295
  throw new ParseError(
1198
- "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",
1296
+ "SELECT / INSERT / UPDATE / DELETE / REORDER / VALIDATE / WITH / SHOW / DESCRIBE / EXPLAIN / CREATE TEMP TABLE / DROP TEMP TABLE / SET / DECLARE / ASSERT / EXIT \u306E\u3044\u305A\u308C\u304B\u3067\u59CB\u307E\u308B SQL \u6587\u304C\u5FC5\u8981\u3067\u3059",
1199
1297
  tok
1200
1298
  );
1201
1299
  }
@@ -1248,6 +1346,10 @@ var Parser = class {
1248
1346
  parseScalarExpr(context, allowScalarSubquery) {
1249
1347
  const tok = this.peek();
1250
1348
  if (tok.kind === "VARIABLE" /* VARIABLE */) {
1349
+ if (this.peekAt(1).kind === "(" /* LPAREN */ && this.capabilities.dialect1) {
1350
+ this.advance();
1351
+ return this.finishVariableReference(tok);
1352
+ }
1251
1353
  throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F\u4ED6\u306E\u5909\u6570\u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093`, tok);
1252
1354
  }
1253
1355
  if (tok.kind === "NULL" /* NULL */) {
@@ -1323,6 +1425,7 @@ var Parser = class {
1323
1425
  this.advance();
1324
1426
  this.expectSoftKeyword("TEMP", "CREATE \u306E\u5F8C\u306B\u306F TEMP TABLE \u304C\u5FC5\u8981\u3067\u3059\uFF08\u4F8B: CREATE TEMP TABLE #temp AS SELECT ...\uFF09");
1325
1427
  this.expectSoftKeyword("TABLE", "CREATE TEMP \u306E\u5F8C\u306B\u306F TABLE \u304C\u5FC5\u8981\u3067\u3059");
1428
+ const bareName = this.isDialect1BareTempTableName();
1326
1429
  const name = this.parseTempTableName();
1327
1430
  this.expect("AS" /* AS */, "CREATE TEMP TABLE \u306B\u306F AS SELECT \u304C\u5FC5\u8981\u3067\u3059");
1328
1431
  const tok = this.peek();
@@ -1334,13 +1437,16 @@ var Parser = class {
1334
1437
  } else {
1335
1438
  throw new ParseError("CREATE TEMP TABLE ... AS \u306E\u5F8C\u306B\u306F SELECT / WITH \u304C\u5FC5\u8981\u3067\u3059", tok);
1336
1439
  }
1440
+ if (bareName !== null) this.bareTempTableNames.add(bareName);
1337
1441
  return { type: "CREATE_TEMP_TABLE", name, query };
1338
1442
  }
1339
1443
  parseDropTempTable() {
1340
1444
  this.advance();
1341
1445
  this.expectSoftKeyword("TEMP", "DROP \u306E\u5F8C\u306B\u306F TEMP TABLE \u304C\u5FC5\u8981\u3067\u3059\uFF08\u4F8B: DROP TEMP TABLE #temp\uFF09");
1342
1446
  this.expectSoftKeyword("TABLE", "DROP TEMP \u306E\u5F8C\u306B\u306F TABLE \u304C\u5FC5\u8981\u3067\u3059");
1447
+ const bareName = this.isDialect1BareTempTableName();
1343
1448
  const name = this.parseTempTableName();
1449
+ if (bareName !== null) this.bareTempTableNames.delete(bareName);
1344
1450
  return { type: "DROP_TEMP_TABLE", name };
1345
1451
  }
1346
1452
  expectSoftKeyword(word, msg) {
@@ -1362,8 +1468,16 @@ var Parser = class {
1362
1468
  this.advance();
1363
1469
  return tok.value;
1364
1470
  }
1471
+ if (this.capabilities.dialect1 && (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */)) {
1472
+ this.advance();
1473
+ return `#${tok.value}`;
1474
+ }
1365
1475
  throw new ParseError("\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u540D\u306F # \u3067\u59CB\u307E\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u4F8B: #temp\uFF09", tok);
1366
1476
  }
1477
+ isDialect1BareTempTableName() {
1478
+ const tok = this.peek();
1479
+ return this.capabilities.dialect1 && (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) && !tok.value.startsWith("#") ? tok.value : null;
1480
+ }
1367
1481
  parseShow() {
1368
1482
  this.advance();
1369
1483
  if (!this.consume("APPS" /* APPS */)) {
@@ -1691,6 +1805,41 @@ var Parser = class {
1691
1805
  // ----------------------------------------------------------
1692
1806
  parseAssert() {
1693
1807
  this.expect("ASSERT" /* ASSERT */);
1808
+ const warnTok = this.peek();
1809
+ const warn = this.isSoftKeyword("WARN");
1810
+ if (warn) {
1811
+ this.requireDialect1(warnTok);
1812
+ this.advance();
1813
+ }
1814
+ const condition = this.parseAssertCondition();
1815
+ const message = this.parseFlowMessage();
1816
+ return {
1817
+ type: "ASSERT",
1818
+ ...condition,
1819
+ ...warn ? { warn: true } : {},
1820
+ ...message !== void 0 ? { message } : {}
1821
+ };
1822
+ }
1823
+ /** EXIT SUCCESS IF <ASSERT と同じ条件>, '<message>' */
1824
+ parseExit() {
1825
+ const exitTok = this.advance();
1826
+ this.requireDialect1(exitTok);
1827
+ if (!this.isSoftKeyword("SUCCESS")) {
1828
+ throw new ParseError("EXIT \u306E\u5F8C\u306B\u306F SUCCESS \u304C\u5FC5\u8981\u3067\u3059", this.peek());
1829
+ }
1830
+ this.advance();
1831
+ if (this.peek().kind !== "IF" /* IF */ && !this.isSoftKeyword("IF")) {
1832
+ throw new ParseError("EXIT SUCCESS \u306E\u5F8C\u306B\u306F IF \u304C\u5FC5\u8981\u3067\u3059", this.peek());
1833
+ }
1834
+ this.advance();
1835
+ const condition = this.parseAssertCondition();
1836
+ if (!this.consume("," /* COMMA */)) {
1837
+ throw new ParseError("EXIT SUCCESS IF \u306B\u306F\u672B\u5C3E\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u6587\u5B57\u5217\u304C\u5FC5\u8981\u3067\u3059", this.peek());
1838
+ }
1839
+ const message = this.expect("STRING" /* STRING */, "EXIT SUCCESS IF \u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
1840
+ return { type: "EXIT", ...condition, message: message.value };
1841
+ }
1842
+ parseAssertCondition() {
1694
1843
  const condStart = this.pos;
1695
1844
  const left = this.parseAssertOperand();
1696
1845
  const opTok = this.peek();
@@ -1703,7 +1852,6 @@ var Parser = class {
1703
1852
  const high = this.parseAssertOperand();
1704
1853
  this.rejectAssertCompound();
1705
1854
  return {
1706
- type: "ASSERT",
1707
1855
  left,
1708
1856
  op: "BETWEEN",
1709
1857
  right: null,
@@ -1722,7 +1870,6 @@ var Parser = class {
1722
1870
  const right = this.parseAssertOperand();
1723
1871
  this.rejectAssertCompound();
1724
1872
  return {
1725
- type: "ASSERT",
1726
1873
  left,
1727
1874
  op,
1728
1875
  right,
@@ -1731,12 +1878,39 @@ var Parser = class {
1731
1878
  text: this.renderTokenRange(condStart, this.pos)
1732
1879
  };
1733
1880
  }
1881
+ /** ASSERT の dialect 1 メッセージ。カンマが無ければ既存形式。 */
1882
+ parseFlowMessage() {
1883
+ if (!this.consume("," /* COMMA */)) return void 0;
1884
+ this.requireDialect1(this.prev());
1885
+ return this.expect("STRING" /* STRING */, "ASSERT \u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059").value;
1886
+ }
1887
+ requireDialect1(tok) {
1888
+ if (!this.capabilities.dialect1) {
1889
+ throw new ParseError("\u3053\u306E\u69CB\u6587\u306B\u306F -- @ksql dialect: 1 \u306E\u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059", tok);
1890
+ }
1891
+ }
1892
+ /** VARIABLE + `()` is the dialect-1 as-of call syntax; a bare VARIABLE stays unchanged. */
1893
+ finishVariableReference(tok) {
1894
+ const ordinary = { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
1895
+ if (this.peek().kind !== "(" /* LPAREN */) return ordinary;
1896
+ if (!this.capabilities.dialect1) return ordinary;
1897
+ const name = tok.value.slice(1).toUpperCase();
1898
+ if (!isAsOfFunctionName(name)) {
1899
+ throw new ParseError(
1900
+ `\u4F7F\u7528\u53EF\u80FD\u306A as-of \u95A2\u6570\u306F ${AS_OF_FUNCTION_NAMES.map((item) => `@${item}`).join("/")} \u3067\u3059`,
1901
+ tok
1902
+ );
1903
+ }
1904
+ this.advance();
1905
+ this.expect(")" /* RPAREN */, `@${name} \u306F\u5F15\u6570\u306A\u3057\u306E () \u3067\u547C\u3073\u51FA\u3057\u3066\u304F\u3060\u3055\u3044`);
1906
+ return { type: "VARIABLE", name: asOfVariableName(name) };
1907
+ }
1734
1908
  /** ASSERT のオペランド: 文字列 / スカラーサブクエリ / 数値算術式 */
1735
1909
  parseAssertOperand() {
1736
1910
  const tok = this.peek();
1737
1911
  if (tok.kind === "VARIABLE" /* VARIABLE */) {
1738
1912
  this.advance();
1739
- return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
1913
+ return this.finishVariableReference(tok);
1740
1914
  }
1741
1915
  if (tok.kind === "STRING" /* STRING */) {
1742
1916
  this.advance();
@@ -2110,7 +2284,7 @@ var Parser = class {
2110
2284
  }
2111
2285
  if (tok.kind === "VARIABLE" /* VARIABLE */) {
2112
2286
  this.advance();
2113
- args.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
2287
+ args.push(this.finishVariableReference(tok));
2114
2288
  continue;
2115
2289
  }
2116
2290
  let sign = "";
@@ -2195,15 +2369,21 @@ var Parser = class {
2195
2369
  return this.withAliasDisplay({ type: "SCALAR_VALUE_COL", expr, alias: parsedAlias2?.alias ?? null }, parsedAlias2);
2196
2370
  }
2197
2371
  if (this.peek().kind === "VARIABLE" /* VARIABLE */) {
2198
- const variable = this.advance();
2199
- if (!this.consume("AS" /* AS */)) {
2200
- throw new ParseError("SELECT \u5217\u306E\u30D0\u30C3\u30C1\u5909\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
2372
+ const variable = this.finishVariableReference(this.advance());
2373
+ const asOfFunction = asOfFunctionNameFromVariable(variable.name);
2374
+ let parsedAlias2 = null;
2375
+ if (asOfFunction === null) {
2376
+ if (!this.consume("AS" /* AS */)) {
2377
+ throw new ParseError("SELECT \u5217\u306E\u30D0\u30C3\u30C1\u5909\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
2378
+ }
2379
+ parsedAlias2 = this.parseAliasName();
2380
+ } else if (this.consume("AS" /* AS */)) {
2381
+ parsedAlias2 = this.parseAliasName();
2201
2382
  }
2202
- const parsedAlias2 = this.parseAliasName();
2203
2383
  return this.withAliasDisplay({
2204
2384
  type: "VARIABLE_COL",
2205
- name: variable.value.slice(1).toLowerCase(),
2206
- alias: parsedAlias2.alias
2385
+ name: variable.name,
2386
+ alias: parsedAlias2?.alias ?? null
2207
2387
  }, parsedAlias2);
2208
2388
  }
2209
2389
  const windowFunc = this.tryWindowFunc();
@@ -2539,7 +2719,7 @@ var Parser = class {
2539
2719
  }
2540
2720
  if (this.peek().kind === "VARIABLE" /* VARIABLE */) {
2541
2721
  const tok = this.advance();
2542
- return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
2722
+ return this.finishVariableReference(tok);
2543
2723
  }
2544
2724
  const aggFunc = this.tryAggregateFunc();
2545
2725
  if (aggFunc !== null) {
@@ -2699,7 +2879,7 @@ var Parser = class {
2699
2879
  }
2700
2880
  if (tok.kind === "VARIABLE" /* VARIABLE */) {
2701
2881
  this.advance();
2702
- return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
2882
+ return this.finishVariableReference(tok);
2703
2883
  }
2704
2884
  if (tok.kind === "CASE" /* CASE */) {
2705
2885
  if (!allowCase) throw new ParseError("\u3053\u306E\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u3067\u306F CASE \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
@@ -2799,7 +2979,7 @@ var Parser = class {
2799
2979
  }
2800
2980
  if (this.allowSelectArithVariable && tok.kind === "VARIABLE" /* VARIABLE */) {
2801
2981
  this.advance();
2802
- return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
2982
+ return this.finishVariableReference(tok);
2803
2983
  }
2804
2984
  if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
2805
2985
  this.advance();
@@ -3121,6 +3301,12 @@ var Parser = class {
3121
3301
  );
3122
3302
  }
3123
3303
  const name = this.parseTableName();
3304
+ if (this.capabilities.dialect1 && this.bareTempTableNames.has(name)) {
3305
+ const normalizedName = `#${name}`;
3306
+ this.tempTableRefs.push({ ...this.prev(), value: normalizedName });
3307
+ const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
3308
+ return { appId: 0, alias: alias2, cteName: normalizedName };
3309
+ }
3124
3310
  if (nameTok.kind === "IDENT" /* IDENT */ && name.startsWith("#")) {
3125
3311
  this.tempTableRefs.push(this.prev());
3126
3312
  const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
@@ -3161,6 +3347,7 @@ var Parser = class {
3161
3347
  if (k === "IDENT" /* IDENT */ || k === "BIDENT" /* BIDENT */) {
3162
3348
  if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "VALIDATE" && this.peekAt(1).kind === "IDENT" /* IDENT */ && this.peekAt(1).value.toUpperCase() === "ONLY") return null;
3163
3349
  if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "CHECK" && this.peekAt(1).kind === "WHEN" /* WHEN */) return null;
3350
+ if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "KEY" && this.peekAt(1).kind === "(" /* LPAREN */) return null;
3164
3351
  return this.parseTableAliasName();
3165
3352
  }
3166
3353
  return null;
@@ -3400,7 +3587,7 @@ var Parser = class {
3400
3587
  }
3401
3588
  if (tok.kind === "VARIABLE" /* VARIABLE */) {
3402
3589
  this.advance();
3403
- return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
3590
+ return this.finishVariableReference(tok);
3404
3591
  }
3405
3592
  throw new ParseError(
3406
3593
  "KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059",
@@ -3593,7 +3780,7 @@ var Parser = class {
3593
3780
  const tok = this.peek();
3594
3781
  if (tok.kind === "VARIABLE" /* VARIABLE */) {
3595
3782
  this.advance();
3596
- return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
3783
+ return this.finishVariableReference(tok);
3597
3784
  }
3598
3785
  if (tok.kind === "STRING" /* STRING */) {
3599
3786
  this.advance();
@@ -3649,6 +3836,9 @@ var Parser = class {
3649
3836
  "VARIABLE" /* VARIABLE */,
3650
3837
  "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"
3651
3838
  );
3839
+ if (this.peek().kind === "(" /* LPAREN */ && this.capabilities.dialect1) {
3840
+ return { type: "IN_LIST", values: [this.finishVariableReference(variable)] };
3841
+ }
3652
3842
  return { type: "VARIABLE_IN_LIST", name: variable.value.slice(1).toLowerCase() };
3653
3843
  }
3654
3844
  parseInListOrSubquery() {
@@ -3680,7 +3870,7 @@ var Parser = class {
3680
3870
  const sign = tok.kind === "-" /* MINUS */ ? "-" : "+";
3681
3871
  values.push(makeNumberLiteral(`${sign}${number.value}`));
3682
3872
  } else if (tok.kind === "VARIABLE" /* VARIABLE */) {
3683
- values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
3873
+ values.push(this.finishVariableReference(tok));
3684
3874
  } else if (tok.kind === "LOGINUSER" /* LOGINUSER */) {
3685
3875
  if (values.length > 0) {
3686
3876
  throw new ParseError(mixedLoginUserMessage, tok);
@@ -4061,6 +4251,15 @@ var Parser = class {
4061
4251
  };
4062
4252
  }
4063
4253
  parseOnDuplicate() {
4254
+ if (this.capabilities.dialect1 && this.consumeSoftKeyword("KEY")) {
4255
+ this.expect("(" /* LPAREN */, "KEY \u306E\u5F8C\u306B\u306F (\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9) \u304C\u5FC5\u8981\u3067\u3059");
4256
+ const keyFields2 = this.parseIdentList();
4257
+ this.expect(")" /* RPAREN */);
4258
+ if (keyFields2.length === 0) {
4259
+ throw new ParseError("KEY \u306B\u306F\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u6700\u4F4E 1 \u3064\u5FC5\u8981\u3067\u3059", this.prev());
4260
+ }
4261
+ return keyFields2;
4262
+ }
4064
4263
  this.expectKeyword("ON" /* ON */, "UPSERT \u306B\u306F ON DUPLICATE (\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9) \u304C\u5FC5\u8981\u3067\u3059");
4065
4264
  if (!this.consume("DUPLICATE" /* DUPLICATE */)) {
4066
4265
  throw new ParseError("ON \u306E\u5F8C\u306B\u306F DUPLICATE \u304C\u5FC5\u8981\u3067\u3059", this.peek());
@@ -4073,6 +4272,238 @@ var Parser = class {
4073
4272
  }
4074
4273
  return keyFields;
4075
4274
  }
4275
+ parseMergeAsUpsert() {
4276
+ const mergeToken = this.advance();
4277
+ this.expect("INTO" /* INTO */, "MERGE \u306E\u5F8C\u306B\u306F INTO \u304C\u5FC5\u8981\u3067\u3059");
4278
+ this.rejectTempTableDml();
4279
+ const targetName = this.parseIdentifier();
4280
+ const { appId, subtableCode } = extractTableRef(targetName, this.prev());
4281
+ if (subtableCode) {
4282
+ throw new ParseError("MERGE \u306F\u307E\u3060\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
4283
+ }
4284
+ this.expect("AS" /* AS */, "MERGE \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059");
4285
+ const targetAlias = this.parseTableAliasName();
4286
+ this.expectSoftKeyword("USING", "MERGE \u306B\u306F USING <source> AS alias \u304C\u5FC5\u8981\u3067\u3059");
4287
+ const sourceStart = this.pos;
4288
+ const source = this.parseTableRef();
4289
+ const explicitSourceAlias = this.tokens.slice(sourceStart, this.pos).some((token) => token.kind === "AS" /* AS */);
4290
+ if (!explicitSourceAlias || source.alias === null) {
4291
+ throw new ParseError("MERGE \u306E USING \u30BD\u30FC\u30B9\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
4292
+ }
4293
+ const sourceAlias = source.alias;
4294
+ this.expect("ON" /* ON */, "MERGE \u306B\u306F ON t.key = s.key \u306E\u5358\u4E00\u30AD\u30FC\u7B49\u5024\u304C\u5FC5\u8981\u3067\u3059");
4295
+ const left = this.parseMergeQualifiedField("MERGE \u306E ON \u5DE6\u8FBA\u306F targetAlias.key \u306E\u5F62\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
4296
+ if (!this.consume("=" /* EQ */)) {
4297
+ throw new ParseError(
4298
+ "MERGE \u306E ON \u306F\u5358\u4E00\u30AD\u30FC\u306E\u7B49\u5024\uFF08t.key = s.key\uFF09\u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\u3002\u8907\u6570\u6761\u4EF6\u3084\u975E\u7B49\u5024\u306F\u9023\u7D50\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9\u3067\u7F6E\u304D\u63DB\u3048\u3066\u304F\u3060\u3055\u3044",
4299
+ this.peek()
4300
+ );
4301
+ }
4302
+ const right = this.parseMergeQualifiedField("MERGE \u306E ON \u53F3\u8FBA\u306F sourceAlias.key \u306E\u5F62\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
4303
+ if (left.alias.toLowerCase() !== targetAlias.toLowerCase() || right.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
4304
+ throw new ParseError(
4305
+ `MERGE \u306E ON \u306F ${targetAlias}.key = ${sourceAlias}.key \u306E\u5225\u540D\u4FEE\u98FE\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044`,
4306
+ mergeToken
4307
+ );
4308
+ }
4309
+ if (this.peek().kind === "AND" /* AND */ || this.peek().kind === "OR" /* OR */) {
4310
+ throw new ParseError(
4311
+ "MERGE \u306E ON \u306F\u5358\u4E00\u30AD\u30FC\u306E\u7B49\u5024\u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\u3002\u8907\u6570\u6761\u4EF6\u306F\u9023\u7D50\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9\u3067\u7F6E\u304D\u63DB\u3048\u3066\u304F\u3060\u3055\u3044",
4312
+ this.peek()
4313
+ );
4314
+ }
4315
+ let matched = null;
4316
+ let insertFields = null;
4317
+ let insertValues = null;
4318
+ while (this.peek().kind === "WHEN" /* WHEN */) {
4319
+ const whenToken = this.advance();
4320
+ if (this.consume("NOT" /* NOT */)) {
4321
+ this.expectSoftKeyword("MATCHED", "WHEN NOT \u306E\u5F8C\u306B\u306F MATCHED \u304C\u5FC5\u8981\u3067\u3059");
4322
+ if (insertFields !== null) throw new ParseError("WHEN NOT MATCHED \u53E5\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059", whenToken);
4323
+ this.expect("THEN" /* THEN */, "WHEN NOT MATCHED \u306E\u5F8C\u306B\u306F THEN \u304C\u5FC5\u8981\u3067\u3059");
4324
+ this.expect("INSERT" /* INSERT */, "WHEN NOT MATCHED THEN \u306E\u5F8C\u306B\u306F INSERT \u304C\u5FC5\u8981\u3067\u3059");
4325
+ this.expect("(" /* LPAREN */, "MERGE INSERT \u306B\u306F\u5217\u30EA\u30B9\u30C8\u304C\u5FC5\u8981\u3067\u3059");
4326
+ insertFields = this.parseIdentList();
4327
+ this.expect(")" /* RPAREN */);
4328
+ this.expect("VALUES" /* VALUES */, "MERGE INSERT \u306E\u5217\u30EA\u30B9\u30C8\u306E\u5F8C\u306B\u306F VALUES \u304C\u5FC5\u8981\u3067\u3059");
4329
+ this.expect("(" /* LPAREN */, "MERGE INSERT VALUES \u306F ( \u3067\u59CB\u3081\u3066\u304F\u3060\u3055\u3044");
4330
+ insertValues = this.parseMergeValueList();
4331
+ this.expect(")" /* RPAREN */);
4332
+ if (insertFields.length !== insertValues.length) {
4333
+ throw new ParseError("MERGE INSERT \u306E\u5217\u6570\u3068 VALUES \u306E\u5024\u6570\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093", whenToken);
4334
+ }
4335
+ } else {
4336
+ this.expectSoftKeyword("MATCHED", "WHEN \u306E\u5F8C\u306B\u306F MATCHED \u307E\u305F\u306F NOT MATCHED \u304C\u5FC5\u8981\u3067\u3059");
4337
+ if (matched !== null) throw new ParseError("WHEN MATCHED \u53E5\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059", whenToken);
4338
+ this.expect("THEN" /* THEN */, "WHEN MATCHED \u306E\u5F8C\u306B\u306F THEN \u304C\u5FC5\u8981\u3067\u3059");
4339
+ this.expect("UPDATE" /* UPDATE */, "WHEN MATCHED THEN \u306E\u5F8C\u306B\u306F UPDATE \u304C\u5FC5\u8981\u3067\u3059");
4340
+ this.expect("SET" /* SET */, "WHEN MATCHED THEN UPDATE \u306E\u5F8C\u306B\u306F SET \u304C\u5FC5\u8981\u3067\u3059");
4341
+ matched = this.parseMergeAssignments(targetAlias);
4342
+ }
4343
+ }
4344
+ if (matched === null || insertFields === null || insertValues === null) {
4345
+ throw new ParseError(
4346
+ "WHEN MATCHED / WHEN NOT MATCHED \u306E\u4E21\u53E5\u304C\u5FC5\u8981\u3067\u3059\uFF08\u66F4\u65B0\u306E\u307F\u306F UPDATE ... FROM\u3001\u633F\u5165\u306E\u307F\u306F INSERT ... SELECT \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF09",
4347
+ this.peek()
4348
+ );
4349
+ }
4350
+ if (!insertFields.some((field) => field.toLowerCase() === left.field.toLowerCase())) {
4351
+ throw new ParseError(
4352
+ `MERGE \u306E ON \u30AD\u30FC ${left.field} \u306F INSERT \u5217\u30EA\u30B9\u30C8\u306B\u542B\u3081\u3066\u304F\u3060\u3055\u3044`,
4353
+ mergeToken
4354
+ );
4355
+ }
4356
+ const expressions = /* @__PURE__ */ new Map();
4357
+ insertFields.forEach((field, index) => expressions.set(field.toLowerCase(), insertValues[index]));
4358
+ for (const assignment of matched) {
4359
+ const key = assignment.field.toLowerCase();
4360
+ const existing = expressions.get(key);
4361
+ if (existing !== void 0 && !this.mergeExpressionsEqual(existing, assignment.value, sourceAlias)) {
4362
+ throw new ParseError(
4363
+ `MERGE \u306E\u5217 ${assignment.field} \u306F\u4E21\u53E5\u306E\u5F0F\u304C\u4E00\u81F4\u3059\u308B\u5834\u5408\u306E\u307F MERGE \u3092 UPSERT \u3078\u6B63\u898F\u5316\u3067\u304D\u307E\u3059`,
4364
+ mergeToken
4365
+ );
4366
+ }
4367
+ if (existing === void 0) {
4368
+ insertFields.push(assignment.field);
4369
+ insertValues.push(assignment.value);
4370
+ expressions.set(key, assignment.value);
4371
+ }
4372
+ }
4373
+ const columns = insertValues.map((value) => this.mergeValueToSelectColumn(value, sourceAlias, mergeToken));
4374
+ const normalizedSource = source.cteName !== null ? { ...source, alias: null } : { ...source, alias: `APP${source.appId}${source.subtableCode ? `$${source.subtableCode}` : ""}` };
4375
+ const select = {
4376
+ type: "SELECT",
4377
+ distinct: false,
4378
+ columns,
4379
+ from: normalizedSource,
4380
+ joins: [],
4381
+ where: null,
4382
+ groupBy: [],
4383
+ having: null,
4384
+ orderMode: "CANONICAL",
4385
+ orderBy: [],
4386
+ limit: null,
4387
+ offset: null
4388
+ };
4389
+ const checkGroups = this.parseCheckGroups();
4390
+ const validation = this.parseDmlControlSuffix();
4391
+ return {
4392
+ type: "UPSERT_SELECT",
4393
+ appId,
4394
+ fields: insertFields,
4395
+ select,
4396
+ keyFields: [left.field],
4397
+ ...checkGroups,
4398
+ ...validation
4399
+ };
4400
+ }
4401
+ parseMergeQualifiedField(message) {
4402
+ const token = this.peek();
4403
+ const path = this.parseFieldPath();
4404
+ const ref = this.splitQualifiedField(path);
4405
+ if (ref.alias === null) throw new ParseError(message, token);
4406
+ return { alias: ref.alias, field: ref.field };
4407
+ }
4408
+ parseMergeAssignments(targetAlias) {
4409
+ const assignments = [];
4410
+ do {
4411
+ const token = this.peek();
4412
+ const path = this.parseFieldPath();
4413
+ const ref = this.splitQualifiedField(path);
4414
+ if (ref.alias !== null && ref.alias.toLowerCase() !== targetAlias.toLowerCase()) {
4415
+ throw new ParseError(`MERGE UPDATE SET \u306E\u5DE6\u8FBA\u306F target alias ${targetAlias} \u3067\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`, token);
4416
+ }
4417
+ this.expect("=" /* EQ */);
4418
+ assignments.push({ field: ref.field, value: this.parseAssignmentValue() });
4419
+ } while (this.consume("," /* COMMA */));
4420
+ return assignments;
4421
+ }
4422
+ parseMergeValueList() {
4423
+ const values = [];
4424
+ if (this.peek().kind === ")" /* RPAREN */) return values;
4425
+ do
4426
+ values.push(this.parseAssignmentValue());
4427
+ while (this.consume("," /* COMMA */));
4428
+ return values;
4429
+ }
4430
+ mergeExpressionsEqual(left, right, sourceAlias) {
4431
+ return this.mergeNormalizedValueEqual(
4432
+ this.normalizeMergeValue(left, sourceAlias, true),
4433
+ this.normalizeMergeValue(right, sourceAlias, true)
4434
+ );
4435
+ }
4436
+ normalizeMergeValue(value, sourceAlias, compareLiteralValues = false) {
4437
+ if (Array.isArray(value)) {
4438
+ return value.map((item) => this.normalizeMergeValue(item, sourceAlias, compareLiteralValues));
4439
+ }
4440
+ if (value === null || typeof value !== "object") return value;
4441
+ const obj = value;
4442
+ if (compareLiteralValues && obj["type"] === "NUMBER") {
4443
+ return { type: "NUMBER", value: obj["value"] };
4444
+ }
4445
+ if (obj["type"] === "SOURCE_FIELD") {
4446
+ if (String(obj["alias"]).toLowerCase() !== sourceAlias.toLowerCase()) return { type: "INVALID_SOURCE" };
4447
+ return { type: "FIELD_REF", field: obj["field"] };
4448
+ }
4449
+ if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
4450
+ const ref = this.splitQualifiedField(obj["field"]);
4451
+ if (ref.alias !== null && ref.alias.toLowerCase() !== sourceAlias.toLowerCase()) return { type: "INVALID_SOURCE" };
4452
+ return { ...obj, field: ref.field };
4453
+ }
4454
+ if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string") {
4455
+ if (obj["tableAlias"].toLowerCase() !== sourceAlias.toLowerCase()) return { type: "INVALID_SOURCE" };
4456
+ return { ...obj, tableAlias: null };
4457
+ }
4458
+ return Object.fromEntries(Object.entries(obj).map(([key, child]) => [
4459
+ key,
4460
+ this.normalizeMergeValue(child, sourceAlias, compareLiteralValues)
4461
+ ]));
4462
+ }
4463
+ mergeNormalizedValueEqual(left, right) {
4464
+ if (left === right) return true;
4465
+ if (Array.isArray(left) || Array.isArray(right)) {
4466
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((item, index) => this.mergeNormalizedValueEqual(item, right[index]));
4467
+ }
4468
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
4469
+ const leftObj = left;
4470
+ const rightObj = right;
4471
+ const leftKeys = Object.keys(leftObj);
4472
+ const rightKeys = Object.keys(rightObj);
4473
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.prototype.hasOwnProperty.call(rightObj, key) && this.mergeNormalizedValueEqual(leftObj[key], rightObj[key]));
4474
+ }
4475
+ mergeValueToSelectColumn(value, sourceAlias, token) {
4476
+ const normalized = this.normalizeMergeValue(value, sourceAlias);
4477
+ if (this.mergeValueContainsInvalidSource(normalized)) {
4478
+ throw new ParseError(`MERGE \u306E\u5F0F\u306F source alias ${sourceAlias} \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u3060\u3051\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044`, token);
4479
+ }
4480
+ const expr = normalized;
4481
+ switch (expr["type"]) {
4482
+ case "FIELD_REF":
4483
+ return { type: "FIELD", field: String(expr["field"]), alias: null };
4484
+ case "STRING":
4485
+ return { type: "LITERAL_COL", value: String(expr["value"]), alias: null };
4486
+ case "NUMBER":
4487
+ return { type: "ARITH_COL", expr, alias: null };
4488
+ case "ARITH":
4489
+ return { type: "ARITH_COL", expr, alias: null };
4490
+ case "STRING_FUNC":
4491
+ return { type: "STRFUNC_COL", expr, alias: null };
4492
+ case "CASE_VALUE":
4493
+ return { type: "CASE_COL", expr: expr["expr"], alias: null };
4494
+ default:
4495
+ throw new ParseError(
4496
+ "MERGE \u306E\u4EE3\u5165\u5F0F\u306F\u30BD\u30FC\u30B9\u30D5\u30A3\u30FC\u30EB\u30C9\u30FB\u30EA\u30C6\u30E9\u30EB\u30FB\u7B97\u8853\u5F0F\u30FB\u6587\u5B57\u5217\u95A2\u6570\u30FBCASE \u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044",
4497
+ token
4498
+ );
4499
+ }
4500
+ }
4501
+ mergeValueContainsInvalidSource(value) {
4502
+ if (Array.isArray(value)) return value.some((item) => this.mergeValueContainsInvalidSource(item));
4503
+ if (value === null || typeof value !== "object") return false;
4504
+ const obj = value;
4505
+ return obj["type"] === "INVALID_SOURCE" || Object.values(obj).some((item) => this.mergeValueContainsInvalidSource(item));
4506
+ }
4076
4507
  isUpsertApplyBranchStart() {
4077
4508
  return this.peek().kind === "ON" /* ON */ && (this.peekAt(1).kind === "INSERT" /* INSERT */ || this.peekAt(1).kind === "UPDATE" /* UPDATE */);
4078
4509
  }
@@ -4889,7 +5320,7 @@ function isDmlType(type) {
4889
5320
  return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER" || type === "IMPORT";
4890
5321
  }
4891
5322
  function isReadOnlyType(type) {
4892
- 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";
5323
+ 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" || type === "EXIT";
4893
5324
  }
4894
5325
  function writesKintone(stmt) {
4895
5326
  return isDmlType(stmt.type) && !("validateOnly" in stmt && stmt.validateOnly === true);
@@ -8259,6 +8690,7 @@ function validateStatement(stmt) {
8259
8690
  case "SET_VARIABLE":
8260
8691
  case "DECLARE_VARIABLE":
8261
8692
  case "ASSERT":
8693
+ case "EXIT":
8262
8694
  validateNestedSelects(stmt);
8263
8695
  return;
8264
8696
  case "UPDATE":
@@ -8961,6 +9393,7 @@ function collectVariableRefs(node, refs, inWhere = false) {
8961
9393
  const obj = node;
8962
9394
  const type = obj["type"];
8963
9395
  if ((type === "VARIABLE" || type === "VARIABLE_COL" || type === "VARIABLE_IN_LIST") && typeof obj["name"] === "string") {
9396
+ if (asOfFunctionNameFromVariable(obj["name"]) !== null) return;
8964
9397
  refs.push({
8965
9398
  name: obj["name"],
8966
9399
  kind: type === "VARIABLE" ? "scalar" : type === "VARIABLE_COL" ? "select-column" : "array-in-list",
@@ -16134,6 +16567,234 @@ function toFlatString(value) {
16134
16567
  }
16135
16568
  }
16136
16569
 
16570
+ // src/core/diagnostics.ts
16571
+ var DiagnosticCodes = {
16572
+ HEADER_UNKNOWN_KEY: "KSQL1001",
16573
+ HEADER_DUPLICATE_KEY: "KSQL1002",
16574
+ HEADER_INVALID_NAME: "KSQL1003",
16575
+ HEADER_INVALID_DEPENDS_ON: "KSQL1004",
16576
+ HEADER_INVALID_TIMEOUT: "KSQL1005",
16577
+ HEADER_INVALID_DIALECT: "KSQL1006",
16578
+ LOGICAL_APP_UNRESOLVED: "KSQL1101",
16579
+ LEX_ERROR: "KSQL1201",
16580
+ PARSE_ERROR: "KSQL1202",
16581
+ DIALECT1_REQUIRED: "KSQL1203",
16582
+ UPDATE_KEY_COMPOSITE: "KSQL1301",
16583
+ UPDATE_KEY_FIELD_TYPE: "KSQL1302",
16584
+ UPDATE_KEY_NOT_UNIQUE: "KSQL1303",
16585
+ SUBTABLE_DML_FORBIDDEN: "KSQL1304",
16586
+ BARE_INSERT_NOT_IDEMPOTENT: "KSQL1305",
16587
+ SERVER_TIME_FUNCTION_NOT_AS_OF: "KSQL1306"
16588
+ };
16589
+ function sourceLocationAt(source, offset) {
16590
+ const target = Math.max(0, Math.min(offset, source.length));
16591
+ let line = 1;
16592
+ let column = 1;
16593
+ for (let i = 0; i < target; i++) {
16594
+ const ch = source[i];
16595
+ if (ch === "\r") {
16596
+ if (source[i + 1] === "\n" && i + 1 < target) i++;
16597
+ line++;
16598
+ column = 1;
16599
+ } else if (ch === "\n") {
16600
+ line++;
16601
+ column = 1;
16602
+ } else {
16603
+ column++;
16604
+ }
16605
+ }
16606
+ return { line, column };
16607
+ }
16608
+ function diagnosticAt(source, offset, diagnostic2) {
16609
+ return { ...diagnostic2, ...sourceLocationAt(source, offset) };
16610
+ }
16611
+
16612
+ // src/core/scriptHeader.ts
16613
+ var HEADER_LINE_RE = /^(\s*)--\s*@ksql\s+([^:\s]+)\s*:\s*(.*)$/i;
16614
+ function parseScriptHeader(source) {
16615
+ const meta = { name: null, dependsOn: [], timeout: null, dialect: 0 };
16616
+ const diagnostics = [];
16617
+ const seen = /* @__PURE__ */ new Set();
16618
+ let hasDirectives = false;
16619
+ let offset = source.charCodeAt(0) === 65279 ? 1 : 0;
16620
+ let headerEnd = offset;
16621
+ while (offset < source.length) {
16622
+ const lineEnd = findLineEnd(source, offset);
16623
+ const line = source.slice(offset, lineEnd.contentEnd);
16624
+ if (!/^\s*--/.test(line)) break;
16625
+ headerEnd = lineEnd.next;
16626
+ const match = HEADER_LINE_RE.exec(line);
16627
+ if (match) {
16628
+ hasDirectives = true;
16629
+ const rawKey = match[2];
16630
+ const key = rawKey.toLowerCase();
16631
+ const rawValue = match[3];
16632
+ const commentAt = rawValue.indexOf("#");
16633
+ const valuePart = commentAt < 0 ? rawValue : rawValue.slice(0, commentAt);
16634
+ const leading = valuePart.match(/^\s*/)?.[0].length ?? 0;
16635
+ const value = valuePart.trim();
16636
+ const valueOffset = offset + match.index + match[0].length - rawValue.length + leading;
16637
+ if (!isHeaderKey(key)) {
16638
+ diagnostics.push(diagnosticAt(source, valueOffset, {
16639
+ severity: "warning",
16640
+ code: DiagnosticCodes.HEADER_UNKNOWN_KEY,
16641
+ message: `Unknown @ksql header key "${rawKey}" was ignored.`
16642
+ }));
16643
+ } else if (seen.has(key)) {
16644
+ diagnostics.push(diagnosticAt(source, valueOffset, {
16645
+ severity: "warning",
16646
+ code: DiagnosticCodes.HEADER_DUPLICATE_KEY,
16647
+ message: `Duplicate @ksql header key "${key}" was ignored; the first value is retained.`
16648
+ }));
16649
+ } else {
16650
+ seen.add(key);
16651
+ applyHeaderValue(meta, key, value, source, valueOffset, diagnostics);
16652
+ }
16653
+ }
16654
+ offset = lineEnd.next;
16655
+ }
16656
+ return { meta, diagnostics, hasDirectives, headerEnd };
16657
+ }
16658
+ function isHeaderKey(value) {
16659
+ return value === "name" || value === "depends_on" || value === "timeout" || value === "dialect";
16660
+ }
16661
+ function applyHeaderValue(meta, key, value, source, valueOffset, diagnostics) {
16662
+ if (key === "name") {
16663
+ if (!value) {
16664
+ diagnostics.push(diagnosticAt(source, valueOffset, {
16665
+ severity: "error",
16666
+ code: DiagnosticCodes.HEADER_INVALID_NAME,
16667
+ message: "@ksql name must not be empty."
16668
+ }));
16669
+ } else {
16670
+ meta.name = value;
16671
+ }
16672
+ return;
16673
+ }
16674
+ if (key === "depends_on") {
16675
+ const dependencies = value.split(",").map((item) => item.trim());
16676
+ if (!value || dependencies.some((item) => !item)) {
16677
+ diagnostics.push(diagnosticAt(source, valueOffset, {
16678
+ severity: "error",
16679
+ code: DiagnosticCodes.HEADER_INVALID_DEPENDS_ON,
16680
+ message: "@ksql depends_on must be a comma-separated list without empty items."
16681
+ }));
16682
+ } else {
16683
+ meta.dependsOn = dependencies;
16684
+ }
16685
+ return;
16686
+ }
16687
+ if (key === "timeout") {
16688
+ if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value))) {
16689
+ diagnostics.push(diagnosticAt(source, valueOffset, {
16690
+ severity: "error",
16691
+ code: DiagnosticCodes.HEADER_INVALID_TIMEOUT,
16692
+ message: "@ksql timeout must be a positive integer."
16693
+ }));
16694
+ } else {
16695
+ meta.timeout = Number(value);
16696
+ }
16697
+ return;
16698
+ }
16699
+ if (value !== "0" && value !== "1") {
16700
+ diagnostics.push(diagnosticAt(source, valueOffset, {
16701
+ severity: "error",
16702
+ code: DiagnosticCodes.HEADER_INVALID_DIALECT,
16703
+ message: "@ksql dialect must be 0 or 1."
16704
+ }));
16705
+ } else {
16706
+ meta.dialect = Number(value);
16707
+ }
16708
+ }
16709
+ function findLineEnd(source, start) {
16710
+ let i = start;
16711
+ while (i < source.length && source[i] !== "\r" && source[i] !== "\n") i++;
16712
+ const contentEnd = i;
16713
+ if (source[i] === "\r" && source[i + 1] === "\n") i += 2;
16714
+ else if (i < source.length) i++;
16715
+ return { contentEnd, next: i };
16716
+ }
16717
+
16718
+ // src/core/sql.ts
16719
+ function parseSqlStatements(sql, capabilities = {}) {
16720
+ const tokens = new Lexer(sql).tokenize();
16721
+ const statements = new Parser(tokens, capabilities).parseStatements();
16722
+ statements.forEach(validateStatementStatic);
16723
+ return statements;
16724
+ }
16725
+ function parseSqlStatementsForScript(sql, capabilities = {}) {
16726
+ const header = parseScriptHeader(sql);
16727
+ const headerError = header.diagnostics.find((diagnostic2) => diagnostic2.severity === "error");
16728
+ if (header.hasDirectives && headerError) {
16729
+ throw new Error(`${headerError.code}: ${headerError.message} (${headerError.line}:${headerError.column})`);
16730
+ }
16731
+ const scriptSql = header.hasDirectives ? sql.slice(header.headerEnd) : sql;
16732
+ const scriptCapabilities = header.hasDirectives ? { ...capabilities, dialect1: header.meta.dialect === 1 } : capabilities;
16733
+ return {
16734
+ statements: parseSqlStatements(scriptSql, scriptCapabilities),
16735
+ meta: header.meta
16736
+ };
16737
+ }
16738
+
16739
+ // src/core/dialect1Validation.ts
16740
+ var DIALECT1_SERVER_TIME_FUNCTION_WARNING = "bare \u306E\u6642\u523B\u4F9D\u5B58\u95A2\u6570\u306F kintone \u30B5\u30FC\u30D0\u30FC\u8A55\u4FA1\u306E\u305F\u3081 as-of \u306E\u5BFE\u8C61\u5916\u3067\u3059\u3002\u518D\u73FE\u6027\u304C\u5FC5\u8981\u306A\u3089 @ \u4ED8\u304D\u95A2\u6570\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
16741
+ function validateDialect1UpdateKey(statement, fieldInfos) {
16742
+ if (fieldInfos === void 0) {
16743
+ return statement.keyFields.length === 1 ? [] : [{
16744
+ code: DiagnosticCodes.UPDATE_KEY_COMPOSITE,
16745
+ severity: "error",
16746
+ message: "dialect 1 \u306E UPSERT / MERGE \u306E\u30AD\u30FC\u306F\u5358\u4E00\u30D5\u30A3\u30FC\u30EB\u30C9\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002\u8907\u5408\u30AD\u30FC\u306E\u4EE3\u308F\u308A\u306B\u3001\u9023\u7D50\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9\uFF08\u4F8B: \u9867\u5BA2\u30B3\u30FC\u30C9_\u5E74\u6708\uFF09\u3092\u30A2\u30D7\u30EA\u5074\u306B\u7528\u610F\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
16747
+ }];
16748
+ }
16749
+ if (statement.keyFields.length !== 1) return [];
16750
+ const key = statement.keyFields[0];
16751
+ const field = fieldInfos.find((candidate) => candidate.code === key);
16752
+ const issues = [];
16753
+ if (field === void 0 || field.fieldType !== "SINGLE_LINE_TEXT" && field.fieldType !== "NUMBER") {
16754
+ issues.push({
16755
+ code: DiagnosticCodes.UPDATE_KEY_FIELD_TYPE,
16756
+ severity: "error",
16757
+ message: field === void 0 ? `UPSERT / MERGE \u306E\u30AD\u30FC\u300C${key}\u300D\u304C APP${statement.appId} \u306E\u30D5\u30A9\u30FC\u30E0\u306B\u5B58\u5728\u3057\u307E\u305B\u3093\u3002\u91CD\u8907\u7981\u6B62\u3092\u8A2D\u5B9A\u3057\u305F\u6587\u5B57\u5217\uFF081\u884C\uFF09\u307E\u305F\u306F\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u30AD\u30FC\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002` : `UPSERT / MERGE \u306E\u30AD\u30FC\u300C${key}\u300D\u306E\u578B ${field.fieldType} \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u91CD\u8907\u7981\u6B62\u3092\u8A2D\u5B9A\u3057\u305F\u6587\u5B57\u5217\uFF081\u884C\uFF09\u307E\u305F\u306F\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u30AD\u30FC\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
16758
+ });
16759
+ }
16760
+ if (field?.isUnique === false) {
16761
+ issues.push({
16762
+ code: DiagnosticCodes.UPDATE_KEY_NOT_UNIQUE,
16763
+ severity: "error",
16764
+ message: `UPSERT / MERGE \u306E\u30AD\u30FC\u300C${key}\u300D\u306F\u91CD\u8907\u7981\u6B62\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u30A2\u30D7\u30EA\u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u8A2D\u5B9A\u3067\u300C\u5024\u306E\u91CD\u8907\u3092\u7981\u6B62\u3059\u308B\u300D\u3092\u6709\u52B9\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
16765
+ });
16766
+ } else if (field !== void 0 && field.isUnique === void 0) {
16767
+ issues.push({
16768
+ code: DiagnosticCodes.UPDATE_KEY_NOT_UNIQUE,
16769
+ severity: "warning",
16770
+ message: `UPSERT / MERGE \u306E\u30AD\u30FC\u300C${key}\u300D\u306E\u91CD\u8907\u7981\u6B62\u8A2D\u5B9A\u3092 schema resolver \u304B\u3089\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002isUnique \u3092\u8FD4\u3059 resolver \u3092\u4F7F\u7528\u3057\u3001\u30A2\u30D7\u30EA\u5074\u3067\u300C\u5024\u306E\u91CD\u8907\u3092\u7981\u6B62\u3059\u308B\u300D\u304C\u6709\u52B9\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
16771
+ });
16772
+ }
16773
+ return issues;
16774
+ }
16775
+ function statementHasBareServerTimeFunctionInWhere(statement) {
16776
+ let found = false;
16777
+ const visit = (node) => {
16778
+ if (found || node === null || typeof node !== "object") return;
16779
+ if (Array.isArray(node)) {
16780
+ node.forEach(visit);
16781
+ return;
16782
+ }
16783
+ const value = node;
16784
+ const where = value["where"];
16785
+ if (where !== null && typeof where === "object") {
16786
+ const names = serverOnlyFunctionOccurrencesInWhere(where);
16787
+ if (names.some((name) => name === "TODAY" || name === "NOW" || isRelativeDateFunctionName(name))) {
16788
+ found = true;
16789
+ return;
16790
+ }
16791
+ }
16792
+ Object.values(value).forEach(visit);
16793
+ };
16794
+ visit(statement);
16795
+ return found;
16796
+ }
16797
+
16137
16798
  // src/core/dmlPrevalidation.ts
16138
16799
  function collectDmlPrevalidationSnapshotFields(fieldIndex) {
16139
16800
  return [
@@ -18155,6 +18816,8 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
18155
18816
  throw new Error("ArgumentError: DECLARE variable requires a batch.");
18156
18817
  case "ASSERT":
18157
18818
  return executeAssert(stmt, client, options, cacheContext);
18819
+ case "EXIT":
18820
+ throw new Error("ArgumentError: EXIT SUCCESS IF \u306F\u30D0\u30C3\u30C1\u5C02\u7528\u3067\u3059");
18158
18821
  }
18159
18822
  }
18160
18823
  var EXISTING_VALIDATION_COLUMNS = [
@@ -18421,8 +19084,10 @@ var BatchTimeoutError = class extends Error {
18421
19084
  }
18422
19085
  };
18423
19086
  async function executeBatch(sql, client, options = {}) {
19087
+ const asOfClock = createAsOfClock(options.asOf ?? /* @__PURE__ */ new Date(), options.timezone);
18424
19088
  resolveRecursiveCteLimits(options);
18425
- const statements = parseSqlBatch(sql, options.enableImport === true);
19089
+ const { statements, meta } = parseSqlStatementsForScript(sql, { import: options.enableImport === true });
19090
+ const dialect1Warnings = meta.dialect === 1 && statements.some(statementHasBareServerTimeFunctionInWhere) ? [DIALECT1_SERVER_TIME_FUNCTION_WARNING] : [];
18426
19091
  const analysis = analyzeBatch(statements);
18427
19092
  statements.forEach((statement) => assertApplyExecutionScope("phase15b", statement));
18428
19093
  if (options.allowApplyMutation !== true && statements.some(
@@ -18456,6 +19121,9 @@ async function executeBatch(sql, client, options = {}) {
18456
19121
  try {
18457
19122
  const tempTables = /* @__PURE__ */ new Map();
18458
19123
  const variables = /* @__PURE__ */ new Map();
19124
+ for (const [name, value] of Object.entries(asOfClock.values)) {
19125
+ variables.set(asOfVariableName(name), { type: "string", value });
19126
+ }
18459
19127
  const results = [];
18460
19128
  const failed = /* @__PURE__ */ new Set();
18461
19129
  let aborted = null;
@@ -18464,7 +19132,7 @@ async function executeBatch(sql, client, options = {}) {
18464
19132
  const base = { index: i, type: info.statementType };
18465
19133
  if (aborted) {
18466
19134
  results.push({ ...base, status: "skipped", skippedReason: aborted });
18467
- failed.add(i);
19135
+ if (aborted !== "exit") failed.add(i);
18468
19136
  continue;
18469
19137
  }
18470
19138
  const brokenDep = info.dependsOn.find((d) => failed.has(d));
@@ -18502,24 +19170,33 @@ async function executeBatch(sql, client, options = {}) {
18502
19170
  info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH" || statementContainsOuterJoin(statements[i])
18503
19171
  );
18504
19172
  const cursorScope = wrapClientWithCursorScope(statementClient);
19173
+ const boundOptions = bindStatementEvaluationContext(stmtOptions);
19174
+ const statementContext = {
19175
+ stmt: statements[i],
19176
+ info,
19177
+ client: cursorScope.client,
19178
+ options: boundOptions,
19179
+ cacheContext,
19180
+ tempTables,
19181
+ variables,
19182
+ relativeDateVariables,
19183
+ clock: statementEvaluationContext(boundOptions),
19184
+ dialect: meta.dialect
19185
+ };
18505
19186
  const outcome = await runWithDeadline(
18506
- executeBatchStatement(
18507
- statements[i],
18508
- info,
18509
- cursorScope.client,
18510
- stmtOptions,
18511
- cacheContext,
18512
- tempTables,
18513
- variables,
18514
- relativeDateVariables
18515
- ),
19187
+ executeBatchStatement(statementContext),
18516
19188
  remaining,
18517
19189
  cursorScope.closeActive
18518
19190
  );
18519
19191
  if (outcome.result) {
18520
19192
  outcome.result = attachSearchAbortWarning(outcome.result, searchAbortCollector);
19193
+ if (dialect1Warnings.length > 0 && statementHasBareServerTimeFunctionInWhere(statements[i]) && outcome.result.type === "SELECT") {
19194
+ outcome.result = mergeSelectWarnings(outcome.result, dialect1Warnings);
19195
+ }
18521
19196
  }
18522
- results.push({ ...base, status: "success", ...outcome });
19197
+ const { exitTriggered, ...statementOutcome } = outcome;
19198
+ results.push({ ...base, status: "success", ...statementOutcome });
19199
+ if (exitTriggered) aborted = "exit";
18523
19200
  } catch (e) {
18524
19201
  results.push({
18525
19202
  ...base,
@@ -18541,11 +19218,12 @@ async function executeBatch(sql, client, options = {}) {
18541
19218
  }
18542
19219
  metrics.elapsedMs = Date.now() - startedAt;
18543
19220
  return {
18544
- ok: results.every((r) => r.status === "success"),
19221
+ ok: results.every((r) => r.status === "success" || r.skippedReason === "exit"),
18545
19222
  statementCount: statements.length,
18546
19223
  statements: results,
18547
19224
  analysis,
18548
- metrics
19225
+ metrics,
19226
+ ...dialect1Warnings.length > 0 ? { warnings: dialect1Warnings } : {}
18549
19227
  };
18550
19228
  } finally {
18551
19229
  releaseMetadataCacheScope(cacheContext);
@@ -18557,8 +19235,19 @@ function statementHasApplyMutation(statement) {
18557
19235
  }
18558
19236
  return statement.type === "UPSERT" && statement.validateOnly !== true && Boolean(statement.onInsertApplyBlocks?.length || statement.onUpdateApplyBlocks?.length);
18559
19237
  }
18560
- async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables, relativeDateVariables) {
18561
- options = bindStatementEvaluationContext(options);
19238
+ async function executeBatchStatement(context) {
19239
+ const {
19240
+ stmt,
19241
+ info,
19242
+ client,
19243
+ options,
19244
+ cacheContext,
19245
+ tempTables,
19246
+ variables,
19247
+ relativeDateVariables,
19248
+ clock,
19249
+ dialect
19250
+ } = context;
18562
19251
  if (stmt.type === "SET_VARIABLE") {
18563
19252
  const resolvedStmt2 = resolveBatchVariableReferences(stmt, variables);
18564
19253
  validateStatementStatic(resolvedStmt2);
@@ -18598,7 +19287,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
18598
19287
  throw e;
18599
19288
  }
18600
19289
  } else {
18601
- variables.set(stmt.name, evaluateScalarExpr(resolvedStmt2.expr, statementEvaluationContext(options)));
19290
+ variables.set(stmt.name, evaluateScalarExpr(resolvedStmt2.expr, clock));
18602
19291
  }
18603
19292
  return {};
18604
19293
  }
@@ -18616,7 +19305,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
18616
19305
  } else {
18617
19306
  const value = evaluateScalarExpr(
18618
19307
  stmt.default,
18619
- statementEvaluationContext(options)
19308
+ clock
18620
19309
  );
18621
19310
  variables.set(stmt.name, {
18622
19311
  type: "string",
@@ -18629,6 +19318,18 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
18629
19318
  assertApplyScope("phase15b", resolvedStmt);
18630
19319
  assertApplyExecutionScope("phase15b", resolvedStmt);
18631
19320
  validateStatementStatic(resolvedStmt);
19321
+ if (dialect === 1 && (resolvedStmt.type === "INSERT" || resolvedStmt.type === "UPDATE" || resolvedStmt.type === "DELETE") && resolvedStmt.subtableCode) {
19322
+ throw new Error(
19323
+ "ArgumentError: dialect 1 \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u3078\u306E DML \u306F\u3067\u304D\u307E\u305B\u3093\u3002SELECT \u306F\u53EF\u80FD\u3067\u3059\u3002\u89AA\u30A2\u30D7\u30EA\u3092\u5BFE\u8C61\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
19324
+ );
19325
+ }
19326
+ if (dialect === 1 && (resolvedStmt.type === "UPSERT" || resolvedStmt.type === "UPSERT_SELECT")) {
19327
+ const staticIssue = validateDialect1UpdateKey(resolvedStmt)[0];
19328
+ if (staticIssue) throw new Error(`ArgumentError: ${staticIssue.message}`);
19329
+ const fieldInfos = await getFieldsCached(resolvedStmt.appId, client, cacheContext);
19330
+ const schemaIssue = validateDialect1UpdateKey(resolvedStmt, fieldInfos)[0];
19331
+ if (schemaIssue) throw new Error(`ArgumentError: ${schemaIssue.message}`);
19332
+ }
18632
19333
  await assertRelativeDateExecutionPlan(resolvedStmt, client, cacheContext);
18633
19334
  if (resolvedStmt.type === "VALIDATE") {
18634
19335
  const result = await executeExistingRecordValidationCore(
@@ -18713,8 +19414,12 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
18713
19414
  }
18714
19415
  }
18715
19416
  if (resolvedStmt.type === "ASSERT") {
18716
- await executeAssert(resolvedStmt, client, options, cacheContext, tempTables);
18717
- return {};
19417
+ const result = await executeAssert(resolvedStmt, client, options, cacheContext, tempTables);
19418
+ return result.warning !== void 0 ? { result } : {};
19419
+ }
19420
+ if (resolvedStmt.type === "EXIT") {
19421
+ const result = await executeExit(resolvedStmt, client, options, cacheContext, tempTables);
19422
+ return { result, ...result.exited ? { exitTriggered: true } : {} };
18718
19423
  }
18719
19424
  if (info.tempTablesReferenced.length > 0) {
18720
19425
  if (resolvedStmt.type === "SELECT" || resolvedStmt.type === "UNION") {
@@ -18838,9 +19543,9 @@ function safeJsonStringify(v) {
18838
19543
  return String(v);
18839
19544
  }
18840
19545
  }
18841
- function parseSqlBatch(sql, enableImport = false) {
19546
+ function parseSqlBatch(sql, enableImport = false, dialect1 = false) {
18842
19547
  const tokens = new Lexer(sql).tokenize();
18843
- return new Parser(tokens, { import: enableImport }).parseStatements();
19548
+ return new Parser(tokens, { import: enableImport, dialect1 }).parseStatements();
18844
19549
  }
18845
19550
  function parseRelativeDateVariableValue(name, value) {
18846
19551
  try {
@@ -18885,6 +19590,8 @@ function evaluateScalarExpr(expr, evaluationContext = {}) {
18885
19590
  }
18886
19591
  return { type: "number", value, raw: String(value) };
18887
19592
  }
19593
+ case "VARIABLE":
19594
+ throw new Error(`InternalError: unresolved variable @${expr.name} reached scalar evaluation.`);
18888
19595
  }
18889
19596
  }
18890
19597
  function resolveBatchVariableReferences(node, variables) {
@@ -18920,7 +19627,7 @@ function resolveBatchVariableReferencesInternal(node, variables, numericArithmet
18920
19627
  raw: numericArithmeticOperand === "AGG_ARITH" ? `@${obj["name"]}` : value.raw ?? String(value.value)
18921
19628
  } : { type: "STRING", value: value.value, fromVariable: true };
18922
19629
  }
18923
- if (obj["type"] === "VARIABLE_COL" && typeof obj["name"] === "string" && typeof obj["alias"] === "string") {
19630
+ if (obj["type"] === "VARIABLE_COL" && typeof obj["name"] === "string" && (typeof obj["alias"] === "string" || obj["alias"] === null)) {
18924
19631
  const value = variables.get(obj["name"]);
18925
19632
  if (value === void 0) throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
18926
19633
  if (value.type === "array") throw new Error(`ParseError: array variable @${obj["name"]} cannot be used as a SELECT column.`);
@@ -19026,6 +19733,31 @@ var ScalarSubqueryError = class extends Error {
19026
19733
  }
19027
19734
  };
19028
19735
  async function executeAssert(stmt, client, options, cacheContext, tempTables) {
19736
+ const evaluation = await evaluateAssertCondition(stmt, client, options, cacheContext, tempTables);
19737
+ if (!evaluation.passed) {
19738
+ if (stmt.warn === true) {
19739
+ return {
19740
+ type: "ASSERT",
19741
+ condition: stmt.text,
19742
+ passed: false,
19743
+ warning: stmt.message ?? `assertion failed: ${stmt.text} (actual: ${evaluation.actual}).`
19744
+ };
19745
+ }
19746
+ const suffix = stmt.message !== void 0 ? ` ${stmt.message}` : "";
19747
+ throw new AssertError(`assertion failed: ${stmt.text} (actual: ${evaluation.actual}).${suffix}`);
19748
+ }
19749
+ return { type: "ASSERT", condition: stmt.text };
19750
+ }
19751
+ async function executeExit(stmt, client, options, cacheContext, tempTables) {
19752
+ const evaluation = await evaluateAssertCondition(stmt, client, options, cacheContext, tempTables);
19753
+ return {
19754
+ type: "EXIT",
19755
+ condition: stmt.text,
19756
+ exited: evaluation.passed,
19757
+ message: stmt.message
19758
+ };
19759
+ }
19760
+ async function evaluateAssertCondition(stmt, client, options, cacheContext, tempTables) {
19029
19761
  const left = await evalAssertOperand(stmt.left, client, options, cacheContext, tempTables);
19030
19762
  const semantics = stmt.left.type === "NUMBER" || stmt.left.type === "ARITH" ? syntheticSemantics("number") : syntheticSemantics("string");
19031
19763
  if (stmt.op === "BETWEEN") {
@@ -19034,19 +19766,16 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
19034
19766
  }
19035
19767
  const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
19036
19768
  const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
19037
- if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
19038
- throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
19039
- }
19040
- return { type: "ASSERT", condition: stmt.text };
19769
+ return {
19770
+ passed: compareScalarValues(">=", left, low, semantics) && compareScalarValues("<=", left, high, semantics),
19771
+ actual: left
19772
+ };
19041
19773
  }
19042
19774
  if (stmt.right === null) {
19043
19775
  throw new Error("ArgumentError: malformed ASSERT statement.");
19044
19776
  }
19045
19777
  const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
19046
- if (!compareScalarValues(stmt.op, left, right, semantics)) {
19047
- throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
19048
- }
19049
- return { type: "ASSERT", condition: stmt.text };
19778
+ return { passed: compareScalarValues(stmt.op, left, right, semantics), actual: left };
19050
19779
  }
19051
19780
  async function evalAssertOperand(operand, client, options, cacheContext, tempTables) {
19052
19781
  switch (operand.type) {
@@ -26380,7 +27109,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
26380
27109
  });
26381
27110
  const invocationCacheContext = createInvocationCacheContext(cacheContext);
26382
27111
  try {
26383
- const statements = parseSqlBatch(sql, enableImport);
27112
+ const { statements, meta } = parseSqlStatementsForScript(sql, { import: enableImport });
26384
27113
  const analysis = analyzeBatch(statements);
26385
27114
  const normalizedInjectedVariables = validateDeclaredBatchVariables(statements, injectedVariables);
26386
27115
  const relativeDateVariables = prepareRelativeDateVariables(statements, normalizedInjectedVariables);
@@ -26461,10 +27190,16 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
26461
27190
  ), cursorMaxActive)
26462
27191
  ];
26463
27192
  const metadataPlan = explainMetadataLines(whereAnalysis);
27193
+ const dialect1Estimate = meta.dialect === 1 ? buildDialect1ApiEstimateLines(
27194
+ planStmt,
27195
+ analysis.statements[i],
27196
+ maxRecords,
27197
+ dmlMaxRows
27198
+ ) : [];
26464
27199
  plans.push({
26465
27200
  index: i,
26466
27201
  type: analysis.statements[i].statementType,
26467
- plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
27202
+ plan: statementPlan.length === 0 ? [...metadataPlan, ...dialect1Estimate] : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1), ...dialect1Estimate]
26468
27203
  });
26469
27204
  fetchStatements.push({
26470
27205
  index: i,
@@ -26489,6 +27224,63 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
26489
27224
  releaseMetadataCacheScope(invocationCacheContext);
26490
27225
  }
26491
27226
  }
27227
+ function buildDialect1ApiEstimateLines(statement, analysis, maxRecords, dmlMaxRows) {
27228
+ const lines = [" estimated API consumption (dialect 1):"];
27229
+ const sources = collectPhysicalExplainSources(statement);
27230
+ if ((statement.type === "UPDATE" || statement.type === "DELETE") && !sources.includes(`APP${statement.appId}`)) {
27231
+ sources.unshift(`APP${statement.appId}`);
27232
+ }
27233
+ const maxReadRequests = Math.ceil(maxRecords / 500);
27234
+ for (const source of sources) {
27235
+ lines.push(
27236
+ ` read ${source}: \u4E0D\u660E\uFF08\u4E0A\u9650 maxRecords=${maxRecords} \u3068\u4EEE\u5B9A: \u6700\u5927 ${maxReadRequests} \u56DE\u3001500 \u4EF6/\u56DE\uFF09`
27237
+ );
27238
+ }
27239
+ const metadataApps = [.../* @__PURE__ */ new Set([
27240
+ ...analysis.appIds,
27241
+ ...analysis.targetAppId === null ? [] : [analysis.targetAppId]
27242
+ ])];
27243
+ lines.push(
27244
+ ` metadata: GET form fields \xD7 ${metadataApps.length} \u30A2\u30D7\u30EA\uFF08\u30AD\u30E3\u30C3\u30B7\u30E5\u6E08\u307F\u306F\u8FFD\u52A0 0 \u56DE\uFF09`
27245
+ );
27246
+ if (statement.type === "UPSERT" || statement.type === "UPSERT_SELECT") {
27247
+ if (statement.type === "UPSERT") {
27248
+ lines.push(
27249
+ ` UPSERT pre-read: ${Math.ceil(statement.values.length / UPSERT_IN_CHUNK_SIZE)} \u56DE\uFF08${statement.values.length} \u884C\u3001${UPSERT_IN_CHUNK_SIZE} \u30AD\u30FC/\u56DE\uFF09`
27250
+ );
27251
+ } else {
27252
+ lines.push(
27253
+ ` UPSERT pre-read: \u4E0D\u660E\uFF08\u4E0A\u9650 dmlMaxRows=${dmlMaxRows} \u3068\u4EEE\u5B9A: \u6700\u5927 ${Math.ceil(dmlMaxRows / UPSERT_IN_CHUNK_SIZE)} \u56DE\u3001${UPSERT_IN_CHUNK_SIZE} \u30AD\u30FC/\u56DE\uFF09`
27254
+ );
27255
+ }
27256
+ }
27257
+ const knownRows = statement.type === "INSERT" || statement.type === "UPSERT" ? statement.values.length : null;
27258
+ if (statement.type === "INSERT" || statement.type === "INSERT_SELECT" || statement.type === "UPSERT" || statement.type === "UPSERT_SELECT" || statement.type === "UPDATE" || statement.type === "DELETE") {
27259
+ lines.push(knownRows === null ? ` write: \u4E0D\u660E\uFF08\u4E0A\u9650 dmlMaxRows=${dmlMaxRows} \u3068\u4EEE\u5B9A: \u6700\u5927 ${Math.ceil(dmlMaxRows / 100)} \u56DE\u3001100 \u4EF6/HTTP \u30EA\u30AF\u30A8\u30B9\u30C8\uFF09` : ` write: ${Math.ceil(knownRows / 100)} \u56DE\uFF08${knownRows} \u884C\u3001100 \u4EF6/HTTP \u30EA\u30AF\u30A8\u30B9\u30C8\uFF09`);
27260
+ lines.push(" reference: bulkRequest \u306F\u672A\u5B9F\u88C5\u3002\u66F8\u8FBC\u30B5\u30D6\u30EA\u30AF\u30A8\u30B9\u30C8\u6570\u306F HTTP \u66F8\u8FBC\u56DE\u6570\u3068\u540C\u3058");
27261
+ }
27262
+ return lines;
27263
+ }
27264
+ function collectPhysicalExplainSources(statement) {
27265
+ const sources = [];
27266
+ const visit = (node) => {
27267
+ if (Array.isArray(node)) {
27268
+ node.forEach(visit);
27269
+ return;
27270
+ }
27271
+ if (node === null || typeof node !== "object") return;
27272
+ const value = node;
27273
+ if (typeof value["appId"] === "number" && Object.prototype.hasOwnProperty.call(value, "alias") && Object.prototype.hasOwnProperty.call(value, "cteName") && value["cteName"] === null && value["appId"] > 0) {
27274
+ const app = `APP${value["appId"]}`;
27275
+ const subtable = typeof value["subtableCode"] === "string" ? `$${value["subtableCode"]}` : "";
27276
+ const alias = typeof value["alias"] === "string" && value["alias"] !== app ? ` AS ${value["alias"]}` : "";
27277
+ sources.push(`${app}${subtable}${alias}`);
27278
+ }
27279
+ Object.values(value).forEach(visit);
27280
+ };
27281
+ visit(statement);
27282
+ return sources;
27283
+ }
26492
27284
  function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGroupByPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }, tempSchemaLedger = /* @__PURE__ */ new Map(), createdSchema, explainContext = defaultRecursiveExplainContext()) {
26493
27285
  if (stmt.type === "CREATE_TEMP_TABLE") {
26494
27286
  return [
@@ -26575,8 +27367,8 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
26575
27367
  }
26576
27368
  if (stmt.type === "ASSERT") {
26577
27369
  const lines = [
26578
- `ASSERT ${stmt.text}`,
26579
- " check: \u5B9F\u884C\u6642\u306B\u6761\u4EF6\u8A55\u4FA1\uFF08\u4E0D\u6210\u7ACB\u306F AssertError \u3067\u30D0\u30C3\u30C1\u505C\u6B62\u3001\u4EE5\u964D\u306E\u6587\u306F skipped\uFF09"
27370
+ `ASSERT${stmt.warn === true ? " WARN" : ""} ${stmt.text}${stmt.message !== void 0 ? `, '${stmt.message.replace(/'/g, "''")}'` : ""}`,
27371
+ stmt.warn === true ? " check: \u5B9F\u884C\u6642\u306B\u6761\u4EF6\u8A55\u4FA1\uFF08\u4E0D\u6210\u7ACB\u306F\u8B66\u544A\u3068\u3057\u3066\u8A18\u9332\u3057\u3001\u5F8C\u7D9A\u6587\u3092\u7D9A\u884C\uFF09" : " check: \u5B9F\u884C\u6642\u306B\u6761\u4EF6\u8A55\u4FA1\uFF08\u4E0D\u6210\u7ACB\u306F AssertError \u3067\u30D0\u30C3\u30C1\u505C\u6B62\u3001\u4EE5\u964D\u306E\u6587\u306F skipped\uFF09"
26580
27372
  ];
26581
27373
  const subqueries = [stmt.left, stmt.right, stmt.low, stmt.high].filter(
26582
27374
  (o) => o !== null && o.type === "SCALAR_SUBQUERY"
@@ -26598,6 +27390,31 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
26598
27390
  });
26599
27391
  return lines;
26600
27392
  }
27393
+ if (stmt.type === "EXIT") {
27394
+ const lines = [
27395
+ `EXIT SUCCESS IF ${stmt.text}, '${stmt.message.replace(/'/g, "''")}'`,
27396
+ " check: \u5B9F\u884C\u6642\u306B\u6761\u4EF6\u8A55\u4FA1\uFF08\u6210\u7ACB\u6642\u306F\u6B63\u5E38\u7D42\u4E86\u3057\u3001\u4EE5\u964D\u306E\u6587\u306F skippedReason: exit\uFF09"
27397
+ ];
27398
+ const subqueries = [stmt.left, stmt.right, stmt.low, stmt.high].filter(
27399
+ (o) => o !== null && o.type === "SCALAR_SUBQUERY"
27400
+ );
27401
+ subqueries.forEach((sq, i) => {
27402
+ lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
27403
+ const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
27404
+ lines.push(...buildPlanForBatchQuery(
27405
+ sq.query,
27406
+ subInfo,
27407
+ capabilities,
27408
+ orderPlans,
27409
+ plainGroupByPlans,
27410
+ collector,
27411
+ "main",
27412
+ tempSchemaLedger,
27413
+ explainContext
27414
+ ).map((line) => ` ${line}`));
27415
+ });
27416
+ return lines;
27417
+ }
26601
27418
  if (stmt.type === "UPDATE" && (stmt.applyBlocks?.length ?? 0) > 0) {
26602
27419
  return buildExplainPlan(
26603
27420
  stmt,
@@ -27943,20 +28760,6 @@ var OperationCancelledError = class extends Error {
27943
28760
  }
27944
28761
  };
27945
28762
 
27946
- // src/core/sql.ts
27947
- function parseSqlStatement(sql, capabilities = {}) {
27948
- const tokens = new Lexer(sql).tokenize();
27949
- const stmt = new Parser(tokens, capabilities).parse();
27950
- validateStatementStatic(stmt);
27951
- return stmt;
27952
- }
27953
- function parseSqlStatements(sql, capabilities = {}) {
27954
- const tokens = new Lexer(sql).tokenize();
27955
- const statements = new Parser(tokens, capabilities).parseStatements();
27956
- statements.forEach(validateStatementStatic);
27957
- return statements;
27958
- }
27959
-
27960
28763
  // src/core/displayFormat.ts
27961
28764
  var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
27962
28765
  var DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
@@ -28085,6 +28888,9 @@ function toMutationSummary(result) {
28085
28888
  ...result.errTable !== void 0 ? { errTable: result.errTable } : {}
28086
28889
  };
28087
28890
  }
28891
+ if (result.type === "EXIT") {
28892
+ return { condition: result.condition, exited: result.exited, message: result.message };
28893
+ }
28088
28894
  return { reorderedParentCount: result.reorderedParentCount };
28089
28895
  }
28090
28896
  function buildBatchEnvelope(batch, options = {}) {
@@ -28142,6 +28948,10 @@ function buildBatchEnvelope(batch, options = {}) {
28142
28948
  ...s.result.deletedRows ? { deletedRows: s.result.deletedRows } : {},
28143
28949
  ...s.result.diagnostic ? { diagnostic: s.result.diagnostic } : {}
28144
28950
  });
28951
+ } else if (s.status === "success" && s.result?.type === "ASSERT") {
28952
+ entry.condition = s.result.condition;
28953
+ if (s.result.passed !== void 0) entry.passed = s.result.passed;
28954
+ if (s.result.warning !== void 0) entry.warning = s.result.warning;
28145
28955
  } else if (s.status === "success" && s.result && s.result.type !== "SELECT" && s.result.type !== "ASSERT") {
28146
28956
  Object.assign(entry, toMutationSummary(s.result));
28147
28957
  }
@@ -29662,7 +30472,7 @@ function toParseInput(sql) {
29662
30472
  }
29663
30473
  function tryParseStatements(sql) {
29664
30474
  try {
29665
- const stmts = new Parser(new Lexer(toParseInput(sql)).tokenize()).parseStatements();
30475
+ const { statements: stmts } = parseSqlStatementsForScript(toParseInput(sql));
29666
30476
  return {
29667
30477
  kind: "ok",
29668
30478
  count: stmts.length,
@@ -29677,7 +30487,11 @@ function tryParseStatements(sql) {
29677
30487
  if (e instanceof ParseError) {
29678
30488
  return { kind: "fail", continuable: e.token.kind === "EOF" /* EOF */, message: e.message };
29679
30489
  }
29680
- throw e;
30490
+ return {
30491
+ kind: "fail",
30492
+ continuable: false,
30493
+ message: e instanceof Error ? e.message : String(e)
30494
+ };
29681
30495
  }
29682
30496
  }
29683
30497
 
@@ -30866,7 +31680,7 @@ async function confirmDmlInConsole(sql, opts, queue, defaultProfile = "dev", res
30866
31680
  if (!opts.allowDml || opts.yes || opts.dryRun) return true;
30867
31681
  try {
30868
31682
  const normalized = normalizeSqlAppProfiles(sql, defaultProfile, resolutionContext);
30869
- const statements = parseSqlStatements(normalized.normalizedSql);
31683
+ const { statements } = parseSqlStatementsForScript(normalized.normalizedSql);
30870
31684
  if (statements.length > 1) {
30871
31685
  const analysis = analyzeBatch(statements);
30872
31686
  if (!analysis.containsDml) return true;
@@ -31316,7 +32130,7 @@ async function run() {
31316
32130
  }
31317
32131
  const importEnabled = Object.keys(args.importCsv).length > 0 || Object.keys(args.importJson).length > 0;
31318
32132
  try {
31319
- const statements = parseSqlStatements(sql, { import: importEnabled });
32133
+ const { statements } = parseSqlStatementsForScript(sql, { import: importEnabled });
31320
32134
  parsedStatements = statements;
31321
32135
  const hasApply = (statement) => statement.type === "UPDATE" || statement.type === "INSERT" ? (statement.applyBlocks?.length ?? 0) > 0 : statement.type === "UPSERT" ? (statement.onInsertApplyBlocks?.length ?? 0) > 0 || (statement.onUpdateApplyBlocks?.length ?? 0) > 0 : false;
31322
32136
  containsApplyStatement = statements.some(hasApply);
@@ -31337,7 +32151,7 @@ async function run() {
31337
32151
  isBatchSql = true;
31338
32152
  batchContainsDml = batchAnalysis.containsDml;
31339
32153
  } else {
31340
- const stmt = parseSqlStatement(sql, { import: importEnabled });
32154
+ const stmt = statements[0];
31341
32155
  parsedStmt = stmt;
31342
32156
  stmtType = getStatementType(stmt);
31343
32157
  isDmlStatement = writesKintone(stmt);