@rex0220/kintone-sql-tools 3.63.0 → 3.65.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
@@ -10008,6 +10008,10 @@ function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
10008
10008
  if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
10009
10009
  if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
10010
10010
  if (field.type === "GROUPING_FIELD") return evalGroupingRef(field.ref, row);
10011
+ if (field.aggregateRef) {
10012
+ const ref = field.aggregateRef;
10013
+ return resolveFieldRef(row, aggregateSyntheticName(ref.func, ref.distinct, ref.arg));
10014
+ }
10011
10015
  const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
10012
10016
  return resolveFieldRef(row, key);
10013
10017
  }
@@ -14129,6 +14133,7 @@ async function deriveEmptyWildcardColumns(fields, subtableCode, loadProcessStatu
14129
14133
  // src/engine/process.ts
14130
14134
  var materializedSelectValues = /* @__PURE__ */ new WeakMap();
14131
14135
  var sourceRows = /* @__PURE__ */ new WeakMap();
14136
+ var UNRESOLVED_AGGREGATE_COMPARISON_WARNING = "\u6BD4\u8F03\u6761\u4EF6\u3067\u53C2\u7167\u3057\u305F\u96C6\u8A08\u5024\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002SELECT \u30EA\u30B9\u30C8\u306B\u540C\u3058\u96C6\u8A08\u5F0F\u3092\u542B\u3081\u3066\u304F\u3060\u3055\u3044\u3002";
14132
14137
  function asProcessingRow(source) {
14133
14138
  if (sourceRows.has(source)) return source;
14134
14139
  let row;
@@ -14177,6 +14182,31 @@ function getMaterializedLookupValue(row, key) {
14177
14182
  function getLegacyMaterializedValue(row, key) {
14178
14183
  return materializedSelectValues.has(row) ? void 0 : row[key];
14179
14184
  }
14185
+ function warnOnUnresolvedAggregateComparisons(node, rows, warnings) {
14186
+ if (!warnings || rows.length === 0) return;
14187
+ const keys = /* @__PURE__ */ new Set();
14188
+ const visit = (value) => {
14189
+ if (value === null || typeof value !== "object") return;
14190
+ if (Array.isArray(value)) {
14191
+ value.forEach(visit);
14192
+ return;
14193
+ }
14194
+ const record = value;
14195
+ if (record["type"] === "FIELD" && record["aggregateRef"] !== void 0) {
14196
+ const ref = record["aggregateRef"];
14197
+ keys.add(aggregateSyntheticName(ref.func, ref.distinct, ref.arg));
14198
+ return;
14199
+ }
14200
+ if (record["type"] === "SELECT" || record["type"] === "SCALAR_SUBQUERY") return;
14201
+ Object.values(record).forEach(visit);
14202
+ };
14203
+ visit(node);
14204
+ if ([...keys].some((key) => rows.some(
14205
+ (row) => getMaterializedLookupValue(row, key) === void 0 && row[key] === void 0
14206
+ ))) {
14207
+ warnings.add(UNRESOLVED_AGGREGATE_COMPARISON_WARNING);
14208
+ }
14209
+ }
14180
14210
  function havingEvaluationRow(row) {
14181
14211
  const lookups = materializedSelectValues.get(row)?.byLookupKey;
14182
14212
  if (!lookups || lookups.size === 0) return row;
@@ -15481,7 +15511,8 @@ function runFullScan(input) {
15481
15511
  tableColumns,
15482
15512
  hiddenQualifiedAliases,
15483
15513
  resolvedGroupingSpec,
15484
- plainGroupByPlan
15514
+ plainGroupByPlan,
15515
+ warnings
15485
15516
  } = input;
15486
15517
  const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns, aggregateSortKindResolver);
15487
15518
  for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
@@ -15537,7 +15568,9 @@ function runFullScan(input) {
15537
15568
  }
15538
15569
  );
15539
15570
  }
15571
+ warnOnUnresolvedAggregateComparisons(stmt.columns, rows, warnings);
15540
15572
  const resolveHavingSemantics = (field) => field.aggregateRef ? aggregateResultSemantics(field.aggregateRef, aggregateSortKindResolver) : havingFieldSemanticsResolver?.(field);
15573
+ warnOnUnresolvedAggregateComparisons(stmt.having, rows, warnings);
15541
15574
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, resolveHavingSemantics);
15542
15575
  rows = applyWindow(
15543
15576
  rows,
@@ -18838,6 +18871,7 @@ async function assertDmlWhereCapability(stmt, client, cacheContext) {
18838
18871
  }
18839
18872
  async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false, forLibraryCapture = false, windowWarningContext = "DIRECT") {
18840
18873
  let result;
18874
+ const subqueryWarnings = /* @__PURE__ */ new Set();
18841
18875
  await validateSelectGroupingPlanning(stmt, client, cacheContext, cteCache);
18842
18876
  if (isNoFromSelect(stmt)) {
18843
18877
  result = executeNoFromSelect(stmt);
@@ -18867,7 +18901,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
18867
18901
  cacheContext,
18868
18902
  cteCache
18869
18903
  );
18870
- await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
18904
+ await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, subqueryWarnings, cteCache);
18871
18905
  const whereCapability = rememberSelectWhereCapability(
18872
18906
  stmt,
18873
18907
  classifyWhereCapability(stmt.where, fieldSemanticsResolver)
@@ -18958,7 +18992,11 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
18958
18992
  await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache, forLibraryCapture)
