@danceroutine/tango-migrations 0.1.0 → 1.0.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/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/{CollectingBuilder-C6qnwyrb.js → CollectingBuilder--4fqDQdE.js} +17 -2
- package/dist/CollectingBuilder--4fqDQdE.js.map +1 -0
- package/dist/{CompilerStrategy-Cv1woBmO.js → CompilerStrategy-yKw-Egxv.js} +19 -8
- package/dist/CompilerStrategy-yKw-Egxv.js.map +1 -0
- package/dist/InternalColumnType-G9zV9StN.js +16 -0
- package/dist/InternalColumnType-G9zV9StN.js.map +1 -0
- package/dist/InternalOperationKind-Bt6Weuon.js +19 -0
- package/dist/InternalOperationKind-Bt6Weuon.js.map +1 -0
- package/dist/{IntrospectorStrategy-BM1Eizfc.js → IntrospectorStrategy-blvwSU3_.js} +13 -4
- package/dist/IntrospectorStrategy-blvwSU3_.js.map +1 -0
- package/dist/Migration-DYQ0hUG7.js +30 -0
- package/dist/Migration-DYQ0hUG7.js.map +1 -0
- package/dist/{MigrationGenerator-Z39LTKmC.js → MigrationGenerator-B1p0jHnx.js} +20 -12
- package/dist/MigrationGenerator-B1p0jHnx.js.map +1 -0
- package/dist/{MigrationRunner-CCFuPUlr.js → MigrationRunner-D1ZfbbS-.js} +52 -9
- package/dist/MigrationRunner-D1ZfbbS-.js.map +1 -0
- package/dist/MigrationSqlSafetyAdapter-CGRbB2k2.js +62 -0
- package/dist/MigrationSqlSafetyAdapter-CGRbB2k2.js.map +1 -0
- package/dist/{SqliteCompilerFactory-DwMwO7xY.js → SqliteCompilerFactory-BAodJW9n.js} +73 -51
- package/dist/SqliteCompilerFactory-BAodJW9n.js.map +1 -0
- package/dist/{SqliteIntrospector-BRdNt6KG.js → SqliteIntrospector-CWwPWhmA.js} +49 -5
- package/dist/SqliteIntrospector-CWwPWhmA.js.map +1 -0
- package/dist/builder/contracts/ColumnSpec.d.ts +2 -1
- package/dist/builder/contracts/UpdateReferentialAction.d.ts +1 -1
- package/dist/builder/index.js +4 -4
- package/dist/builder/ops/OpBuilder.d.ts +48 -7
- package/dist/builder/runtime/CollectingBuilder.d.ts +19 -1
- package/dist/{builder-Dtk8oP_Y.js → builder-xJ-Bq2pk.js} +51 -21
- package/dist/builder-xJ-Bq2pk.js.map +1 -0
- package/dist/cli-DhCn8xiS.js +313 -0
- package/dist/cli-DhCn8xiS.js.map +1 -0
- package/dist/cli.js +17 -169
- package/dist/cli.js.map +1 -1
- package/dist/commands/cli.d.ts +5 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +17 -0
- package/dist/commands-DIJepqNg.js +10 -0
- package/dist/commands-DIJepqNg.js.map +1 -0
- package/dist/compilers/contracts/CompilerFactory.d.ts +4 -0
- package/dist/compilers/contracts/SQLCompiler.d.ts +4 -0
- package/dist/compilers/dialects/PostgresCompiler.d.ts +10 -1
- package/dist/compilers/dialects/SqliteCompiler.d.ts +10 -0
- package/dist/compilers/factories/PostgresCompilerFactory.d.ts +9 -0
- package/dist/compilers/factories/SqliteCompilerFactory.d.ts +9 -0
- package/dist/compilers/index.js +5 -4
- package/dist/{compilers-D8DJuTnQ.js → compilers-dRN0Hzev.js} +2 -2
- package/dist/{compilers-D8DJuTnQ.js.map → compilers-dRN0Hzev.js.map} +1 -1
- package/dist/diff/diffSchema.d.ts +6 -1
- package/dist/diff/index.js +6 -6
- package/dist/{diff-Cs0TPEGR.js → diff-CZZbXAPN.js} +2 -2
- package/dist/{diff-Cs0TPEGR.js.map → diff-CZZbXAPN.js.map} +1 -1
- package/dist/{diffSchema-KgGHP-s3.js → diffSchema-D4oemTWS.js} +5 -4
- package/dist/diffSchema-D4oemTWS.js.map +1 -0
- package/dist/domain/Migration.d.ts +17 -0
- package/dist/domain/MigrationOperation.d.ts +14 -13
- package/dist/domain/index.js +2 -2
- package/dist/domain/internal/InternalColumnType.d.ts +11 -10
- package/dist/domain/internal/InternalDialect.d.ts +5 -4
- package/dist/domain/internal/InternalMigrationMode.d.ts +5 -4
- package/dist/domain/internal/InternalOperationKind.d.ts +14 -13
- package/dist/domain/internal/InternalReferentialAction.d.ts +7 -6
- package/dist/{domain-BXVlG0C0.js → domain-CwR-kUNS.js} +2 -2
- package/dist/{domain-BXVlG0C0.js.map → domain-CwR-kUNS.js.map} +1 -1
- package/dist/generator/MigrationGenerator.d.ts +13 -0
- package/dist/generator/index.js +3 -3
- package/dist/{generator-3yC60b1u.js → generator-DK-_f-PF.js} +2 -2
- package/dist/{generator-3yC60b1u.js.map → generator-DK-_f-PF.js.map} +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +24 -20
- package/dist/internal/MigrationSqlSafetyAdapter.d.ts +24 -0
- package/dist/introspect/DatabaseIntrospector.d.ts +8 -0
- package/dist/introspect/PostgresIntrospector.d.ts +14 -0
- package/dist/introspect/SqliteIntrospector.d.ts +13 -0
- package/dist/introspect/index.js +3 -2
- package/dist/{introspect-ks-QSodq.js → introspect-TPv_jeD6.js} +2 -2
- package/dist/{introspect-ks-QSodq.js.map → introspect-TPv_jeD6.js.map} +1 -1
- package/dist/runner/MigrationRunner.d.ts +7 -1
- package/dist/runner/index.js +9 -8
- package/dist/{runner-BOs-tItW.js → runner-C97xT8_W.js} +2 -2
- package/dist/{runner-BOs-tItW.js.map → runner-C97xT8_W.js.map} +1 -1
- package/dist/runtime/loadModule.d.ts +15 -0
- package/dist/strategies/CompilerStrategy.d.ts +19 -1
- package/dist/strategies/IntrospectorStrategy.d.ts +16 -1
- package/dist/strategies/index.js +8 -7
- package/dist/{strategies-BvHwf4as.js → strategies-D9ymSvbG.js} +3 -3
- package/dist/{strategies-BvHwf4as.js.map → strategies-D9ymSvbG.js.map} +1 -1
- package/package.json +98 -92
- package/dist/CollectingBuilder-C6qnwyrb.js.map +0 -1
- package/dist/CompilerStrategy-Cv1woBmO.js.map +0 -1
- package/dist/InternalColumnType-_YAz7RqI.js +0 -17
- package/dist/InternalColumnType-_YAz7RqI.js.map +0 -1
- package/dist/InternalOperationKind-BPVoOQwD.js +0 -20
- package/dist/InternalOperationKind-BPVoOQwD.js.map +0 -1
- package/dist/IntrospectorStrategy-BM1Eizfc.js.map +0 -1
- package/dist/Migration-D9J6ZbLP.js +0 -25
- package/dist/Migration-D9J6ZbLP.js.map +0 -1
- package/dist/MigrationGenerator-Z39LTKmC.js.map +0 -1
- package/dist/MigrationRunner-CCFuPUlr.js.map +0 -1
- package/dist/SqliteCompilerFactory-DwMwO7xY.js.map +0 -1
- package/dist/SqliteIntrospector-BRdNt6KG.js.map +0 -1
- package/dist/builder/ops/OpBuilder.js +0 -173
- package/dist/builder-Dtk8oP_Y.js.map +0 -1
- package/dist/diffSchema-KgGHP-s3.js.map +0 -1
- package/dist/domain/MigrationOperation.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationRunner-D1ZfbbS-.js","names":["modulePath: string","projectRoot: string","options?: { projectRoot?: string }","client: DBClient","dialect: Dialect","migrationsDir: string","compilerStrategy?: CompilerStrategy","value: unknown","toId?: string","migrations: Migration[]","loaded: unknown","migration: Migration","x: unknown","op: MigrationOperation"],"sources":["../src/runtime/loadModule.ts","../src/runner/MigrationRunner.ts"],"sourcesContent":["import { resolve, extname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { createJiti } from 'jiti';\n\nconst TS_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);\n\nfunction toAbsolutePath(modulePath: string, projectRoot: string): string {\n return resolve(projectRoot, modulePath);\n}\n\nfunction isTypeScriptModule(modulePath: string): boolean {\n return TS_EXTENSIONS.has(extname(modulePath).toLowerCase());\n}\n\n/**\n * Load a module from a Tango app project root.\n *\n * TypeScript modules are loaded through jiti and JavaScript/ESM modules are loaded\n * through native dynamic import so published installs behave like end-user runtime.\n */\nexport async function loadModule(\n modulePath: string,\n options?: { projectRoot?: string }\n): Promise<Record<string, unknown>> {\n const projectRoot = options?.projectRoot ?? process.cwd();\n const absolutePath = toAbsolutePath(modulePath, projectRoot);\n\n if (isTypeScriptModule(absolutePath)) {\n const jiti = createJiti(resolve(projectRoot, 'tango.config.ts'), {\n interopDefault: true,\n moduleCache: true,\n });\n return (await jiti.import<Record<string, unknown>>(absolutePath)) as Record<string, unknown>;\n }\n\n return (await import(pathToFileURL(absolutePath).href)) as Record<string, unknown>;\n}\n\n/**\n * Load a module and return default export when present.\n */\nexport async function loadDefaultExport(modulePath: string, options?: { projectRoot?: string }): Promise<unknown> {\n const loaded = await loadModule(modulePath, options);\n return loaded.default ?? loaded;\n}\n","import { CollectingBuilder } from '../builder/runtime/CollectingBuilder';\nimport type { Dialect } from '../domain/Dialect';\nimport { Migration } from '../domain/Migration';\nimport type { SQL } from '../compilers/contracts/SQL';\nimport type { MigrationOperation } from '../domain/MigrationOperation';\nimport type { CompilerStrategy } from '../strategies/CompilerStrategy';\nimport { createDefaultCompilerStrategy } from '../strategies/CompilerStrategy';\nimport { InternalDialect } from '../domain/internal/InternalDialect';\nimport { isError } from '@danceroutine/tango-core';\nimport { readdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { loadDefaultExport } from '../runtime/loadModule';\n\nconst JOURNAL = '_tango_migrations';\n\n/** DB client contract required by migration execution. */\ninterface DBClient {\n /** Execute SQL with optional parameters. */\n query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }>;\n /** Release underlying database resources. */\n close(): Promise<void>;\n}\n\n/**\n * Manages the lifecycle of database migrations: applying, planning, and tracking status.\n *\n * The runner reads migration files from a directory, compiles operations to SQL\n * for the target dialect, and maintains a journal table to track which migrations\n * have been applied. Each applied migration is checksummed to detect tampering.\n *\n * @example\n * ```typescript\n * const runner = new MigrationRunner(client, 'postgres', './migrations');\n *\n * // Apply all pending migrations\n * await runner.apply();\n *\n * // Apply up to a specific migration\n * await runner.apply('003_add_indexes');\n *\n * // Preview the SQL that would be generated\n * const sql = await runner.plan();\n *\n * // Check which migrations are applied\n * const statuses = await runner.status();\n * ```\n */\nexport class MigrationRunner {\n static readonly BRAND = 'tango.migrations.runner' as const;\n readonly __tangoBrand: typeof MigrationRunner.BRAND = MigrationRunner.BRAND;\n private compilerStrategy: CompilerStrategy;\n\n constructor(\n private client: DBClient,\n private dialect: Dialect,\n private migrationsDir: string = 'migrations',\n compilerStrategy?: CompilerStrategy\n ) {\n this.compilerStrategy = compilerStrategy ?? createDefaultCompilerStrategy();\n }\n\n /**\n * Narrow an unknown value to `MigrationRunner`.\n */\n static isMigrationRunner(value: unknown): value is MigrationRunner {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === MigrationRunner.BRAND\n );\n }\n\n /**\n * Apply all pending migrations, optionally stopping at a specific migration ID.\n * Migrations are applied in file-sort order. Already-applied migrations are skipped.\n * Non-online migrations are wrapped in a transaction on Postgres.\n */\n async apply(toId?: string): Promise<void> {\n await this.ensureJournal();\n const applied = await this.listApplied();\n const migrations = await this.loadMigrations();\n\n for (const migration of migrations) {\n if (toId && migration.id > toId) {\n break;\n }\n if (applied.has(migration.id)) {\n continue;\n }\n\n await this.applyMigration(migration);\n }\n }\n\n /**\n * Generate a dry-run SQL plan for all migrations without executing anything.\n * Useful for reviewing what SQL would be run before applying.\n */\n async plan(): Promise<string> {\n const migrations = await this.loadMigrations();\n let output = '';\n\n migrations.forEach((migration) => {\n const builder = new CollectingBuilder();\n migration.up(builder);\n const sqls = builder.ops.flatMap((op) => this.compileOperation(op));\n\n output += `# ${migration.id}\\n`;\n sqls.forEach((statement) => {\n output += statement.sql + ';\\n';\n });\n if (builder.dataFns.length) {\n output += '-- (data step present)\\n';\n }\n output += '\\n';\n });\n\n return output;\n }\n\n /**\n * Return the applied/pending status of every migration found on disk.\n */\n async status(): Promise<{ id: string; applied: boolean }[]> {\n const applied = await this.listApplied();\n const migrations = await this.loadMigrations();\n\n return migrations.map((m) => ({\n id: m.id,\n applied: applied.has(m.id),\n }));\n }\n\n private async ensureJournal(): Promise<void> {\n const sql =\n this.dialect === InternalDialect.POSTGRES\n ? `CREATE TABLE IF NOT EXISTS \"${JOURNAL}\" (\n id TEXT PRIMARY KEY,\n applied_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n checksum TEXT NOT NULL\n )`\n : `CREATE TABLE IF NOT EXISTS ${JOURNAL} (\n id TEXT PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now')),\n checksum TEXT NOT NULL\n )`;\n\n await this.client.query(sql);\n }\n\n private async listApplied(): Promise<Set<string>> {\n const table = this.dialect === InternalDialect.POSTGRES ? `\"${JOURNAL}\"` : JOURNAL;\n const { rows } = await this.client.query<{ id: string }>(`SELECT id FROM ${table}`);\n return new Set(rows.map((r) => r.id));\n }\n\n private async loadMigrations(): Promise<Migration[]> {\n const files = (await readdir(this.migrationsDir)).filter((f) => f.endsWith('.ts') || f.endsWith('.js')).sort();\n\n const migrations: Migration[] = [];\n\n for (const file of files) {\n const absolutePath = resolve(this.migrationsDir, file);\n let loaded: unknown;\n try {\n loaded = await loadDefaultExport(absolutePath, { projectRoot: process.cwd() });\n } catch (error) {\n const reason = isError(error) ? error.message : String(error);\n throw new Error(`Failed to load migration module '${file}': ${reason}`, { cause: error });\n }\n\n if (Migration.isMigration(loaded)) {\n migrations.push(loaded);\n continue;\n }\n\n if (Migration.isMigrationConstructor(loaded)) {\n migrations.push(new loaded());\n continue;\n }\n\n throw new Error(\n `Invalid migration module '${file}'. Default export must be a Migration subclass or instance.`\n );\n }\n\n return migrations;\n }\n\n private async applyMigration(migration: Migration): Promise<void> {\n const builder = new CollectingBuilder();\n await migration.up(builder);\n\n const sqls = builder.ops.flatMap((op) => this.compileOperation(op));\n const checksum = String(this.hashJSON(builder.ops));\n\n const isOnline = (migration.mode ?? builder.getMode()) === 'online';\n\n if (!isOnline && this.dialect === InternalDialect.POSTGRES) {\n await this.client.query('BEGIN');\n }\n\n try {\n for (const statement of sqls) {\n await this.client.query(statement.sql, statement.params);\n }\n\n for (const fn of builder.dataFns) {\n await fn({ query: (sql, params) => this.client.query(sql, params).then(() => {}) });\n }\n\n const table = this.dialect === InternalDialect.POSTGRES ? `\"${JOURNAL}\"` : JOURNAL;\n const placeholder = this.dialect === InternalDialect.POSTGRES ? '$1, $2' : '?, ?';\n await this.client.query(`INSERT INTO ${table} (id, checksum) VALUES (${placeholder})`, [\n migration.id,\n checksum,\n ]);\n\n if (!isOnline && this.dialect === InternalDialect.POSTGRES) {\n await this.client.query('COMMIT');\n }\n } catch (error) {\n if (!isOnline && this.dialect === InternalDialect.POSTGRES) {\n await this.client.query('ROLLBACK');\n }\n throw error;\n }\n }\n\n /**\n * Compute a simple hash of the migration's operation list.\n * Stored alongside each applied migration in the journal table to detect\n * if a migration file has been modified after it was already applied.\n * Uses a djb2-like hash over the JSON-serialized operations.\n */\n private hashJSON(x: unknown): number {\n const s = JSON.stringify(x);\n let h = 0;\n for (let i = 0; i < s.length; i++) {\n // oxlint-disable-next-line prefer-code-point\n h = Math.imul(31, h) + s.charCodeAt(i);\n // oxlint-disable-next-line prefer-math-trunc\n h = h | 0;\n }\n // oxlint-disable-next-line unicorn/prefer-math-trunc\n return h >>> 0;\n }\n\n private compileOperation(op: MigrationOperation): SQL[] {\n return this.compilerStrategy.compile(this.dialect, op);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { SqlSafetyEngine, isTrustedSqlFragment, quoteSqlIdentifier } from "@danceroutine/tango-core";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/MigrationSqlSafetyAdapter.ts
|
|
4
|
+
var MigrationSqlSafetyAdapter = class {
|
|
5
|
+
constructor(dialect, engine = new SqlSafetyEngine()) {
|
|
6
|
+
this.dialect = dialect;
|
|
7
|
+
this.engine = engine;
|
|
8
|
+
}
|
|
9
|
+
table(value) {
|
|
10
|
+
return this.quote("table", "table", value);
|
|
11
|
+
}
|
|
12
|
+
column(value, allowlist) {
|
|
13
|
+
return this.quote("column", "column", value, allowlist);
|
|
14
|
+
}
|
|
15
|
+
columns(values, allowlist) {
|
|
16
|
+
return values.map((value, index) => this.quote(`column:${index}`, "column", value, allowlist));
|
|
17
|
+
}
|
|
18
|
+
index(value) {
|
|
19
|
+
return this.quote("index", "index", value);
|
|
20
|
+
}
|
|
21
|
+
constraint(value) {
|
|
22
|
+
return this.quote("constraint", "constraint", value);
|
|
23
|
+
}
|
|
24
|
+
schema(value) {
|
|
25
|
+
return this.quote("schema", "schema", value);
|
|
26
|
+
}
|
|
27
|
+
rawFragment(key, value) {
|
|
28
|
+
return this.engine.validate({ rawFragments: [{
|
|
29
|
+
key,
|
|
30
|
+
value
|
|
31
|
+
}] }).rawFragments[key].sql;
|
|
32
|
+
}
|
|
33
|
+
optionalRawFragment(key, value) {
|
|
34
|
+
if (!value) return undefined;
|
|
35
|
+
return this.rawFragment(key, value);
|
|
36
|
+
}
|
|
37
|
+
rawDefault(value, nowSql) {
|
|
38
|
+
if (value === undefined) return undefined;
|
|
39
|
+
if (value === null) return null;
|
|
40
|
+
if (this.isNowDefault(value)) return nowSql;
|
|
41
|
+
return this.rawFragment("default", value);
|
|
42
|
+
}
|
|
43
|
+
isTrustedFragment(value) {
|
|
44
|
+
return isTrustedSqlFragment(value);
|
|
45
|
+
}
|
|
46
|
+
quote(key, role, value, allowlist) {
|
|
47
|
+
const validated = this.engine.validate({ identifiers: [{
|
|
48
|
+
key,
|
|
49
|
+
role,
|
|
50
|
+
value,
|
|
51
|
+
allowlist
|
|
52
|
+
}] });
|
|
53
|
+
return quoteSqlIdentifier(validated.identifiers[key], this.dialect);
|
|
54
|
+
}
|
|
55
|
+
isNowDefault(value) {
|
|
56
|
+
return typeof value === "object" && value !== null && "now" in value && value.now === true;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { MigrationSqlSafetyAdapter };
|
|
62
|
+
//# sourceMappingURL=MigrationSqlSafetyAdapter-CGRbB2k2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationSqlSafetyAdapter-CGRbB2k2.js","names":["dialect: SqlDialect","engine: SqlSafetyEngine","value: string","allowlist?: readonly string[]","values: readonly string[]","key: string","value: TrustedSqlFragment","value?: TrustedSqlFragment","value: TrustedSqlFragment | { now: true } | null | undefined","nowSql: string","value: unknown","role: IdentifierRole","value: TrustedSqlFragment | { now: true }"],"sources":["../src/internal/MigrationSqlSafetyAdapter.ts"],"sourcesContent":["import {\n SqlSafetyEngine,\n isTrustedSqlFragment,\n quoteSqlIdentifier,\n type SqlDialect,\n type SqlIdentifierRole,\n type TrustedSqlFragment,\n} from '@danceroutine/tango-core';\n\ntype IdentifierRole = Extract<SqlIdentifierRole, 'table' | 'column' | 'index' | 'constraint' | 'schema'>;\n\n/**\n * Migrations-local adapter that maps migration operations into the shared SQL\n * safety engine and returns quoted identifiers or trusted raw fragments.\n */\nexport class MigrationSqlSafetyAdapter {\n constructor(\n private readonly dialect: SqlDialect,\n private readonly engine: SqlSafetyEngine = new SqlSafetyEngine()\n ) {}\n\n table(value: string): string {\n return this.quote('table', 'table', value);\n }\n\n column(value: string, allowlist?: readonly string[]): string {\n return this.quote('column', 'column', value, allowlist);\n }\n\n columns(values: readonly string[], allowlist?: readonly string[]): string[] {\n return values.map((value, index) => this.quote(`column:${index}`, 'column', value, allowlist));\n }\n\n index(value: string): string {\n return this.quote('index', 'index', value);\n }\n\n constraint(value: string): string {\n return this.quote('constraint', 'constraint', value);\n }\n\n schema(value: string): string {\n return this.quote('schema', 'schema', value);\n }\n\n rawFragment(key: string, value: TrustedSqlFragment): string {\n return this.engine.validate({\n rawFragments: [{ key, value }],\n }).rawFragments[key]!.sql;\n }\n\n optionalRawFragment(key: string, value?: TrustedSqlFragment): string | undefined {\n if (!value) {\n return undefined;\n }\n\n return this.rawFragment(key, value);\n }\n\n rawDefault(\n value: TrustedSqlFragment | { now: true } | null | undefined,\n nowSql: string\n ): string | null | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (value === null) {\n return null;\n }\n\n if (this.isNowDefault(value)) {\n return nowSql;\n }\n\n return this.rawFragment('default', value);\n }\n\n isTrustedFragment(value: unknown): value is TrustedSqlFragment {\n return isTrustedSqlFragment(value);\n }\n\n private quote(key: string, role: IdentifierRole, value: string, allowlist?: readonly string[]): string {\n const validated = this.engine.validate({\n identifiers: [{ key, role, value, allowlist }],\n });\n\n return quoteSqlIdentifier(validated.identifiers[key]!, this.dialect);\n }\n\n private isNowDefault(value: TrustedSqlFragment | { now: true }): value is { now: true } {\n return typeof value === 'object' && value !== null && 'now' in value && value.now === true;\n }\n}\n"],"mappings":";;;IAea,4BAAN,MAAgC;CACnC,YACqBA,SACAC,SAA0B,IAAI,mBACjD;AAAA,OAFmB,UAAA;AAAA,OACA,SAAA;CACjB;CAEJ,MAAMC,OAAuB;AACzB,SAAO,KAAK,MAAM,SAAS,SAAS,MAAM;CAC7C;CAED,OAAOA,OAAeC,WAAuC;AACzD,SAAO,KAAK,MAAM,UAAU,UAAU,OAAO,UAAU;CAC1D;CAED,QAAQC,QAA2BD,WAAyC;AACxE,SAAO,OAAO,IAAI,CAAC,OAAO,UAAU,KAAK,OAAO,SAAS,MAAM,GAAG,UAAU,OAAO,UAAU,CAAC;CACjG;CAED,MAAMD,OAAuB;AACzB,SAAO,KAAK,MAAM,SAAS,SAAS,MAAM;CAC7C;CAED,WAAWA,OAAuB;AAC9B,SAAO,KAAK,MAAM,cAAc,cAAc,MAAM;CACvD;CAED,OAAOA,OAAuB;AAC1B,SAAO,KAAK,MAAM,UAAU,UAAU,MAAM;CAC/C;CAED,YAAYG,KAAaC,OAAmC;AACxD,SAAO,KAAK,OAAO,SAAS,EACxB,cAAc,CAAC;GAAE;GAAK;EAAQ,CAAA,EACjC,EAAC,CAAC,aAAa,KAAM;CACzB;CAED,oBAAoBD,KAAaE,OAAgD;AAC7E,OAAK,MACD,QAAO;AAGX,SAAO,KAAK,YAAY,KAAK,MAAM;CACtC;CAED,WACIC,OACAC,QACyB;AACzB,MAAI,UAAU,UACV,QAAO;AAGX,MAAI,UAAU,KACV,QAAO;AAGX,MAAI,KAAK,aAAa,MAAM,CACxB,QAAO;AAGX,SAAO,KAAK,YAAY,WAAW,MAAM;CAC5C;CAED,kBAAkBC,OAA6C;AAC3D,SAAO,qBAAqB,MAAM;CACrC;CAED,MAAcL,KAAaM,MAAsBT,OAAeC,WAAuC;EACnG,MAAM,YAAY,KAAK,OAAO,SAAS,EACnC,aAAa,CAAC;GAAE;GAAK;GAAM;GAAO;EAAY,CAAA,EACjD,EAAC;AAEF,SAAO,mBAAmB,UAAU,YAAY,MAAO,KAAK,QAAQ;CACvE;CAED,aAAqBS,OAAmE;AACpF,gBAAc,UAAU,YAAY,UAAU,QAAQ,SAAS,SAAS,MAAM,QAAQ;CACzF;AACJ"}
|
|
@@ -1,98 +1,107 @@
|
|
|
1
|
-
import { InternalOperationKind } from "./InternalOperationKind-
|
|
2
|
-
import { InternalColumnType } from "./InternalColumnType-
|
|
1
|
+
import { InternalOperationKind } from "./InternalOperationKind-Bt6Weuon.js";
|
|
2
|
+
import { InternalColumnType } from "./InternalColumnType-G9zV9StN.js";
|
|
3
|
+
import { MigrationSqlSafetyAdapter } from "./MigrationSqlSafetyAdapter-CGRbB2k2.js";
|
|
3
4
|
|
|
4
5
|
//#region src/compilers/dialects/PostgresCompiler.ts
|
|
5
6
|
var PostgresCompiler = class PostgresCompiler {
|
|
6
7
|
static BRAND = "tango.migrations.postgres_compiler";
|
|
7
8
|
__tangoBrand = PostgresCompiler.BRAND;
|
|
9
|
+
sqlSafety = new MigrationSqlSafetyAdapter("postgres");
|
|
10
|
+
/**
|
|
11
|
+
* Narrow an unknown value to the PostgreSQL migration compiler implementation.
|
|
12
|
+
*/
|
|
8
13
|
static isPostgresCompiler(value) {
|
|
9
14
|
return typeof value === "object" && value !== null && value.__tangoBrand === PostgresCompiler.BRAND;
|
|
10
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Compile a migration operation into one or more PostgreSQL statements.
|
|
18
|
+
*/
|
|
11
19
|
compile(op) {
|
|
12
20
|
switch (op.kind) {
|
|
13
21
|
case InternalOperationKind.TABLE_CREATE: {
|
|
14
22
|
const cols = op.columns.map((c) => this.colDDL(c)).join(", ");
|
|
15
|
-
const pkCols = op.columns.filter((c) => c.primaryKey).map((c) => this.
|
|
23
|
+
const pkCols = op.columns.filter((c) => c.primaryKey).map((c) => this.sqlSafety.column(c.name));
|
|
16
24
|
const constraints = [];
|
|
17
25
|
if (pkCols.length) constraints.push(`PRIMARY KEY (${pkCols.join(", ")})`);
|
|
18
26
|
op.columns.filter((column) => column.references).forEach((column) => {
|
|
27
|
+
const references = column.references;
|
|
19
28
|
const fkName = `${op.table}_${column.name}_fkey`;
|
|
20
|
-
let fk = `CONSTRAINT ${this.
|
|
21
|
-
if (
|
|
22
|
-
if (
|
|
29
|
+
let fk = `CONSTRAINT ${this.sqlSafety.constraint(fkName)} FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})`;
|
|
30
|
+
if (references.onDelete) fk += ` ON DELETE ${references.onDelete}`;
|
|
31
|
+
if (references.onUpdate) fk += ` ON UPDATE ${references.onUpdate}`;
|
|
23
32
|
constraints.push(fk);
|
|
24
33
|
});
|
|
25
34
|
const allParts = [cols, ...constraints].join(", ");
|
|
26
|
-
const sql = `CREATE TABLE ${this.
|
|
35
|
+
const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${allParts})`;
|
|
27
36
|
return [{
|
|
28
37
|
sql,
|
|
29
38
|
params: []
|
|
30
39
|
}];
|
|
31
40
|
}
|
|
32
41
|
case InternalOperationKind.TABLE_DROP: return [{
|
|
33
|
-
sql: `DROP TABLE ${this.
|
|
42
|
+
sql: `DROP TABLE ${this.sqlSafety.table(op.table)}${op.cascade ? " CASCADE" : ""}`,
|
|
34
43
|
params: []
|
|
35
44
|
}];
|
|
36
45
|
case InternalOperationKind.COLUMN_ADD: return [{
|
|
37
|
-
sql: `ALTER TABLE ${this.
|
|
46
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL(op.column)}`,
|
|
38
47
|
params: []
|
|
39
48
|
}];
|
|
40
49
|
case InternalOperationKind.COLUMN_DROP: return [{
|
|
41
|
-
sql: `ALTER TABLE ${this.
|
|
50
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,
|
|
42
51
|
params: []
|
|
43
52
|
}];
|
|
44
53
|
case InternalOperationKind.COLUMN_ALTER: {
|
|
45
54
|
const out = [];
|
|
46
55
|
if (op.to.type) out.push({
|
|
47
|
-
sql: `ALTER TABLE ${this.
|
|
56
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} TYPE ${this.typeToSQL(op.to.type)}`,
|
|
48
57
|
params: []
|
|
49
58
|
});
|
|
50
59
|
if (op.to.notNull !== undefined) out.push({
|
|
51
|
-
sql: `ALTER TABLE ${this.
|
|
60
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} ${op.to.notNull ? "SET NOT NULL" : "DROP NOT NULL"}`,
|
|
52
61
|
params: []
|
|
53
62
|
});
|
|
54
63
|
out.push(...this.compileDefaultChange(op.table, op.column, op.to.default));
|
|
55
64
|
return out;
|
|
56
65
|
}
|
|
57
66
|
case InternalOperationKind.COLUMN_RENAME: return [{
|
|
58
|
-
sql: `ALTER TABLE ${this.
|
|
67
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,
|
|
59
68
|
params: []
|
|
60
69
|
}];
|
|
61
70
|
case InternalOperationKind.INDEX_CREATE: {
|
|
62
|
-
const cols = op.on.join(", ");
|
|
71
|
+
const cols = this.sqlSafety.columns(op.on).join(", ");
|
|
63
72
|
const uniq = op.unique ? "UNIQUE " : "";
|
|
64
73
|
const conc = op.concurrently ? "CONCURRENTLY " : "";
|
|
65
|
-
const where =
|
|
74
|
+
const where = this.sqlSafety.optionalRawFragment("where", op.where);
|
|
66
75
|
return [{
|
|
67
|
-
sql: `CREATE ${uniq}INDEX ${conc}${this.
|
|
76
|
+
sql: `CREATE ${uniq}INDEX ${conc}${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ""}`,
|
|
68
77
|
params: []
|
|
69
78
|
}];
|
|
70
79
|
}
|
|
71
80
|
case InternalOperationKind.INDEX_DROP: {
|
|
72
81
|
const conc = op.concurrently ? "CONCURRENTLY " : "";
|
|
73
82
|
return [{
|
|
74
|
-
sql: `DROP INDEX ${conc}${this.
|
|
83
|
+
sql: `DROP INDEX ${conc}${this.sqlSafety.index(op.name)}`,
|
|
75
84
|
params: []
|
|
76
85
|
}];
|
|
77
86
|
}
|
|
78
87
|
case InternalOperationKind.FK_CREATE: {
|
|
79
|
-
const cols =
|
|
80
|
-
const refs =
|
|
88
|
+
const cols = this.sqlSafety.columns(op.columns).join(", ");
|
|
89
|
+
const refs = this.sqlSafety.columns(op.refColumns).join(", ");
|
|
81
90
|
const name = op.name ?? `${op.table}_${op.columns.join("_")}_fkey`;
|
|
82
91
|
const notValid = op.notValid ? " NOT VALID" : "";
|
|
83
92
|
const onDel = op.onDelete ? ` ON DELETE ${op.onDelete}` : "";
|
|
84
93
|
const onUpd = op.onUpdate ? ` ON UPDATE ${op.onUpdate}` : "";
|
|
85
94
|
return [{
|
|
86
|
-
sql: `ALTER TABLE ${this.
|
|
95
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD CONSTRAINT ${this.sqlSafety.constraint(name)} FOREIGN KEY (${cols}) REFERENCES ${this.sqlSafety.table(op.refTable)} (${refs})${onDel}${onUpd}${notValid}`,
|
|
87
96
|
params: []
|
|
88
97
|
}];
|
|
89
98
|
}
|
|
90
99
|
case InternalOperationKind.FK_VALIDATE: return [{
|
|
91
|
-
sql: `ALTER TABLE ${this.
|
|
100
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} VALIDATE CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,
|
|
92
101
|
params: []
|
|
93
102
|
}];
|
|
94
103
|
case InternalOperationKind.FK_DROP: return [{
|
|
95
|
-
sql: `ALTER TABLE ${this.
|
|
104
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,
|
|
96
105
|
params: []
|
|
97
106
|
}];
|
|
98
107
|
default: return [];
|
|
@@ -105,24 +114,21 @@ var PostgresCompiler = class PostgresCompiler {
|
|
|
105
114
|
compileDefaultChange(table, column, defaultValue) {
|
|
106
115
|
if (defaultValue === undefined) return [];
|
|
107
116
|
if (defaultValue === null) return [{
|
|
108
|
-
sql: `ALTER TABLE ${this.
|
|
117
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} DROP DEFAULT`,
|
|
109
118
|
params: []
|
|
110
119
|
}];
|
|
111
|
-
if (
|
|
112
|
-
sql: `ALTER TABLE ${this.
|
|
120
|
+
if (this.sqlSafety.isTrustedFragment(defaultValue)) return [{
|
|
121
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT ${this.sqlSafety.rawFragment("default", defaultValue)}`,
|
|
113
122
|
params: []
|
|
114
123
|
}];
|
|
115
124
|
if (defaultValue && typeof defaultValue === "object" && "now" in defaultValue && defaultValue.now) return [{
|
|
116
|
-
sql: `ALTER TABLE ${this.
|
|
125
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT now()`,
|
|
117
126
|
params: []
|
|
118
127
|
}];
|
|
119
128
|
return [];
|
|
120
129
|
}
|
|
121
|
-
id(identifier) {
|
|
122
|
-
return `"${identifier}"`;
|
|
123
|
-
}
|
|
124
130
|
colDDL(column) {
|
|
125
|
-
const parts = [this.
|
|
131
|
+
const parts = [this.sqlSafety.column(column.name)];
|
|
126
132
|
switch (column.type) {
|
|
127
133
|
case InternalColumnType.SERIAL:
|
|
128
134
|
parts.push("SERIAL");
|
|
@@ -150,10 +156,8 @@ var PostgresCompiler = class PostgresCompiler {
|
|
|
150
156
|
break;
|
|
151
157
|
}
|
|
152
158
|
if (column.notNull) parts.push("NOT NULL");
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
else if (column.default.now) parts.push("DEFAULT now()");
|
|
156
|
-
}
|
|
159
|
+
const defaultSql = this.sqlSafety.rawDefault(column.default, "now()");
|
|
160
|
+
if (defaultSql) parts.push(`DEFAULT ${defaultSql}`);
|
|
157
161
|
if (column.unique && !column.primaryKey) parts.push("UNIQUE");
|
|
158
162
|
return parts.join(" ");
|
|
159
163
|
}
|
|
@@ -180,9 +184,15 @@ else if (column.default.now) parts.push("DEFAULT now()");
|
|
|
180
184
|
var PostgresCompilerFactory = class PostgresCompilerFactory {
|
|
181
185
|
static BRAND = "tango.migrations.postgres_compiler_factory";
|
|
182
186
|
__tangoBrand = PostgresCompilerFactory.BRAND;
|
|
187
|
+
/**
|
|
188
|
+
* Narrow an unknown value to the factory that provisions PostgreSQL compilers.
|
|
189
|
+
*/
|
|
183
190
|
static isPostgresCompilerFactory(value) {
|
|
184
191
|
return typeof value === "object" && value !== null && value.__tangoBrand === PostgresCompilerFactory.BRAND;
|
|
185
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Create a PostgreSQL SQL compiler instance.
|
|
195
|
+
*/
|
|
186
196
|
create() {
|
|
187
197
|
return new PostgresCompiler();
|
|
188
198
|
}
|
|
@@ -193,51 +203,59 @@ var PostgresCompilerFactory = class PostgresCompilerFactory {
|
|
|
193
203
|
var SqliteCompiler = class SqliteCompiler {
|
|
194
204
|
static BRAND = "tango.migrations.sqlite_compiler";
|
|
195
205
|
__tangoBrand = SqliteCompiler.BRAND;
|
|
206
|
+
sqlSafety = new MigrationSqlSafetyAdapter("sqlite");
|
|
207
|
+
/**
|
|
208
|
+
* Narrow an unknown value to the SQLite migration compiler implementation.
|
|
209
|
+
*/
|
|
196
210
|
static isSqliteCompiler(value) {
|
|
197
211
|
return typeof value === "object" && value !== null && value.__tangoBrand === SqliteCompiler.BRAND;
|
|
198
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Compile a migration operation into one or more SQLite statements.
|
|
215
|
+
*/
|
|
199
216
|
compile(op) {
|
|
200
217
|
switch (op.kind) {
|
|
201
218
|
case InternalOperationKind.TABLE_CREATE: {
|
|
202
219
|
const cols = op.columns.map((c) => this.colDDL(c));
|
|
203
|
-
const pkCols = op.columns.filter((c) => c.primaryKey && c.type !== InternalColumnType.SERIAL).map((c) => c.name);
|
|
220
|
+
const pkCols = op.columns.filter((c) => c.primaryKey && c.type !== InternalColumnType.SERIAL).map((c) => this.sqlSafety.column(c.name));
|
|
204
221
|
if (pkCols.length) cols.push(`PRIMARY KEY (${pkCols.join(", ")})`);
|
|
205
222
|
op.columns.filter((column) => column.references).forEach((column) => {
|
|
206
|
-
|
|
223
|
+
const references = column.references;
|
|
224
|
+
cols.push(`FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})${references.onDelete ? ` ON DELETE ${references.onDelete}` : ""}${references.onUpdate ? ` ON UPDATE ${references.onUpdate}` : ""}`);
|
|
207
225
|
});
|
|
208
|
-
const sql = `CREATE TABLE ${op.table} (${cols.join(", ")})`;
|
|
226
|
+
const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${cols.join(", ")})`;
|
|
209
227
|
return [{
|
|
210
228
|
sql,
|
|
211
229
|
params: []
|
|
212
230
|
}];
|
|
213
231
|
}
|
|
214
232
|
case InternalOperationKind.TABLE_DROP: return [{
|
|
215
|
-
sql: `DROP TABLE ${op.table}`,
|
|
233
|
+
sql: `DROP TABLE ${this.sqlSafety.table(op.table)}`,
|
|
216
234
|
params: []
|
|
217
235
|
}];
|
|
218
236
|
case InternalOperationKind.COLUMN_ADD: return [{
|
|
219
|
-
sql: `ALTER TABLE ${op.table} ADD COLUMN ${this.colDDL(op.column)}`,
|
|
237
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL(op.column)}`,
|
|
220
238
|
params: []
|
|
221
239
|
}];
|
|
222
240
|
case InternalOperationKind.COLUMN_DROP: return [{
|
|
223
|
-
sql: `ALTER TABLE ${op.table} DROP COLUMN ${op.column}`,
|
|
241
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,
|
|
224
242
|
params: []
|
|
225
243
|
}];
|
|
226
244
|
case InternalOperationKind.COLUMN_RENAME: return [{
|
|
227
|
-
sql: `ALTER TABLE ${op.table} RENAME COLUMN ${op.from} TO ${op.to}`,
|
|
245
|
+
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,
|
|
228
246
|
params: []
|
|
229
247
|
}];
|
|
230
248
|
case InternalOperationKind.INDEX_CREATE: {
|
|
231
|
-
const cols = op.on.join(", ");
|
|
249
|
+
const cols = this.sqlSafety.columns(op.on).join(", ");
|
|
232
250
|
const uniq = op.unique ? "UNIQUE " : "";
|
|
233
|
-
const where =
|
|
251
|
+
const where = this.sqlSafety.optionalRawFragment("where", op.where);
|
|
234
252
|
return [{
|
|
235
|
-
sql: `CREATE ${uniq}INDEX ${op.name} ON ${op.table} (${cols})${where}`,
|
|
253
|
+
sql: `CREATE ${uniq}INDEX ${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ""}`,
|
|
236
254
|
params: []
|
|
237
255
|
}];
|
|
238
256
|
}
|
|
239
257
|
case InternalOperationKind.INDEX_DROP: return [{
|
|
240
|
-
sql: `DROP INDEX ${op.name}`,
|
|
258
|
+
sql: `DROP INDEX ${this.sqlSafety.index(op.name)}`,
|
|
241
259
|
params: []
|
|
242
260
|
}];
|
|
243
261
|
case InternalOperationKind.COLUMN_ALTER:
|
|
@@ -248,7 +266,7 @@ var SqliteCompiler = class SqliteCompiler {
|
|
|
248
266
|
}
|
|
249
267
|
}
|
|
250
268
|
colDDL(column) {
|
|
251
|
-
const parts = [column.name];
|
|
269
|
+
const parts = [this.sqlSafety.column(column.name)];
|
|
252
270
|
switch (column.type) {
|
|
253
271
|
case InternalColumnType.SERIAL:
|
|
254
272
|
parts.push("INTEGER PRIMARY KEY AUTOINCREMENT");
|
|
@@ -276,10 +294,8 @@ var SqliteCompiler = class SqliteCompiler {
|
|
|
276
294
|
break;
|
|
277
295
|
}
|
|
278
296
|
if (column.notNull) parts.push("NOT NULL");
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
else if (column.default.now) parts.push("DEFAULT (datetime('now'))");
|
|
282
|
-
}
|
|
297
|
+
const defaultSql = this.sqlSafety.rawDefault(column.default, "(datetime('now'))");
|
|
298
|
+
if (defaultSql) parts.push(`DEFAULT ${defaultSql}`);
|
|
283
299
|
if (column.unique && !column.primaryKey) parts.push("UNIQUE");
|
|
284
300
|
return parts.join(" ");
|
|
285
301
|
}
|
|
@@ -290,9 +306,15 @@ else if (column.default.now) parts.push("DEFAULT (datetime('now'))");
|
|
|
290
306
|
var SqliteCompilerFactory = class SqliteCompilerFactory {
|
|
291
307
|
static BRAND = "tango.migrations.sqlite_compiler_factory";
|
|
292
308
|
__tangoBrand = SqliteCompilerFactory.BRAND;
|
|
309
|
+
/**
|
|
310
|
+
* Narrow an unknown value to the factory that provisions SQLite compilers.
|
|
311
|
+
*/
|
|
293
312
|
static isSqliteCompilerFactory(value) {
|
|
294
313
|
return typeof value === "object" && value !== null && value.__tangoBrand === SqliteCompilerFactory.BRAND;
|
|
295
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Create a SQLite SQL compiler instance.
|
|
317
|
+
*/
|
|
296
318
|
create() {
|
|
297
319
|
return new SqliteCompiler();
|
|
298
320
|
}
|
|
@@ -300,4 +322,4 @@ var SqliteCompilerFactory = class SqliteCompilerFactory {
|
|
|
300
322
|
|
|
301
323
|
//#endregion
|
|
302
324
|
export { PostgresCompiler, PostgresCompilerFactory, SqliteCompiler, SqliteCompilerFactory };
|
|
303
|
-
//# sourceMappingURL=SqliteCompilerFactory-
|
|
325
|
+
//# sourceMappingURL=SqliteCompilerFactory-BAodJW9n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteCompilerFactory-BAodJW9n.js","names":["value: unknown","op: MigrationOperation","constraints: string[]","out: SQL[]","table: string","column: string","defaultValue: unknown","column: ColumnSpec","parts: string[]","type: ColumnType","exhaustive: never","value: unknown","value: unknown","op: MigrationOperation","column: ColumnSpec","parts: string[]","value: unknown"],"sources":["../src/compilers/dialects/PostgresCompiler.ts","../src/compilers/factories/PostgresCompilerFactory.ts","../src/compilers/dialects/SqliteCompiler.ts","../src/compilers/factories/SqliteCompilerFactory.ts"],"sourcesContent":["import type { MigrationOperation } from '../../domain/MigrationOperation';\nimport type { SQL } from '../contracts/SQL';\nimport type { ColumnSpec } from '../../builder/contracts/ColumnSpec';\nimport type { ColumnType } from '../../builder/contracts/ColumnType';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport { MigrationSqlSafetyAdapter } from '../../internal/MigrationSqlSafetyAdapter';\n\n/**\n * PostgreSQL SQL compiler for migration operations.\n */\nexport class PostgresCompiler implements SQLCompiler {\n static readonly BRAND = 'tango.migrations.postgres_compiler' as const;\n readonly __tangoBrand: typeof PostgresCompiler.BRAND = PostgresCompiler.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('postgres');\n\n /**\n * Narrow an unknown value to the PostgreSQL migration compiler implementation.\n */\n static isPostgresCompiler(value: unknown): value is PostgresCompiler {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresCompiler.BRAND\n );\n }\n\n /**\n * Compile a migration operation into one or more PostgreSQL statements.\n */\n compile(op: MigrationOperation): SQL[] {\n switch (op.kind) {\n case InternalOperationKind.TABLE_CREATE: {\n const cols = op.columns.map((c) => this.colDDL(c)).join(', ');\n const pkCols = op.columns.filter((c) => c.primaryKey).map((c) => this.sqlSafety.column(c.name));\n const constraints: string[] = [];\n\n if (pkCols.length) {\n constraints.push(`PRIMARY KEY (${pkCols.join(', ')})`);\n }\n\n op.columns\n .filter((column) => column.references)\n .forEach((column) => {\n const references = column.references!;\n const fkName = `${op.table}_${column.name}_fkey`;\n let fk = `CONSTRAINT ${this.sqlSafety.constraint(fkName)} FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})`;\n if (references.onDelete) {\n fk += ` ON DELETE ${references.onDelete}`;\n }\n if (references.onUpdate) {\n fk += ` ON UPDATE ${references.onUpdate}`;\n }\n constraints.push(fk);\n });\n\n const allParts = [cols, ...constraints].join(', ');\n const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${allParts})`;\n return [{ sql, params: [] }];\n }\n\n case InternalOperationKind.TABLE_DROP:\n return [\n { sql: `DROP TABLE ${this.sqlSafety.table(op.table)}${op.cascade ? ' CASCADE' : ''}`, params: [] },\n ];\n\n case InternalOperationKind.COLUMN_ADD:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_ALTER: {\n const out: SQL[] = [];\n if (op.to.type) {\n out.push({\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} TYPE ${this.typeToSQL(op.to.type)}`,\n params: [],\n });\n }\n if (op.to.notNull !== undefined) {\n out.push({\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} ${op.to.notNull ? 'SET NOT NULL' : 'DROP NOT NULL'}`,\n params: [],\n });\n }\n out.push(...this.compileDefaultChange(op.table, op.column, op.to.default));\n return out;\n }\n\n case InternalOperationKind.COLUMN_RENAME:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.INDEX_CREATE: {\n const cols = this.sqlSafety.columns(op.on).join(', ');\n const uniq = op.unique ? 'UNIQUE ' : '';\n const conc = op.concurrently ? 'CONCURRENTLY ' : '';\n const where = this.sqlSafety.optionalRawFragment('where', op.where);\n return [\n {\n sql: `CREATE ${uniq}INDEX ${conc}${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ''}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.INDEX_DROP: {\n const conc = op.concurrently ? 'CONCURRENTLY ' : '';\n return [{ sql: `DROP INDEX ${conc}${this.sqlSafety.index(op.name)}`, params: [] }];\n }\n\n case InternalOperationKind.FK_CREATE: {\n const cols = this.sqlSafety.columns(op.columns).join(', ');\n const refs = this.sqlSafety.columns(op.refColumns).join(', ');\n const name = op.name ?? `${op.table}_${op.columns.join('_')}_fkey`;\n const notValid = op.notValid ? ' NOT VALID' : '';\n const onDel = op.onDelete ? ` ON DELETE ${op.onDelete}` : '';\n const onUpd = op.onUpdate ? ` ON UPDATE ${op.onUpdate}` : '';\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD CONSTRAINT ${this.sqlSafety.constraint(name)} FOREIGN KEY (${cols}) REFERENCES ${this.sqlSafety.table(op.refTable)} (${refs})${onDel}${onUpd}${notValid}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.FK_VALIDATE:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} VALIDATE CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.FK_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,\n params: [],\n },\n ];\n\n default:\n return [];\n }\n }\n\n /**\n * Compile a DEFAULT value change into ALTER TABLE statements.\n * Extracted to flatten the nested conditional logic.\n */\n private compileDefaultChange(table: string, column: string, defaultValue: unknown): SQL[] {\n if (defaultValue === undefined) {\n return [];\n }\n\n if (defaultValue === null) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} DROP DEFAULT`,\n params: [],\n },\n ];\n }\n\n if (this.sqlSafety.isTrustedFragment(defaultValue)) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT ${this.sqlSafety.rawFragment('default', defaultValue)}`,\n params: [],\n },\n ];\n }\n\n if (\n defaultValue &&\n typeof defaultValue === 'object' &&\n 'now' in defaultValue &&\n (defaultValue as { now?: unknown }).now\n ) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT now()`,\n params: [],\n },\n ];\n }\n\n return [];\n }\n\n private colDDL(column: ColumnSpec): string {\n const parts: string[] = [this.sqlSafety.column(column.name)];\n\n switch (column.type) {\n case InternalColumnType.SERIAL:\n parts.push('SERIAL');\n break;\n case InternalColumnType.INT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.BIGINT:\n parts.push('BIGINT');\n break;\n case InternalColumnType.TEXT:\n parts.push('TEXT');\n break;\n case InternalColumnType.BOOL:\n parts.push('BOOLEAN');\n break;\n case InternalColumnType.TIMESTAMPTZ:\n parts.push('TIMESTAMPTZ');\n break;\n case InternalColumnType.JSONB:\n parts.push('JSONB');\n break;\n case InternalColumnType.UUID:\n parts.push('UUID');\n break;\n }\n\n if (column.notNull) {\n parts.push('NOT NULL');\n }\n const defaultSql = this.sqlSafety.rawDefault(column.default, 'now()');\n if (defaultSql) {\n parts.push(`DEFAULT ${defaultSql}`);\n }\n if (column.unique && !column.primaryKey) {\n parts.push('UNIQUE');\n }\n\n return parts.join(' ');\n }\n\n private typeToSQL(type: ColumnType): string {\n switch (type) {\n case InternalColumnType.SERIAL:\n return 'SERIAL';\n case InternalColumnType.INT:\n return 'INTEGER';\n case InternalColumnType.BIGINT:\n return 'BIGINT';\n case InternalColumnType.TEXT:\n return 'TEXT';\n case InternalColumnType.BOOL:\n return 'BOOLEAN';\n case InternalColumnType.TIMESTAMPTZ:\n return 'TIMESTAMPTZ';\n case InternalColumnType.JSONB:\n return 'JSONB';\n case InternalColumnType.UUID:\n return 'UUID';\n default: {\n const exhaustive: never = type;\n throw new Error(`Unsupported column type: ${exhaustive}`);\n }\n }\n }\n}\n","import type { CompilerFactory } from '../contracts/CompilerFactory';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { PostgresCompiler } from '../dialects/PostgresCompiler';\n\n/**\n * Factory for PostgreSQL migration compilers.\n */\nexport class PostgresCompilerFactory implements CompilerFactory {\n static readonly BRAND = 'tango.migrations.postgres_compiler_factory' as const;\n readonly __tangoBrand: typeof PostgresCompilerFactory.BRAND = PostgresCompilerFactory.BRAND;\n\n /**\n * Narrow an unknown value to the factory that provisions PostgreSQL compilers.\n */\n static isPostgresCompilerFactory(value: unknown): value is PostgresCompilerFactory {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresCompilerFactory.BRAND\n );\n }\n\n /**\n * Create a PostgreSQL SQL compiler instance.\n */\n create(): SQLCompiler {\n return new PostgresCompiler();\n }\n}\n","import type { MigrationOperation } from '../../domain/MigrationOperation';\nimport type { SQL } from '../contracts/SQL';\nimport type { ColumnSpec } from '../../builder/contracts/ColumnSpec';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport { MigrationSqlSafetyAdapter } from '../../internal/MigrationSqlSafetyAdapter';\n\n/**\n * SQLite SQL compiler for migration operations.\n */\nexport class SqliteCompiler implements SQLCompiler {\n static readonly BRAND = 'tango.migrations.sqlite_compiler' as const;\n readonly __tangoBrand: typeof SqliteCompiler.BRAND = SqliteCompiler.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('sqlite');\n\n /**\n * Narrow an unknown value to the SQLite migration compiler implementation.\n */\n static isSqliteCompiler(value: unknown): value is SqliteCompiler {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteCompiler.BRAND\n );\n }\n\n /**\n * Compile a migration operation into one or more SQLite statements.\n */\n compile(op: MigrationOperation): SQL[] {\n switch (op.kind) {\n case InternalOperationKind.TABLE_CREATE: {\n const cols = op.columns.map((c) => this.colDDL(c));\n const pkCols = op.columns\n .filter((c) => c.primaryKey && c.type !== InternalColumnType.SERIAL)\n .map((c) => this.sqlSafety.column(c.name));\n\n if (pkCols.length) {\n cols.push(`PRIMARY KEY (${pkCols.join(', ')})`);\n }\n\n op.columns\n .filter((column) => column.references)\n .forEach((column) => {\n const references = column.references!;\n cols.push(\n `FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})${references.onDelete ? ` ON DELETE ${references.onDelete}` : ''}${references.onUpdate ? ` ON UPDATE ${references.onUpdate}` : ''}`\n );\n });\n\n const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${cols.join(', ')})`;\n return [{ sql, params: [] }];\n }\n\n case InternalOperationKind.TABLE_DROP:\n return [{ sql: `DROP TABLE ${this.sqlSafety.table(op.table)}`, params: [] }];\n\n case InternalOperationKind.COLUMN_ADD:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_RENAME:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.INDEX_CREATE: {\n const cols = this.sqlSafety.columns(op.on).join(', ');\n const uniq = op.unique ? 'UNIQUE ' : '';\n const where = this.sqlSafety.optionalRawFragment('where', op.where);\n return [\n {\n sql: `CREATE ${uniq}INDEX ${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ''}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.INDEX_DROP:\n return [{ sql: `DROP INDEX ${this.sqlSafety.index(op.name)}`, params: [] }];\n\n case InternalOperationKind.COLUMN_ALTER:\n case InternalOperationKind.FK_CREATE:\n case InternalOperationKind.FK_VALIDATE:\n case InternalOperationKind.FK_DROP:\n return [];\n\n default:\n return [];\n }\n }\n\n private colDDL(column: ColumnSpec): string {\n const parts: string[] = [this.sqlSafety.column(column.name)];\n\n switch (column.type) {\n case InternalColumnType.SERIAL:\n parts.push('INTEGER PRIMARY KEY AUTOINCREMENT');\n return parts.join(' ');\n case InternalColumnType.INT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.BIGINT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.TEXT:\n parts.push('TEXT');\n break;\n case InternalColumnType.BOOL:\n parts.push('INTEGER');\n break;\n case InternalColumnType.TIMESTAMPTZ:\n parts.push('TEXT');\n break;\n case InternalColumnType.JSONB:\n parts.push('TEXT');\n break;\n case InternalColumnType.UUID:\n parts.push('TEXT');\n break;\n }\n\n if (column.notNull) {\n parts.push('NOT NULL');\n }\n const defaultSql = this.sqlSafety.rawDefault(column.default, \"(datetime('now'))\");\n if (defaultSql) {\n parts.push(`DEFAULT ${defaultSql}`);\n }\n if (column.unique && !column.primaryKey) {\n parts.push('UNIQUE');\n }\n\n return parts.join(' ');\n }\n}\n","import type { CompilerFactory } from '../contracts/CompilerFactory';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { SqliteCompiler } from '../dialects/SqliteCompiler';\n\n/**\n * Factory for SQLite migration compilers.\n */\nexport class SqliteCompilerFactory implements CompilerFactory {\n static readonly BRAND = 'tango.migrations.sqlite_compiler_factory' as const;\n readonly __tangoBrand: typeof SqliteCompilerFactory.BRAND = SqliteCompilerFactory.BRAND;\n\n /**\n * Narrow an unknown value to the factory that provisions SQLite compilers.\n */\n static isSqliteCompilerFactory(value: unknown): value is SqliteCompilerFactory {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteCompilerFactory.BRAND\n );\n }\n\n /**\n * Create a SQLite SQL compiler instance.\n */\n create(): SQLCompiler {\n return new SqliteCompiler();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
import { MigrationSqlSafetyAdapter } from "./MigrationSqlSafetyAdapter-CGRbB2k2.js";
|
|
1
2
|
|
|
2
3
|
//#region src/introspect/PostgresIntrospector.ts
|
|
3
4
|
var PostgresIntrospector = class PostgresIntrospector {
|
|
4
5
|
static BRAND = "tango.migrations.postgres_introspector";
|
|
5
6
|
__tangoBrand = PostgresIntrospector.BRAND;
|
|
7
|
+
/**
|
|
8
|
+
* Narrow an unknown value to the PostgreSQL schema introspector.
|
|
9
|
+
*/
|
|
6
10
|
static isPostgresIntrospector(value) {
|
|
7
11
|
return typeof value === "object" && value !== null && value.__tangoBrand === PostgresIntrospector.BRAND;
|
|
8
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Read table and column metadata from PostgreSQL system catalogs.
|
|
15
|
+
*/
|
|
9
16
|
async introspect(client) {
|
|
10
17
|
const schema = { tables: {} };
|
|
11
18
|
const tablesRes = await client.query(`
|
|
@@ -33,6 +40,35 @@ var PostgresIntrospector = class PostgresIntrospector {
|
|
|
33
40
|
WHERE i.indrelid = '${tableRow.tbl_oid}'::oid AND i.indisprimary
|
|
34
41
|
`);
|
|
35
42
|
const pks = pkRes.rows.map((pkRow) => pkRow.col);
|
|
43
|
+
const idxRes = await client.query(`
|
|
44
|
+
SELECT
|
|
45
|
+
idx.relname AS name,
|
|
46
|
+
i.indisunique AS unique,
|
|
47
|
+
pg_get_expr(i.indpred, i.indrelid) AS where_clause,
|
|
48
|
+
ARRAY(
|
|
49
|
+
SELECT a.attname
|
|
50
|
+
FROM unnest(i.indkey) WITH ORDINALITY AS k(attnum, ord)
|
|
51
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = k.attnum
|
|
52
|
+
ORDER BY k.ord
|
|
53
|
+
) AS columns
|
|
54
|
+
FROM pg_index i
|
|
55
|
+
JOIN pg_class idx ON idx.oid = i.indexrelid
|
|
56
|
+
LEFT JOIN pg_constraint con ON con.conindid = i.indexrelid
|
|
57
|
+
WHERE i.indrelid = '${tableRow.tbl_oid}'::oid
|
|
58
|
+
AND NOT i.indisprimary
|
|
59
|
+
AND con.oid IS NULL
|
|
60
|
+
`);
|
|
61
|
+
const indexes = idxRes.rows.reduce((accumulator, indexRow) => {
|
|
62
|
+
const name = String(indexRow.name);
|
|
63
|
+
accumulator[name] = {
|
|
64
|
+
name,
|
|
65
|
+
table,
|
|
66
|
+
unique: !!indexRow.unique,
|
|
67
|
+
columns: Array.isArray(indexRow.columns) ? indexRow.columns.map(String) : [],
|
|
68
|
+
where: indexRow.where_clause ? String(indexRow.where_clause) : null
|
|
69
|
+
};
|
|
70
|
+
return accumulator;
|
|
71
|
+
}, {});
|
|
36
72
|
const columns = colsRes.rows.reduce((accumulator, columnRow) => {
|
|
37
73
|
const name = columnRow.name;
|
|
38
74
|
const isPk = pks.includes(name);
|
|
@@ -50,7 +86,7 @@ var PostgresIntrospector = class PostgresIntrospector {
|
|
|
50
86
|
name: table,
|
|
51
87
|
columns,
|
|
52
88
|
pks,
|
|
53
|
-
indexes
|
|
89
|
+
indexes,
|
|
54
90
|
fks: {}
|
|
55
91
|
};
|
|
56
92
|
}));
|
|
@@ -63,9 +99,16 @@ var PostgresIntrospector = class PostgresIntrospector {
|
|
|
63
99
|
var SqliteIntrospector = class SqliteIntrospector {
|
|
64
100
|
static BRAND = "tango.migrations.sqlite_introspector";
|
|
65
101
|
__tangoBrand = SqliteIntrospector.BRAND;
|
|
102
|
+
sqlSafety = new MigrationSqlSafetyAdapter("sqlite");
|
|
103
|
+
/**
|
|
104
|
+
* Narrow an unknown value to the SQLite schema introspector.
|
|
105
|
+
*/
|
|
66
106
|
static isSqliteIntrospector(value) {
|
|
67
107
|
return typeof value === "object" && value !== null && value.__tangoBrand === SqliteIntrospector.BRAND;
|
|
68
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Read table/column/index metadata from SQLite pragmas.
|
|
111
|
+
*/
|
|
69
112
|
async introspect(client) {
|
|
70
113
|
const schema = { tables: {} };
|
|
71
114
|
const tablesRes = await client.query(`
|
|
@@ -73,7 +116,8 @@ var SqliteIntrospector = class SqliteIntrospector {
|
|
|
73
116
|
`);
|
|
74
117
|
for (const tableRow of tablesRes.rows) {
|
|
75
118
|
const table = tableRow.name;
|
|
76
|
-
const
|
|
119
|
+
const safeTable = this.sqlSafety.table(table);
|
|
120
|
+
const colsRes = await client.query(`PRAGMA table_info(${safeTable})`);
|
|
77
121
|
const columns = {};
|
|
78
122
|
const pks = [];
|
|
79
123
|
const indexes = {};
|
|
@@ -90,11 +134,11 @@ var SqliteIntrospector = class SqliteIntrospector {
|
|
|
90
134
|
isUnique: false
|
|
91
135
|
};
|
|
92
136
|
}
|
|
93
|
-
const indexListRes = await client.query(`PRAGMA index_list(${
|
|
137
|
+
const indexListRes = await client.query(`PRAGMA index_list(${safeTable})`);
|
|
94
138
|
for (const indexRow of indexListRes.rows) {
|
|
95
139
|
const name = String(indexRow.name);
|
|
96
140
|
if (name.startsWith("sqlite_autoindex_")) continue;
|
|
97
|
-
const indexInfoRes = await client.query(`PRAGMA index_info(${name})`);
|
|
141
|
+
const indexInfoRes = await client.query(`PRAGMA index_info(${this.sqlSafety.index(name)})`);
|
|
98
142
|
const on = indexInfoRes.rows.map((infoRow) => String(infoRow.name)).filter(Boolean);
|
|
99
143
|
indexes[name] = {
|
|
100
144
|
name,
|
|
@@ -118,4 +162,4 @@ var SqliteIntrospector = class SqliteIntrospector {
|
|
|
118
162
|
|
|
119
163
|
//#endregion
|
|
120
164
|
export { PostgresIntrospector, SqliteIntrospector };
|
|
121
|
-
//# sourceMappingURL=SqliteIntrospector-
|
|
165
|
+
//# sourceMappingURL=SqliteIntrospector-CWwPWhmA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteIntrospector-CWwPWhmA.js","names":["value: unknown","client: DBClient","schema: DbSchema","value: unknown","client: DBClient","schema: DbSchema","columns: Record<string, DbColumn>","pks: string[]","indexes: Record<\n string,\n { name: string; table: string; unique: boolean; columns: string[]; where: string | null }\n >"],"sources":["../src/introspect/PostgresIntrospector.ts","../src/introspect/SqliteIntrospector.ts"],"sourcesContent":["import type { DBClient, DatabaseIntrospector } from './DatabaseIntrospector';\n\n/** Introspected column metadata. */\nexport interface DbColumn {\n name: string;\n type: string;\n notNull: boolean;\n default: string | null;\n isPk: boolean;\n isUnique: boolean;\n}\n\n/** Introspected index metadata. */\nexport interface DbIndex {\n name: string;\n table: string;\n unique: boolean;\n columns: string[];\n where: string | null;\n}\n\n/** Introspected foreign key metadata. */\nexport interface DbForeignKey {\n name: string;\n table: string;\n columns: string[];\n refTable: string;\n refColumns: string[];\n onDelete: string | null;\n onUpdate: string | null;\n validated: boolean;\n}\n\n/** Introspected table metadata. */\nexport interface DbTable {\n name: string;\n columns: Record<string, DbColumn>;\n pks: string[];\n indexes: Record<string, DbIndex>;\n fks: Record<string, DbForeignKey>;\n}\n\n/** Introspected schema metadata. */\nexport interface DbSchema {\n tables: Record<string, DbTable>;\n}\n\n/**\n * PostgreSQL implementation of schema introspection.\n */\nexport class PostgresIntrospector implements DatabaseIntrospector {\n static readonly BRAND = 'tango.migrations.postgres_introspector' as const;\n readonly __tangoBrand: typeof PostgresIntrospector.BRAND = PostgresIntrospector.BRAND;\n\n /**\n * Narrow an unknown value to the PostgreSQL schema introspector.\n */\n static isPostgresIntrospector(value: unknown): value is PostgresIntrospector {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresIntrospector.BRAND\n );\n }\n\n /**\n * Read table and column metadata from PostgreSQL system catalogs.\n */\n async introspect(client: DBClient): Promise<DbSchema> {\n const schema: DbSchema = { tables: {} };\n\n const tablesRes = await client.query<{ tbl_oid: string; table: string }>(`\n SELECT c.oid AS tbl_oid, c.relname AS table\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog','information_schema')\n `);\n\n await Promise.all(\n tablesRes.rows.map(async (tableRow) => {\n const table = tableRow.table as string;\n\n const colsRes = await client.query<{\n name: string;\n type: string;\n not_null: boolean;\n default_expr: string | null;\n }>(`\n SELECT a.attname AS name,\n pg_catalog.format_type(a.atttypid,a.atttypmod) AS type,\n a.attnotnull AS not_null,\n pg_get_expr(ad.adbin, ad.adrelid) AS default_expr\n FROM pg_attribute a\n LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum\n WHERE a.attrelid = '${tableRow.tbl_oid}'::oid AND a.attnum > 0 AND NOT a.attisdropped\n ORDER BY a.attnum\n `);\n\n const pkRes = await client.query<{ col: string }>(`\n SELECT a.attname AS col\n FROM pg_index i\n JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)\n WHERE i.indrelid = '${tableRow.tbl_oid}'::oid AND i.indisprimary\n `);\n const pks = pkRes.rows.map((pkRow) => pkRow.col as string);\n\n const idxRes = await client.query<{\n name: string;\n unique: boolean;\n where_clause: string | null;\n columns: string[];\n }>(`\n SELECT\n idx.relname AS name,\n i.indisunique AS unique,\n pg_get_expr(i.indpred, i.indrelid) AS where_clause,\n ARRAY(\n SELECT a.attname\n FROM unnest(i.indkey) WITH ORDINALITY AS k(attnum, ord)\n JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = k.attnum\n ORDER BY k.ord\n ) AS columns\n FROM pg_index i\n JOIN pg_class idx ON idx.oid = i.indexrelid\n LEFT JOIN pg_constraint con ON con.conindid = i.indexrelid\n WHERE i.indrelid = '${tableRow.tbl_oid}'::oid\n AND NOT i.indisprimary\n AND con.oid IS NULL\n `);\n\n const indexes = idxRes.rows.reduce<Record<string, DbIndex>>((accumulator, indexRow) => {\n const name = String(indexRow.name);\n accumulator[name] = {\n name,\n table,\n unique: !!indexRow.unique,\n columns: Array.isArray(indexRow.columns) ? indexRow.columns.map(String) : [],\n where: indexRow.where_clause ? String(indexRow.where_clause) : null,\n };\n return accumulator;\n }, {});\n\n const columns = colsRes.rows.reduce<Record<string, DbColumn>>((accumulator, columnRow) => {\n const name = columnRow.name as string;\n const isPk = pks.includes(name);\n accumulator[name] = {\n name,\n type: String(columnRow.type),\n notNull: !!columnRow.not_null,\n default: columnRow.default_expr ? String(columnRow.default_expr) : null,\n isPk,\n isUnique: false,\n };\n return accumulator;\n }, {});\n\n schema.tables[table] = {\n name: table,\n columns,\n pks,\n indexes,\n fks: {},\n };\n })\n );\n\n return schema;\n }\n}\n","import type { DBClient, DatabaseIntrospector } from './DatabaseIntrospector';\nimport { MigrationSqlSafetyAdapter } from '../internal/MigrationSqlSafetyAdapter';\n\n/** Introspected column metadata. */\nexport interface DbColumn {\n name: string;\n type: string;\n notNull: boolean;\n default: string | null;\n isPk: boolean;\n isUnique: boolean;\n}\n\n/** Introspected table metadata. */\nexport interface DbTable {\n name: string;\n columns: Record<string, DbColumn>;\n pks: string[];\n indexes: Record<string, { name: string; table: string; unique: boolean; columns: string[]; where: string | null }>;\n fks: Record<\n string,\n {\n name: string;\n table: string;\n columns: string[];\n refTable: string;\n refColumns: string[];\n onDelete: string | null;\n onUpdate: string | null;\n validated: boolean;\n }\n >;\n}\n\n/** Introspected schema metadata. */\nexport interface DbSchema {\n tables: Record<string, DbTable>;\n}\n\n/**\n * SQLite implementation of schema introspection.\n */\nexport class SqliteIntrospector implements DatabaseIntrospector {\n static readonly BRAND = 'tango.migrations.sqlite_introspector' as const;\n readonly __tangoBrand: typeof SqliteIntrospector.BRAND = SqliteIntrospector.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('sqlite');\n\n /**\n * Narrow an unknown value to the SQLite schema introspector.\n */\n static isSqliteIntrospector(value: unknown): value is SqliteIntrospector {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteIntrospector.BRAND\n );\n }\n\n /**\n * Read table/column/index metadata from SQLite pragmas.\n */\n async introspect(client: DBClient): Promise<DbSchema> {\n const schema: DbSchema = { tables: {} };\n\n const tablesRes = await client.query<{ name: string }>(`\n SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\n `);\n\n for (const tableRow of tablesRes.rows) {\n const table = tableRow.name as string;\n const safeTable = this.sqlSafety.table(table);\n\n const colsRes = await client.query<{\n name: string;\n pk: number;\n type: string;\n notnull: number;\n dflt_value: string | null;\n }>(`PRAGMA table_info(${safeTable})`);\n\n const columns: Record<string, DbColumn> = {};\n const pks: string[] = [];\n const indexes: Record<\n string,\n { name: string; table: string; unique: boolean; columns: string[]; where: string | null }\n > = {};\n\n for (const columnRow of colsRes.rows) {\n const name = columnRow.name as string;\n const isPk = columnRow.pk === 1;\n if (isPk) {\n pks.push(name);\n }\n\n columns[name] = {\n name,\n type: String(columnRow.type),\n notNull: columnRow.notnull === 1,\n default: columnRow.dflt_value || null,\n isPk,\n isUnique: false,\n };\n }\n\n const indexListRes = await client.query<{ name: string; unique: number }>(\n `PRAGMA index_list(${safeTable})`\n );\n for (const indexRow of indexListRes.rows) {\n const name = String(indexRow.name);\n if (name.startsWith('sqlite_autoindex_')) {\n continue;\n }\n\n const indexInfoRes = await client.query<{ name: string }>(\n `PRAGMA index_info(${this.sqlSafety.index(name)})`\n );\n const on = indexInfoRes.rows.map((infoRow) => String(infoRow.name)).filter(Boolean);\n indexes[name] = {\n name,\n table,\n columns: on,\n unique: indexRow.unique === 1,\n where: null,\n };\n }\n\n schema.tables[table] = {\n name: table,\n columns,\n pks,\n indexes,\n fks: {},\n };\n }\n\n return schema;\n }\n}\n"],"mappings":"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"}
|