csv-to-pg 3.4.0 → 3.5.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/esm/parse.js +75 -0
- package/package.json +2 -2
- package/parse.js +75 -0
package/esm/parse.js
CHANGED
|
@@ -29,6 +29,37 @@ const parseJson = (value) => {
|
|
|
29
29
|
return value;
|
|
30
30
|
return value ? JSON.stringify(value) : undefined;
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Convert a PostgreSQL interval object to a PostgreSQL interval string.
|
|
34
|
+
* node-postgres returns intervals as objects like { hours: 12, minutes: 6, seconds: 41 }
|
|
35
|
+
*/
|
|
36
|
+
const formatInterval = (value) => {
|
|
37
|
+
if (typeof value === 'string') {
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
if (value && typeof value === 'object') {
|
|
41
|
+
const interval = value;
|
|
42
|
+
const parts = [];
|
|
43
|
+
if (interval.years)
|
|
44
|
+
parts.push(`${interval.years} year${interval.years !== 1 ? 's' : ''}`);
|
|
45
|
+
if (interval.months)
|
|
46
|
+
parts.push(`${interval.months} mon${interval.months !== 1 ? 's' : ''}`);
|
|
47
|
+
if (interval.days)
|
|
48
|
+
parts.push(`${interval.days} day${interval.days !== 1 ? 's' : ''}`);
|
|
49
|
+
// Build time component
|
|
50
|
+
const hours = interval.hours || 0;
|
|
51
|
+
const minutes = interval.minutes || 0;
|
|
52
|
+
const seconds = interval.seconds || 0;
|
|
53
|
+
const milliseconds = interval.milliseconds || 0;
|
|
54
|
+
if (hours || minutes || seconds || milliseconds) {
|
|
55
|
+
const totalSeconds = seconds + milliseconds / 1000;
|
|
56
|
+
const timeStr = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${totalSeconds.toFixed(milliseconds ? 6 : 0).padStart(milliseconds ? 9 : 2, '0')}`;
|
|
57
|
+
parts.push(timeStr);
|
|
58
|
+
}
|
|
59
|
+
return parts.length > 0 ? parts.join(' ') : '00:00:00';
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
};
|
|
32
63
|
/**
|
|
33
64
|
* Escape a single array element for PostgreSQL array literal format.
|
|
34
65
|
* Handles: NULL, quotes, backslashes, commas, braces, and whitespace.
|
|
@@ -255,6 +286,50 @@ const getCoercionFunc = (type, from, opts, fieldName) => {
|
|
|
255
286
|
});
|
|
256
287
|
return wrapValue(val, opts);
|
|
257
288
|
};
|
|
289
|
+
case 'uuid[]':
|
|
290
|
+
return (record) => {
|
|
291
|
+
const rawValue = record[from[0]];
|
|
292
|
+
if (isNullToken(rawValue)) {
|
|
293
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is empty or null');
|
|
294
|
+
}
|
|
295
|
+
// Handle array values - validate each UUID
|
|
296
|
+
if (Array.isArray(rawValue)) {
|
|
297
|
+
if (rawValue.length === 0) {
|
|
298
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'array is empty');
|
|
299
|
+
}
|
|
300
|
+
const uuidRegex = /^([0-9a-fA-F]{8})-(([0-9a-fA-F]{4}-){3})([0-9a-fA-F]{12})$/i;
|
|
301
|
+
for (const item of rawValue) {
|
|
302
|
+
if (!uuidRegex.test(String(item))) {
|
|
303
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, `array contains invalid UUID: ${item}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const arrayLiteral = psqlArray(rawValue);
|
|
307
|
+
if (isEmpty(arrayLiteral)) {
|
|
308
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'failed to format array');
|
|
309
|
+
}
|
|
310
|
+
const val = nodes.aConst({
|
|
311
|
+
sval: ast.string({ sval: String(arrayLiteral) })
|
|
312
|
+
});
|
|
313
|
+
return wrapValue(val, opts);
|
|
314
|
+
}
|
|
315
|
+
// If not an array, treat as empty/null
|
|
316
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is not an array');
|
|
317
|
+
};
|
|
318
|
+
case 'interval':
|
|
319
|
+
return (record) => {
|
|
320
|
+
const rawValue = record[from[0]];
|
|
321
|
+
if (isNullToken(rawValue)) {
|
|
322
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is empty or null');
|
|
323
|
+
}
|
|
324
|
+
const value = formatInterval(rawValue);
|
|
325
|
+
if (isEmpty(value)) {
|
|
326
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is empty or invalid interval');
|
|
327
|
+
}
|
|
328
|
+
const val = nodes.aConst({
|
|
329
|
+
sval: ast.string({ sval: String(value) })
|
|
330
|
+
});
|
|
331
|
+
return wrapValue(val, opts);
|
|
332
|
+
};
|
|
258
333
|
case 'timestamp':
|
|
259
334
|
case 'timestamptz':
|
|
260
335
|
case 'date':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "csv-to-pg",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
5
5
|
"description": "csv to pg statements",
|
|
6
6
|
"main": "index.js",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"js-yaml": "^3.14.0",
|
|
51
51
|
"pgsql-deparser": "^17.17.2"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "481b3a50b4eec2da6b376c4cd1868065e1e28edb"
|
|
54
54
|
}
|
package/parse.js
CHANGED
|
@@ -35,6 +35,37 @@ const parseJson = (value) => {
|
|
|
35
35
|
return value;
|
|
36
36
|
return value ? JSON.stringify(value) : undefined;
|
|
37
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Convert a PostgreSQL interval object to a PostgreSQL interval string.
|
|
40
|
+
* node-postgres returns intervals as objects like { hours: 12, minutes: 6, seconds: 41 }
|
|
41
|
+
*/
|
|
42
|
+
const formatInterval = (value) => {
|
|
43
|
+
if (typeof value === 'string') {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
if (value && typeof value === 'object') {
|
|
47
|
+
const interval = value;
|
|
48
|
+
const parts = [];
|
|
49
|
+
if (interval.years)
|
|
50
|
+
parts.push(`${interval.years} year${interval.years !== 1 ? 's' : ''}`);
|
|
51
|
+
if (interval.months)
|
|
52
|
+
parts.push(`${interval.months} mon${interval.months !== 1 ? 's' : ''}`);
|
|
53
|
+
if (interval.days)
|
|
54
|
+
parts.push(`${interval.days} day${interval.days !== 1 ? 's' : ''}`);
|
|
55
|
+
// Build time component
|
|
56
|
+
const hours = interval.hours || 0;
|
|
57
|
+
const minutes = interval.minutes || 0;
|
|
58
|
+
const seconds = interval.seconds || 0;
|
|
59
|
+
const milliseconds = interval.milliseconds || 0;
|
|
60
|
+
if (hours || minutes || seconds || milliseconds) {
|
|
61
|
+
const totalSeconds = seconds + milliseconds / 1000;
|
|
62
|
+
const timeStr = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${totalSeconds.toFixed(milliseconds ? 6 : 0).padStart(milliseconds ? 9 : 2, '0')}`;
|
|
63
|
+
parts.push(timeStr);
|
|
64
|
+
}
|
|
65
|
+
return parts.length > 0 ? parts.join(' ') : '00:00:00';
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
};
|
|
38
69
|
/**
|
|
39
70
|
* Escape a single array element for PostgreSQL array literal format.
|
|
40
71
|
* Handles: NULL, quotes, backslashes, commas, braces, and whitespace.
|
|
@@ -264,6 +295,50 @@ const getCoercionFunc = (type, from, opts, fieldName) => {
|
|
|
264
295
|
});
|
|
265
296
|
return (0, utils_2.wrapValue)(val, opts);
|
|
266
297
|
};
|
|
298
|
+
case 'uuid[]':
|
|
299
|
+
return (record) => {
|
|
300
|
+
const rawValue = record[from[0]];
|
|
301
|
+
if (isNullToken(rawValue)) {
|
|
302
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is empty or null');
|
|
303
|
+
}
|
|
304
|
+
// Handle array values - validate each UUID
|
|
305
|
+
if (Array.isArray(rawValue)) {
|
|
306
|
+
if (rawValue.length === 0) {
|
|
307
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'array is empty');
|
|
308
|
+
}
|
|
309
|
+
const uuidRegex = /^([0-9a-fA-F]{8})-(([0-9a-fA-F]{4}-){3})([0-9a-fA-F]{12})$/i;
|
|
310
|
+
for (const item of rawValue) {
|
|
311
|
+
if (!uuidRegex.test(String(item))) {
|
|
312
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, `array contains invalid UUID: ${item}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const arrayLiteral = psqlArray(rawValue);
|
|
316
|
+
if (isEmpty(arrayLiteral)) {
|
|
317
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'failed to format array');
|
|
318
|
+
}
|
|
319
|
+
const val = utils_1.nodes.aConst({
|
|
320
|
+
sval: utils_1.ast.string({ sval: String(arrayLiteral) })
|
|
321
|
+
});
|
|
322
|
+
return (0, utils_2.wrapValue)(val, opts);
|
|
323
|
+
}
|
|
324
|
+
// If not an array, treat as empty/null
|
|
325
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is not an array');
|
|
326
|
+
};
|
|
327
|
+
case 'interval':
|
|
328
|
+
return (record) => {
|
|
329
|
+
const rawValue = record[from[0]];
|
|
330
|
+
if (isNullToken(rawValue)) {
|
|
331
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is empty or null');
|
|
332
|
+
}
|
|
333
|
+
const value = formatInterval(rawValue);
|
|
334
|
+
if (isEmpty(value)) {
|
|
335
|
+
return makeNullOrThrow(fieldName, rawValue, type, required, 'value is empty or invalid interval');
|
|
336
|
+
}
|
|
337
|
+
const val = utils_1.nodes.aConst({
|
|
338
|
+
sval: utils_1.ast.string({ sval: String(value) })
|
|
339
|
+
});
|
|
340
|
+
return (0, utils_2.wrapValue)(val, opts);
|
|
341
|
+
};
|
|
267
342
|
case 'timestamp':
|
|
268
343
|
case 'timestamptz':
|
|
269
344
|
case 'date':
|