18959
18993
  );
18960
18994
  }
18961
- return mergeSelectWarnings(result, [...defaultRangeWarnings, ...subtableFieldWarnings]);
18995
+ return mergeSelectWarnings(result, [
18996
+ ...subqueryWarnings,
18997
+ ...defaultRangeWarnings,
18998
+ ...subtableFieldWarnings
18999
+ ]);
18962
19000
  }
18963
19001
  function subtableGroupingAdvice(fieldName, owners) {
18964
19002
  if (owners.length === 1) {
@@ -20426,8 +20464,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
20426
20464
  const warnings = /* @__PURE__ */ new Set();
20427
20465
  const parallel = options.fetchParallel ?? 1;
20428
20466
  await Promise.all([
20429
- resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
20430
- resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
20467
+ resolveSubqueries(stmt.where, client, options, cacheContext, warnings, cteCache),
20468
+ resolveSubqueries(stmt.having, client, options, cacheContext, warnings, cteCache)
20431
20469
  ]);
20432
20470
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
20433
20471
  loadTypedPushdownMeta(stmt, client, cacheContext),
@@ -20461,7 +20499,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
20461
20499
  const mainBoundQuery = boundServerFunctionPlan?.queriesByAlias.get(
20462
20500
  stmt.from.alias
20463
20501
  ) ?? "";
20464
- const scalarCache = await resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
20502
+ const scalarCache = await resolveScalarColumns(
20503
+ stmt.columns,
20504
+ client,
20505
+ options,
20506
+ cacheContext,
20507
+ warnings,
20508
+ cteCache
20509
+ );
20465
20510
  const constantFalse = isConstantFalseWhere(stmt.where);
20466
20511
  const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
20467
20512
  stmt,
@@ -20565,7 +20610,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
20565
20610
  appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
20566
20611
  ...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : boundServerFunctionPlan ? { residualWhere: boundServerFunctionPlan.joinPlan.residualWhere } : {},
20567
20612
  resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt),
20568
- plainGroupByPlan
20613
+ plainGroupByPlan,
20614
+ warnings
20569
20615
  });
20570
20616
  const columns = await restoreEmptyWildcardColumns(
20571
20617
  stmt,
@@ -20832,9 +20878,9 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
20832
20878
  const warnings = /* @__PURE__ */ new Set();
20833
20879
  const parallel = options.fetchParallel ?? 1;
20834
20880
  await Promise.all([
20835
- resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
20836
- resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
20837
- resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
20881
+ resolveSubqueries(stmt.where, client, options, cacheContext, warnings, cteCache),
20882
+ resolveSubqueries(stmt.having, client, options, cacheContext, warnings, cteCache),
20883
+ resolveSelectCaseSubqueries(stmt, client, options, cacheContext, warnings, cteCache)
20838
20884
  ]);
20839
20885
  const whereCapability = classifyWhereCapability(stmt.where, choiceAndWindowResolver);
20840
20886
  if (whereCapability.capability === "UNSUPPORTED") {
@@ -20874,6 +20920,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
20874
20920
  client,
20875
20921
  effectiveOptions,
20876
20922
  cacheContext,
20923
+ warnings,
20877
20924
  cteCache
20878
20925
  );
20879
20926
  const orderByMetaPromise = Promise.resolve(orderMeta);
@@ -24337,17 +24384,17 @@ function parseSql(sql, enableImport = false) {
24337
24384
  throw e;
24338
24385
  }
24339
24386
  }
24340
- async function resolveSubqueries(where, client, options, cacheContext, cteCache) {
24387
+ async function resolveSubqueries(where, client, options, cacheContext, warnings, cteCache) {
24341
24388
  const tasks = [];
24342
- collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache);
24389
+ collectSubqueryTasks(where, client, options, cacheContext, tasks, warnings, cteCache);
24343
24390
  await Promise.all(tasks);
24344
24391
  }
