/** * Database Import — data copy (phase 2) * * Plans and executes a row copy from a source Postgres/Supabase database into a * target database that was rebuilt from the imported schema (phase 1). The * planning logic is pure and fully unit-testable; the actual reads/writes go * through the injectable `SourceReader` / `TargetWriter` interfaces so the * executor can be tested with fakes (mirroring phase 1's `Introspector`). * * Mapping rules mirror phase 1 exactly: * - target table name = snakeCase(entity name) = snakeCase(source table name) * - single-column FK = renamed to the platform's camelCase `Id` * - every other column = copied under its original name */ import { IntrospectedSchema, IntrospectedTable } from "./types"; export interface ColumnMapping { sourceColumn: string; targetColumn: string; } export interface TableCopyPlan { sourceTable: string; targetTable: string; /** Kept source columns (those whose mapped target column exists), in order. */ sourceColumns: string[]; /** Target column names, parallel to sourceColumns. */ targetColumns: string[]; /** Source PK column used to order paged reads (null when there is no PK). */ orderBy: string | null; /** Target PK column whose sequence to reset after copy (null if not serial). */ serialPkColumn: string | null; } export interface CopyPlan { tables: TableCopyPlan[]; skippedColumns: Array<{ table: string; column: string; reason: string; }>; /** Source tables with no corresponding target table (skipped entirely). */ missingTargetTables: string[]; /** Tables involved in a FK cycle (copied best-effort, after acyclic tables). */ cyclicTables: string[]; } /** The target table name the generator produces for a given source table. */ export declare function targetTableName(sourceTable: string): string; /** Map every source column to its target column name (FK rename or identity). */ export declare function buildColumnMapping(table: IntrospectedTable): ColumnMapping[]; /** * Order tables so each is copied after the tables it references via FK * (parents before children). Self-references are ignored; tables in a cycle are * reported and appended best-effort at the end. */ export declare function topoSortTables(tables: IntrospectedTable[]): { order: string[]; cyclic: string[]; }; /** * Build a full copy plan from the source schema and the set of columns that * actually exist in each target table. Source columns whose mapped target * column is missing are dropped (and reported); source tables with no target * table are skipped entirely. */ export declare function planCopy(source: IntrospectedSchema, targetColumnsByTable: Map>, tableFilter?: string[]): CopyPlan; /** Coerce a value read from pg into something safe to bind on insert. */ export declare function coerceValue(value: unknown): unknown; /** Build a parameterized multi-row INSERT statement for a batch. */ export declare function buildInsertSql(table: string, columns: string[], rows: unknown[][], schema?: string): { text: string; values: unknown[]; }; export interface SourceReader { totalRows(table: string): Promise; readBatch(table: string, columns: string[], orderBy: string | null, offset: number, limit: number): Promise; } export interface TargetWriter { rowCount(table: string): Promise; insertBatch(table: string, columns: string[], rows: unknown[][]): Promise; resetSequence(table: string, column: string): Promise; begin(): Promise; commit(): Promise; rollback(): Promise; } export interface CopyResult { dryRun: boolean; perTable: Array<{ table: string; rows: number; }>; skippedColumns: CopyPlan["skippedColumns"]; missingTargetTables: string[]; cyclicTables: string[]; /** Target tables that already had rows (blocks a real, insert-only run). */ blockedTables: string[]; } export interface ExecuteOptions { batchSize?: number; dryRun?: boolean; } /** * Execute a copy plan. Insert-only: if any target table already has rows the * run aborts before writing anything. The whole copy runs in one transaction * on the target, so a failure rolls everything back. */ export declare function executeCopy(plan: CopyPlan, source: SourceReader, target: TargetWriter, options?: ExecuteOptions): Promise; /** Build the target column lookup used by planCopy from an introspected target. */ export declare function targetColumnsFromSchema(target: IntrospectedSchema): Map>;