relq 1.0.2 → 1.0.4

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 (92) hide show
  1. package/dist/cjs/cli/commands/add.cjs +403 -27
  2. package/dist/cjs/cli/commands/branch.cjs +13 -23
  3. package/dist/cjs/cli/commands/checkout.cjs +16 -29
  4. package/dist/cjs/cli/commands/cherry-pick.cjs +3 -4
  5. package/dist/cjs/cli/commands/commit.cjs +21 -29
  6. package/dist/cjs/cli/commands/diff.cjs +28 -32
  7. package/dist/cjs/cli/commands/export.cjs +7 -7
  8. package/dist/cjs/cli/commands/fetch.cjs +15 -21
  9. package/dist/cjs/cli/commands/generate.cjs +28 -54
  10. package/dist/cjs/cli/commands/history.cjs +19 -40
  11. package/dist/cjs/cli/commands/import.cjs +34 -41
  12. package/dist/cjs/cli/commands/init.cjs +69 -59
  13. package/dist/cjs/cli/commands/introspect.cjs +4 -8
  14. package/dist/cjs/cli/commands/log.cjs +26 -32
  15. package/dist/cjs/cli/commands/merge.cjs +24 -41
  16. package/dist/cjs/cli/commands/migrate.cjs +12 -25
  17. package/dist/cjs/cli/commands/pull.cjs +216 -106
  18. package/dist/cjs/cli/commands/push.cjs +35 -75
  19. package/dist/cjs/cli/commands/remote.cjs +2 -1
  20. package/dist/cjs/cli/commands/reset.cjs +22 -43
  21. package/dist/cjs/cli/commands/resolve.cjs +12 -14
  22. package/dist/cjs/cli/commands/rollback.cjs +16 -38
  23. package/dist/cjs/cli/commands/stash.cjs +5 -7
  24. package/dist/cjs/cli/commands/status.cjs +5 -10
  25. package/dist/cjs/cli/commands/sync.cjs +30 -50
  26. package/dist/cjs/cli/commands/tag.cjs +3 -4
  27. package/dist/cjs/cli/index.cjs +72 -9
  28. package/dist/cjs/cli/utils/change-tracker.cjs +107 -3
  29. package/dist/cjs/cli/utils/cli-utils.cjs +217 -0
  30. package/dist/cjs/cli/utils/config-loader.cjs +34 -8
  31. package/dist/cjs/cli/utils/fast-introspect.cjs +109 -3
  32. package/dist/cjs/cli/utils/git-utils.cjs +42 -161
  33. package/dist/cjs/cli/utils/pool-manager.cjs +156 -0
  34. package/dist/cjs/cli/utils/project-root.cjs +56 -5
  35. package/dist/cjs/cli/utils/relqignore.cjs +1 -0
  36. package/dist/cjs/cli/utils/repo-manager.cjs +47 -0
  37. package/dist/cjs/cli/utils/schema-comparator.cjs +301 -11
  38. package/dist/cjs/cli/utils/schema-diff.cjs +202 -1
  39. package/dist/cjs/cli/utils/schema-hash.cjs +2 -1
  40. package/dist/cjs/cli/utils/schema-introspect.cjs +7 -3
  41. package/dist/cjs/cli/utils/snapshot-manager.cjs +1 -0
  42. package/dist/cjs/cli/utils/spinner.cjs +14 -106
  43. package/dist/cjs/cli/utils/sql-generator.cjs +10 -2
  44. package/dist/cjs/cli/utils/type-generator.cjs +28 -16
  45. package/dist/config.d.ts +16 -6
  46. package/dist/esm/cli/commands/add.js +372 -29
  47. package/dist/esm/cli/commands/branch.js +14 -24
  48. package/dist/esm/cli/commands/checkout.js +16 -29
  49. package/dist/esm/cli/commands/cherry-pick.js +3 -4
  50. package/dist/esm/cli/commands/commit.js +22 -30
  51. package/dist/esm/cli/commands/diff.js +6 -10
  52. package/dist/esm/cli/commands/export.js +8 -8
  53. package/dist/esm/cli/commands/fetch.js +14 -20
  54. package/dist/esm/cli/commands/generate.js +28 -54
  55. package/dist/esm/cli/commands/history.js +11 -32
  56. package/dist/esm/cli/commands/import.js +35 -42
  57. package/dist/esm/cli/commands/init.js +65 -55
  58. package/dist/esm/cli/commands/introspect.js +4 -8
  59. package/dist/esm/cli/commands/log.js +6 -12
  60. package/dist/esm/cli/commands/merge.js +20 -37
  61. package/dist/esm/cli/commands/migrate.js +12 -25
  62. package/dist/esm/cli/commands/pull.js +204 -94
  63. package/dist/esm/cli/commands/push.js +21 -61
  64. package/dist/esm/cli/commands/remote.js +2 -1
  65. package/dist/esm/cli/commands/reset.js +16 -37
  66. package/dist/esm/cli/commands/resolve.js +13 -15
  67. package/dist/esm/cli/commands/rollback.js +16 -38
  68. package/dist/esm/cli/commands/stash.js +6 -8
  69. package/dist/esm/cli/commands/status.js +6 -11
  70. package/dist/esm/cli/commands/sync.js +30 -50
  71. package/dist/esm/cli/commands/tag.js +3 -4
  72. package/dist/esm/cli/index.js +72 -9
  73. package/dist/esm/cli/utils/change-tracker.js +107 -3
  74. package/dist/esm/cli/utils/cli-utils.js +169 -0
  75. package/dist/esm/cli/utils/config-loader.js +34 -8
  76. package/dist/esm/cli/utils/fast-introspect.js +109 -3
  77. package/dist/esm/cli/utils/git-utils.js +2 -124
  78. package/dist/esm/cli/utils/pool-manager.js +114 -0
  79. package/dist/esm/cli/utils/project-root.js +55 -5
  80. package/dist/esm/cli/utils/relqignore.js +1 -0
  81. package/dist/esm/cli/utils/repo-manager.js +42 -0
  82. package/dist/esm/cli/utils/schema-comparator.js +301 -11
  83. package/dist/esm/cli/utils/schema-diff.js +202 -1
  84. package/dist/esm/cli/utils/schema-hash.js +2 -1
  85. package/dist/esm/cli/utils/schema-introspect.js +7 -3
  86. package/dist/esm/cli/utils/snapshot-manager.js +1 -0
  87. package/dist/esm/cli/utils/spinner.js +1 -101
  88. package/dist/esm/cli/utils/sql-generator.js +10 -2
  89. package/dist/esm/cli/utils/type-generator.js +28 -16
  90. package/dist/index.d.ts +25 -8
  91. package/dist/schema-builder.d.ts +18 -7
  92. package/package.json +1 -1
