@rex0220/kintone-sql-tools 3.67.0 → 3.68.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.
@@ -29764,7 +29764,7 @@ var import_docsResourceBuilder = __toESM(require_docsResourceBuilder());
29764
29764
 
29765
29765
  // src/mcp/serverVersion.ts
29766
29766
  init_define_KSQL_DOCS();
29767
- var SERVER_VERSION = true ? "3.67.0" : "0.0.0-dev";
29767
+ var SERVER_VERSION = true ? "3.68.0" : "0.0.0-dev";
29768
29768
 
29769
29769
  // src/mcp/docsResources.ts
29770
29770
  function loadFromRepoDocs() {
@@ -30370,7 +30370,7 @@ var Lexer = class {
30370
30370
  // ヘルパー
30371
30371
  // ----------------------------------------------------------
30372
30372
  makeToken(kind, value, pos) {
30373
- return { kind, value, pos };
30373
+ return { kind, value, pos, end: this.pos };
30374
30374
  }
30375
30375
  };
30376
30376
  function isIdentStart(ch) {
@@ -30987,6 +30987,8 @@ var Parser = class {
30987
30987
  this.allowRelativeDateFunctions = false;
30988
30988
  /** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
30989
30989
  this.cteNames = /* @__PURE__ */ new Set();
30990
+ /** dialect 1 の裸名で宣言された一時テーブル名(参照時に # 付きへ正規化) */
30991
+ this.bareTempTableNames = /* @__PURE__ */ new Set();
30990
30992
  /** パース中に出現した一時テーブル参照(#name)のトークン。単文 API での拒否に使う */
30991
30993
  this.tempTableRefs = [];
30992
30994
  /** GROUP BY を読む前に作る B124 候補 leaf の診断位置。AST 公開型へ位置情報を足さない。 */
@@ -31025,7 +31027,12 @@ var Parser = class {
31025
31027
  }
31026
31028
  /** 複文(`;` 区切り)をパースする。空文はスキップする */
31027
31029
  parseStatements() {
31030
+ return this.parseStatementsWithRanges().statements;
31031
+ }
31032
+ /** 複文と、原文上の文ごとの文字範囲を同時に返す。 */
31033
+ parseStatementsWithRanges() {
31028
31034
  const stmts = [];
31035
+ const statementRanges = [];
31029
31036
  while (true) {
31030
31037
  while (this.peek().kind === ";" /* SEMICOLON */) this.advance();
31031
31038
  if (this.peek().kind === "EOF" /* EOF */) break;
@@ -31041,9 +31048,11 @@ var Parser = class {
31041
31048
  if (after.kind !== ";" /* SEMICOLON */ && after.kind !== "EOF" /* EOF */) {
31042
31049
  throw new ParseError("\u6587\u306E\u533A\u5207\u308A\u306B\u306F ; \u304C\u5FC5\u8981\u3067\u3059", after);
31043
31050
  }
31051
+ const lastTok = this.prev();
31052
+ statementRanges.push({ start: startTok.pos, end: lastTok.end ?? after.pos });
31044
31053
  }
31045
31054
  this.expect("EOF" /* EOF */);
31046
- return stmts;
31055
+ return { statements: stmts, statementRanges };
31047
31056
  }
31048
31057
  // ----------------------------------------------------------
31049
31058
  // Statement ディスパッチ
@@ -31082,6 +31091,13 @@ var Parser = class {
31082
31091
  if (upper === "DROP") return this.parseDropTempTable();
31083
31092
  if (upper === "DECLARE") return this.parseDeclareVariable();
31084
31093
  if (upper === "VALIDATE") return this.parseValidate();
31094
+ if (upper === "EXIT") return this.parseExit();
31095
+ if (upper === "MERGE") {
31096
+ if (!this.capabilities.dialect1) {
31097
+ throw new ParseError("MERGE \u306B\u306F -- @ksql dialect: 1 \u306E\u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059", tok);
31098
+ }
31099
+ return this.parseMergeAsUpsert();
31100
+ }
31085
31101
  if (upper === "GENERATE_SERIES") {
31086
31102
  throw new ParseError(
31087
31103
  "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",
@@ -31100,7 +31116,7 @@ var Parser = class {
31100
31116
  break;
31101
31117
  }
31102
31118
  throw new ParseError(
31103
- "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",
31119
+ "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",
31104
31120
  tok
31105
31121
  );
31106
31122
  }
@@ -31228,6 +31244,7 @@ var Parser = class {
31228
31244
  this.advance();
31229
31245
  this.expectSoftKeyword("TEMP", "CREATE \u306E\u5F8C\u306B\u306F TEMP TABLE \u304C\u5FC5\u8981\u3067\u3059\uFF08\u4F8B: CREATE TEMP TABLE #temp AS SELECT ...\uFF09");
31230
31246
  this.expectSoftKeyword("TABLE", "CREATE TEMP \u306E\u5F8C\u306B\u306F TABLE \u304C\u5FC5\u8981\u3067\u3059");
31247
+ const bareName = this.isDialect1BareTempTableName();
31231
31248
  const name = this.parseTempTableName();
31232
31249
  this.expect("AS" /* AS */, "CREATE TEMP TABLE \u306B\u306F AS SELECT \u304C\u5FC5\u8981\u3067\u3059");
31233
31250
  const tok = this.peek();
@@ -31239,13 +31256,16 @@ var Parser = class {
31239
31256
  } else {
31240
31257
  throw new ParseError("CREATE TEMP TABLE ... AS \u306E\u5F8C\u306B\u306F SELECT / WITH \u304C\u5FC5\u8981\u3067\u3059", tok);
31241
31258
  }
31259
+ if (bareName !== null) this.bareTempTableNames.add(bareName);
31242
31260
  return { type: "CREATE_TEMP_TABLE", name, query };
31243
31261
  }
31244
31262
  parseDropTempTable() {
31245
31263
  this.advance();
31246
31264
  this.expectSoftKeyword("TEMP", "DROP \u306E\u5F8C\u306B\u306F TEMP TABLE \u304C\u5FC5\u8981\u3067\u3059\uFF08\u4F8B: DROP TEMP TABLE #temp\uFF09");
31247
31265
  this.expectSoftKeyword("TABLE", "DROP TEMP \u306E\u5F8C\u306B\u306F TABLE \u304C\u5FC5\u8981\u3067\u3059");
31266
+ const bareName = this.isDialect1BareTempTableName();
31248
31267
  const name = this.parseTempTableName();
31268
+ if (bareName !== null) this.bareTempTableNames.delete(bareName);
31249
31269
  return { type: "DROP_TEMP_TABLE", name };
31250
31270
  }
31251
31271
  expectSoftKeyword(word, msg) {
@@ -31267,8 +31287,16 @@ var Parser = class {
31267
31287
  this.advance();
31268
31288
  return tok.value;
31269
31289
  }
31290
+ if (this.capabilities.dialect1 && (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */)) {
31291
+ this.advance();
31292
+ return `#${tok.value}`;
31293
+ }
31270
31294
  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);
31271
31295
  }
31296
+ isDialect1BareTempTableName() {
31297
+ const tok = this.peek();
31298
+ return this.capabilities.dialect1 && (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) && !tok.value.startsWith("#") ? tok.value : null;
31299
+ }
31272
31300
  parseShow() {
31273
31301
  this.advance();
31274
31302
  if (!this.consume("APPS" /* APPS */)) {
@@ -31596,6 +31624,41 @@ var Parser = class {
31596
31624
  // ----------------------------------------------------------
31597
31625
  parseAssert() {
31598
31626
  this.expect("ASSERT" /* ASSERT */);
31627
+ const warnTok = this.peek();
31628
+ const warn = this.isSoftKeyword("WARN");
31629
+ if (warn) {
31630
+ this.requireDialect1(warnTok);
31631
+ this.advance();
31632
+ }
31633
+ const condition = this.parseAssertCondition();
31634
+ const message = this.parseFlowMessage();
31635
+ return {
31636
+ type: "ASSERT",
31637
+ ...condition,
31638
+ ...warn ? { warn: true } : {},
31639
+ ...message !== void 0 ? { message } : {}
31640
+ };
31641
+ }
31642
+ /** EXIT SUCCESS IF <ASSERT と同じ条件>, '<message>' */
31643
+ parseExit() {
31644
+ const exitTok = this.advance();
31645
+ this.requireDialect1(exitTok);
31646
+ if (!this.isSoftKeyword("SUCCESS")) {
31647
+ throw new ParseError("EXIT \u306E\u5F8C\u306B\u306F SUCCESS \u304C\u5FC5\u8981\u3067\u3059", this.peek());
31648
+ }
31649
+ this.advance();
31650
+ if (this.peek().kind !== "IF" /* IF */ && !this.isSoftKeyword("IF")) {
31651
+ throw new ParseError("EXIT SUCCESS \u306E\u5F8C\u306B\u306F IF \u304C\u5FC5\u8981\u3067\u3059", this.peek());
31652
+ }
31653
+ this.advance();
31654
+ const condition = this.parseAssertCondition();
31655
+ if (!this.consume("," /* COMMA */)) {
31656
+ 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());
31657
+ }
31658
+ 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");
31659
+ return { type: "EXIT", ...condition, message: message.value };
31660
+ }
31661
+ parseAssertCondition() {
31599
31662
  const condStart = this.pos;
31600
31663
  const left = this.parseAssertOperand();
31601
31664
  const opTok = this.peek();
@@ -31608,7 +31671,6 @@ var Parser = class {
31608
31671
  const high = this.parseAssertOperand();
31609
31672
  this.rejectAssertCompound();
31610
31673
  return {
31611
- type: "ASSERT",
31612
31674
  left,
31613
31675
  op: "BETWEEN",
31614
31676
  right: null,
@@ -31627,7 +31689,6 @@ var Parser = class {
31627
31689
  const right = this.parseAssertOperand();
31628
31690
  this.rejectAssertCompound();
31629
31691
  return {
31630
- type: "ASSERT",
31631
31692
  left,
31632
31693
  op,
31633
31694
  right,
@@ -31636,6 +31697,17 @@ var Parser = class {
31636
31697
  text: this.renderTokenRange(condStart, this.pos)
31637
31698
  };
31638
31699
  }
31700
+ /** ASSERT の dialect 1 メッセージ。カンマが無ければ既存形式。 */
31701
+ parseFlowMessage() {
31702
+ if (!this.consume("," /* COMMA */)) return void 0;
31703
+ this.requireDialect1(this.prev());
31704
+ 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;
31705
+ }
31706
+ requireDialect1(tok) {
31707
+ if (!this.capabilities.dialect1) {
31708
+ throw new ParseError("\u3053\u306E\u69CB\u6587\u306B\u306F -- @ksql dialect: 1 \u306E\u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059", tok);
31709
+ }
31710
+ }
31639
31711
  /** ASSERT のオペランド: 文字列 / スカラーサブクエリ / 数値算術式 */
31640
31712
  parseAssertOperand() {
31641
31713
  const tok = this.peek();
@@ -33026,6 +33098,12 @@ var Parser = class {
33026
33098
  );
33027
33099
  }
33028
33100
  const name = this.parseTableName();
33101
+ if (this.capabilities.dialect1 && this.bareTempTableNames.has(name)) {
33102
+ const normalizedName = `#${name}`;
33103
+ this.tempTableRefs.push({ ...this.prev(), value: normalizedName });
33104
+ const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
33105
+ return { appId: 0, alias: alias2, cteName: normalizedName };
33106
+ }
33029
33107
  if (nameTok.kind === "IDENT" /* IDENT */ && name.startsWith("#")) {
33030
33108
  this.tempTableRefs.push(this.prev());
33031
33109
  const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
@@ -33066,6 +33144,7 @@ var Parser = class {
33066
33144
  if (k === "IDENT" /* IDENT */ || k === "BIDENT" /* BIDENT */) {
33067
33145
  if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "VALIDATE" && this.peekAt(1).kind === "IDENT" /* IDENT */ && this.peekAt(1).value.toUpperCase() === "ONLY") return null;
33068
33146
  if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "CHECK" && this.peekAt(1).kind === "WHEN" /* WHEN */) return null;
33147
+ if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "KEY" && this.peekAt(1).kind === "(" /* LPAREN */) return null;
33069
33148
  return this.parseTableAliasName();
33070
33149
  }
33071
33150
  return null;
@@ -33966,6 +34045,15 @@ var Parser = class {
33966
34045
  };
33967
34046
  }
33968
34047
  parseOnDuplicate() {
34048
+ if (this.capabilities.dialect1 && this.consumeSoftKeyword("KEY")) {
34049
+ this.expect("(" /* LPAREN */, "KEY \u306E\u5F8C\u306B\u306F (\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9) \u304C\u5FC5\u8981\u3067\u3059");
34050
+ const keyFields2 = this.parseIdentList();
34051
+ this.expect(")" /* RPAREN */);
34052
+ if (keyFields2.length === 0) {
34053
+ throw new ParseError("KEY \u306B\u306F\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u6700\u4F4E 1 \u3064\u5FC5\u8981\u3067\u3059", this.prev());
34054
+ }
34055
+ return keyFields2;
34056
+ }
33969
34057
  this.expectKeyword("ON" /* ON */, "UPSERT \u306B\u306F ON DUPLICATE (\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9) \u304C\u5FC5\u8981\u3067\u3059");
33970
34058
  if (!this.consume("DUPLICATE" /* DUPLICATE */)) {
33971
34059
  throw new ParseError("ON \u306E\u5F8C\u306B\u306F DUPLICATE \u304C\u5FC5\u8981\u3067\u3059", this.peek());
@@ -33978,6 +34066,238 @@ var Parser = class {
33978
34066
  }
33979
34067
  return keyFields;
33980
34068
  }
34069
+ parseMergeAsUpsert() {
34070
+ const mergeToken = this.advance();
34071
+ this.expect("INTO" /* INTO */, "MERGE \u306E\u5F8C\u306B\u306F INTO \u304C\u5FC5\u8981\u3067\u3059");
34072
+ this.rejectTempTableDml();
34073
+ const targetName = this.parseIdentifier();
34074
+ const { appId, subtableCode } = extractTableRef(targetName, this.prev());
34075
+ if (subtableCode) {
34076
+ 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());
34077
+ }
34078
+ this.expect("AS" /* AS */, "MERGE \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059");
34079
+ const targetAlias = this.parseTableAliasName();
34080
+ this.expectSoftKeyword("USING", "MERGE \u306B\u306F USING <source> AS alias \u304C\u5FC5\u8981\u3067\u3059");
34081
+ const sourceStart = this.pos;
34082
+ const source = this.parseTableRef();
34083
+ const explicitSourceAlias = this.tokens.slice(sourceStart, this.pos).some((token) => token.kind === "AS" /* AS */);
34084
+ if (!explicitSourceAlias || source.alias === null) {
34085
+ throw new ParseError("MERGE \u306E USING \u30BD\u30FC\u30B9\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
34086
+ }
34087
+ const sourceAlias = source.alias;
34088
+ this.expect("ON" /* ON */, "MERGE \u306B\u306F ON t.key = s.key \u306E\u5358\u4E00\u30AD\u30FC\u7B49\u5024\u304C\u5FC5\u8981\u3067\u3059");
34089
+ const left = this.parseMergeQualifiedField("MERGE \u306E ON \u5DE6\u8FBA\u306F targetAlias.key \u306E\u5F62\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
34090
+ if (!this.consume("=" /* EQ */)) {
34091
+ throw new ParseError(
34092
+ "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",
34093
+ this.peek()
34094
+ );
34095
+ }
34096
+ const right = this.parseMergeQualifiedField("MERGE \u306E ON \u53F3\u8FBA\u306F sourceAlias.key \u306E\u5F62\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
34097
+ if (left.alias.toLowerCase() !== targetAlias.toLowerCase() || right.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
34098
+ throw new ParseError(
34099
+ `MERGE \u306E ON \u306F ${targetAlias}.key = ${sourceAlias}.key \u306E\u5225\u540D\u4FEE\u98FE\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044`,
34100
+ mergeToken
34101
+ );
34102
+ }
34103
+ if (this.peek().kind === "AND" /* AND */ || this.peek().kind === "OR" /* OR */) {
34104
+ throw new ParseError(
34105
+ "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",
34106
+ this.peek()
34107
+ );
34108
+ }
34109
+ let matched = null;
34110
+ let insertFields = null;
34111
+ let insertValues = null;
34112
+ while (this.peek().kind === "WHEN" /* WHEN */) {
34113
+ const whenToken = this.advance();
34114
+ if (this.consume("NOT" /* NOT */)) {
34115
+ this.expectSoftKeyword("MATCHED", "WHEN NOT \u306E\u5F8C\u306B\u306F MATCHED \u304C\u5FC5\u8981\u3067\u3059");
34116
+ if (insertFields !== null) throw new ParseError("WHEN NOT MATCHED \u53E5\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059", whenToken);
34117
+ this.expect("THEN" /* THEN */, "WHEN NOT MATCHED \u306E\u5F8C\u306B\u306F THEN \u304C\u5FC5\u8981\u3067\u3059");
34118
+ this.expect("INSERT" /* INSERT */, "WHEN NOT MATCHED THEN \u306E\u5F8C\u306B\u306F INSERT \u304C\u5FC5\u8981\u3067\u3059");
34119
+ this.expect("(" /* LPAREN */, "MERGE INSERT \u306B\u306F\u5217\u30EA\u30B9\u30C8\u304C\u5FC5\u8981\u3067\u3059");
34120
+ insertFields = this.parseIdentList();
34121
+ this.expect(")" /* RPAREN */);
34122
+ this.expect("VALUES" /* VALUES */, "MERGE INSERT \u306E\u5217\u30EA\u30B9\u30C8\u306E\u5F8C\u306B\u306F VALUES \u304C\u5FC5\u8981\u3067\u3059");
34123
+ this.expect("(" /* LPAREN */, "MERGE INSERT VALUES \u306F ( \u3067\u59CB\u3081\u3066\u304F\u3060\u3055\u3044");
34124
+ insertValues = this.parseMergeValueList();
34125
+ this.expect(")" /* RPAREN */);
34126
+ if (insertFields.length !== insertValues.length) {
34127
+ throw new ParseError("MERGE INSERT \u306E\u5217\u6570\u3068 VALUES \u306E\u5024\u6570\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093", whenToken);
34128
+ }
34129
+ } else {
34130
+ this.expectSoftKeyword("MATCHED", "WHEN \u306E\u5F8C\u306B\u306F MATCHED \u307E\u305F\u306F NOT MATCHED \u304C\u5FC5\u8981\u3067\u3059");
34131
+ if (matched !== null) throw new ParseError("WHEN MATCHED \u53E5\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059", whenToken);
34132
+ this.expect("THEN" /* THEN */, "WHEN MATCHED \u306E\u5F8C\u306B\u306F THEN \u304C\u5FC5\u8981\u3067\u3059");
34133
+ this.expect("UPDATE" /* UPDATE */, "WHEN MATCHED THEN \u306E\u5F8C\u306B\u306F UPDATE \u304C\u5FC5\u8981\u3067\u3059");
34134
+ this.expect("SET" /* SET */, "WHEN MATCHED THEN UPDATE \u306E\u5F8C\u306B\u306F SET \u304C\u5FC5\u8981\u3067\u3059");
34135
+ matched = this.parseMergeAssignments(targetAlias);
34136
+ }
34137
+ }
34138
+ if (matched === null || insertFields === null || insertValues === null) {
34139
+ throw new ParseError(
34140
+ "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",
34141
+ this.peek()
34142
+ );
34143
+ }
34144
+ if (!insertFields.some((field) => field.toLowerCase() === left.field.toLowerCase())) {
34145
+ throw new ParseError(
34146
+ `MERGE \u306E ON \u30AD\u30FC ${left.field} \u306F INSERT \u5217\u30EA\u30B9\u30C8\u306B\u542B\u3081\u3066\u304F\u3060\u3055\u3044`,
34147
+ mergeToken
34148
+ );
34149
+ }
34150
+ const expressions = /* @__PURE__ */ new Map();
34151
+ insertFields.forEach((field, index) => expressions.set(field.toLowerCase(), insertValues[index]));
34152
+ for (const assignment of matched) {
34153
+ const key = assignment.field.toLowerCase();
34154
+ const existing = expressions.get(key);
34155
+ if (existing !== void 0 && !this.mergeExpressionsEqual(existing, assignment.value, sourceAlias)) {
34156
+ throw new ParseError(
34157
+ `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`,
34158
+ mergeToken
34159
+ );
34160
+ }
34161
+ if (existing === void 0) {
34162
+ insertFields.push(assignment.field);
34163
+ insertValues.push(assignment.value);
34164
+ expressions.set(key, assignment.value);
34165
+ }
34166
+ }
34167
+ const columns = insertValues.map((value) => this.mergeValueToSelectColumn(value, sourceAlias, mergeToken));
34168
+ const normalizedSource = source.cteName !== null ? { ...source, alias: null } : { ...source, alias: `APP${source.appId}${source.subtableCode ? `$${source.subtableCode}` : ""}` };
34169
+ const select = {
34170
+ type: "SELECT",
34171
+ distinct: false,
34172
+ columns,
34173
+ from: normalizedSource,
34174
+ joins: [],
34175
+ where: null,
34176
+ groupBy: [],
34177
+ having: null,
34178
+ orderMode: "CANONICAL",
34179
+ orderBy: [],
34180
+ limit: null,
34181
+ offset: null
34182
+ };
34183
+ const checkGroups = this.parseCheckGroups();
34184
+ const validation = this.parseDmlControlSuffix();
34185
+ return {
34186
+ type: "UPSERT_SELECT",
34187
+ appId,
34188
+ fields: insertFields,
34189
+ select,
34190
+ keyFields: [left.field],
34191
+ ...checkGroups,
34192
+ ...validation
34193
+ };
34194
+ }
34195
+ parseMergeQualifiedField(message) {
34196
+ const token = this.peek();
34197
+ const path = this.parseFieldPath();
34198
+ const ref = this.splitQualifiedField(path);
34199
+ if (ref.alias === null) throw new ParseError(message, token);
34200
+ return { alias: ref.alias, field: ref.field };
34201
+ }
34202
+ parseMergeAssignments(targetAlias) {
34203
+ const assignments = [];
34204
+ do {
34205
+ const token = this.peek();
34206
+ const path = this.parseFieldPath();
34207
+ const ref = this.splitQualifiedField(path);
34208
+ if (ref.alias !== null && ref.alias.toLowerCase() !== targetAlias.toLowerCase()) {
34209
+ throw new ParseError(`MERGE UPDATE SET \u306E\u5DE6\u8FBA\u306F target alias ${targetAlias} \u3067\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`, token);
34210
+ }
34211
+ this.expect("=" /* EQ */);
34212
+ assignments.push({ field: ref.field, value: this.parseAssignmentValue() });
34213
+ } while (this.consume("," /* COMMA */));
34214
+ return assignments;
34215
+ }
34216
+ parseMergeValueList() {
34217
+ const values = [];
34218
+ if (this.peek().kind === ")" /* RPAREN */) return values;
34219
+ do
34220
+ values.push(this.parseAssignmentValue());
34221
+ while (this.consume("," /* COMMA */));
34222
+ return values;
34223
+ }
34224
+ mergeExpressionsEqual(left, right, sourceAlias) {
34225
+ return this.mergeNormalizedValueEqual(
34226
+ this.normalizeMergeValue(left, sourceAlias, true),
34227
+ this.normalizeMergeValue(right, sourceAlias, true)
34228
+ );
34229
+ }
34230
+ normalizeMergeValue(value, sourceAlias, compareLiteralValues = false) {
34231
+ if (Array.isArray(value)) {
34232
+ return value.map((item) => this.normalizeMergeValue(item, sourceAlias, compareLiteralValues));
34233
+ }
34234
+ if (value === null || typeof value !== "object") return value;
34235
+ const obj = value;
34236
+ if (compareLiteralValues && obj["type"] === "NUMBER") {
34237
+ return { type: "NUMBER", value: obj["value"] };
34238
+ }
34239
+ if (obj["type"] === "SOURCE_FIELD") {
34240
+ if (String(obj["alias"]).toLowerCase() !== sourceAlias.toLowerCase()) return { type: "INVALID_SOURCE" };
34241
+ return { type: "FIELD_REF", field: obj["field"] };
34242
+ }
34243
+ if (obj["type"] === "FIELD_REF" && typeof obj["field"] === "string") {
34244
+ const ref = this.splitQualifiedField(obj["field"]);
34245
+ if (ref.alias !== null && ref.alias.toLowerCase() !== sourceAlias.toLowerCase()) return { type: "INVALID_SOURCE" };
34246
+ return { ...obj, field: ref.field };
34247
+ }
34248
+ if (obj["type"] === "FIELD" && typeof obj["tableAlias"] === "string") {
34249
+ if (obj["tableAlias"].toLowerCase() !== sourceAlias.toLowerCase()) return { type: "INVALID_SOURCE" };
34250
+ return { ...obj, tableAlias: null };
34251
+ }
34252
+ return Object.fromEntries(Object.entries(obj).map(([key, child]) => [
34253
+ key,
34254
+ this.normalizeMergeValue(child, sourceAlias, compareLiteralValues)
34255
+ ]));
34256
+ }
34257
+ mergeNormalizedValueEqual(left, right) {
34258
+ if (left === right) return true;
34259
+ if (Array.isArray(left) || Array.isArray(right)) {
34260
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((item, index) => this.mergeNormalizedValueEqual(item, right[index]));
34261
+ }
34262
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
34263
+ const leftObj = left;
34264
+ const rightObj = right;
34265
+ const leftKeys = Object.keys(leftObj);
34266
+ const rightKeys = Object.keys(rightObj);
34267
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.prototype.hasOwnProperty.call(rightObj, key) && this.mergeNormalizedValueEqual(leftObj[key], rightObj[key]));
34268
+ }
34269
+ mergeValueToSelectColumn(value, sourceAlias, token) {
34270
+ const normalized = this.normalizeMergeValue(value, sourceAlias);
34271
+ if (this.mergeValueContainsInvalidSource(normalized)) {
34272
+ 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);
34273
+ }
34274
+ const expr = normalized;
34275
+ switch (expr["type"]) {
34276
+ case "FIELD_REF":
34277
+ return { type: "FIELD", field: String(expr["field"]), alias: null };
34278
+ case "STRING":
34279
+ return { type: "LITERAL_COL", value: String(expr["value"]), alias: null };
34280
+ case "NUMBER":
34281
+ return { type: "ARITH_COL", expr, alias: null };
34282
+ case "ARITH":
34283
+ return { type: "ARITH_COL", expr, alias: null };
34284
+ case "STRING_FUNC":
34285
+ return { type: "STRFUNC_COL", expr, alias: null };
34286
+ case "CASE_VALUE":
34287
+ return { type: "CASE_COL", expr: expr["expr"], alias: null };
34288
+ default:
34289
+ throw new ParseError(
34290
+ "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",
34291
+ token
34292
+ );
34293
+ }
34294
+ }
34295
+ mergeValueContainsInvalidSource(value) {
34296
+ if (Array.isArray(value)) return value.some((item) => this.mergeValueContainsInvalidSource(item));
34297
+ if (value === null || typeof value !== "object") return false;
34298
+ const obj = value;
34299
+ return obj["type"] === "INVALID_SOURCE" || Object.values(obj).some((item) => this.mergeValueContainsInvalidSource(item));
34300
+ }
33981
34301
  isUpsertApplyBranchStart() {
33982
34302
  return this.peek().kind === "ON" /* ON */ && (this.peekAt(1).kind === "INSERT" /* INSERT */ || this.peekAt(1).kind === "UPDATE" /* UPDATE */);
33983
34303
  }
@@ -34798,7 +35118,7 @@ function isDmlType(type) {
34798
35118
  return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER" || type === "IMPORT";
34799
35119
  }
34800
35120
  function isReadOnlyType(type) {
34801
- 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";
35121
+ 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";
34802
35122
  }
34803
35123
  function writesKintone(stmt) {
34804
35124
  return isDmlType(stmt.type) && !("validateOnly" in stmt && stmt.validateOnly === true);
@@ -38194,6 +38514,7 @@ function validateStatement(stmt) {
38194
38514
  case "SET_VARIABLE":
38195
38515
  case "DECLARE_VARIABLE":
38196
38516
  case "ASSERT":
38517
+ case "EXIT":
38197
38518
  validateNestedSelects(stmt);
38198
38519
  return;
38199
38520
  case "UPDATE":
@@ -46135,6 +46456,151 @@ function toFlatString(value) {
46135
46456
  }
46136
46457
  }
46137
46458
 
46459
+ // src/core/scriptHeader.ts
46460
+ init_define_KSQL_DOCS();
46461
+
46462
+ // src/core/diagnostics.ts
46463
+ init_define_KSQL_DOCS();
46464
+ var DiagnosticCodes = {
46465
+ HEADER_UNKNOWN_KEY: "KSQL1001",
46466
+ HEADER_DUPLICATE_KEY: "KSQL1002",
46467
+ HEADER_INVALID_NAME: "KSQL1003",
46468
+ HEADER_INVALID_DEPENDS_ON: "KSQL1004",
46469
+ HEADER_INVALID_TIMEOUT: "KSQL1005",
46470
+ HEADER_INVALID_DIALECT: "KSQL1006",
46471
+ LOGICAL_APP_UNRESOLVED: "KSQL1101",
46472
+ LEX_ERROR: "KSQL1201",
46473
+ PARSE_ERROR: "KSQL1202"
46474
+ };
46475
+ function sourceLocationAt(source, offset) {
46476
+ const target = Math.max(0, Math.min(offset, source.length));
46477
+ let line = 1;
46478
+ let column = 1;
46479
+ for (let i = 0; i < target; i++) {
46480
+ const ch = source[i];
46481
+ if (ch === "\r") {
46482
+ if (source[i + 1] === "\n" && i + 1 < target) i++;
46483
+ line++;
46484
+ column = 1;
46485
+ } else if (ch === "\n") {
46486
+ line++;
46487
+ column = 1;
46488
+ } else {
46489
+ column++;
46490
+ }
46491
+ }
46492
+ return { line, column };
46493
+ }
46494
+ function diagnosticAt(source, offset, diagnostic2) {
46495
+ return { ...diagnostic2, ...sourceLocationAt(source, offset) };
46496
+ }
46497
+
46498
+ // src/core/scriptHeader.ts
46499
+ var HEADER_LINE_RE = /^(\s*)--\s*@ksql\s+([^:\s]+)\s*:\s*(.*)$/i;
46500
+ function parseScriptHeader(source) {
46501
+ const meta3 = { name: null, dependsOn: [], timeout: null, dialect: 0 };
46502
+ const diagnostics = [];
46503
+ const seen = /* @__PURE__ */ new Set();
46504
+ let hasDirectives = false;
46505
+ let offset = source.charCodeAt(0) === 65279 ? 1 : 0;
46506
+ let headerEnd = offset;
46507
+ while (offset < source.length) {
46508
+ const lineEnd = findLineEnd(source, offset);
46509
+ const line = source.slice(offset, lineEnd.contentEnd);
46510
+ if (!/^\s*--/.test(line)) break;
46511
+ headerEnd = lineEnd.next;
46512
+ const match = HEADER_LINE_RE.exec(line);
46513
+ if (match) {
46514
+ hasDirectives = true;
46515
+ const rawKey = match[2];
46516
+ const key = rawKey.toLowerCase();
46517
+ const rawValue = match[3];
46518
+ const commentAt = rawValue.indexOf("#");
46519
+ const valuePart = commentAt < 0 ? rawValue : rawValue.slice(0, commentAt);
46520
+ const leading = valuePart.match(/^\s*/)?.[0].length ?? 0;
46521
+ const value = valuePart.trim();
46522
+ const valueOffset = offset + match.index + match[0].length - rawValue.length + leading;
46523
+ if (!isHeaderKey(key)) {
46524
+ diagnostics.push(diagnosticAt(source, valueOffset, {
46525
+ severity: "warning",
46526
+ code: DiagnosticCodes.HEADER_UNKNOWN_KEY,
46527
+ message: `Unknown @ksql header key "${rawKey}" was ignored.`
46528
+ }));
46529
+ } else if (seen.has(key)) {
46530
+ diagnostics.push(diagnosticAt(source, valueOffset, {
46531
+ severity: "warning",
46532
+ code: DiagnosticCodes.HEADER_DUPLICATE_KEY,
46533
+ message: `Duplicate @ksql header key "${key}" was ignored; the first value is retained.`
46534
+ }));
46535
+ } else {
46536
+ seen.add(key);
46537
+ applyHeaderValue(meta3, key, value, source, valueOffset, diagnostics);
46538
+ }
46539
+ }
46540
+ offset = lineEnd.next;
46541
+ }
46542
+ return { meta: meta3, diagnostics, hasDirectives, headerEnd };
46543
+ }
46544
+ function isHeaderKey(value) {
46545
+ return value === "name" || value === "depends_on" || value === "timeout" || value === "dialect";
46546
+ }
46547
+ function applyHeaderValue(meta3, key, value, source, valueOffset, diagnostics) {
46548
+ if (key === "name") {
46549
+ if (!value) {
46550
+ diagnostics.push(diagnosticAt(source, valueOffset, {
46551
+ severity: "error",
46552
+ code: DiagnosticCodes.HEADER_INVALID_NAME,
46553
+ message: "@ksql name must not be empty."
46554
+ }));
46555
+ } else {
46556
+ meta3.name = value;
46557
+ }
46558
+ return;
46559
+ }
46560
+ if (key === "depends_on") {
46561
+ const dependencies = value.split(",").map((item) => item.trim());
46562
+ if (!value || dependencies.some((item) => !item)) {
46563
+ diagnostics.push(diagnosticAt(source, valueOffset, {
46564
+ severity: "error",
46565
+ code: DiagnosticCodes.HEADER_INVALID_DEPENDS_ON,
46566
+ message: "@ksql depends_on must be a comma-separated list without empty items."
46567
+ }));
46568
+ } else {
46569
+ meta3.dependsOn = dependencies;
46570
+ }
46571
+ return;
46572
+ }
46573
+ if (key === "timeout") {
46574
+ if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value))) {
46575
+ diagnostics.push(diagnosticAt(source, valueOffset, {
46576
+ severity: "error",
46577
+ code: DiagnosticCodes.HEADER_INVALID_TIMEOUT,
46578
+ message: "@ksql timeout must be a positive integer."
46579
+ }));
46580
+ } else {
46581
+ meta3.timeout = Number(value);
46582
+ }
46583
+ return;
46584
+ }
46585
+ if (value !== "0" && value !== "1") {
46586
+ diagnostics.push(diagnosticAt(source, valueOffset, {
46587
+ severity: "error",
46588
+ code: DiagnosticCodes.HEADER_INVALID_DIALECT,
46589
+ message: "@ksql dialect must be 0 or 1."
46590
+ }));
46591
+ } else {
46592
+ meta3.dialect = Number(value);
46593
+ }
46594
+ }
46595
+ function findLineEnd(source, start) {
46596
+ let i = start;
46597
+ while (i < source.length && source[i] !== "\r" && source[i] !== "\n") i++;
46598
+ const contentEnd = i;
46599
+ if (source[i] === "\r" && source[i + 1] === "\n") i += 2;
46600
+ else if (i < source.length) i++;
46601
+ return { contentEnd, next: i };
46602
+ }
46603
+
46138
46604
  // src/core/dmlPrevalidation.ts
46139
46605
  init_define_KSQL_DOCS();
46140
46606
  function collectDmlPrevalidationSnapshotFields(fieldIndex) {
@@ -48175,6 +48641,8 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
48175
48641
  throw new Error("ArgumentError: DECLARE variable requires a batch.");
48176
48642
  case "ASSERT":
48177
48643
  return executeAssert(stmt, client, options, cacheContext);
48644
+ case "EXIT":
48645
+ throw new Error("ArgumentError: EXIT SUCCESS IF \u306F\u30D0\u30C3\u30C1\u5C02\u7528\u3067\u3059");
48178
48646
  }
48179
48647
  }
48180
48648
  var EXISTING_VALIDATION_COLUMNS = [
@@ -48442,7 +48910,16 @@ var BatchTimeoutError = class extends Error {
48442
48910
  };
48443
48911
  async function executeBatch(sql, client, options = {}) {
48444
48912
  resolveRecursiveCteLimits(options);
48445
- const statements = parseSqlBatch(sql, options.enableImport === true);
48913
+ const header = parseScriptHeader(sql);
48914
+ const headerError = header.diagnostics.find((diagnostic2) => diagnostic2.severity === "error");
48915
+ if (header.hasDirectives && headerError) {
48916
+ throw new Error(`${headerError.code}: ${headerError.message} (${headerError.line}:${headerError.column})`);
48917
+ }
48918
+ const statements = parseSqlBatch(
48919
+ header.hasDirectives ? sql.slice(header.headerEnd) : sql,
48920
+ options.enableImport === true,
48921
+ header.hasDirectives && header.meta.dialect === 1
48922
+ );
48446
48923
  const analysis = analyzeBatch(statements);
48447
48924
  statements.forEach((statement) => assertApplyExecutionScope("phase15b", statement));
48448
48925
  if (options.allowApplyMutation !== true && statements.some(
@@ -48484,7 +48961,7 @@ async function executeBatch(sql, client, options = {}) {
48484
48961
  const base = { index: i, type: info.statementType };
48485
48962
  if (aborted2) {
48486
48963
  results.push({ ...base, status: "skipped", skippedReason: aborted2 });
48487
- failed.add(i);
48964
+ if (aborted2 !== "exit") failed.add(i);
48488
48965
  continue;
48489
48966
  }
48490
48967
  const brokenDep = info.dependsOn.find((d) => failed.has(d));
@@ -48522,24 +48999,29 @@ async function executeBatch(sql, client, options = {}) {
48522
48999
  info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH" || statementContainsOuterJoin(statements[i])
48523
49000
  );
48524
49001
  const cursorScope = wrapClientWithCursorScope(statementClient);
49002
+ const boundOptions = bindStatementEvaluationContext(stmtOptions);
49003
+ const statementContext = {
49004
+ stmt: statements[i],
49005
+ info,
49006
+ client: cursorScope.client,
49007
+ options: boundOptions,
49008
+ cacheContext,
49009
+ tempTables,
49010
+ variables,
49011
+ relativeDateVariables,
49012
+ clock: statementEvaluationContext(boundOptions)
49013
+ };
48525
49014
  const outcome = await runWithDeadline(
48526
- executeBatchStatement(
48527
- statements[i],
48528
- info,
48529
- cursorScope.client,
48530
- stmtOptions,
48531
- cacheContext,
48532
- tempTables,
48533
- variables,
48534
- relativeDateVariables
48535
- ),
49015
+ executeBatchStatement(statementContext),
48536
49016
  remaining,
48537
49017
  cursorScope.closeActive
48538
49018
  );
48539
49019
  if (outcome.result) {
48540
49020
  outcome.result = attachSearchAbortWarning(outcome.result, searchAbortCollector);
48541
49021
  }
48542
- results.push({ ...base, status: "success", ...outcome });
49022
+ const { exitTriggered, ...statementOutcome } = outcome;
49023
+ results.push({ ...base, status: "success", ...statementOutcome });
49024
+ if (exitTriggered) aborted2 = "exit";
48543
49025
  } catch (e) {
48544
49026
  results.push({
48545
49027
  ...base,
@@ -48561,7 +49043,7 @@ async function executeBatch(sql, client, options = {}) {
48561
49043
  }
48562
49044
  metrics.elapsedMs = Date.now() - startedAt;
48563
49045
  return {
48564
- ok: results.every((r) => r.status === "success"),
49046
+ ok: results.every((r) => r.status === "success" || r.skippedReason === "exit"),
48565
49047
  statementCount: statements.length,
48566
49048
  statements: results,
48567
49049
  analysis,
@@ -48577,8 +49059,18 @@ function statementHasApplyMutation(statement) {
48577
49059
  }
48578
49060
  return statement.type === "UPSERT" && statement.validateOnly !== true && Boolean(statement.onInsertApplyBlocks?.length || statement.onUpdateApplyBlocks?.length);
48579
49061
  }
48580
- async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables, relativeDateVariables) {
48581
- options = bindStatementEvaluationContext(options);
49062
+ async function executeBatchStatement(context) {
49063
+ const {
49064
+ stmt,
49065
+ info,
49066
+ client,
49067
+ options,
49068
+ cacheContext,
49069
+ tempTables,
49070
+ variables,
49071
+ relativeDateVariables,
49072
+ clock
49073
+ } = context;
48582
49074
  if (stmt.type === "SET_VARIABLE") {
48583
49075
  const resolvedStmt2 = resolveBatchVariableReferences(stmt, variables);
48584
49076
  validateStatementStatic(resolvedStmt2);
@@ -48618,7 +49110,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
48618
49110
  throw e;
48619
49111
  }
48620
49112
  } else {
48621
- variables.set(stmt.name, evaluateScalarExpr(resolvedStmt2.expr, statementEvaluationContext(options)));
49113
+ variables.set(stmt.name, evaluateScalarExpr(resolvedStmt2.expr, clock));
48622
49114
  }
48623
49115
  return {};
48624
49116
  }
@@ -48636,7 +49128,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
48636
49128
  } else {
48637
49129
  const value = evaluateScalarExpr(
48638
49130
  stmt.default,
48639
- statementEvaluationContext(options)
49131
+ clock
48640
49132
  );
48641
49133
  variables.set(stmt.name, {
48642
49134
  type: "string",
@@ -48733,8 +49225,12 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
48733
49225
  }
48734
49226
  }
48735
49227
  if (resolvedStmt.type === "ASSERT") {
48736
- await executeAssert(resolvedStmt, client, options, cacheContext, tempTables);
48737
- return {};
49228
+ const result = await executeAssert(resolvedStmt, client, options, cacheContext, tempTables);
49229
+ return result.warning !== void 0 ? { result } : {};
49230
+ }
49231
+ if (resolvedStmt.type === "EXIT") {
49232
+ const result = await executeExit(resolvedStmt, client, options, cacheContext, tempTables);
49233
+ return { result, ...result.exited ? { exitTriggered: true } : {} };
48738
49234
  }
48739
49235
  if (info.tempTablesReferenced.length > 0) {
48740
49236
  if (resolvedStmt.type === "SELECT" || resolvedStmt.type === "UNION") {
@@ -48858,9 +49354,9 @@ function safeJsonStringify(v) {
48858
49354
  return String(v);
48859
49355
  }
48860
49356
  }
48861
- function parseSqlBatch(sql, enableImport = false) {
49357
+ function parseSqlBatch(sql, enableImport = false, dialect1 = false) {
48862
49358
  const tokens = new Lexer(sql).tokenize();
48863
- return new Parser(tokens, { import: enableImport }).parseStatements();
49359
+ return new Parser(tokens, { import: enableImport, dialect1 }).parseStatements();
48864
49360
  }
48865
49361
  function parseRelativeDateVariableValue(name, value) {
48866
49362
  try {
@@ -49046,6 +49542,31 @@ var ScalarSubqueryError = class extends Error {
49046
49542
  }
49047
49543
  };
49048
49544
  async function executeAssert(stmt, client, options, cacheContext, tempTables) {
49545
+ const evaluation = await evaluateAssertCondition(stmt, client, options, cacheContext, tempTables);
49546
+ if (!evaluation.passed) {
49547
+ if (stmt.warn === true) {
49548
+ return {
49549
+ type: "ASSERT",
49550
+ condition: stmt.text,
49551
+ passed: false,
49552
+ warning: stmt.message ?? `assertion failed: ${stmt.text} (actual: ${evaluation.actual}).`
49553
+ };
49554
+ }
49555
+ const suffix = stmt.message !== void 0 ? ` ${stmt.message}` : "";
49556
+ throw new AssertError(`assertion failed: ${stmt.text} (actual: ${evaluation.actual}).${suffix}`);
49557
+ }
49558
+ return { type: "ASSERT", condition: stmt.text };
49559
+ }
49560
+ async function executeExit(stmt, client, options, cacheContext, tempTables) {
49561
+ const evaluation = await evaluateAssertCondition(stmt, client, options, cacheContext, tempTables);
49562
+ return {
49563
+ type: "EXIT",
49564
+ condition: stmt.text,
49565
+ exited: evaluation.passed,
49566
+ message: stmt.message
49567
+ };
49568
+ }
49569
+ async function evaluateAssertCondition(stmt, client, options, cacheContext, tempTables) {
49049
49570
  const left = await evalAssertOperand(stmt.left, client, options, cacheContext, tempTables);
49050
49571
  const semantics = stmt.left.type === "NUMBER" || stmt.left.type === "ARITH" ? syntheticSemantics("number") : syntheticSemantics("string");
49051
49572
  if (stmt.op === "BETWEEN") {
@@ -49054,19 +49575,16 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
49054
49575
  }
49055
49576
  const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
49056
49577
  const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
49057
- if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
49058
- throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
49059
- }
49060
- return { type: "ASSERT", condition: stmt.text };
49578
+ return {
49579
+ passed: compareScalarValues(">=", left, low, semantics) && compareScalarValues("<=", left, high, semantics),
49580
+ actual: left
49581
+ };
49061
49582
  }
49062
49583
  if (stmt.right === null) {
49063
49584
  throw new Error("ArgumentError: malformed ASSERT statement.");
49064
49585
  }
49065
49586
  const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
49066
- if (!compareScalarValues(stmt.op, left, right, semantics)) {
49067
- throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
49068
- }
49069
- return { type: "ASSERT", condition: stmt.text };
49587
+ return { passed: compareScalarValues(stmt.op, left, right, semantics), actual: left };
49070
49588
  }
49071
49589
  async function evalAssertOperand(operand, client, options, cacheContext, tempTables) {
49072
49590
  switch (operand.type) {
@@ -56400,7 +56918,16 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
56400
56918
  });
56401
56919
  const invocationCacheContext = createInvocationCacheContext(cacheContext);
56402
56920
  try {
56403
- const statements = parseSqlBatch(sql, enableImport);
56921
+ const header = parseScriptHeader(sql);
56922
+ const headerError = header.diagnostics.find((diagnostic2) => diagnostic2.severity === "error");
56923
+ if (header.hasDirectives && headerError) {
56924
+ throw new Error(`${headerError.code}: ${headerError.message} (${headerError.line}:${headerError.column})`);
56925
+ }
56926
+ const statements = parseSqlBatch(
56927
+ header.hasDirectives ? sql.slice(header.headerEnd) : sql,
56928
+ enableImport,
56929
+ header.hasDirectives && header.meta.dialect === 1
56930
+ );
56404
56931
  const analysis = analyzeBatch(statements);
56405
56932
  const normalizedInjectedVariables = validateDeclaredBatchVariables(statements, injectedVariables);
56406
56933
  const relativeDateVariables = prepareRelativeDateVariables(statements, normalizedInjectedVariables);
@@ -56595,8 +57122,8 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
56595
57122
  }
56596
57123
  if (stmt.type === "ASSERT") {
56597
57124
  const lines = [
56598
- `ASSERT ${stmt.text}`,
56599
- " 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"
57125
+ `ASSERT${stmt.warn === true ? " WARN" : ""} ${stmt.text}${stmt.message !== void 0 ? `, '${stmt.message.replace(/'/g, "''")}'` : ""}`,
57126
+ 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"
56600
57127
  ];
56601
57128
  const subqueries = [stmt.left, stmt.right, stmt.low, stmt.high].filter(
56602
57129
  (o) => o !== null && o.type === "SCALAR_SUBQUERY"
@@ -56618,6 +57145,31 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
56618
57145
  });
56619
57146
  return lines;
56620
57147
  }
57148
+ if (stmt.type === "EXIT") {
57149
+ const lines = [
57150
+ `EXIT SUCCESS IF ${stmt.text}, '${stmt.message.replace(/'/g, "''")}'`,
57151
+ " 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"
57152
+ ];
57153
+ const subqueries = [stmt.left, stmt.right, stmt.low, stmt.high].filter(
57154
+ (o) => o !== null && o.type === "SCALAR_SUBQUERY"
57155
+ );
57156
+ subqueries.forEach((sq, i) => {
57157
+ lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
57158
+ const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
57159
+ lines.push(...buildPlanForBatchQuery(
57160
+ sq.query,
57161
+ subInfo,
57162
+ capabilities,
57163
+ orderPlans,
57164
+ plainGroupByPlans,
57165
+ collector,
57166
+ "main",
57167
+ tempSchemaLedger,
57168
+ explainContext
57169
+ ).map((line) => ` ${line}`));
57170
+ });
57171
+ return lines;
57172
+ }
56621
57173
  if (stmt.type === "UPDATE" && (stmt.applyBlocks?.length ?? 0) > 0) {
56622
57174
  return buildExplainPlan(
56623
57175
  stmt,
@@ -58035,6 +58587,9 @@ function toMutationSummary(result) {
58035
58587
  ...result.errTable !== void 0 ? { errTable: result.errTable } : {}
58036
58588
  };
58037
58589
  }
58590
+ if (result.type === "EXIT") {
58591
+ return { condition: result.condition, exited: result.exited, message: result.message };
58592
+ }
58038
58593
  return { reorderedParentCount: result.reorderedParentCount };
58039
58594
  }
58040
58595
  function buildBatchEnvelope(batch, options = {}) {
@@ -58092,6 +58647,10 @@ function buildBatchEnvelope(batch, options = {}) {
58092
58647
  ...s.result.deletedRows ? { deletedRows: s.result.deletedRows } : {},
58093
58648
  ...s.result.diagnostic ? { diagnostic: s.result.diagnostic } : {}
58094
58649
  });
58650
+ } else if (s.status === "success" && s.result?.type === "ASSERT") {
58651
+ entry.condition = s.result.condition;
58652
+ if (s.result.passed !== void 0) entry.passed = s.result.passed;
58653
+ if (s.result.warning !== void 0) entry.warning = s.result.warning;
58095
58654
  } else if (s.status === "success" && s.result && s.result.type !== "SELECT" && s.result.type !== "ASSERT") {
58096
58655
  Object.assign(entry, toMutationSummary(s.result));
58097
58656
  }
@@ -60539,7 +61098,9 @@ function toAssertPayload(result) {
60539
61098
  return {
60540
61099
  ok: true,
60541
61100
  type: result.type,
60542
- condition: result.condition
61101
+ condition: result.condition,
61102
+ ...result.passed !== void 0 ? { passed: result.passed } : {},
61103
+ ...result.warning !== void 0 ? { warning: result.warning } : {}
60543
61104
  };
60544
61105
  }
60545
61106
  function toDmlValidationPayload(result) {
@@ -60612,6 +61173,9 @@ function toMutationPayload(result) {
60612
61173
  ...result.errTable !== void 0 ? { errTable: result.errTable } : {}
60613
61174
  };
60614
61175
  }
61176
+ if (result.type === "EXIT") {
61177
+ return { ok: true, type: result.type, condition: result.condition, exited: result.exited, message: result.message };
61178
+ }
60615
61179
  return {
60616
61180
  ok: true,
60617
61181
  type: result.type,