csv-to-pg 3.4.0 → 3.4.1

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.
Files changed (3) hide show
  1. package/esm/parse.js +75 -0
  2. package/package.json +2 -2
  3. 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.4.0",
3
+ "version": "3.4.1",
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": "67155381d1850ec9fdaf8f80ef1c6b118a6111c7"
53
+ "gitHead": "cf0d7de6f89b52e1e3dcd2cda068e7be27296615"
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':