@rex0220/kintone-sql-tools 3.41.0 → 3.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cli/ksql.js CHANGED
@@ -1038,8 +1038,9 @@ function needsSpaceBetween(prev, cur) {
1038
1038
  return true;
1039
1039
  }
1040
1040
  var ParseError = class extends Error {
1041
- constructor(message, token) {
1042
- super(`${message}\uFF08\u4F4D\u7F6E ${token.pos}\u3001\u30C8\u30FC\u30AF\u30F3: \u300C${token.value}\u300D\uFF09`);
1041
+ constructor(rawMessage, token) {
1042
+ super(`${rawMessage}\uFF08\u4F4D\u7F6E ${token.pos}\u3001\u30C8\u30FC\u30AF\u30F3: \u300C${token.value}\u300D\uFF09`);
1043
+ this.rawMessage = rawMessage;
1043
1044
  this.token = token;
1044
1045
  this.name = "ParseError";
1045
1046
  }
@@ -2425,16 +2426,33 @@ var Parser = class {
2425
2426
  const byKind = PARSER_SCALAR_FUNCTION_TOKEN_MAP[this.peek().kind] ?? null;
2426
2427
  if (byKind !== null && byKind !== "LEFT" && byKind !== "RIGHT") return byKind;
2427
2428
  if (this.peek().kind === "IDENT" /* IDENT */) {
2428
- const name = this.peek().value.toUpperCase();
2429
+ const nameToken = this.peek();
2430
+ const name = nameToken.value.toUpperCase();
2429
2431
  if (PARSER_IDENT_SCALAR_FUNCTIONS.includes(name)) {
2430
2432
  if (this.peekAt(1).kind === "(" /* LPAREN */) {
2431
2433
  return name;
2432
2434
  }
2433
2435
  }
2436
+ if (this.peekAt(1).kind === "(" /* LPAREN */) {
2437
+ const hint = name === "DATE_SUB" ? "\u3002\u65E5\u4ED8\u3092\u6E1B\u7B97\u3059\u308B\u306B\u306F DATE_ADD \u306E\u52A0\u7B97\u5024\u3078\u8CA0\u6570\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044" : "";
2438
+ throw new ParseError(`\u300C${nameToken.value}\u300D\u3068\u3044\u3046\u95A2\u6570\u306F\u3042\u308A\u307E\u305B\u3093${hint}`, nameToken);
2439
+ }
2434
2440
  }
2435
2441
  return null;
2436
2442
  }
2437
2443
  parseStringFuncExpr() {
2444
+ const nameToken = this.peek();
2445
+ const func = this.tryStringFuncName();
2446
+ try {
2447
+ return this.parseStringFuncExprBody();
2448
+ } catch (error) {
2449
+ if (!(error instanceof ParseError)) throw error;
2450
+ const displayName = nameToken.value.toUpperCase();
2451
+ const guidance = func === "DATE_ADD" ? `${displayName} \u306E\u69CB\u6587\u306F DATE_ADD(\u5217, n, '\u5358\u4F4D') \u3067\u3059\u3002` : `${displayName} \u306E\u5F15\u6570\u69CB\u6587\u304C\u4E0D\u6B63\u3067\u3059\u3002`;
2452
+ throw new ParseError(`${guidance}${error.rawMessage}`, error.token);
2453
+ }
2454
+ }
2455
+ parseStringFuncExprBody() {
2438
2456
  const tokenKind = this.peek().kind;
2439
2457
  const func = this.tryStringFuncName();
2440
2458
  this.advance();
@@ -6537,11 +6555,93 @@ function walkObjects2(node, visit) {
6537
6555
  for (const value of Object.values(obj)) walkObjects2(value, visit);
6538
6556
  }
6539
6557
 
6558
+ // src/core/functionArity.ts
6559
+ function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
6560
+ if (args.length >= min && args.length <= max) return;
6561
+ const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
6562
+ throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
6563
+ }
6564
+ function assertStringFunctionArity(func, args) {
6565
+ switch (func) {
6566
+ case "UPPER":
6567
+ case "LOWER":
6568
+ case "TRIM":
6569
+ case "LTRIM":
6570
+ case "RTRIM":
6571
+ case "LENGTH":
6572
+ case "LENGTH_CHAR":
6573
+ case "YEAR":
6574
+ case "MONTH":
6575
+ case "DAY":
6576
+ case "DAYOFWEEK":
6577
+ case "QUARTER":
6578
+ case "WEEK":
6579
+ case "LAST_DAY":
6580
+ case "ABS":
6581
+ case "SQRT":
6582
+ return assertArity(func, args, 1, 1);
6583
+ case "SUBSTRING":
6584
+ case "LPAD":
6585
+ case "RPAD":
6586
+ case "REGEXP_LIKE":
6587
+ case "REGEXP_SUBSTR":
6588
+ return assertArity(func, args, 2, 3);
6589
+ case "LEFT":
6590
+ case "RIGHT":
6591
+ case "INSTR":
6592
+ case "ISNULL":
6593
+ case "NULLIF":
6594
+ case "MOD":
6595
+ case "POWER":
6596
+ case "FORMAT":
6597
+ case "CAST":
6598
+ case "DATE_FORMAT":
6599
+ case "DATEDIFF":
6600
+ return assertArity(func, args, 2, 2);
6601
+ case "CONCAT":
6602
+ case "COALESCE":
6603
+ case "GREATEST":
6604
+ case "LEAST":
6605
+ return assertArity(func, args, 2);
6606
+ case "REPLACE":
6607
+ case "TRANSLATE":
6608
+ case "DATE_ADD":
6609
+ return assertArity(func, args, 3, 3);
6610
+ case "REGEXP_REPLACE":
6611
+ return assertArity(func, args, 3, 5);
6612
+ case "ROUND":
6613
+ case "FLOOR":
6614
+ case "CEIL":
6615
+ case "TRUNCATE":
6616
+ return assertArity(func, args, 1, 2);
6617
+ case "CURRENT_DATE":
6618
+ case "CURRENT_TIMESTAMP":
6619
+ return assertArity(func, args, 0, 0);
6620
+ }
6621
+ }
6622
+
6540
6623
  // src/core/statementValidation.ts
6541
6624
  function validateStatementStatic(stmt) {
6625
+ validateStringFunctionArities(stmt);
6542
6626
  validatePrimaryOrganizationDmlStatement(stmt);
6543
6627
  validateKlikeStatement(stmt);
6544
6628
  }
6629
+ function validateStringFunctionArities(stmt) {
6630
+ const visit = (value) => {
6631
+ if (value === null || typeof value !== "object") return;
6632
+ if (Array.isArray(value)) {
6633
+ value.forEach(visit);
6634
+ return;
6635
+ }
6636
+ const node = value;
6637
+ if (node.type === "STRING_FUNC") {
6638
+ const expr = node;
6639
+ assertStringFunctionArity(expr.func, expr.args);
6640
+ }
6641
+ Object.values(node).forEach(visit);
6642
+ };
6643
+ visit(stmt);
6644
+ }
6545
6645
 
6546
6646
  // src/engine/groupingRowMeta.ts
6547
6647
  var groupingRowMetaKey = /* @__PURE__ */ Symbol("ksql.groupingRowMeta");
@@ -7566,6 +7666,7 @@ function replaceNthMatch(input, globalRe, replacement, n) {
7566
7666
  });
7567
7667
  }
