@rex0220/kintone-sql-tools 3.39.0 → 3.41.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
@@ -22768,6 +22768,10 @@ function serverFunctionClientEvaluationLabel(leaves) {
22768
22768
  (leaf) => leaf.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(leaf.right.name)
22769
22769
  ) ? "relative date client evaluations" : "kintone function client evaluations";
22770
22770
  }
22771
+ var EXPLAIN_FETCH_PLAN = /* @__PURE__ */ Symbol("ksql.explainFetchPlan");
22772
+ function setExplainFetchPlan(result, plan) {
22773
+ result[EXPLAIN_FETCH_PLAN] = plan;
22774
+ }
22771
22775
  async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
22772
22776
  const invocationCacheContext = createInvocationCacheContext(cacheContext);
22773
22777
  try {
@@ -22777,6 +22781,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22777
22781
  const relativeDateVariables = prepareRelativeDateVariables(statements, normalizedInjectedVariables);
22778
22782
  const variables = /* @__PURE__ */ new Map();
22779
22783
  const plans = [];
22784
+ const fetchStatements = [];
22780
22785
  for (let i = 0; i < statements.length; i++) {
22781
22786
  const stmt = statements[i];
22782
22787
  const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveBatchVariableReferences(stmt.expr, variables) } : stmt : resolveBatchVariableReferences(stmt, variables);
@@ -22789,6 +22794,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22789
22794
  maxRecords,
22790
22795
  relativeDatePlan
22791
22796
  );
22797
+ const fetchCollector = { sources: [] };
22792
22798
  const statementPlan = relativeDatePlan.hasServerOnlyWhereFunction && !relativeDatePlan.allowed ? relativeDateExplainLines(relativeDatePlan) : [
22793
22799
  ...relativeDateExplainLines(relativeDatePlan),
22794
22800
  ...addCursorConcurrency(buildBatchStatementPlan(
@@ -22797,7 +22803,8 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22797
22803
  whereAnalysis.capabilities,
22798
22804
  whereAnalysis.orderPlans,
22799
22805
  dmlMaxRows,
22800
- dmlMaxSubtableRows
22806
+ dmlMaxSubtableRows,
22807
+ fetchCollector
22801
22808
  ), cursorMaxActive)
22802
22809
  ];
22803
22810
  const metadataPlan = explainMetadataLines(whereAnalysis);
@@ -22806,22 +22813,36 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
22806
22813
  type: analysis.statements[i].statementType,
22807
22814
  plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
22808
22815
  });
22816
+ fetchStatements.push({
22817
+ index: i,
22818
+ fetch: worstExplainFetch(fetchCollector.sources),
22819
+ sources: fetchCollector.sources
22820
+ });
22809
22821
  if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
22810
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 });
22811
22823
  }
22812
22824
  }
22813
- return { statementCount: statements.length, statements: plans };
22825
+ const result = { statementCount: statements.length, statements: plans };
22826
+ setExplainFetchPlan(result, { statements: fetchStatements });
22827
+ return result;
22814
22828
  } finally {
22815
22829
  releaseMetadataCacheScope(invocationCacheContext);
22816
22830
  }
22817
22831
  }
22818
- 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: [] }) {
22819
22833
  if (stmt.type === "CREATE_TEMP_TABLE") {
22820
22834
  return [
22821
22835
  `CREATE TEMP TABLE ${stmt.name}`,
22822
22836
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
22823
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`,
22824
- ...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}`)
22825
22846
  ];
22826
22847
  }
22827
22848
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -22837,7 +22858,13 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22837
22858
  `SET @${stmt.name} = (SELECT ...)`,
22838
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",
22839
22860
  " subquery:",
22840
- ...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}`)
22841
22868
  ];
22842
22869
  }
22843
22870
  return [
@@ -22859,7 +22886,15 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22859
22886
  }
22860
22887
  if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
22861
22888
  if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
22862
- 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
+ }
22863
22898
  if (stmt.type === "ASSERT") {
22864
22899
  const lines = [
22865
22900
  `ASSERT ${stmt.text}`,
@@ -22871,14 +22906,20 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
22871
22906
  subqueries.forEach((sq, i) => {
22872
22907
  lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
22873
22908
  const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
22874
- 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}`));
22875
22916
  });
