@rex0220/kintone-sql-tools 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cli/ksql.js +803 -113
- package/dist-mcp/ksql-mcp.js +804 -114
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -307,6 +307,10 @@ var Lexer = class {
|
|
|
307
307
|
this.pos += 2;
|
|
308
308
|
return this.makeToken("<=" /* LTE */, "<=", start);
|
|
309
309
|
}
|
|
310
|
+
if (ch === "|" && ch2 === "|") {
|
|
311
|
+
this.pos += 2;
|
|
312
|
+
return this.makeToken("||" /* CONCAT_OP */, "||", start);
|
|
313
|
+
}
|
|
310
314
|
switch (ch) {
|
|
311
315
|
case "=":
|
|
312
316
|
this.pos++;
|
|
@@ -650,6 +654,8 @@ var Parser = class {
|
|
|
650
654
|
constructor(tokens) {
|
|
651
655
|
this.tokens = tokens;
|
|
652
656
|
this.allowUnaryPlusNumber = false;
|
|
657
|
+
this.scalarAllowsAggregateArgs = true;
|
|
658
|
+
this.scalarAllowsCase = true;
|
|
653
659
|
this.pos = 0;
|
|
654
660
|
/** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
|
|
655
661
|
this.cteNames = /* @__PURE__ */ new Set();
|
|
@@ -818,17 +824,19 @@ var Parser = class {
|
|
|
818
824
|
}
|
|
819
825
|
rejectNonScalarExpr(node, tok, context) {
|
|
820
826
|
if (node.type === "STRING" || node.type === "NUMBER") return;
|
|
821
|
-
if (node.type === "FIELD_REF" || node.type === "AGG_REF") {
|
|
827
|
+
if (node.type === "FIELD_REF" || node.type === "FIELD" || node.type === "VARIABLE" || node.type === "AGG_REF") {
|
|
822
828
|
throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u30FB\u96C6\u8A08\u95A2\u6570\u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`, tok);
|
|
823
829
|
}
|
|
824
|
-
if (node.type === "ARITH" || node.type === "AGG_ARITH") {
|
|
830
|
+
if (node.type === "ARITH" || node.type === "SCALAR_ARITH" || node.type === "CONCAT_OP" || node.type === "AGG_ARITH") {
|
|
825
831
|
this.rejectNonScalarExpr(node.left, tok, context);
|
|
826
832
|
this.rejectNonScalarExpr(node.right, tok, context);
|
|
827
833
|
return;
|
|
828
834
|
}
|
|
829
835
|
if (node.type === "STRING_FUNC") {
|
|
830
836
|
for (const arg of node.args) this.rejectNonScalarExpr(arg, tok, context);
|
|
837
|
+
return;
|
|
831
838
|
}
|
|
839
|
+
throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F CASE \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`, tok);
|
|
832
840
|
}
|
|
833
841
|
// ----------------------------------------------------------
|
|
834
842
|
// CREATE TEMP TABLE / DROP TEMP TABLE(バッチ内一時テーブル)
|
|
@@ -1199,6 +1207,11 @@ var Parser = class {
|
|
|
1199
1207
|
if (this.consume("*" /* STAR */)) {
|
|
1200
1208
|
return { type: "WILDCARD" };
|
|
1201
1209
|
}
|
|
1210
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
1211
|
+
const expr = this.parseScalarValueExpr({ allowAggregateArgs: true });
|
|
1212
|
+
const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
1213
|
+
return { type: "SCALAR_VALUE_COL", expr, alias: alias2 };
|
|
1214
|
+
}
|
|
1202
1215
|
const windowFunc = this.tryWindowFunc();
|
|
1203
1216
|
if (windowFunc !== null) {
|
|
1204
1217
|
return this.parseWindowColumn(windowFunc);
|
|
@@ -1314,13 +1327,25 @@ var Parser = class {
|
|
|
1314
1327
|
}
|
|
1315
1328
|
selectColumnHasAggregate(column) {
|
|
1316
1329
|
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
1317
|
-
if (column.type
|
|
1318
|
-
|
|
1330
|
+
if (column.type === "STRFUNC_COL") return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
1331
|
+
if (column.type === "SCALAR_VALUE_COL") return this.scalarValueHasAggregate(column.expr);
|
|
1332
|
+
return false;
|
|
1319
1333
|
}
|
|
1320
1334
|
stringFuncArgHasAggregate(arg) {
|
|
1321
1335
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
1322
|
-
|
|
1323
|
-
|
|
1336
|
+
return this.scalarValueHasAggregate(arg);
|
|
1337
|
+
}
|
|
1338
|
+
scalarValueHasAggregate(expr) {
|
|
1339
|
+
if (expr.type === "STRING_FUNC") return expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
1340
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
1341
|
+
return this.scalarValueHasAggregate(expr.left) || this.scalarValueHasAggregate(expr.right);
|
|
1342
|
+
}
|
|
1343
|
+
if (expr.type === "CASE_WHEN") {
|
|
1344
|
+
const results = [...expr.branches.map((b) => b.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
1345
|
+
return results.some((result) => {
|
|
1346
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
1347
|
+
return this.scalarValueHasAggregate(result);
|
|
1348
|
+
});
|
|
1324
1349
|
}
|
|
1325
1350
|
return false;
|
|
1326
1351
|
}
|
|
@@ -1378,6 +1403,105 @@ var Parser = class {
|
|
|
1378
1403
|
throw new ParseError("\u96C6\u8A08\u7B97\u8853\u5F0F\u306B\u306F\u96C6\u8A08\u95A2\u6570\u307E\u305F\u306F\u6570\u5024\u304C\u5FC5\u8981\u3067\u3059", this.peek());
|
|
1379
1404
|
}
|
|
1380
1405
|
// ──────────────────────────────────────────────────
|
|
1406
|
+
// 汎用スカラー値式パーサー(B38)
|
|
1407
|
+
// ──────────────────────────────────────────────────
|
|
1408
|
+
/** 比較・述語・集約・サブクエリを含まない値式の公開入口。 */
|
|
1409
|
+
parseScalarValueExpr(options = {}) {
|
|
1410
|
+
const previousAggregateArgs = this.scalarAllowsAggregateArgs;
|
|
1411
|
+
const previousCase = this.scalarAllowsCase;
|
|
1412
|
+
this.scalarAllowsAggregateArgs = options.allowAggregateArgs === true;
|
|
1413
|
+
this.scalarAllowsCase = options.allowCase !== false;
|
|
1414
|
+
let expr;
|
|
1415
|
+
try {
|
|
1416
|
+
expr = this.parseScalarAddSubConcat(this.scalarAllowsCase);
|
|
1417
|
+
} finally {
|
|
1418
|
+
this.scalarAllowsAggregateArgs = previousAggregateArgs;
|
|
1419
|
+
this.scalarAllowsCase = previousCase;
|
|
1420
|
+
}
|
|
1421
|
+
const next = this.peek();
|
|
1422
|
+
if (next.kind === "IS" /* IS */ || next.kind === "=" /* EQ */ || next.kind === "!=" /* NEQ */ || next.kind === "<>" /* LT_GT */ || next.kind === ">" /* GT */ || next.kind === "<" /* LT */ || next.kind === ">=" /* GTE */ || next.kind === "<=" /* LTE */ || next.kind === "LIKE" /* LIKE */ || next.kind === "KLIKE" /* KLIKE */ || next.kind === "IN" /* IN */ || next.kind === "BETWEEN" /* BETWEEN */) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u6BD4\u8F03\u30FB\u8FF0\u8A9E\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", next);
|
|
1423
|
+
return expr;
|
|
1424
|
+
}
|
|
1425
|
+
parseScalarAddSubConcat(allowCase) {
|
|
1426
|
+
let left = this.parseScalarMulDiv(allowCase);
|
|
1427
|
+
while (this.peek().kind === "+" /* PLUS */ || this.peek().kind === "-" /* MINUS */ || this.peek().kind === "||" /* CONCAT_OP */) {
|
|
1428
|
+
const token = this.advance();
|
|
1429
|
+
const right = this.parseScalarMulDiv(allowCase);
|
|
1430
|
+
left = token.kind === "||" /* CONCAT_OP */ ? { type: "CONCAT_OP", left, right } : { type: "SCALAR_ARITH", left, op: token.kind === "+" /* PLUS */ ? "+" : "-", right };
|
|
1431
|
+
}
|
|
1432
|
+
return left;
|
|
1433
|
+
}
|
|
1434
|
+
parseScalarMulDiv(allowCase) {
|
|
1435
|
+
let left = this.parseScalarPrimary(allowCase);
|
|
1436
|
+
while (this.peek().kind === "*" /* STAR */ || this.peek().kind === "/" /* SLASH */ || this.peek().kind === "%" /* PERCENT */) {
|
|
1437
|
+
const token = this.advance();
|
|
1438
|
+
const op = token.kind === "*" /* STAR */ ? "*" : token.kind === "/" /* SLASH */ ? "/" : "%";
|
|
1439
|
+
left = { type: "SCALAR_ARITH", left, op, right: this.parseScalarPrimary(allowCase) };
|
|
1440
|
+
}
|
|
1441
|
+
return left;
|
|
1442
|
+
}
|
|
1443
|
+
parseScalarPrimary(allowCase) {
|
|
1444
|
+
const tok = this.peek();
|
|
1445
|
+
if (tok.kind === "(" /* LPAREN */) {
|
|
1446
|
+
if (this.peekAt(1).kind === "SELECT" /* SELECT */) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
1447
|
+
this.advance();
|
|
1448
|
+
const expr = this.parseScalarAddSubConcat(allowCase);
|
|
1449
|
+
this.expect(")" /* RPAREN */);
|
|
1450
|
+
return expr;
|
|
1451
|
+
}
|
|
1452
|
+
if (tok.kind === "+" /* PLUS */ || tok.kind === "-" /* MINUS */) {
|
|
1453
|
+
this.advance();
|
|
1454
|
+
if (this.peek().kind === "+" /* PLUS */ || this.peek().kind === "-" /* MINUS */) {
|
|
1455
|
+
throw new ParseError("\u5358\u9805\u7B26\u53F7\u3092\u91CD\u306D\u3066\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
1456
|
+
}
|
|
1457
|
+
const operand = this.parseScalarPrimary(allowCase);
|
|
1458
|
+
if (operand.type === "NUMBER") {
|
|
1459
|
+
return makeNumberLiteral(`${tok.kind === "-" /* MINUS */ ? "-" : "+"}${numberLiteralText(operand)}`);
|
|
1460
|
+
}
|
|
1461
|
+
if (tok.kind === "+" /* PLUS */) throw new ParseError("\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
1462
|
+
return { type: "SCALAR_ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
1463
|
+
}
|
|
1464
|
+
if (tok.kind === "STRING" /* STRING */) {
|
|
1465
|
+
this.advance();
|
|
1466
|
+
return { type: "STRING", value: tok.value };
|
|
1467
|
+
}
|
|
1468
|
+
if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
1469
|
+
this.advance();
|
|
1470
|
+
return makeNumberLiteral(tok.value);
|
|
1471
|
+
}
|
|
1472
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
1473
|
+
this.advance();
|
|
1474
|
+
return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
|
|
1475
|
+
}
|
|
1476
|
+
if (tok.kind === "CASE" /* CASE */) {
|
|
1477
|
+
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);
|
|
1478
|
+
return this.parseCaseWhenExpr();
|
|
1479
|
+
}
|
|
1480
|
+
if (this.tryAggregateFunc() !== null) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
1481
|
+
if (this.tryStringFuncName() !== null) return this.parseStringFuncExpr();
|
|
1482
|
+
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
1483
|
+
this.advance();
|
|
1484
|
+
if (this.consume("." /* DOT */)) return { type: "FIELD", tableAlias: tok.value, field: this.parseIdentifier() };
|
|
1485
|
+
return { type: "FIELD", tableAlias: null, field: tok.value };
|
|
1486
|
+
}
|
|
1487
|
+
throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306E\u30AA\u30DA\u30E9\u30F3\u30C9\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
1488
|
+
}
|
|
1489
|
+
/** 現在の値の終端までに指定トークンがあるか(括弧内も対象)。 */
|
|
1490
|
+
hasTopLevelTokenBeforeValueEnd(target) {
|
|
1491
|
+
let depth = 0;
|
|
1492
|
+
for (let i = this.pos; i < this.tokens.length; i++) {
|
|
1493
|
+
const kind = this.tokens[i].kind;
|
|
1494
|
+
if (kind === "(" /* LPAREN */ || kind === "[" /* LBRACKET */) depth++;
|
|
1495
|
+
else if (kind === ")" /* RPAREN */ || kind === "]" /* RBRACKET */) {
|
|
1496
|
+
if (depth === 0) break;
|
|
1497
|
+
depth--;
|
|
1498
|
+
}
|
|
1499
|
+
if (kind === target) return true;
|
|
1500
|
+
if (depth === 0 && (kind === "," /* COMMA */ || kind === "AS" /* AS */ || kind === "FROM" /* FROM */ || kind === "WHERE" /* WHERE */ || kind === "WHEN" /* WHEN */ || kind === "THEN" /* THEN */ || kind === "ELSE" /* ELSE */ || kind === "END" /* END */ || kind === ";" /* SEMICOLON */ || kind === "EOF" /* EOF */)) break;
|
|
1501
|
+
}
|
|
1502
|
+
return false;
|
|
1503
|
+
}
|
|
1504
|
+
// ──────────────────────────────────────────────────
|
|
1381
1505
|
// 算術式パーサー(演算子優先順位: * / > + -)
|
|
1382
1506
|
//
|
|
1383
1507
|
// parseArithAddSub : + -(左結合・低優先度)
|
|
@@ -1499,12 +1623,15 @@ var Parser = class {
|
|
|
1499
1623
|
this.expect("END" /* END */);
|
|
1500
1624
|
return { type: "CASE_WHEN", branches, elseResult };
|
|
1501
1625
|
}
|
|
1502
|
-
/** THEN / ELSE
|
|
1626
|
+
/** THEN / ELSE の結果値。`||` を含む場合だけ新スカラー文法へ渡す。 */
|
|
1503
1627
|
parseCaseResult() {
|
|
1504
1628
|
const tok = this.peek();
|
|
1505
1629
|
if (tok.kind === "[" /* LBRACKET */) {
|
|
1506
1630
|
return this.parseArrayLiteral();
|
|
1507
1631
|
}
|
|
1632
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
1633
|
+
return this.parseScalarValueExpr({ allowAggregateArgs: true });
|
|
1634
|
+
}
|
|
1508
1635
|
if (tok.kind === "STRING" /* STRING */) {
|
|
1509
1636
|
this.advance();
|
|
1510
1637
|
return { type: "STRING", value: tok.value };
|
|
@@ -1652,25 +1779,19 @@ var Parser = class {
|
|
|
1652
1779
|
}
|
|
1653
1780
|
return { type: "STRING", value: normalized };
|
|
1654
1781
|
}
|
|
1655
|
-
/** 文字列関数の引数:
|
|
1782
|
+
/** 文字列関数の引数: ScalarValueExpr / 集計算術式 */
|
|
1656
1783
|
parseStringFuncArg() {
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
try {
|
|
1667
|
-
const left = this.parseAggPrimary();
|
|
1668
|
-
const expr = this.continueAggArith(left);
|
|
1669
|
-
if (this.hasAggregateOperand(expr)) return expr;
|
|
1670
|
-
} catch {
|
|
1784
|
+
if (this.scalarAllowsAggregateArgs) {
|
|
1785
|
+
const startPos = this.pos;
|
|
1786
|
+
try {
|
|
1787
|
+
const left = this.parseAggPrimary();
|
|
1788
|
+
const expr = this.continueAggArith(left);
|
|
1789
|
+
if (this.hasAggregateOperand(expr)) return expr;
|
|
1790
|
+
} catch {
|
|
1791
|
+
}
|
|
1792
|
+
this.pos = startPos;
|
|
1671
1793
|
}
|
|
1672
|
-
this.
|
|
1673
|
-
return this.parseArithAddSub();
|
|
1794
|
+
return this.parseScalarAddSubConcat(this.scalarAllowsCase);
|
|
1674
1795
|
}
|
|
1675
1796
|
hasAggregateOperand(node) {
|
|
1676
1797
|
if (node.type === "AGG_REF") return true;
|
|
@@ -1758,6 +1879,7 @@ var Parser = class {
|
|
|
1758
1879
|
const k = this.peek().kind;
|
|
1759
1880
|
if (k === "IDENT" /* IDENT */ || k === "BIDENT" /* BIDENT */) {
|
|
1760
1881
|
if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "VALIDATE" && this.peekAt(1).kind === "IDENT" /* IDENT */ && this.peekAt(1).value.toUpperCase() === "ONLY") return null;
|
|
1882
|
+
if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "CHECK" && this.peekAt(1).kind === "WHEN" /* WHEN */) return null;
|
|
1761
1883
|
return this.parseTableAliasName();
|
|
1762
1884
|
}
|
|
1763
1885
|
return null;
|
|
@@ -2223,8 +2345,9 @@ var Parser = class {
|
|
|
2223
2345
|
if (subtableCode) {
|
|
2224
2346
|
throw new ParseError("INSERT INTO ... SELECT \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u3067\u306F\u672A\u5BFE\u5FDC\u3067\u3059", this.prev());
|
|
2225
2347
|
}
|
|
2348
|
+
const checkGroups2 = this.parseCheckGroups();
|
|
2226
2349
|
const validation2 = this.parseDmlControlSuffix();
|
|
2227
|
-
return { type: "INSERT_SELECT", appId, fields, select, ...validation2 };
|
|
2350
|
+
return { type: "INSERT_SELECT", appId, fields, select, ...checkGroups2, ...validation2 };
|
|
2228
2351
|
}
|
|
2229
2352
|
this.expect("VALUES" /* VALUES */);
|
|
2230
2353
|
const values = [];
|
|
@@ -2234,11 +2357,15 @@ var Parser = class {
|
|
|
2234
2357
|
this.expect(")" /* RPAREN */);
|
|
2235
2358
|
values.push(row);
|
|
2236
2359
|
} while (this.consume("," /* COMMA */));
|
|
2360
|
+
const checkGroups = this.parseCheckGroups();
|
|
2237
2361
|
const validation = this.parseDmlControlSuffix();
|
|
2362
|
+
if (subtableCode && checkGroups.checkGroups) {
|
|
2363
|
+
throw new ParseError("CHECK \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB INSERT \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2364
|
+
}
|
|
2238
2365
|
if (subtableCode && (validation.validateOnly || validation.onErrorSkip)) {
|
|
2239
2366
|
throw new ParseError("VALIDATE ONLY / ON ERROR SKIP \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB INSERT \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2240
2367
|
}
|
|
2241
|
-
return subtableCode ? { type: "INSERT", appId, subtableCode, fields, values, ...validation } : { type: "INSERT", appId, fields, values, ...validation };
|
|
2368
|
+
return subtableCode ? { type: "INSERT", appId, subtableCode, fields, values, ...checkGroups, ...validation } : { type: "INSERT", appId, fields, values, ...checkGroups, ...validation };
|
|
2242
2369
|
}
|
|
2243
2370
|
parseUpsert() {
|
|
2244
2371
|
this.expect("UPSERT" /* UPSERT */);
|
|
@@ -2255,8 +2382,9 @@ var Parser = class {
|
|
|
2255
2382
|
if (this.peek().kind === "SELECT" /* SELECT */) {
|
|
2256
2383
|
const select = this.parseSelect();
|
|
2257
2384
|
const keyFields2 = this.parseOnDuplicate();
|
|
2385
|
+
const checkGroups2 = this.parseCheckGroups();
|
|
2258
2386
|
const validation2 = this.parseDmlControlSuffix();
|
|
2259
|
-
return { type: "UPSERT_SELECT", appId, fields, select, keyFields: keyFields2, ...validation2 };
|
|
2387
|
+
return { type: "UPSERT_SELECT", appId, fields, select, keyFields: keyFields2, ...checkGroups2, ...validation2 };
|
|
2260
2388
|
}
|
|
2261
2389
|
this.expect("VALUES" /* VALUES */);
|
|
2262
2390
|
const values = [];
|
|
@@ -2266,8 +2394,9 @@ var Parser = class {
|
|
|
2266
2394
|
this.expect(")" /* RPAREN */);
|
|
2267
2395
|
} while (this.consume("," /* COMMA */));
|
|
2268
2396
|
const keyFields = this.parseOnDuplicate();
|
|
2397
|
+
const checkGroups = this.parseCheckGroups();
|
|
2269
2398
|
const validation = this.parseDmlControlSuffix();
|
|
2270
|
-
return { type: "UPSERT", appId, fields, values, keyFields, ...validation };
|
|
2399
|
+
return { type: "UPSERT", appId, fields, values, keyFields, ...checkGroups, ...validation };
|
|
2271
2400
|
}
|
|
2272
2401
|
parseOnDuplicate() {
|
|
2273
2402
|
this.expectKeyword("ON" /* ON */, "UPSERT \u306B\u306F ON DUPLICATE (\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9) \u304C\u5FC5\u8981\u3067\u3059");
|
|
@@ -2399,12 +2528,33 @@ var Parser = class {
|
|
|
2399
2528
|
whereTok
|
|
2400
2529
|
);
|
|
2401
2530
|
}
|
|
2531
|
+
const checkGroups = this.parseCheckGroups();
|
|
2402
2532
|
const validation = this.parseDmlControlSuffix();
|
|
2533
|
+
if (subtableCode && checkGroups.checkGroups) {
|
|
2534
|
+
throw new ParseError("CHECK \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2535
|
+
}
|
|
2403
2536
|
if (subtableCode && (validation.validateOnly || validation.onErrorSkip)) {
|
|
2404
2537
|
throw new ParseError("VALIDATE ONLY / ON ERROR SKIP \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2405
2538
|
}
|
|
2406
|
-
if (from !== null) return { type: "UPDATE", appId, assignments, where, from, ...validation };
|
|
2407
|
-
return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where, ...validation } : { type: "UPDATE", appId, assignments, where, ...validation };
|
|
2539
|
+
if (from !== null) return { type: "UPDATE", appId, assignments, where, from, ...checkGroups, ...validation };
|
|
2540
|
+
return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where, ...checkGroups, ...validation } : { type: "UPDATE", appId, assignments, where, ...checkGroups, ...validation };
|
|
2541
|
+
}
|
|
2542
|
+
/** CHECK WHEN ... THEN ... blocks. CHECK is a soft keyword. */
|
|
2543
|
+
parseCheckGroups() {
|
|
2544
|
+
const groups = [];
|
|
2545
|
+
while (this.isSoftKeyword("CHECK") && this.peekAt(1).kind === "WHEN" /* WHEN */) {
|
|
2546
|
+
const check = this.advance();
|
|
2547
|
+
const rules = [];
|
|
2548
|
+
while (this.consume("WHEN" /* WHEN */)) {
|
|
2549
|
+
const condition = this.parseWhereExpr();
|
|
2550
|
+
this.expect("THEN" /* THEN */, "CHECK WHEN \u306E\u6761\u4EF6\u306E\u5F8C\u306B\u306F THEN \u304C\u5FC5\u8981\u3067\u3059");
|
|
2551
|
+
const message = this.parseScalarValueExpr({ allowCase: false });
|
|
2552
|
+
rules.push({ condition, message });
|
|
2553
|
+
}
|
|
2554
|
+
if (rules.length === 0) throw new ParseError("CHECK \u306E\u5F8C\u306B\u306F WHEN \u304C\u6700\u4F4E 1 \u3064\u5FC5\u8981\u3067\u3059", check);
|
|
2555
|
+
groups.push({ rules });
|
|
2556
|
+
}
|
|
2557
|
+
return groups.length > 0 ? { checkGroups: groups } : {};
|
|
2408
2558
|
}
|
|
2409
2559
|
/** DML末尾の VALIDATE ONLY または ON ERROR SKIP。各語はsoft keyword。 */
|
|
2410
2560
|
parseDmlControlSuffix() {
|
|
@@ -2593,6 +2743,12 @@ var Parser = class {
|
|
|
2593
2743
|
*/
|
|
2594
2744
|
parseAssignmentValue() {
|
|
2595
2745
|
const tok = this.peek();
|
|
2746
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
2747
|
+
const expr = this.parseScalarValueExpr();
|
|
2748
|
+
if (expr.type === "CONCAT_OP" || expr.type === "SCALAR_ARITH" || expr.type === "STRING_FUNC") return expr;
|
|
2749
|
+
if (expr.type === "CASE_WHEN") return { type: "CASE_VALUE", expr };
|
|
2750
|
+
throw new ParseError("SET \u306E\u5024\u306B\u306F\u9023\u7D50\u3092\u542B\u3080\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
2751
|
+
}
|
|
2596
2752
|
if (tok.kind === "VARIABLE" /* VARIABLE */) return this.parseSqlValue();
|
|
2597
2753
|
if (tok.kind === "STRING" /* STRING */) return this.parseSqlValue();
|
|
2598
2754
|
if (tok.kind === "TODAY" /* TODAY */ || tok.kind === "NOW" /* NOW */ || tok.kind === "LOGINUSER" /* LOGINUSER */) return this.parseSqlValue();
|
|
@@ -3216,7 +3372,7 @@ function resolveSelectMode(stmt) {
|
|
|
3216
3372
|
if (stmt.distinct) return "FULL_SCAN";
|
|
3217
3373
|
if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
|
|
3218
3374
|
if (stmt.columns.some(
|
|
3219
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
|
|
3375
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate(c.expr)
|
|
3220
3376
|
)) return "FULL_SCAN";
|
|
3221
3377
|
if (whereRequiresJsEval(stmt.where)) return "FULL_SCAN";
|
|
3222
3378
|
if (stmt.orderBy.some((o) => o.key.type !== "FIELD_NAME")) return "FULL_SCAN";
|
|
@@ -3311,6 +3467,8 @@ function extractFields(columns) {
|
|
|
3311
3467
|
collectArithNode(col.expr, fields);
|
|
3312
3468
|
} else if (col.type === "STRFUNC_COL") {
|
|
3313
3469
|
collectStringFuncFields(col.expr, fields);
|
|
3470
|
+
} else if (col.type === "SCALAR_VALUE_COL") {
|
|
3471
|
+
collectScalarValueFields(col.expr, fields);
|
|
3314
3472
|
}
|
|
3315
3473
|
}
|
|
3316
3474
|
return [...new Set(fields)];
|
|
@@ -3336,16 +3494,38 @@ function collectStringFuncFields(expr, out) {
|
|
|
3336
3494
|
}
|
|
3337
3495
|
}
|
|
3338
3496
|
function collectStringFuncArgFields(arg, out) {
|
|
3339
|
-
if (arg.type === "STRING") return;
|
|
3340
|
-
if (arg.type === "STRING_FUNC") {
|
|
3341
|
-
collectStringFuncFields(arg, out);
|
|
3342
|
-
return;
|
|
3343
|
-
}
|
|
3344
3497
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
3345
3498
|
collectAggOperandFields(arg, out);
|
|
3346
3499
|
return;
|
|
3347
3500
|
}
|
|
3348
|
-
|
|
3501
|
+
collectScalarValueFields(arg, out);
|
|
3502
|
+
}
|
|
3503
|
+
function collectScalarValueFields(expr, out) {
|
|
3504
|
+
if (expr.type === "FIELD") {
|
|
3505
|
+
out.push(normalizeSimpleFieldRef(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field));
|
|
3506
|
+
return;
|
|
3507
|
+
}
|
|
3508
|
+
if (expr.type === "STRING_FUNC") {
|
|
3509
|
+
collectStringFuncFields(expr, out);
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3512
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
3513
|
+
collectScalarValueFields(expr.left, out);
|
|
3514
|
+
collectScalarValueFields(expr.right, out);
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
if (expr.type === "CASE_WHEN") {
|
|
3518
|
+
for (const branch of expr.branches) collectCaseResultScalarFields(branch.result, out);
|
|
3519
|
+
if (expr.elseResult) collectCaseResultScalarFields(expr.elseResult, out);
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
function collectCaseResultScalarFields(result, out) {
|
|
3523
|
+
if (result.type === "ARRAY") return;
|
|
3524
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
3525
|
+
collectArithNode(result, out);
|
|
3526
|
+
return;
|
|
3527
|
+
}
|
|
3528
|
+
collectScalarValueFields(result, out);
|
|
3349
3529
|
}
|
|
3350
3530
|
function collectAggOperandFields(node, out) {
|
|
3351
3531
|
if (node.type === "AGG_REF") {
|
|
@@ -3360,10 +3540,23 @@ function collectAggOperandFields(node, out) {
|
|
|
3360
3540
|
function hasAggregateInStringFuncExpr(expr) {
|
|
3361
3541
|
return expr.args.some((arg) => {
|
|
3362
3542
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
3363
|
-
|
|
3364
|
-
return false;
|
|
3543
|
+
return scalarValueHasAggregate(arg);
|
|
3365
3544
|
});
|
|
3366
3545
|
}
|
|
3546
|
+
function scalarValueHasAggregate(expr) {
|
|
3547
|
+
if (expr.type === "STRING_FUNC") return hasAggregateInStringFuncExpr(expr);
|
|
3548
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
3549
|
+
return scalarValueHasAggregate(expr.left) || scalarValueHasAggregate(expr.right);
|
|
3550
|
+
}
|
|
3551
|
+
if (expr.type === "CASE_WHEN") {
|
|
3552
|
+
return expr.branches.some((b) => caseResultHasAggregate(b.result)) || expr.elseResult !== null && caseResultHasAggregate(expr.elseResult);
|
|
3553
|
+
}
|
|
3554
|
+
return false;
|
|
3555
|
+
}
|
|
3556
|
+
function caseResultHasAggregate(result) {
|
|
3557
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
3558
|
+
return scalarValueHasAggregate(result);
|
|
3559
|
+
}
|
|
3367
3560
|
function collectRequiredFieldsByTable(stmt) {
|
|
3368
3561
|
const physicalTables = [stmt.from, ...stmt.joins.map((j) => j.table)].filter((t) => t.cteName === null);
|
|
3369
3562
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -3498,28 +3691,38 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3498
3691
|
}
|
|
3499
3692
|
};
|
|
3500
3693
|
const walkStringArg = (arg, phase = "select") => {
|
|
3501
|
-
if (arg.type === "STRING") return;
|
|
3502
|
-
if (arg.type === "STRING_FUNC") {
|
|
3503
|
-
walkStringFunc(arg, phase);
|
|
3504
|
-
return;
|
|
3505
|
-
}
|
|
3506
3694
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
3507
3695
|
walkAgg(arg, phase);
|
|
3508
3696
|
return;
|
|
3509
3697
|
}
|
|
3510
|
-
|
|
3698
|
+
walkScalar(arg, phase);
|
|
3511
3699
|
};
|
|
3512
3700
|
const walkStringFunc = (expr, phase = "select") => {
|
|
3513
3701
|
for (const arg of expr.args) walkStringArg(arg, phase);
|
|
3514
3702
|
};
|
|
3703
|
+
const walkScalar = (expr, phase = "select") => {
|
|
3704
|
+
if (expr.type === "FIELD") {
|
|
3705
|
+
addFieldRef(expr.field, expr.tableAlias, phase);
|
|
3706
|
+
return;
|
|
3707
|
+
}
|
|
3708
|
+
if (expr.type === "STRING_FUNC") {
|
|
3709
|
+
walkStringFunc(expr, phase);
|
|
3710
|
+
return;
|
|
3711
|
+
}
|
|
3712
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
3713
|
+
walkScalar(expr.left, phase);
|
|
3714
|
+
walkScalar(expr.right, phase);
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
if (expr.type === "CASE_WHEN") walkCase(expr, phase);
|
|
3718
|
+
};
|
|
3515
3719
|
const walkCaseResult = (result, phase = "select") => {
|
|
3516
|
-
if (result.type === "STRING") return;
|
|
3517
3720
|
if (result.type === "ARRAY") return;
|
|
3518
|
-
if (result.type === "
|
|
3519
|
-
|
|
3721
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
3722
|
+
walkArith(result, phase);
|
|
3520
3723
|
return;
|
|
3521
3724
|
}
|
|
3522
|
-
|
|
3725
|
+
walkScalar(result, phase);
|
|
3523
3726
|
};
|
|
3524
3727
|
const walkCase = (expr, phase = "select") => {
|
|
3525
3728
|
for (const b of expr.branches) {
|
|
@@ -3625,6 +3828,9 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3625
3828
|
case "STRFUNC_COL":
|
|
3626
3829
|
walkStringFunc(col.expr, "select");
|
|
3627
3830
|
break;
|
|
3831
|
+
case "SCALAR_VALUE_COL":
|
|
3832
|
+
walkScalar(col.expr, "select");
|
|
3833
|
+
break;
|
|
3628
3834
|
case "SCALAR_SUBQUERY_COL":
|
|
3629
3835
|
break;
|
|
3630
3836
|
case "WINDOW_COL":
|
|
@@ -3675,6 +3881,10 @@ function collectSelectOutputNames(columns) {
|
|
|
3675
3881
|
if (col.alias) names.add(col.alias);
|
|
3676
3882
|
continue;
|
|
3677
3883
|
}
|
|
3884
|
+
if (col.type === "SCALAR_VALUE_COL") {
|
|
3885
|
+
if (col.alias) names.add(col.alias);
|
|
3886
|
+
continue;
|
|
3887
|
+
}
|
|
3678
3888
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
3679
3889
|
names.add(col.alias ?? "(subquery)");
|
|
3680
3890
|
continue;
|
|
@@ -3697,14 +3907,32 @@ function arithNodeLabel(node) {
|
|
|
3697
3907
|
}
|
|
3698
3908
|
function stringFuncLabel(expr) {
|
|
3699
3909
|
const args = expr.args.map((a) => {
|
|
3700
|
-
if (a.type === "STRING") return `'${a.value}'`;
|
|
3701
|
-
if (a.type === "STRING_FUNC") return stringFuncLabel(a);
|
|
3702
3910
|
if (a.type === "AGG_REF") return aggregateSyntheticName(a.func, a.distinct, a.arg);
|
|
3703
3911
|
if (a.type === "AGG_ARITH") return "agg_arith";
|
|
3704
|
-
return
|
|
3912
|
+
return scalarValueLabel(a);
|
|
3705
3913
|
});
|
|
3706
3914
|
return `${expr.func}(${args.join(",")})`;
|
|
3707
3915
|
}
|
|
3916
|
+
function scalarValueLabel(expr) {
|
|
3917
|
+
switch (expr.type) {
|
|
3918
|
+
case "STRING":
|
|
3919
|
+
return `'${expr.value}'`;
|
|
3920
|
+
case "NUMBER":
|
|
3921
|
+
return numberLiteralText(expr);
|
|
3922
|
+
case "VARIABLE":
|
|
3923
|
+
return `@${expr.name}`;
|
|
3924
|
+
case "FIELD":
|
|
3925
|
+
return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
|
|
3926
|
+
case "STRING_FUNC":
|
|
3927
|
+
return stringFuncLabel(expr);
|
|
3928
|
+
case "CASE_WHEN":
|
|
3929
|
+
return "case";
|
|
3930
|
+
case "SCALAR_ARITH":
|
|
3931
|
+
return `(${scalarValueLabel(expr.left)}${expr.op}${scalarValueLabel(expr.right)})`;
|
|
3932
|
+
case "CONCAT_OP":
|
|
3933
|
+
return `(${scalarValueLabel(expr.left)}||${scalarValueLabel(expr.right)})`;
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3708
3936
|
function isAggregateSyntheticName(name) {
|
|
3709
3937
|
return /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT)\(/i.test(name);
|
|
3710
3938
|
}
|
|
@@ -4647,6 +4875,45 @@ function evalArithExpr(expr, row) {
|
|
|
4647
4875
|
return r !== 0 ? l % r : NaN;
|
|
4648
4876
|
}
|
|
4649
4877
|
}
|
|
4878
|
+
function evalScalarValueExpr(expr, row) {
|
|
4879
|
+
switch (expr.type) {
|
|
4880
|
+
case "STRING":
|
|
4881
|
+
return expr.value;
|
|
4882
|
+
case "NUMBER":
|
|
4883
|
+
return expr.value;
|
|
4884
|
+
case "FIELD":
|
|
4885
|
+
return resolveFieldRef(row, expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field);
|
|
4886
|
+
case "VARIABLE":
|
|
4887
|
+
throw new Error(`ArgumentError: unresolved variable @${expr.name} reached scalar evaluator.`);
|
|
4888
|
+
case "STRING_FUNC":
|
|
4889
|
+
return evalStringFunc(expr, row);
|
|
4890
|
+
case "CASE_WHEN":
|
|
4891
|
+
return evalCaseWhen(expr, row);
|
|
4892
|
+
case "CONCAT_OP": {
|
|
4893
|
+
return evalStringFunc({
|
|
4894
|
+
type: "STRING_FUNC",
|
|
4895
|
+
func: "CONCAT",
|
|
4896
|
+
args: [expr.left, expr.right]
|
|
4897
|
+
}, row);
|
|
4898
|
+
}
|
|
4899
|
+
case "SCALAR_ARITH": {
|
|
4900
|
+
const left = Number(evalScalarValueExpr(expr.left, row));
|
|
4901
|
+
const right = Number(evalScalarValueExpr(expr.right, row));
|
|
4902
|
+
switch (expr.op) {
|
|
4903
|
+
case "+":
|
|
4904
|
+
return left + right;
|
|
4905
|
+
case "-":
|
|
4906
|
+
return left - right;
|
|
4907
|
+
case "*":
|
|
4908
|
+
return left * right;
|
|
4909
|
+
case "/":
|
|
4910
|
+
return right !== 0 ? left / right : NaN;
|
|
4911
|
+
case "%":
|
|
4912
|
+
return right !== 0 ? left % right : NaN;
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4650
4917
|
function applyRoundOp(op, num, digits) {
|
|
4651
4918
|
const factor = Math.pow(10, digits);
|
|
4652
4919
|
const raw = Math[op](num * factor) / factor;
|
|
@@ -5049,12 +5316,9 @@ function formatWithComma(num, digits) {
|
|
|
5049
5316
|
return decStr ? `${intFmt}.${decStr}` : intFmt;
|
|
5050
5317
|
}
|
|
5051
5318
|
function evalStringFuncArg(arg, row) {
|
|
5052
|
-
if (arg.type === "STRING") return arg.value;
|
|
5053
|
-
if (arg.type === "STRING_FUNC") return evalStringFunc(arg, row);
|
|
5054
|
-
if (arg.type === "FIELD_REF") return resolveFieldRef(row, arg.field);
|
|
5055
|
-
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
5056
5319
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return "";
|
|
5057
|
-
|
|
5320
|
+
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
5321
|
+
return String(evalScalarValueExpr(arg, row));
|
|
5058
5322
|
}
|
|
5059
5323
|
function resolveFieldRef(row, field) {
|
|
5060
5324
|
const direct = row[field];
|
|
@@ -5277,10 +5541,13 @@ function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
5277
5541
|
}
|
|
5278
5542
|
function evalCaseResult(result, row) {
|
|
5279
5543
|
if (result.type === "ARRAY") return result.elements.map((e) => e.value).join(",");
|
|
5280
|
-
if (result.type === "
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5544
|
+
if (result.type === "FIELD_REF") {
|
|
5545
|
+
return row[result.field] ?? "";
|
|
5546
|
+
}
|
|
5547
|
+
if (result.type === "ARITH") {
|
|
5548
|
+
return String(evalArithExpr(result, row));
|
|
5549
|
+
}
|
|
5550
|
+
return String(evalScalarValueExpr(result, row));
|
|
5284
5551
|
}
|
|
5285
5552
|
function resolveKintoneFunc(name) {
|
|
5286
5553
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -5324,6 +5591,63 @@ function matchLike(value, pattern) {
|
|
|
5324
5591
|
return regex.test(value);
|
|
5325
5592
|
}
|
|
5326
5593
|
|
|
5594
|
+
// src/core/dmlCustomCheck.ts
|
|
5595
|
+
function collectCheckFieldRefs(groups) {
|
|
5596
|
+
return collectRefs2(groups);
|
|
5597
|
+
}
|
|
5598
|
+
function collectCheckComparisonFieldRefs(groups) {
|
|
5599
|
+
return collectRefs2(groups.flatMap((group) => group.rules.map((rule) => rule.condition)));
|
|
5600
|
+
}
|
|
5601
|
+
function collectRefs2(root) {
|
|
5602
|
+
const refs = [];
|
|
5603
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5604
|
+
const visit = (node) => {
|
|
5605
|
+
if (Array.isArray(node)) {
|
|
5606
|
+
node.forEach(visit);
|
|
5607
|
+
return;
|
|
5608
|
+
}
|
|
5609
|
+
if (node === null || typeof node !== "object") return;
|
|
5610
|
+
const obj = node;
|
|
5611
|
+
if (obj.type === "EXISTS" || obj.type === "SUBQUERY_IN_LIST" || obj.type === "SCALAR_SUBQUERY") {
|
|
5612
|
+
throw customCheckParseError("CHECK \u306B\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
5613
|
+
}
|
|
5614
|
+
if (obj.type === "FIELD" && typeof obj.field === "string") {
|
|
5615
|
+
add(typeof obj.tableAlias === "string" ? obj.tableAlias : null, obj.field);
|
|
5616
|
+
} else if (obj.type === "FIELD_REF" && typeof obj.field === "string") {
|
|
5617
|
+
const dot = obj.field.indexOf(".");
|
|
5618
|
+
add(dot > 0 ? obj.field.slice(0, dot) : null, dot > 0 ? obj.field.slice(dot + 1) : obj.field);
|
|
5619
|
+
}
|
|
5620
|
+
for (const value of Object.values(obj)) visit(value);
|
|
5621
|
+
};
|
|
5622
|
+
const add = (tableAlias, field) => {
|
|
5623
|
+
if (/^(COUNT|SUM|AVG|MIN|MAX|GROUP_CONCAT)\(/i.test(field)) {
|
|
5624
|
+
throw customCheckParseError("CHECK \u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
5625
|
+
}
|
|
5626
|
+
const key = `${tableAlias ?? ""}\0${field}`;
|
|
5627
|
+
if (!seen.has(key)) {
|
|
5628
|
+
seen.add(key);
|
|
5629
|
+
refs.push({ tableAlias, field });
|
|
5630
|
+
}
|
|
5631
|
+
};
|
|
5632
|
+
visit(root);
|
|
5633
|
+
return refs;
|
|
5634
|
+
}
|
|
5635
|
+
function customCheckParseError(message) {
|
|
5636
|
+
return new ParseError(message, { kind: "EOF" /* EOF */, value: "CHECK", pos: 0 });
|
|
5637
|
+
}
|
|
5638
|
+
function evaluateCustomChecks(groups, row, resolveFieldType) {
|
|
5639
|
+
const errors = [];
|
|
5640
|
+
groups.forEach((group, groupIndex) => {
|
|
5641
|
+
for (const rule of group.rules) {
|
|
5642
|
+
if (!evalWhere(rule.condition, row, resolveFieldType)) continue;
|
|
5643
|
+
const value = evalScalarValueExpr(rule.message, row);
|
|
5644
|
+
errors.push({ groupIndex, message: value == null ? "" : String(value) });
|
|
5645
|
+
break;
|
|
5646
|
+
}
|
|
5647
|
+
});
|
|
5648
|
+
return errors;
|
|
5649
|
+
}
|
|
5650
|
+
|
|
5327
5651
|
// src/converter/dmlToKintone.ts
|
|
5328
5652
|
function assertDmlWhereIsSafe(where) {
|
|
5329
5653
|
if (whereHasKlike(where)) {
|
|
@@ -5361,10 +5685,11 @@ function buildInsertRecord(fields, row, fieldTypes) {
|
|
|
5361
5685
|
}
|
|
5362
5686
|
function updateToGetQuery(stmt) {
|
|
5363
5687
|
assertDmlWhereIsSafe(stmt.where);
|
|
5688
|
+
const checkFields = collectUpdateCheckTargetFields(stmt);
|
|
5364
5689
|
return {
|
|
5365
5690
|
app: stmt.appId,
|
|
5366
5691
|
query: whereToKintone(stmt.where),
|
|
5367
|
-
fields: ["$id"],
|
|
5692
|
+
fields: ["$id", ...checkFields],
|
|
5368
5693
|
totalCount: false
|
|
5369
5694
|
};
|
|
5370
5695
|
}
|
|
@@ -5378,19 +5703,19 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
|
5378
5703
|
function buildUpdateRecord(assignments, fieldTypes) {
|
|
5379
5704
|
const record = {};
|
|
5380
5705
|
for (const { field, value } of assignments) {
|
|
5381
|
-
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
5706
|
+
if (value.type === "ARITH" || value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
5382
5707
|
record[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
|
|
5383
5708
|
}
|
|
5384
5709
|
return record;
|
|
5385
5710
|
}
|
|
5386
5711
|
function hasArithAssignment(stmt) {
|
|
5387
5712
|
return stmt.assignments.some(
|
|
5388
|
-
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
|
|
5713
|
+
(a) => a.value.type === "ARITH" || a.value.type === "SCALAR_ARITH" || a.value.type === "CONCAT_OP" || a.value.type === "CASE_VALUE"
|
|
5389
5714
|
);
|
|
5390
5715
|
}
|
|
5391
5716
|
function hasRowDependentAssignment(stmt) {
|
|
5392
5717
|
return stmt.assignments.some(
|
|
5393
|
-
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
5718
|
+
(a) => a.value.type === "ARITH" || a.value.type === "SCALAR_ARITH" || a.value.type === "CONCAT_OP" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
5394
5719
|
);
|
|
5395
5720
|
}
|
|
5396
5721
|
function updateToGetQueryForArith(stmt) {
|
|
@@ -5399,12 +5724,15 @@ function updateToGetQueryForArith(stmt) {
|
|
|
5399
5724
|
for (const { value } of stmt.assignments) {
|
|
5400
5725
|
if (value.type === "ARITH") {
|
|
5401
5726
|
collectArithFields2(value, refFields);
|
|
5727
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
5728
|
+
collectScalarValueFields2(value, refFields);
|
|
5402
5729
|
} else if (value.type === "STRING_FUNC") {
|
|
5403
5730
|
collectStringFuncFields2(value, refFields);
|
|
5404
5731
|
} else if (value.type === "CASE_VALUE") {
|
|
5405
5732
|
collectCaseFields(value.expr, refFields);
|
|
5406
5733
|
}
|
|
5407
5734
|
}
|
|
5735
|
+
collectUpdateCheckTargetFields(stmt).forEach((field) => refFields.add(field));
|
|
5408
5736
|
return {
|
|
5409
5737
|
app: stmt.appId,
|
|
5410
5738
|
query: whereToKintone(stmt.where),
|
|
@@ -5425,16 +5753,27 @@ function collectStringFuncFields2(expr, out) {
|
|
|
5425
5753
|
for (const arg of expr.args) collectStringFuncArgFields2(arg, out);
|
|
5426
5754
|
}
|
|
5427
5755
|
function collectStringFuncArgFields2(arg, out) {
|
|
5428
|
-
if (arg.type === "STRING") return;
|
|
5429
|
-
if (arg.type === "STRING_FUNC") {
|
|
5430
|
-
collectStringFuncFields2(arg, out);
|
|
5431
|
-
return;
|
|
5432
|
-
}
|
|
5433
5756
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
5434
5757
|
collectAggOperandFields2(arg, out);
|
|
5435
5758
|
return;
|
|
5436
5759
|
}
|
|
5437
|
-
|
|
5760
|
+
collectScalarValueFields2(arg, out);
|
|
5761
|
+
}
|
|
5762
|
+
function collectScalarValueFields2(expr, out) {
|
|
5763
|
+
if (expr.type === "FIELD") {
|
|
5764
|
+
out.add(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field);
|
|
5765
|
+
return;
|
|
5766
|
+
}
|
|
5767
|
+
if (expr.type === "STRING_FUNC") {
|
|
5768
|
+
collectStringFuncFields2(expr, out);
|
|
5769
|
+
return;
|
|
5770
|
+
}
|
|
5771
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
5772
|
+
collectScalarValueFields2(expr.left, out);
|
|
5773
|
+
collectScalarValueFields2(expr.right, out);
|
|
5774
|
+
return;
|
|
5775
|
+
}
|
|
5776
|
+
if (expr.type === "CASE_WHEN") collectCaseFields(expr, out);
|
|
5438
5777
|
}
|
|
5439
5778
|
function collectAggOperandFields2(node, out) {
|
|
5440
5779
|
if (node.type === "AGG_REF") {
|
|
@@ -5447,9 +5786,12 @@ function collectAggOperandFields2(node, out) {
|
|
|
5447
5786
|
}
|
|
5448
5787
|
}
|
|
5449
5788
|
function collectCaseResultFields(result, out) {
|
|
5450
|
-
if (result.type === "STRING") return;
|
|
5451
5789
|
if (result.type === "ARRAY") return;
|
|
5452
|
-
|
|
5790
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
5791
|
+
collectArithNode2(result, out);
|
|
5792
|
+
return;
|
|
5793
|
+
}
|
|
5794
|
+
collectScalarValueFields2(result, out);
|
|
5453
5795
|
}
|
|
5454
5796
|
function collectCaseFields(expr, out) {
|
|
5455
5797
|
for (const branch of expr.branches) {
|
|
@@ -5487,6 +5829,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
|
|
|
5487
5829
|
for (const { field, value } of stmt.assignments) {
|
|
5488
5830
|
if (value.type === "ARITH") {
|
|
5489
5831
|
record[field] = { value: String(evalArith(value, raw)) };
|
|
5832
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
5833
|
+
record[field] = { value: String(evalScalarValueExpr(value, row)) };
|
|
5490
5834
|
} else if (value.type === "STRING_FUNC") {
|
|
5491
5835
|
record[field] = { value: evalStringFunc(value, row) };
|
|
5492
5836
|
} else if (value.type === "CASE_VALUE") {
|
|
@@ -5541,6 +5885,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
|
|
|
5541
5885
|
throw new DmlConvertError("UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
5542
5886
|
} else if (value.type === "ARITH") {
|
|
5543
5887
|
record[field] = { value: String(evalArith(value, target)) };
|
|
5888
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
5889
|
+
record[field] = { value: String(evalScalarValueExpr(value, targetRow)) };
|
|
5544
5890
|
} else if (value.type === "CASE_VALUE") {
|
|
5545
5891
|
record[field] = { value: evalCaseWhenValue(value.expr, targetRow, fieldType) };
|
|
5546
5892
|
} else {
|
|
@@ -5651,7 +5997,15 @@ function evalCaseResultValue(result, row, fieldType) {
|
|
|
5651
5997
|
if (result.type === "STRING_FUNC") {
|
|
5652
5998
|
return evalStringFunc(result, row);
|
|
5653
5999
|
}
|
|
5654
|
-
|
|
6000
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
6001
|
+
return String(evalArithExpr(result, row));
|
|
6002
|
+
}
|
|
6003
|
+
return String(evalScalarValueExpr(result, row));
|
|
6004
|
+
}
|
|
6005
|
+
function collectUpdateCheckTargetFields(stmt) {
|
|
6006
|
+
if (!stmt.checkGroups) return [];
|
|
6007
|
+
const targetAlias = `app${stmt.appId}`.toLowerCase();
|
|
6008
|
+
return [...new Set(collectCheckFieldRefs(stmt.checkGroups).filter((ref) => ref.tableAlias === null || ref.tableAlias.toLowerCase() === targetAlias).map((ref) => ref.field).filter((field) => field !== "$id"))];
|
|
5655
6009
|
}
|
|
5656
6010
|
function evalCaseWhenValue(expr, row, fieldType) {
|
|
5657
6011
|
for (const branch of expr.branches) {
|
|
@@ -6204,7 +6558,7 @@ function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldS
|
|
|
6204
6558
|
}
|
|
6205
6559
|
function hasAggregateColumns(columns) {
|
|
6206
6560
|
return columns.some(
|
|
6207
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
|
|
6561
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(c.expr)
|
|
6208
6562
|
);
|
|
6209
6563
|
}
|
|
6210
6564
|
function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
@@ -6241,6 +6595,10 @@ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
|
6241
6595
|
const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
|
|
6242
6596
|
const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
|
|
6243
6597
|
outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
|
|
6598
|
+
} else if (col.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(col.expr)) {
|
|
6599
|
+
const outputKey = col.alias ?? scalarValueDefaultKey(col.expr);
|
|
6600
|
+
const resolvedExpr = resolveAggInScalarValue(col.expr, groupRows, resolveAggSortKind);
|
|
6601
|
+
outRow[outputKey] = String(evalScalarValueExpr(resolvedExpr, outRow));
|
|
6244
6602
|
}
|
|
6245
6603
|
}
|
|
6246
6604
|
result.push(outRow);
|
|
@@ -6571,6 +6929,13 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
|
|
|
6571
6929
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
6572
6930
|
break;
|
|
6573
6931
|
}
|
|
6932
|
+
case "SCALAR_VALUE_COL": {
|
|
6933
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? scalarValueDefaultKey(col.expr);
|
|
6934
|
+
const srcKey = scalarValueDefaultKey(col.expr);
|
|
6935
|
+
out[key] = scalarValueHasAggregate2(col.expr) ? row[col.alias ?? srcKey] ?? row[srcKey] ?? "" : String(evalScalarValueExpr(col.expr, row));
|
|
6936
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
6937
|
+
break;
|
|
6938
|
+
}
|
|
6574
6939
|
case "SCALAR_SUBQUERY_COL": {
|
|
6575
6940
|
const key = outputKeys?.[colIdx] ?? col.alias ?? "(subquery)";
|
|
6576
6941
|
out[key] = scalarCache?.get(colIdx) ?? "";
|
|
@@ -6621,6 +6986,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
6621
6986
|
return col.alias ?? "case";
|
|
6622
6987
|
case "STRFUNC_COL":
|
|
6623
6988
|
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
6989
|
+
case "SCALAR_VALUE_COL":
|
|
6990
|
+
return col.alias ?? scalarValueDefaultKey(col.expr);
|
|
6624
6991
|
case "SCALAR_SUBQUERY_COL":
|
|
6625
6992
|
return col.alias ?? "(subquery)";
|
|
6626
6993
|
case "WINDOW_COL":
|
|
@@ -6676,18 +7043,49 @@ function arithColDefaultKey(expr) {
|
|
|
6676
7043
|
}
|
|
6677
7044
|
function stringFuncDefaultKey(expr) {
|
|
6678
7045
|
const argStrs = expr.args.map((a) => {
|
|
6679
|
-
if (a.type === "STRING") return `'${a.value}'`;
|
|
6680
|
-
if (a.type === "STRING_FUNC") return stringFuncDefaultKey(a);
|
|
6681
7046
|
if (a.type === "AGG_REF" || a.type === "AGG_ARITH") return aggArithDefaultKey(a);
|
|
6682
|
-
return
|
|
7047
|
+
return scalarValueDefaultKey(a);
|
|
6683
7048
|
});
|
|
6684
7049
|
return `${expr.func}(${argStrs.join(",")})`;
|
|
6685
7050
|
}
|
|
7051
|
+
function scalarValueDefaultKey(expr) {
|
|
7052
|
+
switch (expr.type) {
|
|
7053
|
+
case "STRING":
|
|
7054
|
+
return `'${expr.value}'`;
|
|
7055
|
+
case "NUMBER":
|
|
7056
|
+
return numberLiteralText(expr);
|
|
7057
|
+
case "VARIABLE":
|
|
7058
|
+
return `@${expr.name}`;
|
|
7059
|
+
case "FIELD":
|
|
7060
|
+
return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
|
|
7061
|
+
case "STRING_FUNC":
|
|
7062
|
+
return stringFuncDefaultKey(expr);
|
|
7063
|
+
case "CASE_WHEN":
|
|
7064
|
+
return "case";
|
|
7065
|
+
case "SCALAR_ARITH":
|
|
7066
|
+
return `${scalarValueDefaultKey(expr.left)}${expr.op}${scalarValueDefaultKey(expr.right)}`;
|
|
7067
|
+
case "CONCAT_OP":
|
|
7068
|
+
return `${scalarValueDefaultKey(expr.left)}||${scalarValueDefaultKey(expr.right)}`;
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
6686
7071
|
function hasAggregateInStringFuncArg(arg) {
|
|
6687
7072
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
6688
|
-
|
|
7073
|
+
return scalarValueHasAggregate2(arg);
|
|
7074
|
+
}
|
|
7075
|
+
function scalarValueHasAggregate2(expr) {
|
|
7076
|
+
if (expr.type === "STRING_FUNC") return hasAggregateInStringFuncExpr2(expr);
|
|
7077
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
7078
|
+
return scalarValueHasAggregate2(expr.left) || scalarValueHasAggregate2(expr.right);
|
|
7079
|
+
}
|
|
7080
|
+
if (expr.type === "CASE_WHEN") {
|
|
7081
|
+
return expr.branches.some((branch) => caseResultHasAggregate2(branch.result)) || expr.elseResult !== null && caseResultHasAggregate2(expr.elseResult);
|
|
7082
|
+
}
|
|
6689
7083
|
return false;
|
|
6690
7084
|
}
|
|
7085
|
+
function caseResultHasAggregate2(result) {
|
|
7086
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
7087
|
+
return scalarValueHasAggregate2(result);
|
|
7088
|
+
}
|
|
6691
7089
|
function hasAggregateInStringFuncExpr2(expr) {
|
|
6692
7090
|
return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
|
|
6693
7091
|
}
|
|
@@ -6703,7 +7101,18 @@ function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
|
6703
7101
|
if (arg.type === "STRING_FUNC") {
|
|
6704
7102
|
return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
|
|
6705
7103
|
}
|
|
6706
|
-
return arg;
|
|
7104
|
+
return resolveAggInScalarValue(arg, rows, resolveAggSortKind);
|
|
7105
|
+
}
|
|
7106
|
+
function resolveAggInScalarValue(expr, rows, resolveAggSortKind) {
|
|
7107
|
+
if (expr.type === "STRING_FUNC") return resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind);
|
|
7108
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
7109
|
+
return {
|
|
7110
|
+
...expr,
|
|
7111
|
+
left: resolveAggInScalarValue(expr.left, rows, resolveAggSortKind),
|
|
7112
|
+
right: resolveAggInScalarValue(expr.right, rows, resolveAggSortKind)
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
7115
|
+
return expr;
|
|
6707
7116
|
}
|
|
6708
7117
|
function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
6709
7118
|
return {
|
|
@@ -6724,7 +7133,7 @@ function deriveOutputOrderSemantics(columns) {
|
|
|
6724
7133
|
} else if (column.func === "GROUP_CONCAT") {
|
|
6725
7134
|
result.set(column.alias, syntheticSemantics("string"));
|
|
6726
7135
|
}
|
|
6727
|
-
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
|
|
7136
|
+
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
6728
7137
|
result.set(column.alias, syntheticSemantics("string"));
|
|
6729
7138
|
} else if (column.type === "STRFUNC_COL") {
|
|
6730
7139
|
result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
|
|
@@ -6997,19 +7406,20 @@ var VALIDATION_META_COLUMNS = [
|
|
|
6997
7406
|
"$err_code",
|
|
6998
7407
|
"$err_message"
|
|
6999
7408
|
];
|
|
7000
|
-
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision) {
|
|
7409
|
+
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision, checkGroups = [], validateMissingCreateFields = true, includePreErrors = true) {
|
|
7001
7410
|
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
7002
7411
|
const errors = [];
|
|
7003
7412
|
const invalid = /* @__PURE__ */ new Set();
|
|
7413
|
+
let firstEvaluationError;
|
|
7004
7414
|
for (const candidate of candidates) {
|
|
7005
7415
|
candidate.record ??= {};
|
|
7006
|
-
const rowErrors = [...candidate.preErrors];
|
|
7416
|
+
const rowErrors = includePreErrors ? [...candidate.preErrors] : [];
|
|
7007
7417
|
for (const code of targetFields) {
|
|
7008
7418
|
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
|
|
7009
7419
|
if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
|
|
7010
7420
|
else candidate.record[code] = { value: result.value };
|
|
7011
7421
|
}
|
|
7012
|
-
if (candidate.mode === "create") {
|
|
7422
|
+
if (validateMissingCreateFields && candidate.mode === "create") {
|
|
7013
7423
|
for (const info of fieldInfos) {
|
|
7014
7424
|
if (info.inSubtable) continue;
|
|
7015
7425
|
if (candidate.payload.has(info.code)) continue;
|
|
@@ -7031,6 +7441,23 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
7031
7441
|
}
|
|
7032
7442
|
}
|
|
7033
7443
|
}
|
|
7444
|
+
if (checkGroups.length > 0) {
|
|
7445
|
+
const row = candidate.evaluationRow ?? Object.fromEntries(
|
|
7446
|
+
[...candidate.payload].map(([field, value]) => [field, renderValidationValue(value)])
|
|
7447
|
+
);
|
|
7448
|
+
const types = candidate.evaluationFieldTypes;
|
|
7449
|
+
const resolveType = (field) => {
|
|
7450
|
+
const qualified = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
7451
|
+
return types?.get(qualified) ?? types?.get(field.field);
|
|
7452
|
+
};
|
|
7453
|
+
try {
|
|
7454
|
+
for (const custom of evaluateCustomChecks(checkGroups, row, resolveType)) {
|
|
7455
|
+
rowErrors.push({ field: "", code: "ERR_CHECK", message: custom.message });
|
|
7456
|
+
}
|
|
7457
|
+
} catch (error) {
|
|
7458
|
+
firstEvaluationError ??= error;
|
|
7459
|
+
}
|
|
7460
|
+
}
|
|
7034
7461
|
if (rowErrors.length > 0) invalid.add(candidate.rowNumber);
|
|
7035
7462
|
for (const error of rowErrors) {
|
|
7036
7463
|
const row = {};
|
|
@@ -7044,6 +7471,7 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
7044
7471
|
errors.push(row);
|
|
7045
7472
|
}
|
|
7046
7473
|
}
|
|
7474
|
+
if (firstEvaluationError !== void 0) throw firstEvaluationError;
|
|
7047
7475
|
return { errors, invalidRows: invalid.size, invalidRowNumbers: invalid };
|
|
7048
7476
|
}
|
|
7049
7477
|
function renderValidationValue(value) {
|
|
@@ -8100,7 +8528,7 @@ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
|
|
|
8100
8528
|
}
|
|
8101
8529
|
} else if (column.type === "STRFUNC_COL") {
|
|
8102
8530
|
semantics = stringFunctionColumnMeta(column.expr).semantics;
|
|
8103
|
-
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
|
|
8531
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
8104
8532
|
semantics = syntheticSemantics("string");
|
|
8105
8533
|
}
|
|
8106
8534
|
if (semantics) aliases.set(column.alias, semantics);
|
|
@@ -8215,10 +8643,19 @@ function arithHasFieldRef(node) {
|
|
|
8215
8643
|
return false;
|
|
8216
8644
|
}
|
|
8217
8645
|
function stringFuncArgHasFieldRef(arg) {
|
|
8218
|
-
if (arg.type === "FIELD_REF") return true;
|
|
8219
|
-
if (arg.type === "ARITH") return arithHasFieldRef(arg);
|
|
8220
|
-
if (arg.type === "STRING_FUNC") return stringFuncHasFieldRef(arg);
|
|
8221
8646
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
8647
|
+
return scalarValueHasFieldRef(arg);
|
|
8648
|
+
}
|
|
8649
|
+
function scalarValueHasFieldRef(expr) {
|
|
8650
|
+
if (expr.type === "FIELD") return true;
|
|
8651
|
+
if (expr.type === "STRING_FUNC") return stringFuncHasFieldRef(expr);
|
|
8652
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
8653
|
+
return scalarValueHasFieldRef(expr.left) || scalarValueHasFieldRef(expr.right);
|
|
8654
|
+
}
|
|
8655
|
+
if (expr.type === "CASE_WHEN") {
|
|
8656
|
+
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
8657
|
+
return results.some((result) => result.type !== "ARRAY" && (result.type === "FIELD_REF" || result.type === "ARITH" ? arithHasFieldRef(result) : scalarValueHasFieldRef(result)));
|
|
8658
|
+
}
|
|
8222
8659
|
return false;
|
|
8223
8660
|
}
|
|
8224
8661
|
function stringFuncHasFieldRef(expr) {
|
|
@@ -8239,6 +8676,11 @@ function validateNoFromColumns(stmt) {
|
|
|
8239
8676
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
8240
8677
|
}
|
|
8241
8678
|
break;
|
|
8679
|
+
case "SCALAR_VALUE_COL":
|
|
8680
|
+
if (scalarValueHasFieldRef(col.expr)) {
|
|
8681
|
+
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
8682
|
+
}
|
|
8683
|
+
break;
|
|
8242
8684
|
case "WINDOW_COL":
|
|
8243
8685
|
if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
|
|
8244
8686
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
@@ -8525,8 +8967,26 @@ function collectStringFuncAggregateRefs(expr, out) {
|
|
|
8525
8967
|
for (const arg of expr.args) {
|
|
8526
8968
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
8527
8969
|
collectAggregateOperandRefs(arg, out);
|
|
8528
|
-
} else
|
|
8529
|
-
|
|
8970
|
+
} else {
|
|
8971
|
+
collectScalarAggregateRefs(arg, out);
|
|
8972
|
+
}
|
|
8973
|
+
}
|
|
8974
|
+
}
|
|
8975
|
+
function collectScalarAggregateRefs(expr, out) {
|
|
8976
|
+
if (expr.type === "STRING_FUNC") {
|
|
8977
|
+
collectStringFuncAggregateRefs(expr, out);
|
|
8978
|
+
return;
|
|
8979
|
+
}
|
|
8980
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
8981
|
+
collectScalarAggregateRefs(expr.left, out);
|
|
8982
|
+
collectScalarAggregateRefs(expr.right, out);
|
|
8983
|
+
return;
|
|
8984
|
+
}
|
|
8985
|
+
if (expr.type === "CASE_WHEN") {
|
|
8986
|
+
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
8987
|
+
for (const result of results) {
|
|
8988
|
+
if (result.type === "STRING_FUNC") collectStringFuncAggregateRefs(result, out);
|
|
8989
|
+
else if (result.type !== "ARRAY" && result.type !== "FIELD_REF" && result.type !== "ARITH") collectScalarAggregateRefs(result, out);
|
|
8530
8990
|
}
|
|
8531
8991
|
}
|
|
8532
8992
|
}
|
|
@@ -8539,6 +8999,8 @@ function collectSelectAggregateSortRefs(columns) {
|
|
|
8539
8999
|
collectAggregateOperandRefs(column.expr, refs);
|
|
8540
9000
|
} else if (column.type === "STRFUNC_COL") {
|
|
8541
9001
|
collectStringFuncAggregateRefs(column.expr, refs);
|
|
9002
|
+
} else if (column.type === "SCALAR_VALUE_COL") {
|
|
9003
|
+
collectScalarAggregateRefs(column.expr, refs);
|
|
8542
9004
|
}
|
|
8543
9005
|
}
|
|
8544
9006
|
return refs;
|
|
@@ -8704,10 +9166,11 @@ function stringFunctionColumnMeta(expr) {
|
|
|
8704
9166
|
function caseResultColumnMeta(result, resolveField2) {
|
|
8705
9167
|
if (result.type === "STRING") return syntheticColumnMeta("string");
|
|
8706
9168
|
if (result.type === "ARRAY") return unsupportedColumnMeta();
|
|
8707
|
-
if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
|
|
9169
|
+
if (result.type === "NUMBER" || result.type === "ARITH" || result.type === "SCALAR_ARITH") return syntheticColumnMeta("number");
|
|
8708
9170
|
if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
|
|
8709
|
-
|
|
8710
|
-
return
|
|
9171
|
+
if (result.type === "FIELD_REF") return resolveField2(aggregateFieldRef(result.field)) ?? unknownStringColumnMeta();
|
|
9172
|
+
if (result.type === "FIELD") return resolveField2(result) ?? unknownStringColumnMeta();
|
|
9173
|
+
return unknownStringColumnMeta();
|
|
8711
9174
|
}
|
|
8712
9175
|
function mergeExpressionColumnMeta(candidates) {
|
|
8713
9176
|
if (candidates.length === 0) return unknownStringColumnMeta();
|
|
@@ -8809,7 +9272,7 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
8809
9272
|
}
|
|
8810
9273
|
} else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
|
|
8811
9274
|
meta = syntheticColumnMeta("number");
|
|
8812
|
-
} else if (column.type === "LITERAL_COL") {
|
|
9275
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
8813
9276
|
meta = syntheticColumnMeta("string");
|
|
8814
9277
|
} else if (column.type === "STRFUNC_COL") {
|
|
8815
9278
|
meta = stringFunctionColumnMeta(column.expr);
|
|
@@ -9516,7 +9979,7 @@ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, material
|
|
|
9516
9979
|
if (column.type === "FIELD") meta = resolveField2(aggregateFieldRef(column.field));
|
|
9517
9980
|
else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
9518
9981
|
meta = syntheticColumnMeta("number");
|
|
9519
|
-
} else if (column.type === "LITERAL_COL") meta = syntheticColumnMeta("string");
|
|
9982
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_VALUE_COL") meta = syntheticColumnMeta("string");
|
|
9520
9983
|
else if (column.type === "STRFUNC_COL") meta = stringFunctionColumnMeta(column.expr);
|
|
9521
9984
|
else if (column.type === "SCALAR_SUBQUERY_COL") meta = unknownStringColumnMeta();
|
|
9522
9985
|
else if (column.type === "CASE_COL") {
|
|
@@ -9704,7 +10167,7 @@ var RejectLimitExceededError = class extends Error {
|
|
|
9704
10167
|
this.name = "RejectLimitExceededError";
|
|
9705
10168
|
}
|
|
9706
10169
|
};
|
|
9707
|
-
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
10170
|
+
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber, validateMissingCreateFields = true, includePreErrors = true) {
|
|
9708
10171
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
9709
10172
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
9710
10173
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
@@ -9736,7 +10199,10 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
9736
10199
|
targetFields,
|
|
9737
10200
|
fieldInfos,
|
|
9738
10201
|
statementNumber,
|
|
9739
|
-
numberPrecision
|
|
10202
|
+
numberPrecision,
|
|
10203
|
+
stmt.checkGroups ?? [],
|
|
10204
|
+
validateMissingCreateFields,
|
|
10205
|
+
includePreErrors
|
|
9740
10206
|
);
|
|
9741
10207
|
const columns = [...payloadFields, ...VALIDATION_META_COLUMNS];
|
|
9742
10208
|
const result = {
|
|
@@ -9846,15 +10312,33 @@ async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTable
|
|
|
9846
10312
|
async function materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode) {
|
|
9847
10313
|
if (stmt.type === "UPDATE") return materializeUpdateValidationCandidates(stmt, client, options, cacheContext, tempTables);
|
|
9848
10314
|
let rows;
|
|
10315
|
+
let sourceRows;
|
|
10316
|
+
let evaluationTypes;
|
|
9849
10317
|
if (stmt.type === "INSERT" || stmt.type === "UPSERT") {
|
|
10318
|
+
assertInsertCheckRefs(stmt, stmt.fields);
|
|
10319
|
+
evaluationTypes = new Map(stmt.fields.map((field) => [field, infoByCode.get(field)?.fieldType ?? ""]));
|
|
10320
|
+
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
9850
10321
|
rows = stmt.values.map((row) => row.map(
|
|
9851
10322
|
(value, i) => value.type === "CASE_VALUE" ? evalCaseWhenValue(value.expr, {}, infoByCode.get(stmt.fields[i])?.fieldType) : value
|
|
9852
10323
|
));
|
|
9853
10324
|
} else {
|
|
9854
|
-
const selectResult = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, { ...options, onLimitReached: "error" }, tempTables, cacheContext) : await executeSelect(stmt.select, client, { ...options, onLimitReached: "error" }, cacheContext);
|
|
9855
|
-
|
|
10325
|
+
const selectResult = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, { ...options, onLimitReached: "error" }, tempTables, cacheContext) : await executeSelect(stmt.select, client, { ...options, onLimitReached: "error" }, cacheContext, void 0, true);
|
|
10326
|
+
const hasChecks = (stmt.checkGroups?.length ?? 0) > 0;
|
|
10327
|
+
if (selectResult.columns.length < stmt.fields.length || !hasChecks && selectResult.columns.length !== stmt.fields.length) {
|
|
9856
10328
|
throw new Error(`SELECT \u306E\u5217\u6570\uFF08${selectResult.columns.length}\uFF09\u3068 DML \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`);
|
|
9857
10329
|
}
|
|
10330
|
+
if (hasChecks && new Set(selectResult.columns).size !== selectResult.columns.length) {
|
|
10331
|
+
throw customCheckParseError("CHECK \u4ED8\u304D DML \u30BD\u30FC\u30B9 SELECT \u306E\u51FA\u529B\u540D\u306F\u4E00\u610F\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
|
|
10332
|
+
}
|
|
10333
|
+
assertInsertCheckRefs(stmt, selectResult.columns);
|
|
10334
|
+
sourceRows = selectResult.rows;
|
|
10335
|
+
const meta = materializedMetaBySelectResult.get(selectResult);
|
|
10336
|
+
evaluationTypes = new Map(selectResult.columns.map((column) => {
|
|
10337
|
+
const columnMeta = meta?.get(column);
|
|
10338
|
+
const type = columnMeta?.fieldType ?? (columnMeta?.semantics?.compareMode === "number" || columnMeta?.sortKind === "number" ? "NUMBER" : "SINGLE_LINE_TEXT");
|
|
10339
|
+
return [column, type];
|
|
10340
|
+
}));
|
|
10341
|
+
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
9858
10342
|
rows = selectResult.rows.map((row) => selectResult.columns.map((column) => row[column] ?? ""));
|
|
9859
10343
|
}
|
|
9860
10344
|
const candidates = rows.map((values, index) => ({
|
|
@@ -9863,7 +10347,11 @@ async function materializeValidationCandidates(stmt, operation, client, options,
|
|
|
9863
10347
|
mode: "create",
|
|
9864
10348
|
payload: new Map(stmt.fields.map((field, i) => [field, values[i]])),
|
|
9865
10349
|
preErrors: [],
|
|
9866
|
-
record: {}
|
|
10350
|
+
record: {},
|
|
10351
|
+
evaluationRow: sourceRows?.[index] ?? Object.fromEntries(
|
|
10352
|
+
stmt.fields.map((field, i) => [field, renderValidationValue(values[i])])
|
|
10353
|
+
),
|
|
10354
|
+
evaluationFieldTypes: evaluationTypes
|
|
9867
10355
|
}));
|
|
9868
10356
|
if (stmt.type !== "UPSERT" && stmt.type !== "UPSERT_SELECT") return candidates;
|
|
9869
10357
|
for (const key of stmt.keyFields) {
|
|
@@ -9892,26 +10380,74 @@ async function materializeValidationCandidates(stmt, operation, client, options,
|
|
|
9892
10380
|
});
|
|
9893
10381
|
return candidates;
|
|
9894
10382
|
}
|
|
10383
|
+
function checkRefs(stmt) {
|
|
10384
|
+
return stmt.checkGroups ? collectCheckFieldRefs(stmt.checkGroups) : [];
|
|
10385
|
+
}
|
|
10386
|
+
function assertInsertCheckRefs(stmt, available) {
|
|
10387
|
+
const names = new Set(available);
|
|
10388
|
+
for (const ref of checkRefs(stmt)) {
|
|
10389
|
+
if (ref.tableAlias !== null) {
|
|
10390
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.tableAlias}.${ref.field} \u306F\u3053\u306E\u8A55\u4FA1\u884C\u3067\u306F\u4FEE\u98FE\u3067\u304D\u307E\u305B\u3093`);
|
|
10391
|
+
}
|
|
10392
|
+
if (!names.has(ref.field)) {
|
|
10393
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u8A55\u4FA1\u884C\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
10394
|
+
}
|
|
10395
|
+
}
|
|
10396
|
+
}
|
|
10397
|
+
var CHECK_UNSUPPORTED_COMPARISON_TYPES = /* @__PURE__ */ new Set([
|
|
10398
|
+
"CHECK_BOX",
|
|
10399
|
+
"MULTI_SELECT",
|
|
10400
|
+
"USER_SELECT",
|
|
10401
|
+
"ORGANIZATION_SELECT",
|
|
10402
|
+
"GROUP_SELECT",
|
|
10403
|
+
"FILE",
|
|
10404
|
+
"KSQL_ARRAY"
|
|
10405
|
+
]);
|
|
10406
|
+
function assertCheckComparisonTypes(stmt, types) {
|
|
10407
|
+
if (!stmt.checkGroups) return;
|
|
10408
|
+
for (const ref of collectCheckComparisonFieldRefs(stmt.checkGroups)) {
|
|
10409
|
+
const key = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
|
|
10410
|
+
const type = types.get(key) ?? types.get(ref.field);
|
|
10411
|
+
if (CHECK_UNSUPPORTED_COMPARISON_TYPES.has(type ?? "")) {
|
|
10412
|
+
throw customCheckParseError(`CHECK \u306E\u6BD4\u8F03\u3067\u306F ${type} \u30D5\u30A3\u30FC\u30EB\u30C9 ${key} \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
|
|
10413
|
+
}
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
9895
10416
|
async function materializeUpdateValidationCandidates(stmt, client, options, cacheContext, tempTables) {
|
|
9896
10417
|
if (stmt.from) return materializeUpdateFromValidationCandidates(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
9897
10418
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
9898
10419
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
10420
|
+
const checkTargetFields = assertUpdateCheckRefs(stmt, fieldTypes);
|
|
10421
|
+
assertCheckComparisonTypes(stmt, updateEvaluationTypes(fieldTypes, stmt.appId));
|
|
9899
10422
|
let records;
|
|
10423
|
+
let evaluationById = /* @__PURE__ */ new Map();
|
|
9900
10424
|
if (hasRowDependentAssignment(stmt)) {
|
|
9901
10425
|
const getParams = updateToGetQueryForArith(stmt);
|
|
9902
|
-
const
|
|
10426
|
+
const fields = [.../* @__PURE__ */ new Set([...getParams.fields, ...checkTargetFields])];
|
|
10427
|
+
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, fields, {
|
|
9903
10428
|
maxRecords: options.maxRecords ?? 1e4,
|
|
9904
10429
|
parallel: options.fetchParallel ?? 1,
|
|
9905
10430
|
onLimit: "error"
|
|
9906
10431
|
});
|
|
10432
|
+
evaluationById = new Map(resolved.records.map((record) => [Number(record["$id"]?.value), record]));
|
|
9907
10433
|
records = updateToPutBatchesArith(stmt, resolved.records, fieldTypes).flatMap((batch) => batch.records);
|
|
9908
10434
|
} else {
|
|
9909
10435
|
const getParams = updateToGetQuery(stmt);
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
10436
|
+
if (checkTargetFields.length > 0) {
|
|
10437
|
+
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [.../* @__PURE__ */ new Set(["$id", ...checkTargetFields])], {
|
|
10438
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
10439
|
+
parallel: options.fetchParallel ?? 1,
|
|
10440
|
+
onLimit: "error"
|
|
10441
|
+
});
|
|
10442
|
+
evaluationById = new Map(resolved.records.map((record) => [Number(record["$id"]?.value), record]));
|
|
10443
|
+
records = updateToPutBatches(stmt, [...evaluationById.keys()], fieldTypes).flatMap((batch) => batch.records);
|
|
10444
|
+
} else {
|
|
10445
|
+
const resolved = await resolveDmlTargetIds(client.getRecords, getParams.app, getParams.query, {
|
|
10446
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
10447
|
+
parallel: options.fetchParallel ?? 1
|
|
10448
|
+
});
|
|
10449
|
+
records = updateToPutBatches(stmt, resolved.ids, fieldTypes).flatMap((batch) => batch.records);
|
|
10450
|
+
}
|
|
9915
10451
|
}
|
|
9916
10452
|
return records.sort((a, b) => a.id - b.id).map((entry, index) => ({
|
|
9917
10453
|
rowNumber: index + 1,
|
|
@@ -9920,13 +10456,48 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
|
|
|
9920
10456
|
payload: new Map([["$id", String(entry.id)], ...stmt.assignments.map((a) => [a.field, entry.record[a.field]?.value ?? ""])]),
|
|
9921
10457
|
preErrors: [],
|
|
9922
10458
|
record: entry.record,
|
|
9923
|
-
targetId: entry.id
|
|
10459
|
+
targetId: entry.id,
|
|
10460
|
+
evaluationRow: updateEvaluationRow(evaluationById.get(entry.id), stmt.appId),
|
|
10461
|
+
evaluationFieldTypes: updateEvaluationTypes(fieldTypes, stmt.appId)
|
|
9924
10462
|
}));
|
|
9925
10463
|
}
|
|
10464
|
+
function assertUpdateCheckRefs(stmt, targetTypes) {
|
|
10465
|
+
if (stmt.from) return [];
|
|
10466
|
+
const fields = /* @__PURE__ */ new Set();
|
|
10467
|
+
for (const ref of checkRefs(stmt)) {
|
|
10468
|
+
if (ref.tableAlias !== null && ref.tableAlias.toLowerCase() !== `app${stmt.appId}`.toLowerCase()) {
|
|
10469
|
+
throw customCheckParseError(`CHECK \u306E\u4FEE\u98FE\u5B50 ${ref.tableAlias} \u306F\u66F4\u65B0\u5148 APP${stmt.appId} \u3067\u306F\u3042\u308A\u307E\u305B\u3093`);
|
|
10470
|
+
}
|
|
10471
|
+
if (ref.field !== "$id" && !targetTypes.has(ref.field)) {
|
|
10472
|
+
throw customCheckParseError(`CHECK \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
10473
|
+
}
|
|
10474
|
+
fields.add(ref.field);
|
|
10475
|
+
}
|
|
10476
|
+
return [...fields];
|
|
10477
|
+
}
|
|
10478
|
+
function updateEvaluationRow(record, appId) {
|
|
10479
|
+
if (!record) return {};
|
|
10480
|
+
const plain = flatten(record, null);
|
|
10481
|
+
return Object.fromEntries([
|
|
10482
|
+
...Object.entries(plain),
|
|
10483
|
+
...Object.entries(plain).map(([field, value]) => [`APP${appId}.${field}`, value])
|
|
10484
|
+
]);
|
|
10485
|
+
}
|
|
10486
|
+
function updateEvaluationTypes(types, appId) {
|
|
10487
|
+
return new Map([
|
|
10488
|
+
...types,
|
|
10489
|
+
...[...types].map(([field, type]) => [`APP${appId}.${field}`, type]),
|
|
10490
|
+
["$id", "RECORD_NUMBER"],
|
|
10491
|
+
[`APP${appId}.$id`, "RECORD_NUMBER"]
|
|
10492
|
+
]);
|
|
10493
|
+
}
|
|
9926
10494
|
async function materializeUpdateFromValidationCandidates(stmt, from, client, options, cacheContext, tempTables) {
|
|
10495
|
+
const scope = await resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables);
|
|
10496
|
+
assertCheckComparisonTypes(stmt, scope.evaluationTypes);
|
|
9927
10497
|
const matched = await resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables);
|
|
9928
10498
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
9929
10499
|
const records = updateFromToPutBatches(stmt, matched, fieldTypes).flatMap((batch) => batch.records);
|
|
10500
|
+
const matchedById = new Map(matched.map((pair) => [Number(pair.target["$id"]?.value), pair]));
|
|
9930
10501
|
return records.sort((a, b) => a.id - b.id).map((entry, index) => ({
|
|
9931
10502
|
rowNumber: index + 1,
|
|
9932
10503
|
operation: "UPDATE",
|
|
@@ -9934,7 +10505,9 @@ async function materializeUpdateFromValidationCandidates(stmt, from, client, opt
|
|
|
9934
10505
|
payload: new Map([["$id", String(entry.id)], ...stmt.assignments.map((a) => [a.field, entry.record[a.field]?.value ?? ""])]),
|
|
9935
10506
|
preErrors: [],
|
|
9936
10507
|
record: entry.record,
|
|
9937
|
-
targetId: entry.id
|
|
10508
|
+
targetId: entry.id,
|
|
10509
|
+
evaluationRow: updateFromEvaluationRow(matchedById.get(entry.id), stmt.appId, from.alias),
|
|
10510
|
+
evaluationFieldTypes: scope.evaluationTypes
|
|
9938
10511
|
}));
|
|
9939
10512
|
}
|
|
9940
10513
|
var UPDATE_FROM_KEY_CHUNK_SIZE = UPSERT_IN_CHUNK_SIZE;
|
|
@@ -9948,7 +10521,8 @@ var UPDATE_FROM_UNSUPPORTED_SOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
|
9948
10521
|
]);
|
|
9949
10522
|
async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables) {
|
|
9950
10523
|
const joinKind = await resolveUpdateFromTargetJoinKind(stmt, from, client, cacheContext);
|
|
9951
|
-
const
|
|
10524
|
+
const checkScope = await resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables);
|
|
10525
|
+
const sourceFields = [...new Set(stmt.assignments.filter((a) => a.value.type === "SOURCE_FIELD").map((a) => a.value.type === "SOURCE_FIELD" ? a.value.field : "").concat(checkScope.sourceFields))];
|
|
9952
10526
|
const requiredSourceFields = [.../* @__PURE__ */ new Set([from.joinKeyField, ...sourceFields])];
|
|
9953
10527
|
const sourceRows = await loadUpdateFromSourceRows(
|
|
9954
10528
|
from,
|
|
@@ -9974,8 +10548,8 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
9974
10548
|
}
|
|
9975
10549
|
if (sourceByKey.size === 0) return [];
|
|
9976
10550
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
9977
|
-
const targetFields = collectUpdateFromTargetFields(stmt);
|
|
9978
|
-
const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter }).query;
|
|
10551
|
+
const targetFields = [.../* @__PURE__ */ new Set([...collectUpdateFromTargetFields(stmt), ...checkScope.targetFields])];
|
|
10552
|
+
const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter, checkGroups: void 0 }).query;
|
|
9979
10553
|
const targetRecords = [];
|
|
9980
10554
|
const seenTargetIds = /* @__PURE__ */ new Set();
|
|
9981
10555
|
let fetchedTargetCount = 0;
|
|
@@ -10093,7 +10667,54 @@ function normalizeUpdateFromJoinKey(raw, kind, side) {
|
|
|
10093
10667
|
}
|
|
10094
10668
|
return JSON.stringify(decimal);
|
|
10095
10669
|
}
|
|
10670
|
+
async function executeCheckedPlainDml(stmt, client, options, cacheContext, tempTables) {
|
|
10671
|
+
const prepared = await prepareDmlValidation(
|
|
10672
|
+
stmt,
|
|
10673
|
+
client,
|
|
10674
|
+
options,
|
|
10675
|
+
cacheContext,
|
|
10676
|
+
tempTables,
|
|
10677
|
+
1,
|
|
10678
|
+
false,
|
|
10679
|
+
false
|
|
10680
|
+
);
|
|
10681
|
+
if (prepared.result.errors.length > 0) {
|
|
10682
|
+
const first = prepared.result.errors[0];
|
|
10683
|
+
throw new Error(
|
|
10684
|
+
`DmlValidationError: ${first["$err_code"]} ${first["$err_message"]} (row=${first["$err_row"]}, field=${first["$err_field"]})`
|
|
10685
|
+
);
|
|
10686
|
+
}
|
|
10687
|
+
const candidates = prepared.candidates;
|
|
10688
|
+
const confirmOperation = stmt.type.startsWith("INSERT") ? "INSERT" : "UPDATE";
|
|
10689
|
+
if (options.confirm && candidates.length > 0) {
|
|
10690
|
+
const ok = await options.confirm(candidates.length, confirmOperation);
|
|
10691
|
+
if (!ok) throw new OperationCancelledError(confirmOperation, candidates.length);
|
|
10692
|
+
}
|
|
10693
|
+
if (stmt.type === "INSERT" || stmt.type === "INSERT_SELECT") {
|
|
10694
|
+
const createdIds = [];
|
|
10695
|
+
for (let i = 0; i < candidates.length; i += 100) {
|
|
10696
|
+
const response = await client.postRecords({ app: stmt.appId, records: candidates.slice(i, i + 100).map((c) => c.record) });
|
|
10697
|
+
createdIds.push(response.ids);
|
|
10698
|
+
}
|
|
10699
|
+
return { type: "INSERT", createdIds, insertedCount: createdIds.flat().length };
|
|
10700
|
+
}
|
|
10701
|
+
if (stmt.type === "UPDATE") {
|
|
10702
|
+
const updates2 = candidates.map((candidate) => ({ id: candidate.targetId, record: candidate.record }));
|
|
10703
|
+
for (let i = 0; i < updates2.length; i += 100) await client.putRecords({ app: stmt.appId, records: updates2.slice(i, i + 100) });
|
|
10704
|
+
return { type: "UPDATE", updatedCount: updates2.length };
|
|
10705
|
+
}
|
|
10706
|
+
const inserts = candidates.filter((candidate) => candidate.mode === "create");
|
|
10707
|
+
const updates = candidates.filter((candidate) => candidate.mode === "update").map((candidate) => ({ id: candidate.targetId, record: candidate.record }));
|
|
10708
|
+
let insertedCount = 0;
|
|
10709
|
+
for (let i = 0; i < inserts.length; i += 100) {
|
|
10710
|
+
const response = await client.postRecords({ app: stmt.appId, records: inserts.slice(i, i + 100).map((c) => c.record) });
|
|
10711
|
+
insertedCount += response.ids.length;
|
|
10712
|
+
}
|
|
10713
|
+
for (let i = 0; i < updates.length; i += 100) await client.putRecords({ app: stmt.appId, records: updates.slice(i, i + 100) });
|
|
10714
|
+
return { type: "UPSERT", insertedCount, updatedCount: updates.length };
|
|
10715
|
+
}
|
|
10096
10716
|
async function executeInsert(stmt, client, options, cacheContext) {
|
|
10717
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext);
|
|
10097
10718
|
if (stmt.subtableCode) {
|
|
10098
10719
|
return executeInsertSubtable(stmt, client, options, cacheContext);
|
|
10099
10720
|
}
|
|
@@ -10114,6 +10735,7 @@ async function executeInsert(stmt, client, options, cacheContext) {
|
|
|
10114
10735
|
};
|
|
10115
10736
|
}
|
|
10116
10737
|
async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
10738
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
|
|
10117
10739
|
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10118
10740
|
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
10119
10741
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
@@ -10151,6 +10773,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
10151
10773
|
};
|
|
10152
10774
|
}
|
|
10153
10775
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
10776
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, tempTables);
|
|
10154
10777
|
if (stmt.subtableCode) {
|
|
10155
10778
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
10156
10779
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
@@ -10276,6 +10899,7 @@ async function executeDelete(stmt, client, options, cacheContext) {
|
|
|
10276
10899
|
return { type: "DELETE", deletedCount: ids.length };
|
|
10277
10900
|
}
|
|
10278
10901
|
async function executeUpsert(stmt, client, options, cacheContext) {
|
|
10902
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext);
|
|
10279
10903
|
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10280
10904
|
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
10281
10905
|
const toInsert = [];
|
|
@@ -10710,6 +11334,7 @@ function evalOrderKeyForRow(key, row) {
|
|
|
10710
11334
|
}
|
|
10711
11335
|
}
|
|
10712
11336
|
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
11337
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
|
|
10713
11338
|
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10714
11339
|
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
10715
11340
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
@@ -11457,6 +12082,7 @@ function collectArithRefFields(stmt) {
|
|
|
11457
12082
|
for (const { value } of stmt.assignments) {
|
|
11458
12083
|
if (value.type === "ARITH") collectArithNodeRefs(value, refs);
|
|
11459
12084
|
if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
|
|
12085
|
+
if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") collectScalarNodeRefs(value, refs);
|
|
11460
12086
|
}
|
|
11461
12087
|
return [...refs];
|
|
11462
12088
|
}
|
|
@@ -11471,10 +12097,74 @@ function collectArithNodeRefs(node, out) {
|
|
|
11471
12097
|
}
|
|
11472
12098
|
if (node.type === "STRING_FUNC") {
|
|
11473
12099
|
for (const arg of node.args) {
|
|
11474
|
-
if (arg.type !== "
|
|
11475
|
-
|
|
12100
|
+
if (arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") collectScalarNodeRefs(arg, out);
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
12103
|
+
}
|
|
12104
|
+
async function resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables) {
|
|
12105
|
+
const refs = checkRefs(stmt);
|
|
12106
|
+
const targetTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
12107
|
+
const sourceTableName = from.cteName;
|
|
12108
|
+
const sourceTypes = sourceTableName !== null ? new Map((tempTables?.get(sourceTableName)?.columns ?? []).map((column) => [
|
|
12109
|
+
column,
|
|
12110
|
+
tempTables?.get(sourceTableName)?.columnMeta?.get(column)?.fieldType ?? (tempTables?.get(sourceTableName)?.columnMeta?.get(column)?.semantics?.compareMode === "number" ? "NUMBER" : "SINGLE_LINE_TEXT")
|
|
12111
|
+
])) : await getFieldTypeMap(from.appId, client, cacheContext);
|
|
12112
|
+
const targetFields = /* @__PURE__ */ new Set();
|
|
12113
|
+
const sourceFields = /* @__PURE__ */ new Set();
|
|
12114
|
+
for (const ref of refs) {
|
|
12115
|
+
if (ref.tableAlias !== null) {
|
|
12116
|
+
if (ref.tableAlias.toLowerCase() === `app${stmt.appId}`.toLowerCase()) {
|
|
12117
|
+
if (ref.field !== "$id" && !targetTypes.has(ref.field)) throw customCheckParseError(`CHECK \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
12118
|
+
targetFields.add(ref.field);
|
|
12119
|
+
} else if (ref.tableAlias.toLowerCase() === from.alias.toLowerCase()) {
|
|
12120
|
+
if (ref.field !== "$id" && !sourceTypes.has(ref.field)) throw customCheckParseError(`CHECK \u306E\u30BD\u30FC\u30B9\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
12121
|
+
sourceFields.add(ref.field);
|
|
12122
|
+
} else {
|
|
12123
|
+
throw customCheckParseError(`CHECK \u306E\u4FEE\u98FE\u5B50 ${ref.tableAlias} \u306F\u66F4\u65B0\u5148\u307E\u305F\u306F FROM alias \u3067\u306F\u3042\u308A\u307E\u305B\u3093`);
|
|
11476
12124
|
}
|
|
12125
|
+
continue;
|
|
11477
12126
|
}
|
|
12127
|
+
const inTarget = ref.field === "$id" || targetTypes.has(ref.field);
|
|
12128
|
+
const inSource = ref.field === "$id" || sourceTypes.has(ref.field);
|
|
12129
|
+
if (!inTarget) {
|
|
12130
|
+
throw customCheckParseError(`UPDATE FROM \u306E CHECK \u3067\u306F\u30BD\u30FC\u30B9\u5217 ${ref.field} \u3092\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`);
|
|
12131
|
+
}
|
|
12132
|
+
if (inSource) {
|
|
12133
|
+
throw customCheckParseError(`UPDATE FROM \u306E CHECK \u306E\u975E\u4FEE\u98FE\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u66D6\u6627\u3067\u3059`);
|
|
12134
|
+
}
|
|
12135
|
+
targetFields.add(ref.field);
|
|
12136
|
+
}
|
|
12137
|
+
const evaluationTypes = /* @__PURE__ */ new Map();
|
|
12138
|
+
for (const [field, type] of targetTypes) {
|
|
12139
|
+
evaluationTypes.set(field, type);
|
|
12140
|
+
evaluationTypes.set(`APP${stmt.appId}.${field}`, type);
|
|
12141
|
+
}
|
|
12142
|
+
evaluationTypes.set("$id", "RECORD_NUMBER");
|
|
12143
|
+
evaluationTypes.set(`APP${stmt.appId}.$id`, "RECORD_NUMBER");
|
|
12144
|
+
for (const [field, type] of sourceTypes) evaluationTypes.set(`${from.alias}.${field}`, type);
|
|
12145
|
+
return { targetFields: [...targetFields], sourceFields: [...sourceFields], evaluationTypes };
|
|
12146
|
+
}
|
|
12147
|
+
function updateFromEvaluationRow(pair, appId, sourceAlias) {
|
|
12148
|
+
if (!pair) return {};
|
|
12149
|
+
const target = flatten(pair.target, null);
|
|
12150
|
+
return Object.fromEntries([
|
|
12151
|
+
...Object.entries(target),
|
|
12152
|
+
...Object.entries(target).map(([field, value]) => [`APP${appId}.${field}`, value]),
|
|
12153
|
+
...Object.entries(pair.source).map(([field, value]) => [`${sourceAlias}.${field}`, value])
|
|
12154
|
+
]);
|
|
12155
|
+
}
|
|
12156
|
+
function collectScalarNodeRefs(node, out) {
|
|
12157
|
+
if (node.type === "FIELD") {
|
|
12158
|
+
out.add(node.tableAlias ? `${node.tableAlias}.${node.field}` : node.field);
|
|
12159
|
+
return;
|
|
12160
|
+
}
|
|
12161
|
+
if (node.type === "STRING_FUNC") {
|
|
12162
|
+
for (const arg of node.args) if (arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") collectScalarNodeRefs(arg, out);
|
|
12163
|
+
return;
|
|
12164
|
+
}
|
|
12165
|
+
if (node.type === "SCALAR_ARITH" || node.type === "CONCAT_OP") {
|
|
12166
|
+
collectScalarNodeRefs(node.left, out);
|
|
12167
|
+
collectScalarNodeRefs(node.right, out);
|
|
11478
12168
|
}
|
|
11479
12169
|
}
|
|
11480
12170
|
function formatAssignment(a) {
|