refactor: remove legacy ktx compatibility shims (#211)

* refactor: remove legacy ktx compatibility shims

* fix: restore overlay collision guidance
This commit is contained in:
Andrey Avtomonov 2026-05-24 16:57:23 +02:00 committed by GitHub
parent a954a29a76
commit 96952fb43c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 294 additions and 342 deletions

View file

@ -8,12 +8,8 @@ import type { KtxTableRef } from './types.js';
*
* Accepted entry forms:
* "catalog.db.name" fully qualified
* "db.name" schema-qualified (catalog = null; legacy / Postgres-shape)
* "db.name" schema-qualified (catalog = null)
* "name" bare (catalog = db = null; SQLite-shape)
* { catalog?, db?, name } escape hatch for identifiers containing dots
*
* The setup wizard writes the fully-qualified form going forward; the lenient
* parser keeps existing project configs working.
*/
export function resolveEnabledTables(
connection: Record<string, unknown> | undefined,
@ -33,16 +29,6 @@ function parseEnabledTableEntry(value: unknown): KtxTableRef | null {
if (typeof value === 'string') {
return parseDottedEntry(value);
}
if (value && typeof value === 'object' && !Array.isArray(value)) {
const entry = value as { catalog?: unknown; db?: unknown; name?: unknown };
const name = typeof entry.name === 'string' ? entry.name : null;
if (!name) return null;
return {
catalog: typeof entry.catalog === 'string' ? entry.catalog : null,
db: typeof entry.db === 'string' ? entry.db : null,
name,
};
}
return null;
}

View file

@ -1878,6 +1878,15 @@ describe('resolveEnabledTables', () => {
expect(result!.has(tableRefKey({ catalog: null, db: 'public', name: 'orders' }))).toBe(true);
});
it('ignores legacy enabled_tables object entries', () => {
expect(
resolveEnabledTables({
driver: 'postgres',
enabled_tables: [{ catalog: null, db: 'public', name: 'orders' }],
}),
).toBeNull();
});
it('returns null for undefined connection', () => {
expect(resolveEnabledTables(undefined)).toBeNull();
});

View file

@ -126,19 +126,17 @@ function normalizeDriver(driver: string | undefined): KtxConnectionDriver {
const normalized = (driver ?? '').toLowerCase();
if (
normalized === 'postgres' ||
normalized === 'postgresql' ||
normalized === 'sqlite' ||
normalized === 'sqlite3' ||
normalized === 'mysql' ||
normalized === 'clickhouse' ||
normalized === 'sqlserver' ||
normalized === 'bigquery' ||
normalized === 'snowflake'
) {
return normalized === 'sqlite3' ? 'sqlite' : normalized;
return normalized;
}
throw new Error(
`Standalone ktx scan supports postgres/postgresql/sqlite/mysql/clickhouse/sqlserver/bigquery/snowflake in this phase, received "${driver ?? 'unknown'}"`,
`Standalone ktx scan supports postgres/sqlite/mysql/clickhouse/sqlserver/bigquery/snowflake in this phase, received "${driver ?? 'unknown'}"`,
);
}

View file

@ -47,9 +47,9 @@ describe('scopedTableNames', () => {
expect(scopedTableNames(scope, { catalog: 'ANALYTICS', db: 'STAGING' })).toEqual(['LISTINGS']);
});
it('treats null in the scope entry as a wildcard for that segment', () => {
it('requires non-null scope segments to match the namespace', () => {
const scope = tableRefSet([{ catalog: null, db: 'public', name: 'users' }]);
expect(scopedTableNames(scope, { catalog: 'any-catalog', db: 'public' })).toEqual(['users']);
expect(scopedTableNames(scope, { catalog: 'any-catalog', db: 'public' })).toEqual([]);
});
it('returns empty when no scope entry matches the namespace', () => {
@ -57,7 +57,7 @@ describe('scopedTableNames', () => {
expect(scopedTableNames(scope, { catalog: 'X', db: 'Y' })).toEqual([]);
});
it('dedupes when the same name appears under different catalog projections', () => {
it('dedupes exact namespace matches only', () => {
const scope: ReadonlySet<KtxTableRefKey> = tableRefSet([
{ catalog: null, db: 'public', name: 'users' },
{ catalog: 'A', db: 'public', name: 'users' },

View file

@ -33,8 +33,7 @@ export function tableRefSet(refs: readonly KtxTableRef[]): ReadonlySet<KtxTableR
/**
* Return the bare table names from a scope that fall within the given
* (catalog, db) namespace. `catalog: null` is treated as a wildcard so that
* legacy 2-part `"db.name"` entries continue to match. Same for `db: null`.
* (catalog, db) namespace.
*/
export function scopedTableNames(
scope: ReadonlySet<KtxTableRefKey>,
@ -45,8 +44,8 @@ export function scopedTableNames(
const wantDb = namespace.db ?? null;
for (const key of scope) {
const ref = tableRefFromKey(key);
if (wantCatalog !== null && ref.catalog !== null && ref.catalog !== wantCatalog) continue;
if (wantDb !== null && ref.db !== null && ref.db !== wantDb) continue;
if (ref.catalog !== wantCatalog) continue;
if (ref.db !== wantDb) continue;
names.add(ref.name);
}
return [...names];

View file

@ -3,7 +3,6 @@ import type { KtxTableRefKey } from './table-ref.js';
export type KtxConnectionDriver =
| 'sqlite'
| 'postgres'
| 'postgresql'
| 'sqlserver'
| 'bigquery'
| 'snowflake'

View file

@ -8,7 +8,7 @@ import type {
KtxTableRef,
} from './types.js';
type CatalogDriver = KtxConnectionDriver | 'sqlite3';
type CatalogDriver = KtxConnectionDriver;
export interface WarehouseCatalogServiceDeps {
fileStore: KtxFileStorePort;
@ -129,7 +129,7 @@ function splitDisplay(display: string): string[] {
}
function formatDisplay(driver: CatalogDriver, table: KtxTableRef): string {
if (driver === 'sqlite' || driver === 'sqlite3') {
if (driver === 'sqlite') {
return table.name;
}
return [table.catalog, table.db, table.name].filter((part): part is string => Boolean(part)).join('.');
@ -137,7 +137,7 @@ function formatDisplay(driver: CatalogDriver, table: KtxTableRef): string {
function parseDisplay(driver: CatalogDriver, display: string): KtxTableRef | null {
const parts = splitDisplay(display);
if (driver === 'sqlite' || driver === 'sqlite3') {
if (driver === 'sqlite') {
return parts.length === 1 ? { catalog: null, db: null, name: parts[0]! } : null;
}
if (driver === 'bigquery' || driver === 'snowflake' || driver === 'sqlserver') {
@ -156,7 +156,7 @@ function parseDisplay(driver: CatalogDriver, display: string): KtxTableRef | nul
}
function expectedDisplayPartCount(driver: CatalogDriver): number {
if (driver === 'sqlite' || driver === 'sqlite3') {
if (driver === 'sqlite') {
return 1;
}
if (driver === 'bigquery' || driver === 'snowflake' || driver === 'sqlserver') {