7568
7668
  function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7669
+ assertStringFunctionArity(expr.func, expr.args);
7569
7670
  const args = expr.args.map((a) => evalStringFuncArg(a, row, resolveFieldType, resolveFieldSemantics2));
7570
7671
  switch (expr.func) {
7571
7672
  case "UPPER":
@@ -7581,7 +7682,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7581
7682
  case "LENGTH":
7582
7683
  return String((args[0] ?? "").length);
7583
7684
  case "LENGTH_CHAR":
7584
- assertArity("LENGTH_CHAR", args, 1, 1);
7585
7685
  return String([...args[0] ?? ""].length);
7586
7686
  case "SUBSTRING": {
7587
7687
  const str = args[0] ?? "";
@@ -7590,23 +7690,19 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7590
7690
  return sliceSafeRange(str, start, len !== void 0 ? start + len : str.length);
7591
7691
  }
7592
7692
  case "LEFT": {
7593
- assertArity("LEFT", args, 2, 2);
7594
7693
  const str = args[0];
7595
7694
  const n = Math.trunc(Number(args[1]));
7596
7695
  return Number.isNaN(n) || n <= 0 ? "" : sliceSafePrefix(str, n);
7597
7696
  }
7598
7697
  case "RIGHT": {
7599
- assertArity("RIGHT", args, 2, 2);
7600
7698
  const str = args[0];
7601
7699
  const n = Math.trunc(Number(args[1]));
7602
7700
  return Number.isNaN(n) || n <= 0 ? "" : sliceSafeSuffix(str, n);
7603
7701
  }
7604
7702
  case "INSTR":
7605
- assertArity("INSTR", args, 2, 2);
7606
7703
  return String(args[0].indexOf(args[1]) + 1);
7607
7704
  case "LPAD":
7608
7705
  case "RPAD": {
7609
- assertArity(expr.func, args, 2, 3);
7610
7706
  const str = args[0];
7611
7707
  const n = Math.trunc(Number(args[1]));
7612
7708
  if (Number.isNaN(n) || n <= 0) return "";
@@ -7618,7 +7714,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7618
7714
  }
7619
7715
  case "GREATEST":
7620
7716
  case "LEAST":
7621
- assertArity(expr.func, args, 2);
7622
7717
  return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
7623
7718
  case "CONCAT":
7624
7719
  return args.join("");
@@ -7629,22 +7724,18 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7629
7724
  return from === "" ? str : str.split(from).join(to);
7630
7725
  }
