@rex0220/kintone-sql-tools 3.38.0 → 3.40.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
  }
@@ -22668,14 +22768,20 @@ function serverFunctionClientEvaluationLabel(leaves) {
22668
22768
  (leaf) => leaf.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(leaf.right.name)
22669
22769
  ) ? "relative date client evaluations" : "kintone function client evaluations";
22670
22770
  }
22771
+ var EXPLAIN_FETCH_PLAN = /* @__PURE__ */ Symbol("ksql.explainFetchPlan");
22772
+ function setExplainFetchPlan(result, plan) {
22773
+ result[EXPLAIN_FETCH_PLAN] = plan;
22774
+ }
22671
22775
  async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
22672
22776
  const invocationCacheContext = createInvocationCacheContext(cacheContext);
22673
22777
  try {
22674
22778
  const statements = parseSqlBatch(sql, enableImport);
22675
22779
  const analysis = analyzeBatch(statements);
22676
- validateDeclaredBatchVariables(statements, injectedVariables);
22780
+ const normalizedInjectedVariables = validateDeclaredBatchVariables(statements, injectedVariables);
22781
+ const relativeDateVariables = prepareRelativeDateVariables(statements, normalizedInjectedVariables);
22677
22782
  const variables = /* @__PURE__ */ new Map();
22678
22783
  const plans = [];
22784
+ const fetchStatements = [];
22679
22785
  for (let i = 0; i < statements.length; i++) {
22680
22786
  const stmt = statements[i];
22681
22787
  const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveBatchVariableReferences(stmt.expr, variables) } : stmt : resolveBatchVariableReferences(stmt, variables);
@@ -22688,6 +22794,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22688
22794
  maxRecords,
22689
22795
  relativeDatePlan
22690
22796
  );
22797
+ const fetchCollector = { sources: [] };
22691
22798
  const statementPlan = relativeDatePlan.hasServerOnlyWhereFunction && !relativeDatePlan.allowed ? relativeDateExplainLines(relativeDatePlan) : [
22692
22799
  ...relativeDateExplainLines(relativeDatePlan),
22693
22800
  ...addCursorConcurrency(buildBatchStatementPlan(
@@ -22696,7 +22803,8 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22696
22803
  whereAnalysis.capabilities,
22697
22804
  whereAnalysis.orderPlans,
22698
22805
  dmlMaxRows,
22699
- dmlMaxSubtableRows
22806
+ dmlMaxSubtableRows,
22807
+ fetchCollector
22700
22808
  ), cursorMaxActive)
22701
22809
  ];
22702
22810
  const metadataPlan = explainMetadataLines(whereAnalysis);
@@ -22705,22 +22813,36 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22705
22813
  type: analysis.statements[i].statementType,
22706
22814
  plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
22707
22815
  });
22816
+ fetchStatements.push({
22817
+ index: i,
22818
+ fetch: worstExplainFetch(fetchCollector.sources),
22819
+ sources: fetchCollector.sources
22820
+ });
22708
22821
  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 });
22822
+ 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
22823
  }
22711
22824
  }
22712
- return { statementCount: statements.length, statements: plans };
22825
+ const result = { statementCount: statements.length, statements: plans };
22826
+ setExplainFetchPlan(result, { statements: fetchStatements });
22827
+ return result;
22713
22828
  } finally {
22714
22829
  releaseMetadataCacheScope(invocationCacheContext);
22715
22830
  }
22716
22831
  }
22717
- function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
22832
+ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }) {
22718
22833
  if (stmt.type === "CREATE_TEMP_TABLE") {
22719
22834
  return [
22720
22835
  `CREATE TEMP TABLE ${stmt.name}`,
22721
22836
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
22722
22837
  ` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u65E2\u5B9A\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001tempTableMaxRows \u3067\u5909\u66F4\u53EF\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
22723
- ...buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans).map((l) => ` ${l}`)
22838
+ ...buildPlanForBatchQuery(
22839
+ stmt.query,
22840
+ info,
22841
+ capabilities,
22842
+ orderPlans,
22843
+ collector,
22844
+ "main"
22845
+ ).map((l) => ` ${l}`)
22724
22846
  ];
22725
22847
  }
22726
22848
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -22736,7 +22858,13 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22736
22858
  `SET @${stmt.name} = (SELECT ...)`,
22737
22859
  " value: \u30B5\u30D6\u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u6642\u306B1\u56DE\u8A55\u4FA1\uFF081\u884C1\u5217\u30FB\u30D0\u30C3\u30C1\u5185\u5B9A\u6570\u30FB\u7D50\u679C\u30E1\u30BF\u30C7\u30FC\u30BF\u306B\u306F\u975E\u516C\u958B\uFF09",
22738
22860
  " subquery:",
22739
- ...buildPlanForBatchQuery(stmt.expr.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`)
22861
+ ...buildPlanForBatchQuery(
22862
+ stmt.expr.query,
22863
+ subInfo,
22864
+ capabilities,
22865
+ orderPlans,
22866
+ collector
22867
+ ).map((l) => ` ${l}`)
22740
22868
  ];
22741
22869
  }