24345
- async function resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache) {
24392
+ async function resolveSelectCaseSubqueries(stmt, client, options, cacheContext, warnings, cteCache) {
24346
24393
  const tasks = [];
24347
24394
  for (const column of stmt.columns) {
24348
24395
  if (column.type !== "CASE_COL") continue;
24349
24396
  for (const branch of column.expr.branches) {
24350
- tasks.push(resolveSubqueries(branch.condition, client, options, cacheContext, cteCache));
24397
+ tasks.push(resolveSubqueries(branch.condition, client, options, cacheContext, warnings, cteCache));
24351
24398
  }
24352
24399
  }
24353
24400
  await Promise.all(tasks);
@@ -24358,19 +24405,21 @@ function runSubquery(query, client, options, cacheContext, cteCache) {
24358
24405
  }
24359
24406
  return executeSelect(query, client, options, cacheContext);
24360
24407
  }
24361
- function collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache) {
24408
+ function collectSubqueryTasks(where, client, options, cacheContext, tasks, warnings, cteCache) {
24362
24409
  if (where === null) return;
24363
24410
  switch (where.type) {
24364
24411
  case "BINARY": {
24365
24412
  const right = where.right;
24366
24413
  if (right.type === "SUBQUERY_IN_LIST") {
24367
24414
  tasks.push(runSubquery(right.query, client, options, cacheContext, cteCache).then((result) => {
24415
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24368
24416
  const col = right.column ?? (result.columns[0] ?? "");
24369
24417
  right.resolved = new Set(result.rows.map((r) => r[col] ?? ""));
24370
24418
  }));
24371
24419
  }
24372
24420
  if (right.type === "SCALAR_SUBQUERY") {
24373
24421
  tasks.push(runSubquery(right.query, client, options, cacheContext, cteCache).then((result) => {
24422
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24374
24423
  if (result.rowCount === 0) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u5024\u3092\u8FD4\u3057\u307E\u305B\u3093\u3067\u3057\u305F");
24375
24424
  if (result.rowCount > 1) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u8907\u6570\u884C\u3092\u8FD4\u3057\u307E\u3057\u305F\uFF081\u884C\u306E\u307F\u8A31\u53EF\uFF09");
24376
24425
  const col = result.columns[0] ?? "";
@@ -24380,16 +24429,17 @@ function collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCa
24380
24429
  break;
24381
24430
  }
24382
24431
  case "LOGICAL":
24383
- collectSubqueryTasks(where.left, client, options, cacheContext, tasks, cteCache);
24384
- collectSubqueryTasks(where.right, client, options, cacheContext, tasks, cteCache);
24432
+ collectSubqueryTasks(where.left, client, options, cacheContext, tasks, warnings, cteCache);
24433
+ collectSubqueryTasks(where.right, client, options, cacheContext, tasks, warnings, cteCache);
24385
24434
  break;
24386
24435
  case "NOT":
24387
24436
  case "GROUP":
24388
- collectSubqueryTasks(where.expr, client, options, cacheContext, tasks, cteCache);
24437
+ collectSubqueryTasks(where.expr, client, options, cacheContext, tasks, warnings, cteCache);
24389
24438
  break;
24390
24439
  case "EXISTS": {
24391
24440
  const node = where;
24392
24441
  tasks.push(runSubquery(node.query, client, options, cacheContext, cteCache).then((result) => {
24442
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24393
24443
  node.resolved = result.rowCount > 0;
24394
24444
  }));
24395
24445
  break;
@@ -24409,7 +24459,7 @@ async function resolveSetSubqueries(assignments, client, options, cacheContext)
24409
24459
  a.value = { type: "STRING", value: resolved };
24410
24460
  }
24411
24461
  }
24412
- async function resolveScalarColumns(columns, client, options, cacheContext, cteCache) {
24462
+ async function resolveScalarColumns(columns, client, options, cacheContext, warnings, cteCache) {
24413
24463
  const byQuery = /* @__PURE__ */ new Map();
24414
24464
  const pending = [];
24415
24465
  for (let i = 0; i < columns.length; i++) {
@@ -24419,6 +24469,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
24419
24469
  let promise = byQuery.get(key);
24420
24470
  if (!promise) {
24421
24471
  promise = runSubquery(col.query, client, options, cacheContext, cteCache).then((result) => {
24472
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24422
24473
  if (result.rowCount === 0) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u5024\u3092\u8FD4\u3057\u307E\u305B\u3093\u3067\u3057\u305F");
24423
24474
  if (result.rowCount > 1) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u8907\u6570\u884C\u3092\u8FD4\u3057\u307E\u3057\u305F\uFF081\u884C\u306E\u307F\u8A31\u53EF\uFF09");
24424
24475
  const firstCol = result.columns[0] ?? "";
@@ -24597,6 +24648,10 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
24597
24648
  for (const cte of withStatement.ctes) {
24598
24649
  await preflightExplainRelations(cte.query);
24599
24650
  if (cte.query.type === "GENERATE_SERIES") {
24651
+ if (cte.query.args.some((arg) => arg.type === "VARIABLE")) {
24652
+ explainRelations.set(cte.name, { rows: [], columns: [cte.query.columnAlias] });
24653
+ continue;
24654
+ }
24600
24655
  const generated = executeGenerateSeries(cte.query);
24601
24656
  explainRelations.set(cte.name, {
24602
24657
  rows: generated.rows,
@@ -24637,13 +24692,19 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
24637
24692
  cacheContext,
24638
24693
  explainRelations
24639
24694
  );
24640
- const plainPlan = await buildRuntimePlainGroupByPlan(
24641
- select,
24642
- tracedClient,
24643
- cacheContext,
24644
- explainRelations
24695
+ const sources = [select.from, ...select.joins.map((join2) => join2.table)];
24696
+ const hasUnavailableMaterializedSource = sources.some(
24697
+ (source) => source.cteName !== null && !explainRelations.has(source.cteName)
24645
24698
  );
24646
- if (plainPlan) plainGroupByPlans.set(select, plainPlan);
24699
+ if (!hasUnavailableMaterializedSource) {
24700
+ const plainPlan = await buildRuntimePlainGroupByPlan(
24701
+ select,
24702
+ tracedClient,
24703
+ cacheContext,
24704
+ explainRelations
24705
+ );
24706
+ if (plainPlan) plainGroupByPlans.set(select, plainPlan);
24707
+ }
24647
24708
  return;
24648
24709
  }
24649
24710
  for (const child of Object.values(typed)) await preflightExplainRelations(child);
@@ -25172,6 +25233,106 @@ function serverFunctionClientEvaluationLabel(leaves) {
25172
25233
  (leaf) => leaf.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(leaf.right.name)
25173
25234
  ) ? "relative date client evaluations" : "kintone function client evaluations";
25174
25235
  }
25236
+ var explainSeriesBindings = /* @__PURE__ */ new WeakMap();
25237
+ function resolveExplainGenerateSeriesDefaults(node, literalDefaults) {
25238
+ if (Array.isArray(node)) {
25239
+ return node.map((value) => resolveExplainGenerateSeriesDefaults(value, literalDefaults));
25240
+ }
25241
+ if (node === null || typeof node !== "object") return node;
25242
+ const object = node;
25243
+ if (object["type"] === "GENERATE_SERIES") {
25244
+ const variableNames = /* @__PURE__ */ new Map();
25245
+ const defaultBoundIndexes = /* @__PURE__ */ new Set();
25246
+ const args = object["args"].map((arg, index) => {
25247
+ if (arg.type !== "STRING" || arg.fromVariable !== true || !arg.value.startsWith("@")) return arg;
25248
+ const name = arg.value.slice(1);
25249
+ variableNames.set(index, name);
25250
+ const defaultValue = literalDefaults.get(name);
25251
+ if (defaultValue === void 0) return { type: "VARIABLE", name };
25252
+ defaultBoundIndexes.add(index);
25253
+ return { type: "STRING", value: defaultValue, fromVariable: true };
25254
+ });
25255
+ const resolved = { ...object, args };
25256
+ explainSeriesBindings.set(resolved, { variableNames, defaultBoundIndexes });
25257
+ return resolved;
25258
+ }
25259
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [
25260
+ key,
25261
+ resolveExplainGenerateSeriesDefaults(value, literalDefaults)
25262
+ ]));
25263
+ }
25264
+ function inferStaticExplainSchema(node, relations) {
25265
+ if (node.type === "SHOW_APPS") return { status: "STATIC", columns: [...SHOW_APPS_COLUMNS] };
25266
+ if (node.type === "DESCRIBE") return { status: "STATIC", columns: [...DESCRIBE_COLUMNS] };
25267
+ if (node.type === "GENERATE_SERIES") {
25268
+ return { status: "STATIC", columns: [node.columnAlias] };
25269
+ }
25270
+ if (node.type === "WITH") {
25271
+ const local = new Map(relations);
25272
+ for (const cte of node.ctes) {
25273
+ const schema = inferStaticExplainSchema(cte.query, local);
25274
+ local.set(cte.name, schema);
25275
+ }
25276
+ return inferStaticExplainSchema(node.query, local);
25277
+ }
25278
+ if (node.type === "UNION") {
25279
+ const left = inferStaticExplainSchema(node.left, relations);
25280
+ const right = inferStaticExplainSchema(node.right, relations);
25281
+ return left.status === "STATIC" && right.status === "STATIC" ? { status: "STATIC", columns: left.columns } : { status: "DEFERRED" };
25282
+ }
25283
+ const sources = [node.from, ...node.joins.map((join2) => join2.table)];
25284
+ const relationSources = sources.filter((source) => source.cteName !== null && source.cteName !== NO_FROM_CTE_NAME);
25285
+ if (relationSources.some((source) => relations.get(source.cteName)?.status !== "STATIC")) {
25286
+ return { status: "DEFERRED" };
25287
+ }
25288
+ const hasWildcard = node.columns.some(
25289
+ (column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD"
25290
+ );
25291
+ if (hasWildcard) {
25292
+ if (sources.length !== 1 || sources[0].cteName === null) return { status: "DEFERRED" };
25293
+ }
25294
+ const sourceColumns2 = relationSources.flatMap((source) => {
25295
+ const schema = relations.get(source.cteName);
25296
+ return schema?.status === "STATIC" ? [...schema.columns] : [];
25297
+ });
25298
+ const columns = [];
25299
+ for (const column of node.columns) {
25300
+ if (column.type === "WILDCARD") columns.push(...sourceColumns2);
25301
+ else if (column.type === "PARENT_WILDCARD") {
25302
+ columns.push(...sourceColumns2.filter((name) => name.startsWith("_p.")));
25303
+ } else {
25304
+ columns.push(...project([], [column]).columns);
25305
+ }
25306
+ }
25307
+ return { status: "STATIC", columns };
25308
+ }
25309
+ function staticSchemaRelations(ledger) {
25310
+ return new Map([...ledger].flatMap(
25311
+ ([name, entry]) => entry.status === "STATIC" ? [[name, entry.relation]] : []
25312
+ ));
25313
+ }
25314
+ async function buildStaticTempPlainGroupByPlans(node, client, cacheContext, relations) {
25315
+ const plans = /* @__PURE__ */ new Map();
25316
+ const visit = async (value) => {
25317
+ if (value === null || typeof value !== "object") return;
25318
+ if (Array.isArray(value)) {
25319
+ for (const child of value) await visit(child);
25320
+ return;
25321
+ }
25322
+ const object = value;
25323
+ if (object["type"] === "SELECT") {
25324
+ const select = value;
25325
+ const sources = [select.from, ...select.joins.map((join2) => join2.table)];
25326
+ if (sources.some((source) => source.cteName !== null) && sources.every((source) => source.cteName === NO_FROM_CTE_NAME || source.cteName !== null && relations.has(source.cteName))) {
25327
+ const plan = await buildRuntimePlainGroupByPlan(select, client, cacheContext, relations);
25328
+ if (plan) plans.set(select, plan);
25329
+ }
25330
+ }
25331
+ for (const child of Object.values(object)) await visit(child);
25332
+ };
25333
+ await visit(node);
25334
+ return plans;
25335
+ }
25175
25336
  var EXPLAIN_FETCH_PLAN = /* @__PURE__ */ Symbol("ksql.explainFetchPlan");
25176
25337
  function setExplainFetchPlan(result, plan) {
25177
25338
  result[EXPLAIN_FETCH_PLAN] = plan;
@@ -25184,19 +25345,27 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
25184
25345
  const normalizedInjectedVariables = validateDeclaredBatchVariables(statements, injectedVariables);
25185
25346
  const relativeDateVariables = prepareRelativeDateVariables(statements, normalizedInjectedVariables);
25186
25347
  const variables = /* @__PURE__ */ new Map();
25348
+ const literalDeclareDefaults = /* @__PURE__ */ new Map();
25349
+ const tempSchemaLedger = /* @__PURE__ */ new Map();
25187
25350
  const plans = [];
25188
25351
  const fetchStatements = [];
25189
25352
  for (let i = 0; i < statements.length; i++) {
25190
25353
  const stmt = statements[i];
25191
- const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveBatchVariableReferences(stmt.expr, variables) } : stmt : resolveBatchVariableReferences(stmt, variables);
25354
+ const placeholderResolvedStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveBatchVariableReferences(stmt.expr, variables) } : stmt : resolveBatchVariableReferences(stmt, variables);
25355
+ const planStmt = resolveExplainGenerateSeriesDefaults(
25356
+ placeholderResolvedStmt,
25357
+ literalDeclareDefaults
25358
+ );
25192
25359
  validateStatementStatic(planStmt);
25193
25360
  const relativeDatePlan = await resolveRelativeDateExecutionPlan(planStmt, client, invocationCacheContext);
25361
+ const initialRelations = staticSchemaRelations(tempSchemaLedger);
25194
25362
  const whereAnalysis = resolveMetadata ? await buildExplainWhereAnalysis(
25195
25363
  planStmt,
25196
25364
  client,
25197
25365
  invocationCacheContext,
25198
25366
  maxRecords,
25199
- relativeDatePlan
25367
+ relativeDatePlan,
25368
+ initialRelations
25200
25369
  ) : {
25201
25370
  capabilities: /* @__PURE__ */ new Map(),
25202
25371
  orderPlans: /* @__PURE__ */ new Map(),
@@ -25206,6 +25375,34 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
25206
25375
  numberPrecisionApps: /* @__PURE__ */ new Set(),
25207
25376
  relativeDatePlan
25208
25377
  };
25378
+ if (!resolveMetadata) {
25379
+ const staticPlans = await buildStaticTempPlainGroupByPlans(
25380
+ planStmt,
25381
+ client,
25382
+ invocationCacheContext,
25383
+ initialRelations
25384
+ );
25385
+ for (const [select, plan] of staticPlans) whereAnalysis.plainGroupByPlans.set(select, plan);
25386
+ }
25387
+ let createdSchema;
25388
+ if (planStmt.type === "CREATE_TEMP_TABLE") {
25389
+ const relationSchemas = new Map([...tempSchemaLedger].map(([name, entry]) => [
25390
+ name,
25391
+ entry.status === "STATIC" ? { status: "STATIC", columns: entry.columns } : { status: "DEFERRED" }
25392
+ ]));
25393
+ const inferred = inferStaticExplainSchema(planStmt.query, relationSchemas);
25394
+ createdSchema = inferred.status === "STATIC" ? {
25395
+ status: "STATIC",
25396
+ columns: inferred.columns,
25397
+ relation: { rows: [], columns: [...inferred.columns] },
25398
+ producerStatement: i + 1
25399
+ } : {
25400
+ status: "DEFERRED",
25401
+ producerStatement: i + 1,
25402
+ reason: "EXPLAIN_TEMP_SCHEMA_UNAVAILABLE"
25403
+ };
25404
+ tempSchemaLedger.set(planStmt.name, createdSchema);
25405
+ }
25209
25406
  const fetchCollector = { sources: [] };
25210
25407
  const statementPlan = relativeDatePlan.hasServerOnlyWhereFunction && !relativeDatePlan.allowed ? relativeDateExplainLines(relativeDatePlan) : [
25211
25408
  ...relativeDateExplainLines(relativeDatePlan),
@@ -25214,9 +25411,12 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
25214
25411
  analysis.statements[i],
25215
25412
  whereAnalysis.capabilities,
25216
25413
  whereAnalysis.orderPlans,
25414
+ whereAnalysis.plainGroupByPlans,
25217
25415
  dmlMaxRows,
25218
25416
  dmlMaxSubtableRows,
25219
- fetchCollector
25417
+ fetchCollector,
25418
+ tempSchemaLedger,
25419
+ createdSchema
25220
25420
  ), cursorMaxActive)
25221
25421
  ];
25222
25422
  const metadataPlan = explainMetadataLines(whereAnalysis);
@@ -25230,8 +25430,15 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
25230
25430
  fetch: worstExplainFetch(fetchCollector.sources),
25231
25431
  sources: fetchCollector.sources
25232
25432
  });
25433
+ if (planStmt.type === "DROP_TEMP_TABLE") tempSchemaLedger.delete(planStmt.name);
25233
25434
  if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
25234
25435
  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 });
25436
+ if (stmt.type === "DECLARE_VARIABLE" && stmt.annotation === void 0 && (stmt.default.type === "STRING" || stmt.default.type === "NUMBER")) {
25437
+ literalDeclareDefaults.set(
25438
+ stmt.name,
25439
+ stmt.default.type === "STRING" ? stmt.default.value : numberLiteralText(stmt.default)
25440
+ );
25441
+ }
25235
25442
  }
25236
25443
  }
25237
25444
  const result = { statementCount: statements.length, statements: plans };
@@ -25241,19 +25448,28 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
25241
25448
  releaseMetadataCacheScope(invocationCacheContext);
25242
25449
  }
25243
25450
  }
25244
- function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }) {
25451
+ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGroupByPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }, tempSchemaLedger = /* @__PURE__ */ new Map(), createdSchema) {
25245
25452
  if (stmt.type === "CREATE_TEMP_TABLE") {
25246
25453
  return [
25247
25454
  `CREATE TEMP TABLE ${stmt.name}`,
25248
25455
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
25456
+ ...createdSchema?.status === "STATIC" ? [
25457
+ ` schema: ${createdSchema.columns.join(", ")}`,
25458
+ ` schema source: SELECT output of statement ${createdSchema.producerStatement}`
25459
+ ] : createdSchema ? [
25460
+ " schema: deferred (could not be derived statically)",
25461
+ ` plan status: deferred (temp table schema; reason=${createdSchema.reason})`
25462
+ ] : [],
25249
25463
  ` 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`,
25250
25464
  ...buildPlanForBatchQuery(
25251
25465
  stmt.query,
25252
25466
  info,
25253
25467
  capabilities,
25254
25468
  orderPlans,
25469
+ plainGroupByPlans,
25255
25470
  collector,
25256
- "main"
25471
+ "main",
25472
+ tempSchemaLedger
25257
25473
  ).map((l) => ` ${l}`)
25258
25474
  ];
25259
25475
  }
@@ -25275,7 +25491,10 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
25275
25491
  subInfo,
25276
25492
  capabilities,
25277
25493
  orderPlans,
25278
- collector
25494
+ plainGroupByPlans,
25495
+ collector,
25496
+ "main",
25497
+ tempSchemaLedger
25279
25498
  ).map((l) => ` ${l}`)