22876
22917
  return lines;
22877
22918
  }
22878
22919
  if (stmt.type === "UPDATE" && (stmt.applyBlocks?.length ?? 0) > 0) {
22879
22920
  return buildExplainPlan(stmt, void 0, capabilities, orderPlans, dmlMaxRows, dmlMaxSubtableRows);
22880
22921
  }
22881
- return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
22922
+ return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans, collector);
22882
22923
  }
22883
22924
  function hasTempTableRef(node) {
22884
22925
  if (Array.isArray(node)) return node.some(hasTempTableRef);
@@ -22890,9 +22931,21 @@ function hasTempTableRef(node) {
22890
22931
  }
22891
22932
  return false;
22892
22933
  }
22893
- function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
22934
+ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector = { sources: [] }, sourceRole = "main") {
22894
22935
  if (info.tempTablesReferenced.length === 0) {
22895
- 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
+ );
22896
22949
  }
22897
22950
  const lines = [];
22898
22951
  if (query.type === "INSERT_SELECT") {
@@ -22926,8 +22979,9 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
22926
22979
  maxRecords,
22927
22980
  sharedPlan
22928
22981
  );
22982
+ const fetchCollector = { sources: [] };
22929
22983
  const relativeLines = relativeDateExplainLines(sharedPlan);
22930
- const lines = sharedPlan.hasServerOnlyWhereFunction && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
22984
+ const planLines = sharedPlan.hasServerOnlyWhereFunction && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
22931
22985
  ...explainMetadataLines(analysis),
22932
22986
  ...relativeLines,
22933
22987
  ...addCursorConcurrency(
@@ -22939,17 +22993,28 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
22939
22993
  dmlMaxRows,
22940
22994
  dmlMaxSubtableRows,
22941
22995
  maxRecords,
22942
- analysis.plainGroupByPlans
22996
+ analysis.plainGroupByPlans,
22997
+ true,
22998
+ fetchCollector
22943
22999
  ),
22944
23000
  cursorMaxActive
22945
23001
  )
22946
23002
  ];
22947
- return {
23003
+ const lines = addFetchSummary(planLines, fetchCollector.sources);
23004
+ const result = {
22948
23005
  type: "SELECT",
22949
23006
  columns: ["plan"],
22950
23007
  rows: lines.map((line) => ({ plan: line })),
22951
23008
  rowCount: lines.length
22952
23009
  };
23010
+ setExplainFetchPlan(result, {
23011
+ statements: [{
23012
+ index: 0,
23013
+ fetch: worstExplainFetch(fetchCollector.sources),
23014
+ sources: fetchCollector.sources
23015
+ }]
23016
+ });
23017
+ return result;
22953
23018
  }
22954
23019
  function addCursorConcurrency(lines, cursorMaxActive) {
22955
23020
  const result = [];
@@ -22962,9 +23027,28 @@ function addCursorConcurrency(lines, cursorMaxActive) {
22962
23027
  }
22963
23028
  return result;
22964
23029
  }
22965
- function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4, plainGroupByPlans) {
22966
- if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans, plainGroupByPlans);
22967
- 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
+ }
22968
23052
  if (query.type === "INSERT") return buildInsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
22969
23053
  if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
22970
23054
  if (query.type === "UPSERT") return buildUpsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
@@ -23054,7 +23138,63 @@ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 1
23054
23138
  ` duplicateKey: preflight before lookup/write (requires load)`
23055
23139
  ];
23056
23140
  }