22742
22870
  return [
@@ -22745,6 +22873,12 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22745
22873
  ];
22746
22874
  }
22747
22875
  if (stmt.type === "DECLARE_VARIABLE") {
22876
+ if (stmt.annotation === "RELATIVE_DATE") {
22877
+ return [
22878
+ `DECLARE @${stmt.name} RELATIVE_DATE = <relative-date token>`,
22879
+ " 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"
22880
+ ];
22881
+ }
22748
22882
  return [
22749
22883
  `DECLARE @${stmt.name} = <default scalar expression>`,
22750
22884
  " 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"
@@ -22752,7 +22886,15 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22752
22886
  }
22753
22887
  if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
22754
22888
  if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
22755
- if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans);
22889
+ if (stmt.type === "EXPLAIN") {
22890
+ return buildPlanForBatchQuery(
22891
+ stmt.query,
22892
+ info,
22893
+ capabilities,
22894
+ orderPlans,
22895
+ collector
22896
+ );
22897
+ }
22756
22898
  if (stmt.type === "ASSERT") {
22757
22899
  const lines = [
22758
22900
  `ASSERT ${stmt.text}`,
@@ -22764,14 +22906,20 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22764
22906
  subqueries.forEach((sq, i) => {
22765
22907
  lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
22766
22908
  const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
22767
- lines.push(...buildPlanForBatchQuery(sq.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`));
22909
+ lines.push(...buildPlanForBatchQuery(
22910
+ sq.query,
22911
+ subInfo,
22912
+ capabilities,
22913
+ orderPlans,
22914
+ collector
22915
+ ).map((l) => ` ${l}`));
22768
22916
  });
22769
22917
  return lines;
22770
22918
  }
22771
22919
  if (stmt.type === "UPDATE" && (stmt.applyBlocks?.length ?? 0) > 0) {
22772
22920
  return buildExplainPlan(stmt, void 0, capabilities, orderPlans, dmlMaxRows, dmlMaxSubtableRows);
22773
22921
  }
22774
- return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
22922
+ return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans, collector);
22775
22923
  }
22776
22924
  function hasTempTableRef(node) {
22777
22925
  if (Array.isArray(node)) return node.some(hasTempTableRef);
@@ -22783,9 +22931,21 @@ function hasTempTableRef(node) {
22783
22931
  }
22784
22932
  return false;
22785
22933
  }
22786
- function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
22934
+ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector = { sources: [] }, sourceRole = "main") {
22787
22935
  if (info.tempTablesReferenced.length === 0) {
22788
- return buildExplainPlan(query, void 0, capabilities, orderPlans);
22936
+ return buildExplainPlan(
22937
+ query,
22938
+ void 0,
22939
+ capabilities,
22940
+ orderPlans,
22941
+ 100,
22942
+ DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
22943
+ 1e4,
22944
+ void 0,
22945
+ true,
22946
+ collector,
22947
+ sourceRole
22948
+ );
22789
22949
  }
22790
22950
  const lines = [];
22791
22951
  if (query.type === "INSERT_SELECT") {
@@ -22819,8 +22979,9 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
22819
22979
  maxRecords,
22820
22980
  sharedPlan
22821
22981
  );
22982
+ const fetchCollector = { sources: [] };
22822
22983
  const relativeLines = relativeDateExplainLines(sharedPlan);
22823
- const lines = sharedPlan.hasServerOnlyWhereFunction && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
22984
+ const planLines = sharedPlan.hasServerOnlyWhereFunction && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
22824
22985
  ...explainMetadataLines(analysis),
22825
22986
  ...relativeLines,
22826
22987
  ...addCursorConcurrency(
@@ -22832,17 +22993,28 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
22832
22993
  dmlMaxRows,
22833
22994
  dmlMaxSubtableRows,
22834
22995
  maxRecords,
22835
- analysis.plainGroupByPlans
22996
+ analysis.plainGroupByPlans,
22997
+ true,
22998
+ fetchCollector
22836
22999
  ),
22837
23000
  cursorMaxActive
22838
23001
  )
22839
23002
  ];
22840
- return {
23003
+ const lines = addFetchSummary(planLines, fetchCollector.sources);
23004
+ const result = {
22841
23005
  type: "SELECT",
22842
23006
  columns: ["plan"],
22843
23007
  rows: lines.map((line) => ({ plan: line })),
22844
23008
  rowCount: lines.length
22845
23009
  };
23010
+ setExplainFetchPlan(result, {
23011
+ statements: [{
23012
+ index: 0,
23013
+ fetch: worstExplainFetch(fetchCollector.sources),
23014
+ sources: fetchCollector.sources
23015
+ }]
23016
+ });
23017
+ return result;
22846
23018
  }
22847
23019
  function addCursorConcurrency(lines, cursorMaxActive) {
22848
23020
  const result = [];
@@ -22855,9 +23027,28 @@ function addCursorConcurrency(lines, cursorMaxActive) {
22855
23027
  }
22856
23028
  return result;
22857
23029
  }
22858
- function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4, plainGroupByPlans) {
22859
- if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans, plainGroupByPlans);
22860
- if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans, plainGroupByPlans);
23030
+ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4, plainGroupByPlans, includeFetchSummary = true, collector, sourceRole = "main") {
23031
+ const fetchCollector = collector ?? { sources: [] };
23032
+ if (query.type === "UNION") {
23033
+ const lines2 = buildUnionPlan(
23034
+ query,
23035
+ capabilities,
23036
+ orderPlans,
23037
+ plainGroupByPlans,
23038
+ fetchCollector
23039
+ );
23040
+ return includeFetchSummary ? addFetchSummary(lines2, fetchCollector.sources) : lines2;
23041
+ }
23042
+ if (query.type === "WITH") {
23043
+ const lines2 = buildWithPlan(
23044
+ query,
23045
+ capabilities,
23046
+ orderPlans,
23047
+ plainGroupByPlans,
23048
+ fetchCollector
23049
+ );
23050
+ return includeFetchSummary ? addFetchSummary(lines2, fetchCollector.sources) : lines2;
23051
+ }
22861
23052
  if (query.type === "INSERT") return buildInsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
22862
23053
  if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
22863
23054
  if (query.type === "UPSERT") return buildUpsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
@@ -22947,7 +23138,62 @@ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 1
22947
23138
  ` duplicateKey: preflight before lookup/write (requires load)`
22948
23139
  ];
22949
23140
  }
22950
- return buildSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
23141
+ const lines = buildSelectPlan(
23142
+ query,
23143
+ label,
23144
+ capabilities,
23145
+ orderPlans,
23146
+ plainGroupByPlans,
23147
+ true,
23148
+ true,
23149
+ fetchCollector,
23150
+ sourceRole
23151
+ );
23152
+ return includeFetchSummary ? addFetchSummary(lines, fetchCollector.sources) : lines;
23153
+ }
23154
+ var EXPLAIN_FETCH_VALUE_RANK = {
23155
+ none: 0,
23156
+ exact: 1,
23157
+ prefiltered: 2,
23158
+ all: 3
23159
+ };
23160
+ function worstExplainFetch(sources) {
23161
+ return sources.reduce(
23162
+ (worst, source) => EXPLAIN_FETCH_VALUE_RANK[worst] >= EXPLAIN_FETCH_VALUE_RANK[source.fetch] ? worst : source.fetch,
23163
+ "none"
23164
+ );
23165
+ }
23166
+ function addFetchSummary(lines, sources) {
23167
+ const detailLines = lines.filter((line) => !line.startsWith("fetch summary:"));
23168
+ if (sources.length === 0) return detailLines;
23169
+ return [`fetch summary: ${worstExplainFetch(sources).toUpperCase()}`, ...detailLines];
23170
+ }
23171
+ function pushedQueryLimit(query) {
23172
+ const match = /(?:^|\s)limit\s+(\d+)(?:\s|$)/i.exec(query);
23173
+ return match ? Number(match[1]) : null;
23174
+ }
23175
+ function createExplainFetchSource(collector, app, alias, role, scope, query, pending = false) {
23176
+ const source = {
23177
+ app,
23178
+ alias,
23179
+ role,
23180
+ fetch: scope.toLowerCase(),
23181
+ pending,
23182
+ kintoneQuery: query === "(\u5168\u4EF6\u53D6\u5F97)" || query === "(\u306A\u3057)" || query === "" ? null : query,
23183
+ limit: pushedQueryLimit(query)
23184
+ };
23185
+ collector.sources.push(source);
23186
+ return source;
23187
+ }
23188
+ function formatFetchScope(source) {
23189
+ return [
23190
+ source.fetch.toUpperCase(),
23191
+ ...source.limit === null ? [] : [`(limit ${source.limit})`],
23192
+ ...source.pending ? ["(\u672A\u78BA\u5B9A)"] : []
23193
+ ].join(" ");
23194
+ }
23195
+ function renderFetchScope(source) {
23196
+ return ` fetch: ${formatFetchScope(source)}`;
22951
23197
  }
22952
23198
  function buildValidatePlan(stmt, label) {
22953
23199
  const info = validateExplainInfo.get(stmt);
@@ -22974,7 +23220,7 @@ function buildValidatePlan(stmt, label) {
22974
23220
  lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
22975
23221
  return lines;
22976
23222
  }
22977
- function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans, allowTotalCountPlan = true) {
23223
+ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans, allowTotalCountPlan = true, emitFetch = true, collector = { sources: [] }, sourceRole = "main") {
22978
23224
  const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
22979
23225
  const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
22980
23226
  const plainGroupByPlan = plainGroupByPlans?.get(stmt) ?? (plainGroupByPlans ? [...plainGroupByPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
@@ -23032,6 +23278,17 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23032
23278
  lines.push(
23033
23279
  ` kintone query: ${baseQuery}${baseQuery ? " " : ""}limit 1`
23034
23280
  );
23281
+ if (emitFetch) {
23282
+ const source = createExplainFetchSource(
23283
+ collector,
23284
+ stmt.from.appId,
23285
+ stmt.from.alias,
23286
+ sourceRole,
23287
+ "NONE",
23288
+ `${baseQuery}${baseQuery ? " " : ""}limit 1`
23289
+ );
23290
+ lines.push(renderFetchScope(source));
23291
+ }
23035
23292
  lines.push(" fields: $id");
23036
23293
  lines.push(" fetch API: GET records.json (totalCount=true)");
23037
23294
  lines.push(" REST execution: single GET");
@@ -23078,6 +23335,17 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23078
23335
  lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
23079
23336
  const displayedQuery = orderPlan?.kind === "KORDER_CURSOR" ? buildKorderCursorQuery(stmt) : params.query;
23080
23337
  lines.push(` kintone query: ${displayedQuery || "(\u306A\u3057)"}`);
23338
+ if (emitFetch && stmt.from.cteName === null) {
23339
+ const fetchScope = displayedQuery ? "EXACT" : "ALL";
23340
+ lines.push(renderFetchScope(createExplainFetchSource(
23341
+ collector,
23342
+ stmt.from.appId,
23343
+ stmt.from.alias,
23344
+ sourceRole,
23345
+ fetchScope,
23346
+ displayedQuery
23347
+ )));
23348
+ }
23081
23349
  lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
23082
23350
  } else {
23083
23351
  const runtimeJoinPlan = explainJoinPushdownPlans.get(stmt);
@@ -23127,6 +23395,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23127
23395
  const mainFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
23128
23396
  (consumption) => consumption.targetAlias === stmt.from.alias
23129
23397
  );
23398
+ if (emitFetch && stmt.from.cteName === null) {
23399
+ const mainPending = !runtimeJoinPlan && mainCandidate !== null;
23400
+ const mainFetchScope = mainQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : mainPending ? "PREFILTERED" : mainJoinItem?.relation === "exact" || mainFunctionConsumption || exactOriginalWhere !== "" ? "EXACT" : "PREFILTERED";
23401
+ lines.push(renderFetchScope(createExplainFetchSource(
23402
+ collector,
23403
+ stmt.from.appId,
23404
+ stmt.from.alias,
23405
+ sourceRole,
23406
+ mainFetchScope,
23407
+ mainQ,
23408
+ mainPending
23409
+ )));
23410
+ }
23130
23411
  if (mainJoinItem || mainFunctionConsumption) {
23131
23412
  lines.push(` pushdown applied: ${mainBoundQuery}`);
23132
23413
  lines.push(` relation: ${mainJoinItem?.relation ?? "exact"}`);
@@ -23150,6 +23431,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23150
23431
  const joinFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
23151
23432
  (consumption) => consumption.targetAlias === join2.table.alias
23152
23433
  );
23434
+ if (emitFetch && join2.table.cteName === null) {
23435
+ const joinPending = !runtimeJoinPlan && joinCandidate !== null;
23436
+ const joinFetchScope = joinQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : joinPending ? "PREFILTERED" : joinPlanItem?.relation === "exact" || joinFunctionConsumption ? "EXACT" : "PREFILTERED";
23437
+ lines.push(renderFetchScope(createExplainFetchSource(
23438
+ collector,
23439
+ join2.table.appId,
23440
+ join2.table.alias,
23441
+ "join",
23442
+ joinFetchScope,
23443
+ joinQ,
23444
+ joinPending
23445
+ )));
23446
+ }
23153
23447
  if (joinPlanItem || joinFunctionConsumption) {
23154
23448
  lines.push(` pushdown applied: ${joinBoundQuery}`);
23155
23449
  lines.push(` relation: ${joinPlanItem?.relation ?? "exact"}`);
@@ -23159,10 +23453,16 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23159
23453
  lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
23160
23454
  }
23161
23455
  }
23162
- lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans));
23456
+ lines.push(...collectSubqueryPlans(
23457
+ stmt,
23458
+ capabilities,
23459
+ orderPlans,
23460
+ plainGroupByPlans,
23461
+ collector
23462
+ ));
23163
23463
  return lines;
23164
23464
  }
23165
- function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23465
+ function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
23166
23466
  const selects = [];
23167
23467
  const collect = (u) => {
23168
23468
  if (u.type === "SELECT") {
@@ -23176,15 +23476,35 @@ function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23176
23476
  const lines = [];
23177
23477
  selects.forEach((sel, i) => {
23178
23478
  if (i > 0) lines.push("");
23179
- lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans, plainGroupByPlans, true));
23479
+ lines.push(...buildSelectPlan(
23480
+ sel,
23481
+ `[union:${i + 1}]`,
23482
+ capabilities,
23483
+ orderPlans,
23484
+ plainGroupByPlans,
23485
+ true,
23486
+ true,
23487
+ collector,
23488
+ "union"
23489
+ ));
23180
23490
  });
23181
23491
  return lines;
23182
23492
  }
23183
- function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23493
+ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
23184
23494
  const lines = [];
23185
23495
  for (const cte of stmt.ctes) {
23186
23496
  if (cte.query.type === "SELECT") {
23187
- lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans, plainGroupByPlans, false));
23497
+ lines.push(...buildSelectPlan(
23498
+ cte.query,
23499
+ `[cte: ${cte.name}]`,
23500
+ capabilities,
23501
+ orderPlans,
23502
+ plainGroupByPlans,
23503
+ false,
23504
+ true,
23505
+ collector,
23506
+ "cte"
23507
+ ));
23188
23508
  lines.push("");
23189
23509
  }
23190
23510
  }
@@ -23197,13 +23517,25 @@ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23197
23517
  100,
23198
23518
  DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
23199
23519
  1e4,
23200
- plainGroupByPlans
23520
+ plainGroupByPlans,
23521
+ false,
23522
+ collector
23201
23523
  ));
23202
23524
  }
23203
23525
  if (canInlineSingleCte(stmt)) {
23204
23526
  lines.push("");
23205
23527
  const inlined = buildInlinedQuery(stmt);
23206
- lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans, plainGroupByPlans, false));
23528
+ lines.push(...buildSelectPlan(
23529
+ inlined,
23530
+ "[effective: inlined CTE]",
23531
+ capabilities,
23532
+ orderPlans,
23533
+ plainGroupByPlans,
23534
+ false,
23535
+ true,
23536
+ collector,
23537
+ "cte"
23538
+ ));
23207
23539
  }
23208
23540
  return lines;
23209
23541
  }
@@ -23236,7 +23568,7 @@ function collectFullScanReasons(stmt) {
23236
23568
  r.push("ORDER BY \u306B\u5F0F");
23237
23569
  return r;
23238
23570
  }
23239
- function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans) {
23571
+ function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
23240
23572
  const lines = [];
23241
23573
  let idx = 1;
23242
23574
  const visitWhere = (w) => {
@@ -23245,16 +23577,16 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans)
23245
23577
  case "BINARY":
23246
23578
  if (w.right.type === "SCALAR_SUBQUERY") {
23247
23579
  lines.push("");
23248
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23580
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23249
23581
  }
23250
23582
  if (w.right.type === "SUBQUERY_IN_LIST") {
23251
23583
  lines.push("");
23252
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23584
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23253
23585
  }
23254
23586
  break;
23255
23587
  case "EXISTS":
23256
23588
  lines.push("");
23257
- lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23589
+ lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23258
23590
  break;
23259
23591
  case "LOGICAL":
23260
23592
  visitWhere(w.left);
@@ -23274,7 +23606,7 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans)
23274
23606
  for (const col of stmt.columns) {
23275
23607
  if (col.type === "SCALAR_SUBQUERY_COL") {
23276
23608
  lines.push("");
23277
- lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23609
+ lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23278
23610
  }
23279
23611
  }
23280
23612
  if (stmt.having) visitWhere(stmt.having);
@@ -23300,7 +23632,15 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroup
23300
23632
  lines.push(` fields: ${stmt.fields.join(", ")}`);
23301
23633
  lines.push(` api: POST /k/v1/records.json\uFF08\u4EF6\u6570\u306F SELECT \u7D50\u679C\u306B\u4F9D\u5B58\u3001100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`);
23302
23634
  lines.push("");
23303
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans, false));
23635
+ lines.push(...buildSelectPlan(
23636
+ stmt.select,
23637
+ "[source SELECT]",
23638
+ capabilities,
23639
+ orderPlans,
23640
+ plainGroupByPlans,
23641
+ false,
23642
+ false
23643
+ ));
23304
23644
  return lines;
23305
23645
  }
23306
23646
  function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
@@ -23347,7 +23687,15 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100
23347
23687
  for (const a of stmt.assignments) {
23348
23688
  if (a.value.type === "SCALAR_SUBQUERY") {
23349
23689
  lines.push("");
23350
- lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans, void 0, false));
23690
+ lines.push(...buildSelectPlan(
23691
+ a.value.query,
23692
+ `[subquery: ${a.field}]`,
23693
+ capabilities,
23694
+ orderPlans,
23695
+ void 0,
23696
+ false,
23697
+ false
23698
+ ));
23351
23699
  }
23352
23700
  }
23353
23701
  return lines;
@@ -23464,7 +23812,15 @@ function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroup
23464
23812
  ` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json\uFF08100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`,
23465
23813
  ``
23466
23814
  ];
23467
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans, false));
23815
+ lines.push(...buildSelectPlan(
23816
+ stmt.select,
23817
+ "[source SELECT]",
23818
+ capabilities,
23819
+ orderPlans,
23820
+ plainGroupByPlans,
23821
+ false,
23822
+ false
23823
+ ));
23468
23824
  return lines;
23469
23825
  }
23470
23826
  function buildReorderPlan(stmt, label) {
@@ -25374,13 +25730,17 @@ function restoreSqlDiagnosticValue(value, bindings, options = {}) {
25374
25730
  return `${dmlTarget[1]}${target}`;
25375
25731
  }
25376
25732
  }
25377
- let restored = value;
25378
- for (const binding of bindings.values()) {
25379
- const internal = `APP${binding.mappedAppId}`;
25733
+ if (bindings.size === 0) return value;
25734
+ const mappedIds = [...bindings.keys()].sort((a, b) => String(b).length - String(a).length);
25735
+ const internalApp = new RegExp(
25736
+ `APP(${mappedIds.join("|")})(?!\\d)(\\s+AS\\s+[^\\s()]+)?(?:\\s+\\(\\1\\))?`,
25737
+ "g"
25738
+ );
25739
+ return value.replace(internalApp, (_match, mappedIdText, alias) => {
25740
+ const binding = bindings.get(Number(mappedIdText));
25380
25741
  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;
25742
+ return `${display}${alias ?? ""}`;
25743
+ });
25384
25744
  }
25385
25745
  if (Array.isArray(value)) {
25386
25746
  return value.map((item) => restoreSqlDiagnosticValue(item, bindings, options));