25280
25499
  ];
25281
25500
  }
@@ -25304,7 +25523,10 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
25304
25523
  info,
25305
25524
  capabilities,
25306
25525
  orderPlans,
25307
- collector
25526
+ plainGroupByPlans,
25527
+ collector,
25528
+ "main",
25529
+ tempSchemaLedger
25308
25530
  );
25309
25531
  }
25310
25532
  if (stmt.type === "ASSERT") {
@@ -25323,15 +25545,36 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
25323
25545
  subInfo,
25324
25546
  capabilities,
25325
25547
  orderPlans,
25326
- collector
25548
+ plainGroupByPlans,
25549
+ collector,
25550
+ "main",
25551
+ tempSchemaLedger
25327
25552
  ).map((l) => ` ${l}`));
25328
25553
  });
25329
25554
  return lines;
25330
25555
  }
25331
25556
  if (stmt.type === "UPDATE" && (stmt.applyBlocks?.length ?? 0) > 0) {
25332
- return buildExplainPlan(stmt, void 0, capabilities, orderPlans, dmlMaxRows, dmlMaxSubtableRows);
25557
+ return buildExplainPlan(
25558
+ stmt,
25559
+ void 0,
25560
+ capabilities,
25561
+ orderPlans,
25562
+ dmlMaxRows,
25563
+ dmlMaxSubtableRows,
25564
+ 1e4,
25565
+ plainGroupByPlans
25566
+ );
25333
25567
  }
