pg-migration-engine 0.1.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.
Files changed (104) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +304 -0
  3. package/package.json +52 -0
  4. package/src/behavioral/behavioral-applier.js +374 -0
  5. package/src/behavioral/behavioral-extractor.js +266 -0
  6. package/src/behavioral/behavioral-puller.js +35 -0
  7. package/src/behavioral/index.js +4 -0
  8. package/src/behavioral/phase-sorter.js +24 -0
  9. package/src/constants.js +97 -0
  10. package/src/ddl-generator/alter-generator.js +1512 -0
  11. package/src/ddl-generator/comment-generator.js +95 -0
  12. package/src/ddl-generator/create-generator.js +1426 -0
  13. package/src/ddl-generator/drop-generator.js +194 -0
  14. package/src/ddl-generator/generator.js +78 -0
  15. package/src/ddl-generator/grant-generator.js +31 -0
  16. package/src/ddl-generator/index.js +3 -0
  17. package/src/ddl-generator/pg-version.js +15 -0
  18. package/src/ddl-generator/rename-generator.js +88 -0
  19. package/src/ddl-generator/safe-patterns.js +241 -0
  20. package/src/differ/change-classifier.js +192 -0
  21. package/src/differ/dependency-resolver.js +523 -0
  22. package/src/differ/index.js +12 -0
  23. package/src/differ/object-matcher.js +510 -0
  24. package/src/differ/property-differ.js +1134 -0
  25. package/src/differ/risk-tagger.js +561 -0
  26. package/src/differ/schema-differ.js +309 -0
  27. package/src/differ/utils/levenshtein.js +210 -0
  28. package/src/differ/utils/path-builder.js +198 -0
  29. package/src/differ/utils/type-compatibility.js +472 -0
  30. package/src/errors.js +147 -0
  31. package/src/executor/drift-detector.js +221 -0
  32. package/src/executor/index.js +7 -0
  33. package/src/executor/lock-manager.js +308 -0
  34. package/src/executor/migration-executor.js +1249 -0
  35. package/src/executor/progress-tracker.js +81 -0
  36. package/src/executor/recovery-manager.js +47 -0
  37. package/src/executor/snapshot-manager.js +22 -0
  38. package/src/executor/sql-splitter.js +120 -0
  39. package/src/executor/transaction-manager.js +288 -0
  40. package/src/index.js +483 -0
  41. package/src/introspection/index.js +4 -0
  42. package/src/introspection/introspector.js +250 -0
  43. package/src/introspection/queries/access-methods.js +29 -0
  44. package/src/introspection/queries/casts.js +38 -0
  45. package/src/introspection/queries/collations.js +51 -0
  46. package/src/introspection/queries/comments.js +66 -0
  47. package/src/introspection/queries/constraints.js +65 -0
  48. package/src/introspection/queries/conversions.js +39 -0
  49. package/src/introspection/queries/databases.js +43 -0
  50. package/src/introspection/queries/default-privileges.js +37 -0
  51. package/src/introspection/queries/event-triggers.js +43 -0
  52. package/src/introspection/queries/extensions.js +21 -0
  53. package/src/introspection/queries/foreign-data.js +136 -0
  54. package/src/introspection/queries/functions.js +75 -0
  55. package/src/introspection/queries/grants.js +38 -0
  56. package/src/introspection/queries/index.js +33 -0
  57. package/src/introspection/queries/indexes.js +99 -0
  58. package/src/introspection/queries/inheritance.js +24 -0
  59. package/src/introspection/queries/multiranges.js +37 -0
  60. package/src/introspection/queries/operators.js +180 -0
  61. package/src/introspection/queries/partitions.js +42 -0
  62. package/src/introspection/queries/pg18-19.js +43 -0
  63. package/src/introspection/queries/policies.js +35 -0
  64. package/src/introspection/queries/procedural-languages.js +36 -0
  65. package/src/introspection/queries/publications.js +114 -0
  66. package/src/introspection/queries/roles.js +66 -0
  67. package/src/introspection/queries/rules.js +49 -0
  68. package/src/introspection/queries/sequences.js +37 -0
  69. package/src/introspection/queries/statistics.js +74 -0
  70. package/src/introspection/queries/subscriptions.js +99 -0
  71. package/src/introspection/queries/tables.js +151 -0
  72. package/src/introspection/queries/tablespaces.js +36 -0
  73. package/src/introspection/queries/text-search.js +146 -0
  74. package/src/introspection/queries/triggers.js +61 -0
  75. package/src/introspection/queries/types.js +106 -0
  76. package/src/introspection/queries/views.js +146 -0
  77. package/src/introspection/translator.js +1283 -0
  78. package/src/introspection/version-detector.js +25 -0
  79. package/src/planner/backfill-planner.js +36 -0
  80. package/src/planner/dry-run.js +21 -0
  81. package/src/planner/index.js +6 -0
  82. package/src/planner/migration-planner.js +356 -0
  83. package/src/planner/smart-migrator.js +82 -0
  84. package/src/planner/step-sequencer.js +61 -0
  85. package/src/planner/type-registry.js +47 -0
  86. package/src/risk/compatibility-checker.js +29 -0
  87. package/src/risk/data-loss-checker.js +32 -0
  88. package/src/risk/destructive-checker.js +38 -0
  89. package/src/risk/index.js +6 -0
  90. package/src/risk/lock-analyzer.js +39 -0
  91. package/src/risk/recommendations.js +44 -0
  92. package/src/risk/risk-engine.js +25 -0
  93. package/src/storage/index.js +5 -0
  94. package/src/storage/memory-storage.js +87 -0
  95. package/src/storage/migration-table.js +423 -0
  96. package/src/storage/rollback-generator.js +344 -0
  97. package/src/types/changes.js +136 -0
  98. package/src/types/connection.js +17 -0
  99. package/src/types/execution.js +107 -0
  100. package/src/types/index.js +1 -0
  101. package/src/types/migration.js +67 -0
  102. package/src/types/risk.js +32 -0
  103. package/src/types/schema.d.ts +1004 -0
  104. package/src/types/schema.js +561 -0