@@ -1,14 +1,18 @@
1
1
  import { createChange } from "./change-tracker.js";
2
+ function arraysEqual(a, b) {
3
+ if (a.length !== b.length)
4
+ return false;
5
+ return a.every((val, idx) => val === b[idx]);
6
+ }
2
7
  export function compareSchemas(before, after) {
3
8
  const changes = [];
4
9
  changes.push(...compareExtensions(before.extensions || [], after.extensions || []));
5
10
  changes.push(...compareEnums(before.enums || [], after.enums || []));
6
11
  changes.push(...compareDomains(before.domains || [], after.domains || []));
7
12
  changes.push(...compareCompositeTypes(before.compositeTypes || [], after.compositeTypes || []));
13
+ changes.push(...compareSequences(before.sequences || [], after.sequences || []));
8
14
  changes.push(...compareTables(before.tables, after.tables));
9
- const beforeViews = before.views || [];
10
- const afterViews = after.views || [];
11
- changes.push(...compareViews(beforeViews, afterViews));
15
+ changes.push(...compareViews(before.views || [], after.views || []));
12
16
  changes.push(...compareFunctions(before.functions || [], after.functions || []));
13
17
  changes.push(...compareTriggers(before.triggers || [], after.triggers || []));
14
18
  return changes;
@@ -98,6 +102,38 @@ function compareCompositeTypes(before, after) {
98
102
  }
99
103
  return changes;
100
104
  }
