mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-10 08:05:14 +02:00
* feat(context): add warehouse dialect dispatch * feat(context): read warehouse scan catalog * feat(context): add entity details verification tool * feat(context): add ingest SQL verification tool * feat(context): add raw warehouse discovery tool * feat(context): expose warehouse verification tools to ingest * docs(context): add ingest identifier verification protocol * test(context): guard ingest identifier verification prompts * chore(context): verify warehouse verification tools * docs: add warehouse verification tools plan and spec * fix(context): expose target warehouses to Notion ingest * fix(context): update ingest prompts for warehouse verification tools * fix(context): scope raw schema discovery to allowed connections * fix(context): verify warehouse column display targets * docs: add notion warehouse verification gap closure plan * fix(context): include raw discovery connection names * fix(context): expose warehouse targets for LookML and MetricFlow * fix(context): pass connection config to ingest query executors * fix(cli): enable read-only SQL probes for local ingest * docs: add warehouse verification final v1 closure plan * fix(context): align warehouse sql probe prompt shape * docs: add warehouse verification prompt shape closure plan * test(context): catch connectionless sql execution prompt examples * fix(context): include connection name in sl capture sql example * docs: add warehouse verification sql example closure plan * fix(context): report structured entity detail misses * docs: add warehouse verification structured target miss closure plan * fix: report untracked squash merge conflicts * feat: require ingest verification ledger * fix: stabilize ingest wiki references
102 lines
3 KiB
TypeScript
102 lines
3 KiB
TypeScript
import type { KtxSchemaDimensionType, KtxTableRef } from '../scan/types.js';
|
|
|
|
export type SupportedDriver =
|
|
| 'postgres'
|
|
| 'postgresql'
|
|
| 'mysql'
|
|
| 'sqlserver'
|
|
| 'snowflake'
|
|
| 'bigquery'
|
|
| 'clickhouse'
|
|
| 'sqlite'
|
|
| 'sqlite3';
|
|
|
|
export interface KtxDialect {
|
|
readonly type: SupportedDriver;
|
|
quoteIdentifier(identifier: string): string;
|
|
formatTableName(table: KtxTableRef): string;
|
|
mapToDimensionType(nativeType: string): KtxSchemaDimensionType;
|
|
}
|
|
|
|
const supportedDrivers: SupportedDriver[] = [
|
|
'bigquery',
|
|
'clickhouse',
|
|
'mysql',
|
|
'postgres',
|
|
'postgresql',
|
|
'sqlite',
|
|
'sqlite3',
|
|
'snowflake',
|
|
'sqlserver',
|
|
];
|
|
|
|
function doubleQuoted(identifier: string): string {
|
|
return `"${identifier.replace(/"/g, '""')}"`;
|
|
}
|
|
|
|
function backtickQuoted(identifier: string): string {
|
|
return `\`${identifier.replace(/`/g, '``')}\``;
|
|
}
|
|
|
|
function bigQueryQuoted(identifier: string): string {
|
|
return `\`${identifier.replace(/`/g, '\\`')}\``;
|
|
}
|
|
|
|
function bracketQuoted(identifier: string): string {
|
|
return `[${identifier.replace(/\]/g, ']]')}]`;
|
|
}
|
|
|
|
function inferDimensionType(nativeType: string): KtxSchemaDimensionType {
|
|
const normalized = nativeType.toLowerCase().trim();
|
|
if (normalized.includes('date') || normalized.includes('time')) {
|
|
return 'time';
|
|
}
|
|
if (
|
|
normalized.includes('int') ||
|
|
normalized.includes('num') ||
|
|
normalized.includes('dec') ||
|
|
normalized.includes('float') ||
|
|
normalized.includes('double') ||
|
|
normalized.includes('real')
|
|
) {
|
|
return 'number';
|
|
}
|
|
if (normalized.includes('bool') || normalized === 'bit') {
|
|
return 'boolean';
|
|
}
|
|
return 'string';
|
|
}
|
|
|
|
function formatWithParts(table: KtxTableRef, quote: (identifier: string) => string, sqlite = false): string {
|
|
const parts = sqlite ? [table.name] : [table.catalog, table.db, table.name].filter((part): part is string => !!part);
|
|
return parts.map(quote).join('.');
|
|
}
|
|
|
|
function createDialect(type: SupportedDriver, quote: (identifier: string) => string, sqlite = false): KtxDialect {
|
|
return {
|
|
type,
|
|
quoteIdentifier: quote,
|
|
formatTableName: (table) => formatWithParts(table, quote, sqlite),
|
|
mapToDimensionType: inferDimensionType,
|
|
};
|
|
}
|
|
|
|
const dialects: Record<SupportedDriver, KtxDialect> = {
|
|
postgres: createDialect('postgres', doubleQuoted),
|
|
postgresql: createDialect('postgresql', doubleQuoted),
|
|
mysql: createDialect('mysql', backtickQuoted),
|
|
clickhouse: createDialect('clickhouse', backtickQuoted),
|
|
sqlite: createDialect('sqlite', doubleQuoted, true),
|
|
sqlite3: createDialect('sqlite3', doubleQuoted, true),
|
|
snowflake: createDialect('snowflake', doubleQuoted),
|
|
bigquery: createDialect('bigquery', bigQueryQuoted),
|
|
sqlserver: createDialect('sqlserver', bracketQuoted),
|
|
};
|
|
|
|
export function getDialectForDriver(driver: string): KtxDialect {
|
|
const normalized = driver.toLowerCase().trim();
|
|
if (normalized in dialects) {
|
|
return dialects[normalized as SupportedDriver];
|
|
}
|
|
throw new Error(`Unsupported warehouse driver "${driver}". Supported drivers: ${supportedDrivers.join(', ')}`);
|
|
}
|