23057
- 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
+ count_only: 1,
23157
+ exact: 2,
23158
+ prefiltered: 3,
23159
+ all: 4
23160
+ };
23161
+ function worstExplainFetch(sources) {
23162
+ return sources.reduce(
23163
+ (worst, source) => EXPLAIN_FETCH_VALUE_RANK[worst] >= EXPLAIN_FETCH_VALUE_RANK[source.fetch] ? worst : source.fetch,
23164
+ "none"
23165
+ );
23166
+ }
23167
+ function addFetchSummary(lines, sources) {
23168
+ const detailLines = lines.filter((line) => !line.startsWith("fetch summary:"));
23169
+ if (sources.length === 0) return detailLines;
23170
+ return [`fetch summary: ${worstExplainFetch(sources).toUpperCase()}`, ...detailLines];
23171
+ }
23172
+ function pushedQueryLimit(query) {
23173
+ const match = /(?:^|\s)limit\s+(\d+)(?:\s|$)/i.exec(query);
23174
+ return match ? Number(match[1]) : null;
23175
+ }
23176
+ function createExplainFetchSource(collector, app, alias, role, scope, query, pending = false) {
23177
+ const source = {
23178
+ app,
23179
+ alias,
23180
+ role,
23181
+ fetch: scope.toLowerCase(),
23182
+ pending,
23183
+ kintoneQuery: query === "(\u5168\u4EF6\u53D6\u5F97)" || query === "(\u306A\u3057)" || query === "" ? null : query,
23184
+ limit: pushedQueryLimit(query)
23185
+ };
23186
+ collector.sources.push(source);
23187
+ return source;
23188
+ }
23189
+ function formatFetchScope(source) {
23190
+ return [
23191
+ source.fetch.toUpperCase(),
23192
+ ...source.limit === null ? [] : [`(limit ${source.limit})`],
23193
+ ...source.pending ? ["(\u672A\u78BA\u5B9A)"] : []
23194
+ ].join(" ");
23195
+ }
23196
+ function renderFetchScope(source) {
23197
+ return ` fetch: ${formatFetchScope(source)}`;
23058
23198
  }
