Skip to content

Commit 32ed549

Browse files
committed
Simplify CSV header parsing - remove cleaning/validation, quote all identifiers
- Remove all header cleaning (no lowercase, no trim, no normalization) - Remove validation against SQL identifier regex - Quote all column identifiers in COPY command for exact case-sensitive matching - Properly escape double quotes in column names (" -> "") - Use csv-parse with minimal config: bom, to_line=1, skip_empty_lines - Let PostgreSQL handle any invalid column names or mismatches This preserves CSV headers exactly as-is and passes them to PostgreSQL with quoted identifiers, ensuring case-sensitive matching between CSV headers and table columns. Co-Authored-By: Dan Lynch <pyramation@gmail.com>
1 parent ec13f90 commit 32ed549

1 file changed

Lines changed: 9 additions & 24 deletions

File tree

  • packages/pgsql-test/src/seed

packages/pgsql-test/src/seed/csv.ts

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import { SeedAdapter, SeedContext } from './types';
1111

1212
const log = new Logger('csv');
1313

14-
const VALID_IDENTIFIER = /^[a-z_][a-z0-9_]*$/;
15-
1614
interface CsvSeedMap {
1715
[tableName: string]: string;
1816
}
@@ -36,9 +34,7 @@ async function parseCsvHeader(filePath: string): Promise<string[]> {
3634
const parser = parse({
3735
bom: true,
3836
to_line: 1,
39-
relax_column_count: true,
4037
skip_empty_lines: true,
41-
trim: true,
4238
});
4339

4440
return new Promise<string[]>((resolve, reject) => {
@@ -51,26 +47,14 @@ async function parseCsvHeader(filePath: string): Promise<string[]> {
5147
parser.on('readable', () => {
5248
const row = parser.read() as string[] | null;
5349
if (!row) return;
54-
try {
55-
const cols = row.map((c) => {
56-
const cleaned = c.trim().replace(/\s+/g, '_').toLowerCase();
57-
if (!VALID_IDENTIFIER.test(cleaned)) {
58-
throw new Error(
59-
`Invalid column "${c}" → "${cleaned}". Must match /^[a-z_][a-z0-9_]*$/.`
60-
);
61-
}
62-
return cleaned;
63-
});
64-
65-
if (cols.length === 0) {
66-
throw new Error('CSV header has no columns');
67-
}
68-
69-
cleanup();
70-
resolve(cols);
71-
} catch (e) {
72-
cleanup(e);
50+
51+
if (row.length === 0) {
52+
cleanup(new Error('CSV header has no columns'));
53+
return;
7354
}
55+
56+
cleanup();
57+
resolve(row);
7458
});
7559

7660
parser.on('error', cleanup);
@@ -85,7 +69,8 @@ export async function copyCsvIntoTable(pg: PgTestClient, table: string, filePath
8569

8670
const columns = await parseCsvHeader(filePath);
8771

88-
const columnList = columns.join(', ');
72+
const quotedColumns = columns.map(col => `"${col.replace(/"/g, '""')}"`);
73+
const columnList = quotedColumns.join(', ');
8974
const copyCommand = `COPY ${table} (${columnList}) FROM STDIN WITH CSV HEADER`;
9075

9176
log.info(`Using columns: ${columnList}`);

0 commit comments

Comments
 (0)