Skip to content

Commit 9b7851c

Browse files
authored
feat(vnext): prepare local relation evidence (#191)
## Summary - add a package-private prepared statement handle that composes authenticated query-site and CTE-visibility evidence - preserve qualified, inactive, ambiguous, opaque, and resource-limited states without inventing local candidates - authenticate complete CTE layout results so active-statement resource failures survive orchestration - add four-dialect integration coverage and cold/warm performance baselines ## Why The relation-completion session needs one immutable artifact it can cache per source, statement slot, and dialect. Keeping preparation separate from position projection avoids rebuilding CTE layout state as the cursor moves, while the opaque handle prevents stale or independently mixable evidence from crossing sessions. This remains package-private until the catalog and end-to-end completion slice prove the public API described by ADR 0005. ## Evidence - 1,691 passing unit tests plus one governed expected failure - all five TypeScript configurations, oxlint, integrity, package smoke, worker placement, and browser suites pass - changed coverage: 97.05% statements, 95.77% branches, 100% functions, 97.04% lines - warm projection: approximately 0.65 ms for a 10 KiB statement and 0.32 ms for 256 CTEs Roadmap: #169 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Introduce a package-private prepared statement that authenticates query-site and CTE visibility to power local relation completion. It preserves qualified/inactive/opaque/ambiguous/resource-limited states and adds four-dialect coverage; supports #169. - **New Features** - Added `prepareSqlLocalRelationStatement` and `analyzeSqlLocalRelationSite` for deterministic, cacheable local evidence (results are frozen). - Propagated authenticated CTE layout through `relation-query-site`, including active-statement resource failures. - Added `isSqlStatementSlotSnapshotFor` and authenticated layout registration/lookup to validate provenance. - Added `bench:local-relation-site` plus tests and benches across PostgreSQL, DuckDB, BigQuery, and Dremio. <sup>Written for commit 74e9edf. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/marimo-team/codemirror-sql/pull/191?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
1 parent 1dc15d8 commit 9b7851c

8 files changed

Lines changed: 1047 additions & 42 deletions

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"test:browser": "vitest run --config vitest.browser.config.ts",
2727
"test:worker-placement": "node ./scripts/worker-placement.mjs",
2828
"test:integrity": "node ./scripts/check-test-integrity.mjs",
29+
"bench:local-relation-site": "vitest bench --run src/vnext/__tests__/local-relation-site.bench.ts",
2930
"bench:parser-adapter": "vitest bench --run src/vnext/__tests__/node-sql-parser-adapter.bench.ts",
3031
"bench:query-site": "vitest bench --run src/vnext/__tests__/query-site.bench.ts",
3132
"bench:statement-index": "vitest bench --run src/vnext/__tests__/statement-index.bench.ts",
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { bench, describe } from "vitest";
2+
import { MAX_CTE_DECLARATIONS } from "../cte-layout.js";
3+
import {
4+
analyzeSqlLocalRelationSite,
5+
prepareSqlLocalRelationStatement,
6+
type SqlLocalRelationStatement,
7+
} from "../local-relation-site.js";
8+
import { DUCKDB_SQL_RELATION_DIALECT } from "../relation-dialect.js";
9+
import { createIdentitySqlSource } from "../source.js";
10+
import {
11+
buildSqlStatementIndex,
12+
findSqlStatementSlot,
13+
} from "../statement-index.js";
14+
15+
interface Fixture {
16+
readonly index: ReturnType<typeof buildSqlStatementIndex>;
17+
readonly position: number;
18+
readonly slot: ReturnType<typeof findSqlStatementSlot>;
19+
readonly source: ReturnType<typeof createIdentitySqlSource>;
20+
readonly statement: SqlLocalRelationStatement;
21+
}
22+
23+
function fixture(text: string): Fixture {
24+
const source = createIdentitySqlSource(text);
25+
const index = buildSqlStatementIndex(
26+
source.analysisText,
27+
DUCKDB_SQL_RELATION_DIALECT.querySite.lexicalProfile,
28+
);
29+
const position = text.length;
30+
const slot = findSqlStatementSlot(index, position, "left");
31+
const preparation = prepareSqlLocalRelationStatement(
32+
source,
33+
index,
34+
slot,
35+
DUCKDB_SQL_RELATION_DIALECT,
36+
);
37+
if (preparation.status === "unavailable") {
38+
throw new Error("Local relation benchmark must prepare");
39+
}
40+
return {
41+
index,
42+
position,
43+
slot,
44+
source,
45+
statement: preparation.statement,
46+
};
47+
}
48+
49+
function coldAnalyze(value: Fixture) {
50+
const preparation = prepareSqlLocalRelationStatement(
51+
value.source,
52+
value.index,
53+
value.slot,
54+
DUCKDB_SQL_RELATION_DIALECT,
55+
);
56+
if (preparation.status === "unavailable") {
57+
throw new Error("Cold local relation benchmark must prepare");
58+
}
59+
return analyzeSqlLocalRelationSite(
60+
preparation.statement,
61+
value.position,
62+
);
63+
}
64+
65+
const tenKibibytes = 10 * 1_024;
66+
const withPrefix = "WITH local_cte AS (SELECT 1) SELECT ";
67+
const withSuffix = " FROM target";
68+
const projectionLength =
69+
tenKibibytes - withPrefix.length - withSuffix.length;
70+
const withText = `${withPrefix}${"x,".repeat(
71+
Math.floor((projectionLength - 1) / 2),
72+
)}x${withSuffix}`;
73+
const paddedWithText = `${withText.slice(
74+
0,
75+
-withSuffix.length,
76+
)}${" ".repeat(tenKibibytes - withText.length)}${withSuffix}`;
77+
if (paddedWithText.length !== tenKibibytes) {
78+
throw new Error("Local relation benchmark must be exactly 10 KiB");
79+
}
80+
const withFixture = fixture(paddedWithText);
81+
82+
const maximumCteText = `WITH ${Array.from(
83+
{ length: MAX_CTE_DECLARATIONS },
84+
(_, index) => `c${index} AS (SELECT ${index})`,
85+
).join(", ")} SELECT * FROM target`;
86+
const maximumCteFixture = fixture(maximumCteText);
87+
88+
const withPreflight = analyzeSqlLocalRelationSite(
89+
withFixture.statement,
90+
withFixture.position,
91+
);
92+
const maximumCtePreflight = analyzeSqlLocalRelationSite(
93+
maximumCteFixture.statement,
94+
maximumCteFixture.position,
95+
);
96+
if (
97+
withPreflight.status !== "ready" ||
98+
withPreflight.local.kind !== "unqualified" ||
99+
withPreflight.local.cteVisibility.ctes.length !== 1
100+
) {
101+
throw new Error("10 KiB benchmark must expose one visible CTE");
102+
}
103+
if (
104+
maximumCtePreflight.status !== "ready" ||
105+
maximumCtePreflight.local.kind !== "unqualified" ||
106+
maximumCtePreflight.local.cteVisibility.ctes.length !==
107+
MAX_CTE_DECLARATIONS
108+
) {
109+
throw new Error("Maximum benchmark must expose every CTE");
110+
}
111+
112+
describe("local relation site", () => {
113+
bench("cold 10 KiB WITH statement", () => {
114+
coldAnalyze(withFixture);
115+
});
116+
117+
bench("warm 10 KiB WITH statement", () => {
118+
analyzeSqlLocalRelationSite(
119+
withFixture.statement,
120+
withFixture.position,
121+
);
122+
});
123+
124+
bench("cold 256-CTE statement", () => {
125+
coldAnalyze(maximumCteFixture);
126+
});
127+
128+
bench("warm 256-CTE statement", () => {
129+
analyzeSqlLocalRelationSite(
130+
maximumCteFixture.statement,
131+
maximumCteFixture.position,
132+
);
133+
});
134+
});

0 commit comments

Comments
 (0)