23059
23199
  function buildValidatePlan(stmt, label) {
23060
23200
  const info = validateExplainInfo.get(stmt);
@@ -23081,7 +23221,7 @@ function buildValidatePlan(stmt, label) {
23081
23221
  lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
23082
23222
  return lines;
23083
23223
  }
23084
- function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans, allowTotalCountPlan = true) {
23224
+ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans, allowTotalCountPlan = true, emitFetch = true, collector = { sources: [] }, sourceRole = "main") {
23085
23225
  const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
23086
23226
  const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
23087
23227
  const plainGroupByPlan = plainGroupByPlans?.get(stmt) ?? (plainGroupByPlans ? [...plainGroupByPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
@@ -23139,6 +23279,17 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23139
23279
  lines.push(
23140
23280
  ` kintone query: ${baseQuery}${baseQuery ? " " : ""}limit 1`
23141
23281
  );
23282
+ if (emitFetch) {
23283
+ const source = createExplainFetchSource(
23284
+ collector,
23285
+ stmt.from.appId,
23286
+ stmt.from.alias,
23287
+ sourceRole,
23288
+ "COUNT_ONLY",
23289
+ `${baseQuery}${baseQuery ? " " : ""}limit 1`
23290
+ );
23291
+ lines.push(renderFetchScope(source));
23292
+ }
23142
23293
  lines.push(" fields: $id");
23143
23294
  lines.push(" fetch API: GET records.json (totalCount=true)");
23144
23295
  lines.push(" REST execution: single GET");
@@ -23185,6 +23336,17 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23185
23336
  lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
23186
23337
  const displayedQuery = orderPlan?.kind === "KORDER_CURSOR" ? buildKorderCursorQuery(stmt) : params.query;
23187
23338
  lines.push(` kintone query: ${displayedQuery || "(\u306A\u3057)"}`);
23339
+ if (emitFetch && stmt.from.cteName === null) {
23340
+ const fetchScope = displayedQuery ? "EXACT" : "ALL";
23341
+ lines.push(renderFetchScope(createExplainFetchSource(
23342
+ collector,
23343
+ stmt.from.appId,
23344
+ stmt.from.alias,
23345
+ sourceRole,
23346
+ fetchScope,
23347
+ displayedQuery
23348
+ )));
23349
+ }
23188
23350
  lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
23189
23351
  } else {
23190
23352
  const runtimeJoinPlan = explainJoinPushdownPlans.get(stmt);
@@ -23234,6 +23396,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23234
23396
  const mainFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
23235
23397
  (consumption) => consumption.targetAlias === stmt.from.alias
23236
23398
  );
23399
+ if (emitFetch && stmt.from.cteName === null) {
23400
+ const mainPending = !runtimeJoinPlan && mainCandidate !== null;
23401
+ const mainFetchScope = mainQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : mainPending ? "PREFILTERED" : mainJoinItem?.relation === "exact" || mainFunctionConsumption || exactOriginalWhere !== "" ? "EXACT" : "PREFILTERED";
23402
+ lines.push(renderFetchScope(createExplainFetchSource(
23403
+ collector,
23404
+ stmt.from.appId,
23405
+ stmt.from.alias,
23406
+ sourceRole,
23407
+ mainFetchScope,
23408
+ mainQ,
23409
+ mainPending
23410
+ )));
23411
+ }
23237
23412
  if (mainJoinItem || mainFunctionConsumption) {
23238
23413
  lines.push(` pushdown applied: ${mainBoundQuery}`);
23239
23414
  lines.push(` relation: ${mainJoinItem?.relation ?? "exact"}`);
@@ -23257,6 +23432,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23257
23432
  const joinFunctionConsumption = runtimeJoinPlan?.joinPlan.serverFunctionConsumptions.find(
23258
23433
  (consumption) => consumption.targetAlias === join2.table.alias
23259
23434
  );
23435
+ if (emitFetch && join2.table.cteName === null) {
23436
+ const joinPending = !runtimeJoinPlan && joinCandidate !== null;
23437
+ const joinFetchScope = joinQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : joinPending ? "PREFILTERED" : joinPlanItem?.relation === "exact" || joinFunctionConsumption ? "EXACT" : "PREFILTERED";
23438
+ lines.push(renderFetchScope(createExplainFetchSource(
23439
+ collector,
23440
+ join2.table.appId,
23441
+ join2.table.alias,
23442
+ "join",
23443
+ joinFetchScope,
23444
+ joinQ,
23445
+ joinPending
23446
+ )));
23447
+ }
23260
23448
  if (joinPlanItem || joinFunctionConsumption) {
23261
23449
  lines.push(` pushdown applied: ${joinBoundQuery}`);
23262
23450
  lines.push(` relation: ${joinPlanItem?.relation ?? "exact"}`);
@@ -23266,10 +23454,16 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23266
23454
  lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
23267
23455
  }
23268
23456
  }
23269
- lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans));
23457
+ lines.push(...collectSubqueryPlans(
23458
+ stmt,
23459
+ capabilities,
23460
+ orderPlans,
23461
+ plainGroupByPlans,
23462
+ collector
23463
+ ));
23270
23464
  return lines;
23271
23465
  }
23272
- function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23466
+ function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
23273
23467
  const selects = [];
23274
23468
  const collect = (u) => {
23275
23469
  if (u.type === "SELECT") {
@@ -23283,15 +23477,35 @@ function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23283
23477
  const lines = [];
23284
23478
  selects.forEach((sel, i) => {
23285
23479
  if (i > 0) lines.push("");
23286
- lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans, plainGroupByPlans, true));
23480
+ lines.push(...buildSelectPlan(
23481
+ sel,
23482
+ `[union:${i + 1}]`,
23483
+ capabilities,
23484
+ orderPlans,
23485
+ plainGroupByPlans,
23486
+ true,
23487
+ true,
23488
+ collector,
23489
+ "union"
23490
+ ));
23287
23491
  });
23288
23492
  return lines;
23289
23493
  }
23290
- function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23494
+ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
23291
23495
  const lines = [];
23292
23496
  for (const cte of stmt.ctes) {
23293
23497
  if (cte.query.type === "SELECT") {
23294
- lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans, plainGroupByPlans, false));
23498
+ lines.push(...buildSelectPlan(
23499
+ cte.query,
23500
+ `[cte: ${cte.name}]`,
23501
+ capabilities,
23502
+ orderPlans,
23503
+ plainGroupByPlans,
23504
+ false,
23505
+ true,
23506
+ collector,
23507
+ "cte"
23508
+ ));
23295
23509
  lines.push("");
23296
23510
  }
23297
23511
  }
