@rex0220/kintone-sql-tools 3.38.0 → 3.39.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
@@ -1175,13 +1175,38 @@ var Parser = class {
1175
1175
  parseDeclareVariable() {
1176
1176
  this.advance();
1177
1177
  const variable = this.expect("VARIABLE" /* VARIABLE */, "DECLARE \u306E\u5F8C\u306B\u306F\u5909\u6570\u540D\uFF08\u4F8B: @name\uFF09\u304C\u5FC5\u8981\u3067\u3059");
1178
+ const relativeDate = this.peek().kind === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "RELATIVE_DATE";
1179
+ if (relativeDate) this.advance();
1178
1180
  this.expect("=" /* EQ */);
1181
+ if (relativeDate) {
1182
+ return {
1183
+ type: "DECLARE_VARIABLE",
1184
+ name: variable.value.slice(1).toLowerCase(),
1185
+ annotation: "RELATIVE_DATE",
1186
+ default: this.parseRelativeDateVariableToken()
1187
+ };
1188
+ }
1179
1189
  const expr = this.parseScalarExpr("DECLARE", false);
1180
1190
  if (expr.type === "SCALAR_SUBQUERY") {
1181
1191
  throw new ParseError("DECLARE \u306E\u65E2\u5B9A\u5024\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
1182
1192
  }
1183
1193
  return { type: "DECLARE_VARIABLE", name: variable.value.slice(1).toLowerCase(), default: expr };
1184
1194
  }
1195
+ /** RELATIVE_DATE 宣言専用。WHERE と同じ関数パーサーを使い、日付系14個だけを許可する。 */
1196
+ parseRelativeDateVariableToken() {
1197
+ const tok = this.peek();
1198
+ const contextualFunction = PARSER_CONTEXTUAL_FUNCTION_TOKEN_MAP[tok.kind];
1199
+ if (contextualFunction === "TODAY" || contextualFunction === "NOW") {
1200
+ return this.parseSqlValue();
1201
+ }
1202
+ if (tok.kind === "IDENT" /* IDENT */ && this.peekAt(1).kind === "(" /* LPAREN */ && isRelativeDateFunctionName(tok.value.toUpperCase())) {
1203
+ return this.parseRelativeDateFunction();
1204
+ }
1205
+ throw new ParseError(
1206
+ "RELATIVE_DATE \u306E\u65E2\u5B9A\u5024\u306B\u306F\u30B5\u30DD\u30FC\u30C8\u5BFE\u8C61\u306E\u76F8\u5BFE\u65E5\u4ED8\u95A2\u6570\u30C8\u30FC\u30AF\u30F3\u304C\u5FC5\u8981\u3067\u3059",
1207
+ tok
1208
+ );
1209
+ }
1185
1210
  /** SET / DECLARE RHS 専用。既存式パーサーで構文を読み、フィールド参照を明示的に拒否する。 */
1186
1211
  parseScalarExpr(context, allowScalarSubquery) {
1187
1212
  const tok = this.peek();
@@ -6807,9 +6832,10 @@ function collectRefs(node, tempRefs, appIds) {
6807
6832
  for (const v of Object.values(obj)) collectRefs(v, tempRefs, appIds);
6808
6833
  }
6809
6834
  }
6810
- function collectVariableRefs(node, refs) {
6835
+ var RELATIVE_DATE_COMPARISON_OPS = /* @__PURE__ */ new Set(["=", "!=", "<>", ">", "<", ">=", "<="]);
6836
+ function collectVariableRefs(node, refs, inWhere = false) {
6811
6837
  if (Array.isArray(node)) {
6812
- for (const v of node) collectVariableRefs(v, refs);
6838
+ for (const v of node) collectVariableRefs(v, refs, inWhere);
6813
6839
  return;
6814
6840
  }
6815
6841
  if (node !== null && typeof node === "object") {
@@ -6818,13 +6844,27 @@ function collectVariableRefs(node, refs) {
6818
6844
  if ((type === "VARIABLE" || type === "VARIABLE_COL" || type === "VARIABLE_IN_LIST") && typeof obj["name"] === "string") {
6819
6845
  refs.push({
6820
6846
  name: obj["name"],
6821
- kind: type === "VARIABLE" ? "scalar" : type === "VARIABLE_COL" ? "select-column" : "array-in-list"
6847
+ kind: type === "VARIABLE" ? "scalar" : type === "VARIABLE_COL" ? "select-column" : "array-in-list",
6848
+ relativeDateAllowed: inWhere
6822
6849
  });
6823
6850
  return;
6824
6851
  }
6825
- for (const v of Object.values(obj)) collectVariableRefs(v, refs);
6852
+ const preservesWhereContext = type === "BINARY" || type === "LOGICAL" || type === "NOT" || type === "GROUP" || type === "NULL_CHECK" || type === "BOOLEAN";
6853
+ for (const [key, value] of Object.entries(obj)) {
6854
+ const directVariable = value !== null && typeof value === "object" && value["type"] === "VARIABLE";
6855
+ const directComparisonRight = inWhere && type === "BINARY" && key === "right" && directVariable && RELATIVE_DATE_COMPARISON_OPS.has(String(obj["op"]));
6856
+ collectVariableRefs(
6857
+ value,
6858
+ refs,
6859
+ key === "where" ? true : type === "BINARY" && key === "right" && directVariable ? directComparisonRight : preservesWhereContext ? inWhere : false
6860
+ );
6861
+ }
6826
6862
  }
6827
6863
  }
6864
+ function isRelativeDateDmlUse(stmt) {
6865
+ const target = stmt.type === "EXPLAIN" ? stmt.query : stmt;
6866
+ return isDmlType(target.type) || target.type === "VALIDATE";
6867
+ }
6828
6868
  function validateGroupingStaticQueries(node) {
6829
6869
  if (Array.isArray(node)) {
6830
6870
  for (const value of node) validateGroupingStaticQueries(value);
@@ -6919,6 +6959,18 @@ function analyzeBatch(statements) {
6919
6959
  index
6920
6960
  );
6921
6961
  }
6962
+ if (def.relativeDate && isRelativeDateDmlUse(stmt)) {
6963
+ throw new BatchAnalysisError(
6964
+ `ArgumentError: RELATIVE_DATE variable @${use.name} cannot be used in DML or VALIDATE statements.`,
6965
+ index
6966
+ );
6967
+ }
6968
+ if (def.relativeDate && !use.relativeDateAllowed) {
6969
+ throw new BatchAnalysisError(
6970
+ `ArgumentError: RELATIVE_DATE variable @${use.name} can only be used as a WHERE comparison right operand or BETWEEN boundary.`,
6971
+ index
6972
+ );
6973
+ }
6922
6974
  if (!referencedThisStatement.has(use.name)) {
6923
6975
  def.referencedBy.push(index);
6924
6976
  referencedThisStatement.add(use.name);
@@ -6931,6 +6983,7 @@ function analyzeBatch(statements) {
6931
6983
  variableDefs.set(stmt.name, {
6932
6984
  index,
6933
6985
  kind: stmt.type === "SET_VARIABLE" && stmt.expr.type === "ARRAY" ? "array" : "scalar",
6986
+ relativeDate: stmt.type === "DECLARE_VARIABLE" && stmt.annotation === "RELATIVE_DATE",
6934
6987
  referencedBy: []
6935
6988
  });
6936
6989
  variableOrder.push(stmt.name);
@@ -16123,6 +16176,7 @@ async function executeBatch(sql, client, options = {}) {
16123
16176
  throw new Error("UnsupportedError: APPLY mutation requires allowApplyMutation=true");
16124
16177
  }
16125
16178
  const injectedVariables = validateDeclaredBatchVariables(statements, options.variables);
16179
+ const relativeDateVariables = prepareRelativeDateVariables(statements, injectedVariables);
16126
16180
  const batchOptions = { ...options, variables: injectedVariables };
16127
16181
  if (options.continueOnError && analysis.containsDml) {
16128
16182
  throw new Error("ArgumentError: continueOnError is not allowed for batches containing DML.");
@@ -16194,7 +16248,16 @@ async function executeBatch(sql, client, options = {}) {
16194
16248
  );
16195
16249
  const cursorScope = wrapClientWithCursorScope(statementClient);
16196
16250
  const outcome = await runWithDeadline(
16197
- executeBatchStatement(statements[i], info, cursorScope.client, stmtOptions, cacheContext, tempTables, variables),
16251
+ executeBatchStatement(
16252
+ statements[i],
16253
+ info,
16254
+ cursorScope.client,
16255
+ stmtOptions,
16256
+ cacheContext,
16257
+ tempTables,
16258
+ variables,
16259
+ relativeDateVariables
16260
+ ),
16198
16261
  remaining,
16199
16262
  cursorScope.closeActive
16200
16263
  );
@@ -16239,7 +16302,7 @@ function statementHasApplyMutation(statement) {
16239
16302
  }
16240
16303
  return statement.type === "UPSERT" && statement.validateOnly !== true && Boolean(statement.onInsertApplyBlocks?.length || statement.onUpdateApplyBlocks?.length);
16241
16304
  }
16242
- async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables) {
16305
+ async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables, variables, relativeDateVariables) {
16243
16306
  if (stmt.type === "SET_VARIABLE") {
16244
16307
  const resolvedStmt2 = resolveBatchVariableReferences(stmt, variables);
16245
16308
  validateStatementStatic(resolvedStmt2);
@@ -16284,6 +16347,13 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
16284
16347
  return {};
16285
16348
  }
16286
16349
  if (stmt.type === "DECLARE_VARIABLE") {
16350
+ if (stmt.annotation === "RELATIVE_DATE") {
16351
+ variables.set(stmt.name, {
16352
+ type: "relative-date",
16353
+ value: relativeDateVariables.get(stmt.name)
16354
+ });
16355
+ return {};
16356
+ }
16287
16357
  const injected = options.variables ?? {};
16288
16358
  if (Object.prototype.hasOwnProperty.call(injected, stmt.name)) {
16289
16359
  variables.set(stmt.name, { type: "string", value: injected[stmt.name] });
@@ -16507,6 +16577,32 @@ function parseSqlBatch(sql, enableImport = false) {
16507
16577
  const tokens = new Lexer(sql).tokenize();
16508
16578
  return new Parser(tokens, { import: enableImport }).parseStatements();
16509
16579
  }
16580
+ function parseRelativeDateVariableValue(name, value) {
16581
+ try {
16582
+ const statements = parseSqlBatch(`DECLARE @__b111 RELATIVE_DATE = ${value}`);
16583
+ const declaration = statements[0];
16584
+ if (statements.length !== 1 || declaration?.type !== "DECLARE_VARIABLE" || declaration.annotation !== "RELATIVE_DATE") {
16585
+ throw new Error("token was not consumed as one RELATIVE_DATE declaration");
16586
+ }
16587
+ return declaration.default;
16588
+ } catch (error) {
16589
+ const detail = error instanceof Error ? ` ${error.message}` : "";
16590
+ throw new Error(
16591
+ `ArgumentError: RELATIVE_DATE variable @${name} requires one supported relative-date function token.${detail}`
16592
+ );
16593
+ }
16594
+ }
16595
+ function prepareRelativeDateVariables(statements, injectedVariables) {
16596
+ const prepared = /* @__PURE__ */ new Map();
16597
+ for (const stmt of statements) {
16598
+ if (stmt.type !== "DECLARE_VARIABLE" || stmt.annotation !== "RELATIVE_DATE") continue;
16599
+ prepared.set(
16600
+ stmt.name,
16601
+ Object.prototype.hasOwnProperty.call(injectedVariables, stmt.name) ? parseRelativeDateVariableValue(stmt.name, injectedVariables[stmt.name]) : stmt.default
16602
+ );
16603
+ }
16604
+ return prepared;
16605
+ }
16510
16606
  function evaluateScalarExpr(expr) {
16511
16607
  switch (expr.type) {
16512
16608
  case "STRING":
@@ -16543,7 +16639,8 @@ function resolveBatchVariableReferencesInternal(node, variables, numericArithmet
16543
16639
  if (value.type === "array") {
16544
16640
  throw new Error(`ParseError: array variable @${obj["name"]} can only be used as IN @${obj["name"]}.`);
16545
16641
  }
16546
- if (numericArithmeticOperand && value.type !== "number" && value.placeholder !== true) {
16642
+ if (value.type === "relative-date") return value.value;
16643
+ if (numericArithmeticOperand && value.type !== "number" && !(value.type === "string" && value.placeholder === true)) {
16547
16644
  throw new Error(
16548
16645
  `ArgumentError: variable @${obj["name"]} is not numeric and cannot be used in arithmetic.`
16549
16646
  );
@@ -16554,6 +16651,9 @@ function resolveBatchVariableReferencesInternal(node, variables, numericArithmet
16554
16651
  const value = variables.get(obj["name"]);
16555
16652
  if (value === void 0) throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
16556
16653
  if (value.type === "array") throw new Error(`ParseError: array variable @${obj["name"]} cannot be used as a SELECT column.`);
16654
+ if (value.type === "relative-date") {
16655
+ throw new Error(`InternalError: RELATIVE_DATE variable @${obj["name"]} reached a SELECT column.`);
16656
+ }
16557
16657
  const aliasDisplay = typeof obj["aliasDisplay"] === "string" ? { aliasDisplay: obj["aliasDisplay"] } : {};
16558
16658
  return value.type === "number" ? { type: "ARITH_COL", expr: { type: "NUMBER", value: value.value, raw: value.raw ?? String(value.value) }, alias: obj["alias"], ...aliasDisplay } : { type: "LITERAL_COL", value: value.value, alias: obj["alias"], ...aliasDisplay };
16559
16659
  }
@@ -22673,7 +22773,8 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22673
22773
  try {
22674
22774
  const statements = parseSqlBatch(sql, enableImport);
22675
22775
  const analysis = analyzeBatch(statements);
22676
- validateDeclaredBatchVariables(statements, injectedVariables);
22776
+ const normalizedInjectedVariables = validateDeclaredBatchVariables(statements, injectedVariables);
22777
+ const relativeDateVariables = prepareRelativeDateVariables(statements, normalizedInjectedVariables);
22677
22778
  const variables = /* @__PURE__ */ new Map();
22678
22779
  const plans = [];
22679
22780
  for (let i = 0; i < statements.length; i++) {
@@ -22706,7 +22807,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22706
22807
  plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
22707
22808
  });
22708
22809
  if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
22709
- variables.set(stmt.name, stmt.type === "SET_VARIABLE" && stmt.expr.type === "ARRAY" ? { type: "array", elements: stmt.expr.elements.map((element) => ({ type: "string", value: element.value })) } : { type: "string", value: `@${stmt.name}`, placeholder: true });
22810
+ variables.set(stmt.name, stmt.type === "DECLARE_VARIABLE" && stmt.annotation === "RELATIVE_DATE" ? { type: "relative-date", value: relativeDateVariables.get(stmt.name) } : stmt.type === "SET_VARIABLE" && stmt.expr.type === "ARRAY" ? { type: "array", elements: stmt.expr.elements.map((element) => ({ type: "string", value: element.value })) } : { type: "string", value: `@${stmt.name}`, placeholder: true });
22710
22811
  }
22711
22812
  }
22712
22813
  return { statementCount: statements.length, statements: plans };
@@ -22745,6 +22846,12 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22745
22846
  ];
22746
22847
  }
22747
22848
  if (stmt.type === "DECLARE_VARIABLE") {
22849
+ if (stmt.annotation === "RELATIVE_DATE") {
22850
+ return [
22851
+ `DECLARE @${stmt.name} RELATIVE_DATE = <relative-date token>`,
22852
+ " value: \u5916\u90E8\u6CE8\u5165\u304C\u3042\u308C\u3070\u63A1\u7528\u3001\u306A\u3051\u308C\u3070\u65E2\u5B9A\u30C8\u30FC\u30AF\u30F3\u3092\u4F7F\u7528\uFF08\u5024\u306F\u975E\u516C\u958B\uFF09"
22853
+ ];
22854
+ }
22748
22855
  return [
22749
22856
  `DECLARE @${stmt.name} = <default scalar expression>`,
22750
22857
  " value: \u5916\u90E8\u6CE8\u5165\u304C\u3042\u308C\u3070\u63A1\u7528\u3001\u306A\u3051\u308C\u3070\u65E2\u5B9A\u5024\u3092\u5B9F\u884C\u6642\u306B1\u56DE\u8A55\u4FA1\uFF08\u5024\u306F\u975E\u516C\u958B\uFF09"
@@ -25374,13 +25481,17 @@ function restoreSqlDiagnosticValue(value, bindings, options = {}) {
25374
25481
  return `${dmlTarget[1]}${target}`;
25375
25482
  }
25376
25483
  }
25377
- let restored = value;
25378
- for (const binding of bindings.values()) {
25379
- const internal = `APP${binding.mappedAppId}`;
25484
+ if (bindings.size === 0) return value;
25485
+ const mappedIds = [...bindings.keys()].sort((a, b) => String(b).length - String(a).length);
25486
+ const internalApp = new RegExp(
25487
+ `APP(${mappedIds.join("|")})(?!\\d)(\\s+AS\\s+[^\\s()]+)?(?:\\s+\\(\\1\\))?`,
25488
+ "g"
25489
+ );
25490
+ return value.replace(internalApp, (_match, mappedIdText, alias) => {
25491
+ const binding = bindings.get(Number(mappedIdText));
25380
25492
  const display = binding.source === "logical" ? displayMode === "physical" ? `LAPP_${binding.logicalName} -> APP${binding.appId}` : `LAPP_${binding.logicalName}@${binding.profile}` : displayMode === "physical" ? `APP${binding.appId}` : `APP${binding.appId}@${binding.profile}`;
25381
- restored = restored.split(`${internal} (${binding.mappedAppId})`).join(display).split(internal).join(display);
25382
- }
25383
- return restored;
25493
+ return `${display}${alias ?? ""}`;
25494
+ });
25384
25495
  }
25385
25496
  if (Array.isArray(value)) {
25386
25497
  return value.map((item) => restoreSqlDiagnosticValue(item, bindings, options));