@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 +375 -74
- package/dist-engine/index.cjs +14 -14
- package/dist-engine/index.mjs +14 -14
- package/dist-engine/ksql-engine.umd.js +14 -14
- package/dist-engine/meta/bundle-baseline.json +7 -7
- package/dist-engine/meta/cjs.json +7 -7
- package/dist-engine/meta/esm.json +7 -7
- package/dist-engine/meta/umd.json +7 -7
- package/dist-mcp/ksql-mcp.js +370 -75
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
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, [
|
|
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(
|
|
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
|
|
24641
|
-
|
|
24642
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
25695
|
-
|
|
25696
|
-
|
|
25697
|
-
|
|
25698
|
-
|
|
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
|
-
|
|
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
|
|
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: ${
|
|
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
|
-
|
|
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;
|