@@ -23304,13 +23518,25 @@ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
23304
23518
  100,
23305
23519
  DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
23306
23520
  1e4,
23307
- plainGroupByPlans
23521
+ plainGroupByPlans,
23522
+ false,
23523
+ collector
23308
23524
  ));
23309
23525
  }
23310
23526
  if (canInlineSingleCte(stmt)) {
23311
23527
  lines.push("");
23312
23528
  const inlined = buildInlinedQuery(stmt);
23313
- lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans, plainGroupByPlans, false));
23529
+ lines.push(...buildSelectPlan(
23530
+ inlined,
23531
+ "[effective: inlined CTE]",
23532
+ capabilities,
23533
+ orderPlans,
23534
+ plainGroupByPlans,
23535
+ false,
23536
+ true,
23537
+ collector,
23538
+ "cte"
23539
+ ));
23314
23540
  }
23315
23541
  return lines;
23316
23542
  }
@@ -23343,7 +23569,7 @@ function collectFullScanReasons(stmt) {
23343
23569
  r.push("ORDER BY \u306B\u5F0F");
23344
23570
  return r;
23345
23571
  }
23346
- function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans) {
23572
+ function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
23347
23573
  const lines = [];
23348
23574
  let idx = 1;
23349
23575
  const visitWhere = (w) => {
@@ -23352,16 +23578,16 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans)
23352
23578
  case "BINARY":
23353
23579
  if (w.right.type === "SCALAR_SUBQUERY") {
23354
23580
  lines.push("");
23355
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23581
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23356
23582
  }
23357
23583
  if (w.right.type === "SUBQUERY_IN_LIST") {
23358
23584
  lines.push("");
23359
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23585
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23360
23586
  }
23361
23587
  break;
23362
23588
  case "EXISTS":
23363
23589
  lines.push("");
23364
- lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23590
+ lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23365
23591
  break;
23366
23592
  case "LOGICAL":
23367
23593
  visitWhere(w.left);
@@ -23381,7 +23607,7 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans)
23381
23607
  for (const col of stmt.columns) {
23382
23608
  if (col.type === "SCALAR_SUBQUERY_COL") {
23383
23609
  lines.push("");
23384
- lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false));
23610
+ lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans, false, true, collector, "subquery"));
23385
23611
  }
23386
23612
  }
23387
23613
  if (stmt.having) visitWhere(stmt.having);
@@ -23407,7 +23633,15 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroup
23407
23633
  lines.push(` fields: ${stmt.fields.join(", ")}`);
23408
23634
  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`);
23409
23635
  lines.push("");
23410
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans, false));
23636
+ lines.push(...buildSelectPlan(
23637
+ stmt.select,
23638
+ "[source SELECT]",
23639
+ capabilities,
23640
+ orderPlans,
23641
+ plainGroupByPlans,
23642
+ false,
23643
+ false
23644
+ ));
23411
23645
  return lines;
23412
23646
  }
23413
23647
  function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
@@ -23454,7 +23688,15 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100
23454
23688
  for (const a of stmt.assignments) {
23455
23689
  if (a.value.type === "SCALAR_SUBQUERY") {
23456
23690
  lines.push("");
23457
- lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans, void 0, false));
23691
+ lines.push(...buildSelectPlan(
23692
+ a.value.query,
23693
+ `[subquery: ${a.field}]`,
23694
+ capabilities,
23695
+ orderPlans,
23696
+ void 0,
23697
+ false,
23698
+ false
23699
+ ));
23458
23700
  }
23459
23701
  }
23460
23702
  return lines;
@@ -23571,7 +23813,15 @@ function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroup
23571
23813
  ` 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`,
23572
23814
  ``
23573
23815
  ];
23574
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans, false));
23816
+ lines.push(...buildSelectPlan(
23817
+ stmt.select,
23818
+ "[source SELECT]",
23819
+ capabilities,
23820
+ orderPlans,
23821
+ plainGroupByPlans,
23822
+ false,
23823
+ false
23824
+ ));
23575
23825
  return lines;
23576
23826
  }
23577
23827
  function buildReorderPlan(stmt, label) {