@@ -0,0 +1,95 @@
1
+ function ident(name) {
2
+ if (!name) return '';
3
+ if (name.includes('"') || name.includes(' ')) {
4
+ return `"${name.replace(/"/g, '""')}"`;
5
+ }
6
+ return `"${name}"`;
7
+ }
8
+
9
+ function escapeString(str) {
10
+ if (typeof str !== 'string') str = String(str);
11
+ return str.replace(/'/g, "''");
12
+ }
13
+
14
+ /**
15
+ * @param {import('../types/changes.js').SchemaChange} change
16
+ * @returns {string}
17
+ */
18
+ export function generateCommentSql(change) {
19
+ const type = change.changeType || change.type;
20
+ if (type !== 'COMMENT') return '';
21
+
22
+ const typeMap = {
23
+ table: 'TABLE',
24
+ column: 'COLUMN',
25
+ index: 'INDEX',
26
+ constraint: 'CONSTRAINT',
27
+ view: 'VIEW',
28
+ materializedView: 'MATERIALIZED VIEW',
29
+ function: 'FUNCTION',
30
+ procedure: 'PROCEDURE',
31
+ trigger: 'TRIGGER',
32
+ eventTrigger: 'EVENT TRIGGER',
33
+ policy: 'POLICY',
34
+ rule: 'RULE',
35
+ aggregate: 'AGGREGATE',
36
+ sequence: 'SEQUENCE',
37
+ type: 'TYPE',
38
+ extension: 'EXTENSION',
39
+ schema: 'SCHEMA',
40
+ statistics: 'STATISTICS',
41
+ collation: 'COLLATION',
42
+ operator: 'OPERATOR',
43
+ operatorClass: 'OPERATOR CLASS',
44
+ operatorFamily: 'OPERATOR FAMILY',
45
+ cast: 'CAST',
46
+ foreignTable: 'FOREIGN TABLE',
47
+ foreignDataWrapper: 'FOREIGN DATA WRAPPER',
48
+ foreignServer: 'SERVER',
49
+ publication: 'PUBLICATION',
50
+ subscription: 'SUBSCRIPTION',
51
+ textSearchConfig: 'TEXT SEARCH CONFIGURATION',
52
+ textSearchDict: 'TEXT SEARCH DICTIONARY',
53
+ textSearchParser: 'TEXT SEARCH PARSER',
54
+ textSearchTemplate: 'TEXT SEARCH TEMPLATE',
55
+ };
56
+
57
+ const typeKeyword = typeMap[change.objectType] || change.objectType.toUpperCase();
58
+ const name = change.objectKey;
59
+ const val = change.desiredValue !== undefined ? change.desiredValue : (change.after?.comment || '');
60
+
61
+ let formattedName = name;
62
+ if (change.objectType === 'column') {
63
+ const parts = name.split('.');
64
+ const col = parts.pop();
65
+ const tab = parts.join('.');
66
+ formattedName = `${tab}.${ident(col)}`;
67
+ } else if (change.objectType === 'constraint') {
68
+ const parts = name.split('.');
69
+ const conName = parts.pop();
70
+ const tab = parts.join('.');
71
+ formattedName = `${ident(conName)} ON ${tab}`;
72
+ } else if (change.objectType === 'trigger' || change.objectType === 'rule' || change.objectType === 'policy') {
73
+ const parts = name.split('.');
74
+ const objName = parts.pop();
75
+ const tab = parts.join('.');
76
+ formattedName = `${ident(objName)} ON ${tab}`;
77
+ } else if (change.objectType === 'function' || change.objectType === 'procedure') {
78
+ const fn = change.after || change.before || {};
79
+ const argTypesList = fn.argumentTypes || fn.arguments || [];
80
+ const argsStr = Array.isArray(argTypesList) ? argTypesList.join(', ') : '';
81
+ formattedName = `${name}(${argsStr})`;
82
+ } else if (change.objectType === 'operator') {
83
+ const leftType = change.before?.leftType || change.after?.leftType || 'NONE';
84
+ const rightType = change.before?.rightType || change.after?.rightType || 'NONE';
85
+ formattedName = `${name} (${leftType}, ${rightType})`;
86
+ } else if (change.objectType === 'operatorClass' || change.objectType === 'operatorFamily') {
87
+ const accessMethod = change.after?.accessMethod || change.before?.accessMethod || 'btree';
88
+ formattedName = `${name} USING ${accessMethod}`;
89
+ }
90
+
91
+ if (val === null || val === undefined || val === '') {
92
+ return `COMMENT ON ${typeKeyword} ${formattedName} IS NULL;`;
93
+ }
94
+ return `COMMENT ON ${typeKeyword} ${formattedName} IS '${escapeString(val)}';`;
95
+ }