@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/README.md +38 -0
- package/dist-cli/ksql.js +889 -75
- package/dist-engine/index.cjs +16 -12
- package/dist-engine/index.mjs +16 -12
- package/dist-engine/ksql-engine.umd.js +17 -13
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +129 -45
- package/dist-engine/meta/esm.json +129 -45
- package/dist-engine/meta/umd.json +129 -45
- package/dist-flow/flow-library/errors.d.ts +6 -0
- package/dist-flow/flow-library/index.d.ts +12 -0
- package/dist-flow/flow-library/publicTypes.d.ts +225 -0
- package/dist-flow/flow-library/writableClient.d.ts +2 -0
- package/dist-flow/index.cjs +18 -0
- package/dist-flow/index.mjs +18 -0
- package/dist-flow/meta/cjs.json +2333 -0
- package/dist-flow/meta/esm.json +2343 -0
- package/dist-flow/types/ast.d.ts +881 -0
- package/dist-mcp/ksql-mcp.js +1255 -174
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +12 -3
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
|
|
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(
|
|
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
|
-
|
|
2200
|
-
|
|
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.
|
|
2206
|
-
alias: parsedAlias2
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
18561
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
32154
|
+
const stmt = statements[0];
|
|
31341
32155
|
parsedStmt = stmt;
|
|
31342
32156
|
stmtType = getStatementType(stmt);
|
|
31343
32157
|
isDmlStatement = writesKintone(stmt);
|