Skip to content

Commit f42fa3d

Browse files
committed
fix quote handling in table import
1 parent 7a3166c commit f42fa3d

File tree

2 files changed

+22
-7
lines changed

2 files changed

+22
-7
lines changed

src/crdb/batchProcessor.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ function parseCSVLine(line: string, delimiter: string): string[] {
7878
return result;
7979
}
8080

81+
// Escape a CSV field - wrap in quotes if it contains delimiter, newline, or quote
82+
function escapeCsvField(field: string, delimiter: string): string {
83+
// Check if field needs escaping
84+
const needsEscaping = field.includes(delimiter) || field.includes('\n') || field.includes('\r') || field.includes('"');
85+
86+
if (!needsEscaping) {
87+
return field;
88+
}
89+
90+
// Escape quotes by doubling them
91+
const escapedField = field.replace(/"/g, '""');
92+
93+
// Wrap in quotes
94+
return `"${escapedField}"`;
95+
}
96+
8197
// Helper functions from csvPreprocessor.ts
8298
function hexToBytes(hex: string): Uint8Array {
8399
hex = hex.replace(/^\\x/i, "");
@@ -287,7 +303,7 @@ export async function preprocessAndLoadInBatches(
287303
// Extract a complete CSV line (handling quoted fields with newlines)
288304
let line = '';
289305
let inQuotes = false;
290-
let lineStart = position;
306+
const lineStart = position;
291307

292308
while (position < content.length) {
293309
const char = content[position];
@@ -338,7 +354,9 @@ export async function preprocessAndLoadInBatches(
338354
protoDecoder: options.protoDecoder,
339355
});
340356

341-
const rowString = processedRow.join(delimiter);
357+
// Escape each field properly before joining
358+
const escapedRow = processedRow.map(field => escapeCsvField(field, delimiter));
359+
const rowString = escapedRow.join(delimiter);
342360
const rowSize = rowString.length + 1; // +1 for newline
343361

344362
// Check if adding this row would exceed batch limits
@@ -361,7 +379,6 @@ export async function preprocessAndLoadInBatches(
361379
const sql = generateCsvReadSql({
362380
fileName: batchFileName,
363381
tableName,
364-
delimiter,
365382
operation,
366383
nodeId, // Always pass nodeId for both CREATE and INSERT
367384
typeHints,
@@ -401,7 +418,6 @@ export async function preprocessAndLoadInBatches(
401418
const sql = generateCsvReadSql({
402419
fileName: batchFileName,
403420
tableName,
404-
delimiter,
405421
operation,
406422
nodeId, // Always pass nodeId for both CREATE and INSERT
407423
typeHints,

src/crdb/csvUtils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
export interface CsvReadOptions {
66
fileName: string;
77
tableName: string;
8-
delimiter: string;
98
operation: 'create' | 'insert';
109
nodeId?: number;
1110
typeHints?: Map<string, string>;
1211
headers?: string[];
1312
}
1413

1514
export function generateCsvReadSql(options: CsvReadOptions): string {
16-
const { fileName, tableName, delimiter, operation, nodeId, typeHints, headers } = options;
15+
const { fileName, tableName, operation, nodeId, typeHints, headers } = options;
1716
const quotedTableName = `"${tableName}"`;
1817

1918
// Build column specification if we have type hints and headers
@@ -31,7 +30,7 @@ export function generateCsvReadSql(options: CsvReadOptions): string {
3130

3231
const readOptions = `
3332
'${fileName}',
34-
delim = '${delimiter}',
33+
delim = '\t',
3534
quote = '"',
3635
escape = '"',
3736
header = true,

0 commit comments

Comments
 (0)