7631
7726
  case "REGEXP_LIKE": {
7632
- assertArity("REGEXP_LIKE", args, 2, 3);
7633
7727
  return compileRegexp(args[1], args[2] ?? "").test(args[0]) ? "1" : "0";
7634
7728
  }
7635
7729
  case "REGEXP_REPLACE": {
7636
- assertArity("REGEXP_REPLACE", args, 3, 5);
7637
7730
  assertRegexpReplacement(args[2]);
7638
7731
  const occurrence = parseRegexpOccurrence(args[4]);
7639
7732
  const regexp = compileRegexp(args[1], args[3] ?? "", true);
7640
7733
  return occurrence === 0 ? args[0].replace(regexp, args[2]) : replaceNthMatch(args[0], regexp, args[2], occurrence);
7641
7734
  }
7642
7735
  case "REGEXP_SUBSTR": {
7643
- assertArity("REGEXP_SUBSTR", args, 2, 3);
7644
7736
  return compileRegexp(args[1], args[2] ?? "").exec(args[0])?.[0] ?? "";
7645
7737
  }
7646
7738
  case "TRANSLATE": {
7647
- assertArity("TRANSLATE", args, 3, 3);
7648
7739
  const from = [...args[1]];
7649
7740
  const to = [...args[2]];
7650
7741
  if (from.length !== to.length) {
@@ -7671,7 +7762,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7671
7762
  case "CEIL":
7672
7763
  return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
7673
7764
  case "TRUNCATE":
7674
- assertArity("TRUNCATE", args, 1, 2);
7675
7765
  return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
7676
7766
  case "CAST": {
7677
7767
  const val = args[0] ?? "";
@@ -7700,13 +7790,10 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7700
7790
  return d.length >= 10 ? String(parseInt(d.slice(8, 10), 10)) : "";
7701
7791
  }
7702
7792
  case "DAYOFWEEK":
7703
- assertArity(expr.func, args, 1, 1);
7704
7793
  return isValidYmd(args[0]) ? String(dayOfWeekIndex(args[0]) + 1) : "";
7705
7794
  case "QUARTER":
7706
- assertArity(expr.func, args, 1, 1);
7707
7795
  return isValidYmd(args[0]) ? String(Math.ceil(Number(args[0].slice(5, 7)) / 3)) : "";
7708
7796
  case "WEEK":
7709
- assertArity(expr.func, args, 1, 1);
7710
7797
  return isValidYmd(args[0]) ? String(isoWeekNumber(args[0])) : "";
7711
7798
  case "DATE_FORMAT":
7712
7799
  return applyDateFormat(args[0] ?? "", args[1] ?? "");
@@ -7715,7 +7802,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7715
7802
  case "DATE_ADD":
7716
7803
  return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
7717
7804
  case "LAST_DAY":
7718
- assertArity("LAST_DAY", args, 1, 1);
7719
7805
  return applyLastDay(args[0]);
7720
7806
  case "ABS":
7721
7807
  return String(Math.abs(Number(args[0] ?? "0")));
@@ -7739,11 +7825,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
7739
7825
  return (/* @__PURE__ */ new Date()).toISOString();
7740
7826
  }
7741
7827
  }
7742
- function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
7743
- if (args.length >= min && args.length <= max) return;
7744
- const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
7745
- throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
7746
- }
7747
7828
  function parseDateParts(s) {
7748
7829
  return {
7749
7830
  y: s.slice(0, 4) || "0000",
@@ -7842,12 +7923,21 @@ function applyDateAdd(dateStr, n, unit) {
7842
7923
  if (!dateStr || dateStr.length < 10) return dateStr;
7843
7924
  const { y, mo, d } = parseDateParts(dateStr);
7844
7925
  const dt = new Date(Date.UTC(+y, +mo - 1, +d));
7926
+ const addCalendarUnit = (applyTarget) => {
7927
+ const originalDay = dt.getUTCDate();
7928
+ dt.setUTCDate(1);
7929
+ applyTarget();
7930
+ const endOfTargetMonth = new Date(dt.getTime());
7931
+ endOfTargetMonth.setUTCMonth(endOfTargetMonth.getUTCMonth() + 1);
7932
+ endOfTargetMonth.setUTCDate(0);
7933
+ dt.setUTCDate(Math.min(originalDay, endOfTargetMonth.getUTCDate()));
7934
+ };
7845
7935
  switch (unit) {
7846
7936
  case "YEAR":
7847
- dt.setUTCFullYear(dt.getUTCFullYear() + n);
7937
+ addCalendarUnit(() => dt.setUTCFullYear(dt.getUTCFullYear() + n));
7848
7938
  break;
7849
7939
  case "MONTH":
7850
- dt.setUTCMonth(dt.getUTCMonth() + n);
7940
+ addCalendarUnit(() => dt.setUTCMonth(dt.getUTCMonth() + n));
7851
7941
  break;
7852
7942
  case "DAY":
7853
7943
  dt.setUTCDate(dt.getUTCDate() + n);