105
+ function compareSequences(before, after) {
106
+ const changes = [];
107
+ const beforeMap = new Map(before.map(s => [s.name, s]));
108
+ const afterMap = new Map(after.map(s => [s.name, s]));
109
+ for (const [name, seq] of afterMap) {
110
+ if (!beforeMap.has(name)) {
111
+ changes.push(createChange('CREATE', 'SEQUENCE', name, null, seq));
112
+ }
113
+ else {
114
+ const beforeSeq = beforeMap.get(name);
115
+ if (hasSequenceChanged(beforeSeq, seq)) {
116
+ changes.push(createChange('ALTER', 'SEQUENCE', name, beforeSeq, seq));
117
+ }
118
+ }
119
+ }
120
+ for (const [name, seq] of beforeMap) {
121
+ if (!afterMap.has(name)) {
122
+ changes.push(createChange('DROP', 'SEQUENCE', name, seq, null));
123
+ }
124
+ }
125
+ return changes;
126
+ }
127
+ function hasSequenceChanged(before, after) {
128
+ return (before.dataType !== after.dataType ||
129
+ before.start !== after.start ||
130
+ before.increment !== after.increment ||
131
+ before.minValue !== after.minValue ||
132
+ before.maxValue !== after.maxValue ||
133
+ before.cache !== after.cache ||
134
+ before.cycle !== after.cycle ||
135
+ before.ownedBy !== after.ownedBy);
136
+ }
101
137
  function compareTables(before, after) {
102
138
  const changes = [];
103
139
  const beforeMap = new Map(before.map(t => [t.name, t]));
@@ -105,12 +141,57 @@ function compareTables(before, after) {
105
141
  for (const [name, table] of afterMap) {
106
142
  if (!beforeMap.has(name)) {
107
143
  changes.push(createChange('CREATE', 'TABLE', name, null, tableToChangeData(table)));
144
+ const tableIndexes = table.indexes || [];
145
+ for (const idx of tableIndexes) {
146
+ if (!idx.isPrimary) {
147
+ changes.push(createChange('CREATE', 'INDEX', idx.name, null, {
148
+ name: idx.name,
149
+ tableName: name,
150
+ columns: Array.isArray(idx.columns) ? idx.columns : [idx.columns],
151
+ isUnique: idx.isUnique,
152
+ type: idx.type,
153
+ }));
154
+ }
155
+ }
156
+ if (table.isPartitioned && table.partitionType && table.partitionKey) {
157
+ changes.push(createChange('CREATE', 'PARTITION', name, null, {
158
+ tableName: name,
159
+ type: table.partitionType,
160
+ key: table.partitionKey,
161
+ }));
162
+ const childPartitions = table.childPartitions || [];
163
+ for (const cp of childPartitions) {
164
+ changes.push(createChange('CREATE', 'PARTITION_CHILD', cp.name, null, {
165
+ name: cp.name,
166
+ parentTable: name,
167
+ bound: cp.partitionBound,
168
+ }, name));
169
+ }
170
+ }
171
+ if (table.comment) {
172
+ changes.push(createChange('CREATE', 'TABLE_COMMENT', name, null, {
173
+ tableName: name,
174
+ comment: table.comment,
175
+ }, name));
176
+ }
177
+ for (const col of table.columns) {
178
+ if (col.comment) {
179
+ changes.push(createChange('CREATE', 'COLUMN_COMMENT', col.name, null, {
180
+ tableName: name,
181
+ columnName: col.name,
182
+ comment: col.comment,
183
+ }, name));
184
+ }
185
+ }
108
186
  }
109
187
  else {
110
188
  const beforeTable = beforeMap.get(name);
111
189
  changes.push(...compareColumns(beforeTable.columns, table.columns, name));
112
190
  changes.push(...compareIndexes(beforeTable.indexes || [], table.indexes || [], name));
113
191
  changes.push(...compareConstraints(beforeTable.constraints || [], table.constraints || [], name));
192
+ changes.push(...compareTableComments(beforeTable, table, name));
193
+ changes.push(...compareColumnComments(beforeTable.columns, table.columns, name));
194
+ changes.push(...comparePartitions(beforeTable, table, name));
114
195
  }
115
196
  }
116
197
  for (const [name, table] of beforeMap) {
@@ -176,10 +257,65 @@ function columnToChangeData(col) {
176
257
  scale: col.scale,
177
258
  };
178
259
  }
260
+ function normalizeDataType(type) {
261
+ if (!type)
262
+ return '';
263
+ let normalized = type.toLowerCase().trim();
264
+ const typeMap = {
265
+ 'int4': 'integer',
266
+ 'int8': 'bigint',
267
+ 'int2': 'smallint',
268
+ 'float4': 'real',
269
+ 'float8': 'double precision',
270
+ 'bool': 'boolean',
271
+ 'timestamptz': 'timestamp',
272
+ 'timetz': 'time',
273
+ '_text': 'text[]',
274
+ '_int4': 'integer[]',
275
+ '_int8': 'bigint[]',
276
+ '_varchar': 'varchar[]',
277
+ '_uuid': 'uuid[]',
278
+ '_bool': 'boolean[]',
279
+ '_jsonb': 'jsonb[]',
280
+ 'character varying': 'varchar',
281
+ 'character': 'char',
282
+ 'double precision': 'doubleprecision',
283
+ };
284
+ if (normalized.startsWith('_') && !typeMap[normalized]) {
285
+ return normalized.substring(1) + '[]';
286
+ }
287
+ return typeMap[normalized] || normalized;
288
+ }
289
+ function normalizeDefault(value) {
290
+ if (value == null)
291
+ return null;
292
+ let normalized = value.trim().toLowerCase();
293
+ normalized = normalized.replace(/array\[\]::([\w\[\]]+)/gi, "'empty_array'");
294
+ normalized = normalized.replace(/::\w+(?:\s+\w+)?(?:\[\])?/g, '');
295
+ normalized = normalized.replace(/^array\[\]$/i, "'empty_array'");
296
+ normalized = normalized.replace(/now\s*\(\s*\)/g, 'now()');
297
+ normalized = normalized.replace(/gen_random_uuid\s*\(\s*\)/g, 'gen_random_uuid()');
298
+ normalized = normalized.replace(/current_date/g, 'current_date');
299
+ normalized = normalized.replace(/current_timestamp/g, 'current_timestamp');
300
+ normalized = normalized.replace(/'\[\]'/g, "'empty_array'");
301
+ normalized = normalized.replace(/'\{\}'/g, "'empty_array'");
302
+ normalized = normalized.replace(/:\s+/g, ':');
303
+ normalized = normalized.replace(/,\s+/g, ',');
304
+ normalized = normalized.replace(/\[\s+/g, '[');
305
+ normalized = normalized.replace(/\s+\]/g, ']');
306
+ normalized = normalized.replace(/\{\s+/g, '{');
307
+ normalized = normalized.replace(/\s+\}/g, '}');
308
+ if (normalized === 'true' || normalized === "'t'" || normalized === 't')
309
+ normalized = 'true';
310
+ if (normalized === 'false' || normalized === "'f'" || normalized === 'f')
311
+ normalized = 'false';
312
+ normalized = normalized.replace(/^'([^']*)'$/, '$1');
313
+ return normalized || null;
314
+ }
179
315
  function hasColumnChanged(before, after) {
180
- return (before.dataType.toLowerCase() !== after.dataType.toLowerCase() ||
316
+ return (normalizeDataType(before.dataType) !== normalizeDataType(after.dataType) ||
181
317
  before.isNullable !== after.isNullable ||
182
- before.defaultValue !== after.defaultValue ||
318
+ normalizeDefault(before.defaultValue) !== normalizeDefault(after.defaultValue) ||
183
319
  before.isPrimaryKey !== after.isPrimaryKey ||
184
320
  before.isUnique !== after.isUnique);
185
321
  }
@@ -211,10 +347,14 @@ function compareIndexes(before, after, tableName) {
211
347
  }
212
348
  function compareConstraints(before, after, tableName) {
213
349
  const changes = [];
214
- const beforeMap = new Map(before.map(c => [c.name, c]));
215
- const afterMap = new Map(after.map(c => [c.name, c]));
216
- for (const [name, con] of afterMap) {
217
- if (!beforeMap.has(name)) {
350
+ const beforeChecks = before.filter(c => c.type?.toUpperCase() === 'CHECK');
351
+ const afterChecks = after.filter(c => c.type?.toUpperCase() === 'CHECK');
352
+ const beforeOther = before.filter(c => c.type?.toUpperCase() !== 'CHECK');
353
+ const afterOther = after.filter(c => c.type?.toUpperCase() !== 'CHECK');
354
+ const beforeOtherMap = new Map(beforeOther.map(c => [c.name, c]));
355
+ const afterOtherMap = new Map(afterOther.map(c => [c.name, c]));
356
+ for (const [name, con] of afterOtherMap) {
357
+ if (!beforeOtherMap.has(name)) {
218
358
  const objectType = constraintTypeToObjectType(con.type);
219
359
  changes.push(createChange('CREATE', objectType, name, null, {
220
360
  name: con.name,
@@ -222,8 +362,8 @@ function compareConstraints(before, after, tableName) {
222
362
  }, tableName));
223
363
  }
224
364
  }
225
- for (const [name, con] of beforeMap) {
226
- if (!afterMap.has(name)) {
365
+ for (const [name, con] of beforeOtherMap) {
366
+ if (!afterOtherMap.has(name)) {
227
367
  const objectType = constraintTypeToObjectType(con.type);
228
368
  changes.push(createChange('DROP', objectType, name, {
229
369
  name: con.name,
@@ -231,6 +371,59 @@ function compareConstraints(before, after, tableName) {
231
371
  }, null, tableName));
232
372
  }
233
373
  }
374
+ const extractColumnFromCheckDef = (definition) => {
375
+ const enumMatch = definition.match(/\((\w+)\)::text\s*=\s*ANY/i);
376
+ if (enumMatch)
377
+ return enumMatch[1].toLowerCase();
378
+ const compMatch = definition.match(/\(\(?(\w+)\s*(?:>=?|<=?|<>|!=|=)/i);
379
+ if (compMatch)
380
+ return compMatch[1].toLowerCase();
381
+ return null;
382
+ };
383
+ const extractColumnFromCheckName = (name, tableName) => {
384
+ const lower = name.toLowerCase();
385
+ const tablePrefix = tableName.toLowerCase() + '_';
386
+ let colPart = lower.startsWith(tablePrefix) ? lower.slice(tablePrefix.length) : lower;
387
+ if (colPart.startsWith('check_'))
388
+ colPart = colPart.slice(6);
389
+ if (colPart.endsWith('_check'))
390
+ colPart = colPart.slice(0, -6);
391
+ return colPart;
392
+ };
393
+ const beforeChecksByCol = new Map();
394
+ const afterChecksByCol = new Map();
395
+ for (const c of beforeChecks) {
396
+ const col = extractColumnFromCheckDef(c.definition) || extractColumnFromCheckName(c.name, tableName);
397
+ beforeChecksByCol.set(col, c);
398
+ }
399
+ for (const c of afterChecks) {
400
+ const col = extractColumnFromCheckDef(c.definition) || extractColumnFromCheckName(c.name, tableName);
401
+ afterChecksByCol.set(col, c);
402
+ }
403
+ const matchedBefore = new Set();
404
+ const matchedAfter = new Set();
405
+ for (const [col, afterCon] of afterChecksByCol) {
406
+ if (beforeChecksByCol.has(col)) {
407
+ matchedBefore.add(beforeChecksByCol.get(col).name);
408
+ matchedAfter.add(afterCon.name);
409
+ }
410
+ }
411
+ for (const c of afterChecks) {
412
+ if (!matchedAfter.has(c.name)) {
413
+ changes.push(createChange('CREATE', 'CHECK', c.name, null, {
414
+ name: c.name,
415
+ definition: c.definition,
416
+ }, tableName));
417
+ }
418
+ }
419
+ for (const c of beforeChecks) {
420
+ if (!matchedBefore.has(c.name)) {
421
+ changes.push(createChange('DROP', 'CHECK', c.name, {
422
+ name: c.name,
423
+ definition: c.definition,
424
+ }, null, tableName));
425
+ }
426
+ }
234
427
  return changes;
235
428
  }
236
429
  function constraintTypeToObjectType(type) {
@@ -245,6 +438,103 @@ function constraintTypeToObjectType(type) {
245
438
  return 'EXCLUSION';
246
439
  return 'CONSTRAINT';
247
440
  }
441
+ function compareTableComments(before, after, tableName) {
442
+ const changes = [];
443
+ const beforeComment = before.comment;
444
+ const afterComment = after.comment;
445
+ if (afterComment && !beforeComment) {
446
+ changes.push(createChange('CREATE', 'TABLE_COMMENT', tableName, null, {
447
+ tableName,
448
+ comment: afterComment,
449
+ }, tableName));
450
+ }
451
+ else if (!afterComment && beforeComment) {
452
+ changes.push(createChange('DROP', 'TABLE_COMMENT', tableName, {
453
+ tableName,
454
+ comment: beforeComment,
455
+ }, null, tableName));
456
+ }
457
+ else if (afterComment && beforeComment && afterComment !== beforeComment) {
458
+ changes.push(createChange('ALTER', 'TABLE_COMMENT', tableName, {
459
+ tableName,
460
+ comment: beforeComment,
461
+ }, {
462
+ tableName,
463
+ comment: afterComment,
464
+ }, tableName));
465
+ }
466
+ return changes;
467
+ }
468
+ function compareColumnComments(before, after, tableName) {
469
+ const changes = [];
470
+ const beforeMap = new Map(before.map(c => [c.name, c]));
471
+ const afterMap = new Map(after.map(c => [c.name, c]));
472
+ for (const [name, col] of afterMap) {
473
+ const afterComment = col.comment;
474
+ const beforeCol = beforeMap.get(name);
475
+ const beforeComment = beforeCol ? beforeCol.comment : undefined;
476
+ if (afterComment && !beforeComment) {
477
+ changes.push(createChange('CREATE', 'COLUMN_COMMENT', name, null, {
478
+ tableName,
479
+ columnName: name,
480
+ comment: afterComment,
481
+ }, tableName));
482
+ }
483
+ else if (!afterComment && beforeComment) {
484
+ changes.push(createChange('DROP', 'COLUMN_COMMENT', name, {
485
+ tableName,
486
+ columnName: name,
487
+ comment: beforeComment,
488
+ }, null, tableName));
489
+ }
490
+ else if (afterComment && beforeComment && afterComment !== beforeComment) {
491
+ changes.push(createChange('ALTER', 'COLUMN_COMMENT', name, {
492
+ tableName,
493
+ columnName: name,
494
+ comment: beforeComment,
495
+ }, {
496
+ tableName,
497
+ columnName: name,
498
+ comment: afterComment,
499
+ }, tableName));
500
+ }
501
+ }
502
+ return changes;
503
+ }
504
+ function comparePartitions(before, after, tableName) {
505
+ const changes = [];
506
+ const beforePartitioned = before.isPartitioned;
507
+ const afterPartitioned = after.isPartitioned;
508
+ if (afterPartitioned && !beforePartitioned) {
509
+ changes.push(createChange('CREATE', 'PARTITION', tableName, null, {
510
+ tableName,
511
+ type: after.partitionType,
512
+ key: after.partitionKey,
513
+ }));
514
+ }
515
+ else if (!afterPartitioned && beforePartitioned) {
516
+ changes.push(createChange('DROP', 'PARTITION', tableName, {
517
+ tableName,
518
+ type: before.partitionType,
519
+ key: before.partitionKey,
520
+ }, null));
521
+ }
522
+ else if (afterPartitioned && beforePartitioned) {
523
+ const keysMatch = arraysEqual(before.partitionKey || [], after.partitionKey || []);
524
+ if (before.partitionType !== after.partitionType || !keysMatch) {
525
+ changes.push(createChange('ALTER', 'PARTITION', tableName, {
526
+ tableName,
527
+ type: before.partitionType,
528
+ key: before.partitionKey,
529
+ }, {
530
+ tableName,
531
+ type: after.partitionType,
532
+ key: after.partitionKey,
533
+ }));
534
+ }
535
+ }
536
+ return changes;
537
+ }
248
538
  function compareViews(before, after) {
249
539
  const changes = [];
250
540
  const beforeMap = new Map(before.map(v => [v.name, v]));
@@ -1,4 +1,203 @@
1
1
  import { colors } from "./spinner.js";
2
+ const TYPE_ALIASES = {
3
+ 'int': 'integer',
4
+ 'int2': 'smallint',
5
+ 'int4': 'integer',
6
+ 'int8': 'bigint',
7
+ 'integer': 'integer',
8
+ 'smallint': 'smallint',
9
+ 'bigint': 'bigint',
10
+ 'serial': 'serial',
11
+ 'serial2': 'smallserial',
12
+ 'serial4': 'serial',
13
+ 'serial8': 'bigserial',
14
+ 'smallserial': 'smallserial',
15
+ 'bigserial': 'bigserial',
16
+ 'float': 'double precision',
17
+ 'float4': 'real',
18
+ 'float8': 'double precision',
19
+ 'real': 'real',
20
+ 'double precision': 'double precision',
21
+ 'decimal': 'numeric',
22
+ 'numeric': 'numeric',
23
+ 'bool': 'boolean',
24
+ 'boolean': 'boolean',
25
+ 'char': 'character',
26
+ 'character': 'character',
27
+ 'bpchar': 'character',
28
+ 'varchar': 'character varying',
29
+ 'character varying': 'character varying',
30
+ 'text': 'text',
31
+ 'name': 'name',
32
+ 'citext': 'citext',
33
+ 'timestamp': 'timestamp without time zone',
34
+ 'timestamp without time zone': 'timestamp without time zone',
35
+ 'timestamptz': 'timestamp with time zone',
36
+ 'timestamp with time zone': 'timestamp with time zone',
37
+ 'date': 'date',
38
+ 'time': 'time without time zone',
39
+ 'time without time zone': 'time without time zone',
40
+ 'timetz': 'time with time zone',
41
+ 'time with time zone': 'time with time zone',
42
+ 'interval': 'interval',
43
+ 'abstime': 'abstime',
44
+ 'reltime': 'reltime',
45
+ 'tinterval': 'tinterval',
46
+ 'bytea': 'bytea',
47
+ 'bit': 'bit',
48
+ 'varbit': 'bit varying',
49
+ 'bit varying': 'bit varying',
50
+ 'uuid': 'uuid',
51
+ 'json': 'json',
52
+ 'jsonb': 'jsonb',
53
+ 'jsonpath': 'jsonpath',
54
+ 'xml': 'xml',
55
+ 'money': 'money',
56
+ 'inet': 'inet',
57
+ 'cidr': 'cidr',
58
+ 'macaddr': 'macaddr',
59
+ 'macaddr8': 'macaddr8',
60
+ 'point': 'point',
61
+ 'line': 'line',
62
+ 'lseg': 'lseg',
63
+ 'box': 'box',
64
+ 'path': 'path',
65
+ 'polygon': 'polygon',
66
+ 'circle': 'circle',
67
+ 'int4range': 'int4range',
68
+ 'int8range': 'int8range',
69
+ 'numrange': 'numrange',
70
+ 'tsrange': 'tsrange',
71
+ 'tstzrange': 'tstzrange',
72
+ 'daterange': 'daterange',
73
+ 'int4multirange': 'int4multirange',
74
+ 'int8multirange': 'int8multirange',
75
+ 'nummultirange': 'nummultirange',
76
+ 'tsmultirange': 'tsmultirange',
77
+ 'tstzmultirange': 'tstzmultirange',
78
+ 'datemultirange': 'datemultirange',
79
+ 'tsvector': 'tsvector',
80
+ 'tsquery': 'tsquery',
81
+ 'gtsvector': 'gtsvector',
82
+ 'oid': 'oid',
83
+ 'regclass': 'regclass',
84
+ 'regcollation': 'regcollation',
85
+ 'regconfig': 'regconfig',
86
+ 'regdictionary': 'regdictionary',
87
+ 'regnamespace': 'regnamespace',
88
+ 'regoper': 'regoper',
89
+ 'regoperator': 'regoperator',
90
+ 'regproc': 'regproc',
91
+ 'regprocedure': 'regprocedure',
92
+ 'regrole': 'regrole',
93
+ 'regtype': 'regtype',
94
+ 'xid': 'xid',
95
+ 'xid8': 'xid8',
96
+ 'cid': 'cid',
97
+ 'tid': 'tid',
98
+ 'aclitem': 'aclitem',
99
+ 'smgr': 'smgr',
100
+ 'unknown': 'unknown',
101
+ 'internal': 'internal',
102
+ 'opaque': 'opaque',
103
+ 'anyelement': 'anyelement',
104
+ 'anyarray': 'anyarray',
105
+ 'anynonarray': 'anynonarray',
106
+ 'anyenum': 'anyenum',
107
+ 'anyrange': 'anyrange',
108
+ 'anymultirange': 'anymultirange',
109
+ 'anycompatible': 'anycompatible',
110
+ 'anycompatiblearray': 'anycompatiblearray',
111
+ 'anycompatiblenonarray': 'anycompatiblenonarray',
112
+ 'anycompatiblerange': 'anycompatiblerange',
113
+ 'anycompatiblemultirange': 'anycompatiblemultirange',
114
+ 'cstring': 'cstring',
115
+ 'record': 'record',
116
+ 'trigger': 'trigger',
117
+ 'event_trigger': 'event_trigger',
118
+ 'pg_lsn': 'pg_lsn',
119
+ 'pg_snapshot': 'pg_snapshot',
120
+ 'txid_snapshot': 'txid_snapshot',
121
+ 'fdw_handler': 'fdw_handler',
122
+ 'index_am_handler': 'index_am_handler',
123
+ 'tsm_handler': 'tsm_handler',
124
+ 'table_am_handler': 'table_am_handler',
125
+ 'language_handler': 'language_handler',
126
+ 'void': 'void',
127
+ 'refcursor': 'refcursor',
128
+ '_int2': 'smallint[]',
129
+ '_int4': 'integer[]',
130
+ '_int8': 'bigint[]',
131
+ '_float4': 'real[]',
132
+ '_float8': 'double precision[]',
133
+ '_numeric': 'numeric[]',
134
+ '_bool': 'boolean[]',
135
+ '_text': 'text[]',
136
+ '_varchar': 'character varying[]',
137
+ '_bpchar': 'character[]',
138
+ '_char': 'character[]',
139
+ '_name': 'name[]',
140
+ '_bytea': 'bytea[]',
141
+ '_bit': 'bit[]',
142
+ '_varbit': 'bit varying[]',
143
+ '_uuid': 'uuid[]',
144
+ '_json': 'json[]',
145
+ '_jsonb': 'jsonb[]',
146
+ '_xml': 'xml[]',
147
+ '_money': 'money[]',
148
+ '_timestamp': 'timestamp without time zone[]',
149
+ '_timestamptz': 'timestamp with time zone[]',
150
+ '_date': 'date[]',
151
+ '_time': 'time without time zone[]',
152
+ '_timetz': 'time with time zone[]',
153
+ '_interval': 'interval[]',
154
+ '_inet': 'inet[]',
155
+ '_cidr': 'cidr[]',
156
+ '_macaddr': 'macaddr[]',
157
+ '_macaddr8': 'macaddr8[]',
158
+ '_point': 'point[]',
159
+ '_line': 'line[]',
160
+ '_lseg': 'lseg[]',
161
+ '_box': 'box[]',
162
+ '_path': 'path[]',
163
+ '_polygon': 'polygon[]',
164
+ '_circle': 'circle[]',
165
+ '_int4range': 'int4range[]',
166
+ '_int8range': 'int8range[]',
167
+ '_numrange': 'numrange[]',
168
+ '_tsrange': 'tsrange[]',
169
+ '_tstzrange': 'tstzrange[]',
170
+ '_daterange': 'daterange[]',
171
+ '_tsvector': 'tsvector[]',
172
+ '_tsquery': 'tsquery[]',
173
+ '_oid': 'oid[]',
174
+ '_regclass': 'regclass[]',
175
+ '_regtype': 'regtype[]',
176
+ '_regproc': 'regproc[]',
177
+ '_xid': 'xid[]',
178
+ '_cid': 'cid[]',
179
+ '_tid': 'tid[]',
180
+ '_aclitem': 'aclitem[]',
181
+ '_cstring': 'cstring[]',
182
+ '_record': 'record[]',
183
+ '_pg_lsn': 'pg_lsn[]',
184
+ '_txid_snapshot': 'txid_snapshot[]',
185
+ '_refcursor': 'refcursor[]',
186
+ '_citext': 'citext[]',
187
+ };
188
+ function normalizeType(type) {
189
+ if (!type)
190
+ return type;
191
+ let normalized = type.toLowerCase().trim();
192
+ if (TYPE_ALIASES[normalized]) {
193
+ return TYPE_ALIASES[normalized];
194
+ }
195
+ normalized = normalized.replace(' without time zone', '');
196
+ if (TYPE_ALIASES[normalized]) {
197
+ return TYPE_ALIASES[normalized];
198
+ }
199
+ return normalized;
200
+ }
2
201
  export function diffSchemas(local, remote) {
3
202
  const tables = diffTables(local.tables, remote.tables);
4
203
  const extensions = diffExtensions(local.extensions, remote.extensions);
@@ -75,7 +274,9 @@ function diffColumns(local, remote) {
75
274
  }
76
275
  function compareColumns(local, remote) {
77
276
  const changes = [];
78
- if (local.type !== remote.type) {
277
+ const localType = normalizeType(local.type);
278
+ const remoteType = normalizeType(remote.type);
279
+ if (localType !== remoteType) {
79
280
  changes.push({ field: 'type', from: local.type, to: remote.type });
80
281
  }
81
282
  if (local.nullable !== remote.nullable) {
@@ -23,8 +23,9 @@ function normalizeTable(table) {
23
23
  columns,
24
24
  indexes,
25
25
  constraints,
26
+ isPartitioned: table.isPartitioned,
26
27
  partitionType: table.partitionType,
27
- partitionKey: table.partitionKey?.sort(),
28
+ partitionKey: table.partitionKey ? [...table.partitionKey] : undefined,
28
29
  };
29
30
  }
30
31
  function normalizeColumn(col) {
@@ -31,7 +31,7 @@ export async function introspectDatabase(connection, onProgress, options) {
31
31
  WHERE n.nspname = 'public' AND c.relispartition = true;
32
32
  `);
33
33
  const partitionTableNames = new Set(partitionNamesResult.rows.map((r) => r.name));
34
- const nonPartitionTables = tablesResult.rows.filter((r) => !partitionTableNames.has(r.table_name) && !r.table_name.startsWith('_relq'));
34
+ const nonPartitionTables = tablesResult.rows.filter((r) => !partitionTableNames.has(r.table_name) && !r.table_name.startsWith('_relq') && !r.table_name.startsWith('_kuery'));
35
35
  const totalTables = nonPartitionTables.length;
36
36
  let tableIndex = 0;
37
37
  for (let i = 0; i < tablesResult.rows.length; i++) {
@@ -39,9 +39,12 @@ export async function introspectDatabase(connection, onProgress, options) {
39
39
  const tableName = row.table_name;
40
40
  const tableSchema = row.table_schema;
41
41
  const rowCount = parseInt(row.row_count) || 0;
42
+ const isInternal = tableName.startsWith('_relq') || tableName.startsWith('_kuery');
43
+ if (isInternal) {
44
+ continue;
45
+ }
42
46
  const isPartition = partitionTableNames.has(tableName);
43
- const isInternal = tableName.startsWith('_relq');
44
- if (!isPartition && !isInternal) {
47
+ if (!isPartition) {
45
48
  tableIndex++;
46
49
  onProgress?.('parsing_table', `${tableName} (${tableIndex}/${totalTables})`);
47
50
  }
@@ -371,6 +374,7 @@ export async function introspectDatabase(connection, onProgress, options) {
371
374
  domains: [],
372
375
  compositeTypes: [],
373
376
  sequences: [],
377
+ collations: [],
374
378
  functions,
375
379
  triggers,
376
380
  policies,
@@ -101,6 +101,7 @@ export function snapshotToDatabaseSchema(snapshot) {
101
101
  domains: [],
102
102
  compositeTypes: [],
103
103
  sequences: [],
104
+ collations: [],
104
105
  functions: [],
105
106
  triggers: [],
106
107
  policies: [],