25334
- return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans, collector);
25568
+ return buildPlanForBatchQuery(
25569
+ stmt,
25570
+ info,
25571
+ capabilities,
25572
+ orderPlans,
25573
+ plainGroupByPlans,
25574
+ collector,
25575
+ "main",
25576
+ tempSchemaLedger
25577
+ );
25335
25578
  }
25336
25579
  function hasTempTableRef(node) {
25337
25580
  if (Array.isArray(node)) return node.some(hasTempTableRef);
@@ -25343,7 +25586,7 @@ function hasTempTableRef(node) {
25343
25586
  }
25344
25587
  return false;
25345
25588
  }
25346
- function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector = { sources: [] }, sourceRole = "main") {
25589
+ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }, sourceRole = "main", tempSchemaLedger = /* @__PURE__ */ new Map()) {
25347
25590
  if (info.tempTablesReferenced.length === 0) {
25348
25591
  return buildExplainPlan(
25349
25592
  query,
@@ -25353,7 +25596,7 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector
25353
25596
  100,
25354
25597
  DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
25355
25598
  1e4,
25356
- void 0,
25599
+ plainGroupByPlans,
25357
25600
  true,
25358
25601
  collector,
25359
25602
  sourceRole
@@ -25373,6 +25616,26 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector
25373
25616
  lines.push(
25374
25617
  ` temp: ${info.tempTablesReferenced.join(", ")}\uFF08\u30A4\u30F3\u30E1\u30E2\u30EA\u8D70\u67FB\u3002\u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u884C\u6570\u4E0D\u660E\uFF09`
25375
25618
  );
25619
+ const entries = info.tempTablesReferenced.map((name) => [name, tempSchemaLedger.get(name)]);
25620
+ for (const [name, entry] of entries) {
25621
+ if (entry?.status === "STATIC") {
25622
+ lines.push(` source: temp table ${name} (schema from statement ${entry.producerStatement})`);
25623
+ } else {
25624
+ lines.push(` source: temp table ${name}`);
25625
+ lines.push(" schema: deferred (could not be derived statically)");
25626
+ }
25627
+ }
25628
+ lines.push(" rows: runtime (not materialized by EXPLAIN)");
25629
+ const directSelect = query.type === "SELECT" ? query : query.type === "EXPLAIN" && query.query.type === "SELECT" ? query.query : void 0;
25630
+ if (directSelect) {
25631
+ lines.push(...renderPlainGroupByExplainLines(
25632
+ directSelect,
25633
+ plainGroupByPlans?.get(directSelect),
25634
+ entries.some(([, entry]) => entry?.status !== "STATIC")
25635
+ ));
25636
+ }
25637
+ lines.push(entries.every(([, entry]) => entry?.status === "STATIC") ? " plan status: static schema / runtime rows" : " plan status: deferred (temp table schema)");
25638
+ lines.push(" records API: none");
25376
25639
  const apps = info.appIds.filter(
25377
25640
  (a) => query.type !== "INSERT_SELECT" && query.type !== "UPSERT_SELECT" || a !== query.appId
25378
25641
  );
@@ -25642,6 +25905,33 @@ function formatChoiceEqualityRewrite(rewrite) {
25642
25905
  const normalizedOperator = rewrite.normalizedOperator === "IN" ? "in" : "not in";
25643
25906
  return ` pushdown normalized: ${field} ${rewrite.originalOperator} '${originalValue}' -> ${field} ${normalizedOperator} ("${normalizedValue}")`;
25644
25907
  }
25908
+ function renderPlainGroupByExplainLines(stmt, plainGroupByPlan, schemaDeferred) {
25909
+ const normalizedGrouping = normalizeGroupingSpec(stmt);
25910
+ if (normalizedGrouping.type !== "PLAIN") return [];
25911
+ const lines = [];
25912
+ if (plainGroupByPlan) {
25913
+ plainGroupByPlan.items.forEach((item, index) => {
25914
+ const key = normalizedGrouping.allItems[index];
25915
+ if (key?.type !== "FIELD_NAME") return;
25916
+ if (item.kind === "PHYSICAL") {
25917
+ lines.push(
25918
+ ` group key ${key.name}: PHYSICAL (source=${item.sourceIndex}, field=${item.fieldCode})`
25919
+ );
25920
+ } else if (item.kind === "ALIAS_SAFE") {
25921
+ lines.push(` group key ${key.name}: ALIAS_SAFE (column=${item.columnIndex})`);
25922
+ } else if (item.kind === "EXPRESSION") {
25923
+ lines.push(` group key ${key.name}: EXPRESSION`);
25924
+ }
25925
+ });
25926
+ } else if (schemaDeferred) {
25927
+ for (const key of normalizedGrouping.allItems) {
25928
+ if (key.type === "FIELD_NAME") {
25929
+ lines.push(` group key ${key.name}: DEFERRED (temp table schema unavailable)`);
25930
+ }
25931
+ }
25932
+ }
25933
+ return lines;
25934
+ }
25645
25935
  function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans, allowTotalCountPlan = true, emitFetch = true, collector = { sources: [] }, sourceRole = "main") {
25646
25936
  const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
25647
25937
  const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
@@ -25691,35 +25981,11 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
25691
25981
  ` grouping output rows: runtime checked (limit: ${groupingMetadata.outputRowLimit}, before HAVING/DISTINCT/LIMIT)`
25692
25982
  );
25693
25983
  }
25694
- const normalizedGrouping = normalizeGroupingSpec(stmt);
25695
- if (normalizedGrouping.type === "PLAIN") {
25696
- const groupBy = normalizedGrouping.allItems;
25697
- if (plainGroupByPlan) {
25698
- plainGroupByPlan.items.forEach((item, index) => {
25699
- const key = groupBy[index];
25700
- if (key?.type !== "FIELD_NAME") return;
25701
- if (item.kind === "PHYSICAL") {
25702
- lines.push(
25703
- ` group key ${key.name}: PHYSICAL (source=${item.sourceIndex}, field=${item.fieldCode})`
25704
- );
25705
- } else if (item.kind === "ALIAS_SAFE") {
25706
- lines.push(
25707
- ` group key ${key.name}: ALIAS_SAFE (column=${item.columnIndex})`
25708
- );
25709
- } else if (item.kind === "EXPRESSION") {
25710
- lines.push(` group key ${key.name}: EXPRESSION`);
25711
- }
25712
- });
25713
- } else if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) {
25714
- for (const key of groupBy) {
25715
- if (key.type === "FIELD_NAME") {
25716
- lines.push(
25717
- ` group key ${key.name}: DEFERRED (materialized schema unavailable)`
25718
- );
25719
- }
25720
- }
25721
- }
25722
- }
25984
+ lines.push(...renderPlainGroupByExplainLines(
25985
+ stmt,
25986
+ plainGroupByPlan,
25987
+ [stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)
25988
+ ));
25723
25989
  for (const column of stmt.columns) {
25724
25990
  if (column.type !== "WINDOW_COL" || column.windowKind === void 0 || column.windowKind === "RANKING") continue;
25725
25991
  const clauses = [];
@@ -26041,7 +26307,9 @@ function populateWithCrossJoinExplain(stmt) {
26041
26307
  };
26042
26308
  for (const cte of stmt.ctes) {
26043
26309
  if (cte.query.type === "GENERATE_SERIES") {
26044
- exactRows.set(cte.name, resolveGenerateSeries(cte.query).rowCount);
26310
+ if (!cte.query.args.some((arg) => arg.type === "VARIABLE")) {
26311
+ exactRows.set(cte.name, resolveGenerateSeries(cte.query).rowCount);
26312
+ }
26045
26313
  } else if (cte.query.type === "SELECT") {
26046
26314
  const rows = analyze(cte.query);
26047
26315
  if (rows !== null) exactRows.set(cte.name, rows);
@@ -26069,18 +26337,45 @@ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collec
26069
26337
  ));
26070
26338
  lines.push("");
26071
26339
  } else if (cte.query.type === "GENERATE_SERIES") {
26072
- const series = resolveGenerateSeries(cte.query);
26340
+ const seriesStatement = cte.query;
26341
+ const binding = explainSeriesBindings.get(seriesStatement);
26342
+ const unresolved = seriesStatement.args.some((arg) => arg.type === "VARIABLE");
26343
+ if (unresolved) {
26344
+ const argumentLabel = (index) => {
26345
+ const arg = seriesStatement.args[index];
26346
+ if (!arg) return index === 2 ? "runtime" : "deferred";
26347
+ if (arg.type === "VARIABLE") return `@${arg.name} (runtime)`;
26348
+ if (index < 2) return "literal";
26349
+ return arg.type === "NUMBER" ? numberLiteralText(arg) : arg.value;
26350
+ };
26351
+ lines.push(
26352
+ `[cte: ${cte.name}]`,
26353
+ " source: GENERATE_SERIES",
26354
+ ` column: ${seriesStatement.columnAlias}`,
26355
+ " series type: deferred (variable)",
26356
+ ` start: ${argumentLabel(0)}`,
26357
+ ` stop: ${argumentLabel(1)}`,
26358
+ ` step: ${argumentLabel(2)}`,
26359
+ " rows: runtime",
26360
+ ` row guard: runtime / ${GENERATE_SERIES_MAX_ROWS}`,
26361
+ " records API: none",
26362
+ ""
26363
+ );
26364
+ continue;
26365
+ }
26366
+ const series = resolveGenerateSeries(seriesStatement);
26073
26367
  const step = series.kind === "DATE" ? `${series.step} ${String(series.dateUnit ?? "DAY").toLowerCase()}${Math.abs(series.step) === 1 ? "" : "s"}` : String(series.step);
26074
26368
  lines.push(
26075
26369
  `[cte: ${cte.name}]`,
26076
26370
  " source: GENERATE_SERIES",
26077
- ` column: ${cte.query.columnAlias}`,
26078
- ` series type: ${series.kind}`,
26079
- ` start: ${series.start}`,
26080
- ` stop: ${series.stop}`,
26371
+ ` column: ${seriesStatement.columnAlias}`,
26372
+ ` series type: ${series.kind}${binding?.defaultBoundIndexes.size ? " (DECLARE default)" : ""}`,
26373
+ ` start: ${binding?.variableNames.has(0) ? `@${binding.variableNames.get(0)} (DECLARE default; value hidden)` : series.start}`,
26374
+ ` stop: ${binding?.variableNames.has(1) ? `@${binding.variableNames.get(1)} (DECLARE default; value hidden)` : series.stop}`,
26081
26375
  ` step: ${step}`,
26082
- ` rows: ${series.rowCount}`,
26376
+ ` rows: ${series.rowCount}${binding?.defaultBoundIndexes.size ? " (DECLARE default estimate)" : ""}`,
26083
26377
  ` row guard: ${series.rowCount} / ${GENERATE_SERIES_MAX_ROWS}`,
26378
+ ...binding?.defaultBoundIndexes.size ? [" binding: DECLARE defaults; runtime injection may change this plan"] : [],
26084
26379
  " records API: none",
26085
26380
  ""
26086
26381
  );
@@ -29204,6 +29499,9 @@ function createDryRunClient() {
29204
29499
  function hasStaticTypedPushdownCandidate(statement) {
29205
29500
  if (statement === null || typeof statement !== "object") return false;
29206
29501
  const node = statement;
29502
+ if (node["type"] === "CREATE_TEMP_TABLE") {
29503
+ return hasStaticTypedPushdownCandidate(node["query"]);
29504
+ }
29207
29505
  if (node["type"] === "WITH") {
29208
29506
  const query = node["query"];
29209
29507
  const containsCross = (value) => {
@@ -29923,7 +30221,10 @@ async function run() {
29923
30221
  return false;
29924
30222
  });
29925
30223
  dryRunNeedsMetadata = statements.some(explainNeedsAppMetadata);
29926
- dryRunUsesStaticTypedPlan = statements.some(hasStaticTypedPushdownCandidate) && !statements.some(statementUsesRelativeDateResolution);
30224
+ const staticEligible = statements.every(
30225
+ (statement) => !explainNeedsAppMetadata(statement) || hasStaticTypedPushdownCandidate(statement)
30226
+ );
30227
+ dryRunUsesStaticTypedPlan = statements.some(hasStaticTypedPushdownCandidate) && staticEligible && !statements.some(statementUsesRelativeDateResolution);
29927
30228
  if (statements.length > 1) {
29928
30229
  batchAnalysis = analyzeBatch(statements);
29929
30230
  isBatchSql = true;