relq 1.0.4 → 1.0.6
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/dist/cjs/cli/commands/add.cjs +252 -12
- package/dist/cjs/cli/commands/commit.cjs +12 -1
- package/dist/cjs/cli/commands/export.cjs +25 -19
- package/dist/cjs/cli/commands/import.cjs +219 -100
- package/dist/cjs/cli/commands/init.cjs +86 -14
- package/dist/cjs/cli/commands/pull.cjs +104 -23
- package/dist/cjs/cli/commands/push.cjs +38 -3
- package/dist/cjs/cli/index.cjs +9 -1
- package/dist/cjs/cli/utils/ast/codegen/builder.cjs +297 -0
- package/dist/cjs/cli/utils/ast/codegen/constraints.cjs +185 -0
- package/dist/cjs/cli/utils/ast/codegen/defaults.cjs +311 -0
- package/dist/cjs/cli/utils/ast/codegen/index.cjs +24 -0
- package/dist/cjs/cli/utils/ast/codegen/type-map.cjs +116 -0
- package/dist/cjs/cli/utils/ast/codegen/utils.cjs +69 -0
- package/dist/cjs/cli/utils/ast/index.cjs +19 -0
- package/dist/cjs/cli/utils/ast/transformer/helpers.cjs +154 -0
- package/dist/cjs/cli/utils/ast/transformer/index.cjs +25 -0
- package/dist/cjs/cli/utils/ast/types.cjs +2 -0
- package/dist/cjs/cli/utils/ast-codegen.cjs +949 -0
- package/dist/cjs/cli/utils/ast-transformer.cjs +916 -0
- package/dist/cjs/cli/utils/change-tracker.cjs +50 -1
- package/dist/cjs/cli/utils/cli-utils.cjs +151 -0
- package/dist/cjs/cli/utils/fast-introspect.cjs +149 -23
- package/dist/cjs/cli/utils/pg-parser.cjs +1 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +121 -4
- package/dist/cjs/cli/utils/schema-comparator.cjs +98 -14
- package/dist/cjs/cli/utils/schema-introspect.cjs +56 -19
- package/dist/cjs/cli/utils/snapshot-manager.cjs +0 -1
- package/dist/cjs/cli/utils/sql-generator.cjs +353 -64
- package/dist/cjs/cli/utils/type-generator.cjs +114 -15
- package/dist/cjs/core/relq-client.cjs +22 -6
- package/dist/cjs/schema-definition/column-types.cjs +150 -13
- package/dist/cjs/schema-definition/defaults.cjs +72 -0
- package/dist/cjs/schema-definition/index.cjs +15 -1
- package/dist/cjs/schema-definition/introspection.cjs +7 -3
- package/dist/cjs/schema-definition/pg-relations.cjs +169 -0
- package/dist/cjs/schema-definition/pg-view.cjs +30 -0
- package/dist/cjs/schema-definition/table-definition.cjs +110 -4
- package/dist/cjs/types/config-types.cjs +13 -4
- package/dist/cjs/utils/aws-dsql.cjs +177 -0
- package/dist/config.d.ts +146 -1
- package/dist/esm/cli/commands/add.js +250 -13
- package/dist/esm/cli/commands/commit.js +12 -1
- package/dist/esm/cli/commands/export.js +25 -19
- package/dist/esm/cli/commands/import.js +221 -102
- package/dist/esm/cli/commands/init.js +86 -14
- package/dist/esm/cli/commands/pull.js +106 -25
- package/dist/esm/cli/commands/push.js +39 -4
- package/dist/esm/cli/index.js +9 -1
- package/dist/esm/cli/utils/ast/codegen/builder.js +291 -0
- package/dist/esm/cli/utils/ast/codegen/constraints.js +176 -0
- package/dist/esm/cli/utils/ast/codegen/defaults.js +305 -0
- package/dist/esm/cli/utils/ast/codegen/index.js +6 -0
- package/dist/esm/cli/utils/ast/codegen/type-map.js +111 -0
- package/dist/esm/cli/utils/ast/codegen/utils.js +60 -0
- package/dist/esm/cli/utils/ast/index.js +3 -0
- package/dist/esm/cli/utils/ast/transformer/helpers.js +141 -0
- package/dist/esm/cli/utils/ast/transformer/index.js +2 -0
- package/dist/esm/cli/utils/ast/types.js +1 -0
- package/dist/esm/cli/utils/ast-codegen.js +945 -0
- package/dist/esm/cli/utils/ast-transformer.js +907 -0
- package/dist/esm/cli/utils/change-tracker.js +50 -1
- package/dist/esm/cli/utils/cli-utils.js +147 -0
- package/dist/esm/cli/utils/fast-introspect.js +149 -23
- package/dist/esm/cli/utils/pg-parser.js +1 -0
- package/dist/esm/cli/utils/repo-manager.js +114 -4
- package/dist/esm/cli/utils/schema-comparator.js +98 -14
- package/dist/esm/cli/utils/schema-introspect.js +56 -19
- package/dist/esm/cli/utils/snapshot-manager.js +0 -1
- package/dist/esm/cli/utils/sql-generator.js +353 -64
- package/dist/esm/cli/utils/type-generator.js +114 -15
- package/dist/esm/core/relq-client.js +23 -7
- package/dist/esm/schema-definition/column-types.js +147 -12
- package/dist/esm/schema-definition/defaults.js +69 -0
- package/dist/esm/schema-definition/index.js +3 -0
- package/dist/esm/schema-definition/introspection.js +7 -3
- package/dist/esm/schema-definition/pg-relations.js +161 -0
- package/dist/esm/schema-definition/pg-view.js +24 -0
- package/dist/esm/schema-definition/table-definition.js +110 -4
- package/dist/esm/types/config-types.js +12 -4
- package/dist/esm/utils/aws-dsql.js +139 -0
- package/dist/index.d.ts +159 -1
- package/dist/schema-builder.d.ts +1314 -32
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const RESERVED_WORDS = new Set([
|
|
2
|
-
'
|
|
2
|
+
'order', 'check', 'table', 'index', 'column', 'constraint',
|
|
3
3
|
'primary', 'foreign', 'key', 'references', 'unique', 'default',
|
|
4
4
|
'null', 'not', 'and', 'or', 'in', 'like', 'between', 'case',
|
|
5
5
|
'when', 'then', 'else', 'end', 'select', 'from', 'where', 'group',
|
|
6
|
-
'having', '
|
|
6
|
+
'having', 'by', 'limit', 'offset', 'join', 'left', 'right',
|
|
7
7
|
'inner', 'outer', 'cross', 'on', 'as', 'into', 'values', 'insert',
|
|
8
8
|
'update', 'delete', 'create', 'alter', 'drop', 'grant', 'revoke',
|
|
9
9
|
'all', 'any', 'some', 'exists', 'true', 'false', 'boolean', 'int',
|
|
@@ -45,7 +45,7 @@ const RESERVED_WORDS = new Set([
|
|
|
45
45
|
'off', 'oids', 'old', 'only', 'operator', 'option', 'options',
|
|
46
46
|
'ordinality', 'others', 'out', 'over', 'overlaps', 'overlay',
|
|
47
47
|
'overriding', 'owned', 'owner', 'parallel', 'parser', 'partial',
|
|
48
|
-
'partition', 'passing', '
|
|
48
|
+
'partition', 'passing', 'placing', 'plans', 'policy',
|
|
49
49
|
'position', 'preceding', 'prepare', 'prepared', 'preserve', 'prior',
|
|
50
50
|
'privileges', 'procedural', 'program', 'publication', 'quote',
|
|
51
51
|
'range', 'read', 'reassign', 'recheck', 'refresh', 'reindex',
|
|
@@ -81,6 +81,34 @@ export function quoteIdentifier(name) {
|
|
|
81
81
|
}
|
|
82
82
|
return name;
|
|
83
83
|
}
|
|
84
|
+
function parseFKDefinition(definition) {
|
|
85
|
+
if (!definition)
|
|
86
|
+
return null;
|
|
87
|
+
const constraintNameMatch = definition.match(/^CONSTRAINT\s+"?([^"]+)"?\s+/i);
|
|
88
|
+
const constraintName = constraintNameMatch?.[1];
|
|
89
|
+
const fkPart = constraintName
|
|
90
|
+
? definition.substring(constraintNameMatch[0].length)
|
|
91
|
+
: definition;
|
|
92
|
+
const fkMatch = fkPart.match(/FOREIGN\s+KEY\s*\(\s*([^)]+)\s*\)\s*REFERENCES\s+"?([^"\s(]+)"?\s*(?:\(\s*([^)]+)\s*\))?/i);
|
|
93
|
+
if (!fkMatch)
|
|
94
|
+
return null;
|
|
95
|
+
const parseColumnList = (str) => {
|
|
96
|
+
return str.split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
97
|
+
};
|
|
98
|
+
const columns = parseColumnList(fkMatch[1]);
|
|
99
|
+
const referencedTable = fkMatch[2].replace(/^"|"$/g, '');
|
|
100
|
+
const referencedColumns = fkMatch[3] ? parseColumnList(fkMatch[3]) : ['id'];
|
|
101
|
+
const onDeleteMatch = definition.match(/ON\s+DELETE\s+(CASCADE|SET\s+NULL|SET\s+DEFAULT|RESTRICT|NO\s+ACTION)/i);
|
|
102
|
+
const onUpdateMatch = definition.match(/ON\s+UPDATE\s+(CASCADE|SET\s+NULL|SET\s+DEFAULT|RESTRICT|NO\s+ACTION)/i);
|
|
103
|
+
return {
|
|
104
|
+
constraintName,
|
|
105
|
+
columns,
|
|
106
|
+
referencedTable,
|
|
107
|
+
referencedColumns,
|
|
108
|
+
onDelete: onDeleteMatch?.[1]?.toUpperCase().replace(/\s+/g, ' '),
|
|
109
|
+
onUpdate: onUpdateMatch?.[1]?.toUpperCase().replace(/\s+/g, ' '),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
84
112
|
function quoteColumnRef(name) {
|
|
85
113
|
const lowerName = name.toLowerCase();
|
|
86
114
|
if (RESERVED_WORDS.has(lowerName)) {
|
|
@@ -91,12 +119,25 @@ function quoteColumnRef(name) {
|
|
|
91
119
|
}
|
|
92
120
|
return name;
|
|
93
121
|
}
|
|
122
|
+
const PG_TYPE_MAP = {
|
|
123
|
+
'int2': 'smallint',
|
|
124
|
+
'int4': 'integer',
|
|
125
|
+
'int8': 'bigint',
|
|
126
|
+
'float4': 'real',
|
|
127
|
+
'float8': 'double precision',
|
|
128
|
+
'bool': 'boolean',
|
|
129
|
+
'timestamptz': 'timestamp with time zone',
|
|
130
|
+
'timetz': 'time with time zone',
|
|
131
|
+
'bpchar': 'character',
|
|
132
|
+
'varbit': 'bit varying',
|
|
133
|
+
};
|
|
94
134
|
function normalizePgType(pgType) {
|
|
95
135
|
if (pgType.startsWith('_')) {
|
|
96
136
|
const baseType = pgType.slice(1);
|
|
97
|
-
|
|
137
|
+
const friendlyBase = PG_TYPE_MAP[baseType] || baseType;
|
|
138
|
+
return `${friendlyBase}[]`;
|
|
98
139
|
}
|
|
99
|
-
return pgType;
|
|
140
|
+
return PG_TYPE_MAP[pgType] || pgType;
|
|
100
141
|
}
|
|
101
142
|
export function generateExtensionSQL(extensionName) {
|
|
102
143
|
const quotedName = extensionName.includes('-') ? `"${extensionName}"` : extensionName;
|
|
@@ -174,15 +215,116 @@ export function generateSequenceSQL(seq) {
|
|
|
174
215
|
return parts.join(' ') + ';';
|
|
175
216
|
}
|
|
176
217
|
export function generateTableSQL(table, options = {}) {
|
|
177
|
-
const { includeConstraints = true } = options;
|
|
218
|
+
const { includeConstraints = true, ifNotExists = false } = options;
|
|
178
219
|
const lines = [];
|
|
179
|
-
|
|
220
|
+
const ifNotExistsClause = ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
221
|
+
lines.push(`-- DEBUG: generateTableSQL running for ${table.name}`);
|
|
222
|
+
lines.push(`CREATE TABLE ${ifNotExistsClause}${quoteIdentifier(table.name)} (`);
|
|
223
|
+
let singleColumnPkName = null;
|
|
224
|
+
let pkConstraintIndex = -1;
|
|
225
|
+
const compositeUniqueColumns = new Set();
|
|
226
|
+
const inlineFKs = new Map();
|
|
227
|
+
const skipFKIndices = new Set();
|
|
228
|
+
if (table.constraints) {
|
|
229
|
+
const pkIndex = table.constraints.findIndex(c => c.type === 'PRIMARY KEY');
|
|
230
|
+
if (pkIndex !== -1) {
|
|
231
|
+
const pkConstraint = table.constraints[pkIndex];
|
|
232
|
+
pkConstraintIndex = pkIndex;
|
|
233
|
+
if (pkConstraint.columns && pkConstraint.columns.length === 1) {
|
|
234
|
+
singleColumnPkName = pkConstraint.columns[0];
|
|
235
|
+
}
|
|
236
|
+
else if (pkConstraint.definition) {
|
|
237
|
+
const match = pkConstraint.definition.match(/PRIMARY\s+KEY\s*\(\s*"?([^",\s)]+)"?\s*\)/i);
|
|
238
|
+
if (match && !pkConstraint.definition.includes(',')) {
|
|
239
|
+
singleColumnPkName = match[1];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (const constraint of table.constraints) {
|
|
244
|
+
if (constraint.type === 'UNIQUE') {
|
|
245
|
+
let cols = [];
|
|
246
|
+
if (constraint.columns && constraint.columns.length > 0) {
|
|
247
|
+
if (Array.isArray(constraint.columns)) {
|
|
248
|
+
cols = constraint.columns;
|
|
249
|
+
}
|
|
250
|
+
else if (typeof constraint.columns === 'string') {
|
|
251
|
+
const str = constraint.columns;
|
|
252
|
+
if (str.startsWith('{') && str.endsWith('}')) {
|
|
253
|
+
cols = str.slice(1, -1).split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else if (constraint.definition) {
|
|
258
|
+
const match = constraint.definition.match(/UNIQUE\s*\(([^)]+)\)/i);
|
|
259
|
+
if (match) {
|
|
260
|
+
cols = match[1].split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (cols.length > 1) {
|
|
264
|
+
for (const col of cols) {
|
|
265
|
+
compositeUniqueColumns.add(col);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
table.constraints.forEach((constraint, index) => {
|
|
271
|
+
if (constraint.type === 'FOREIGN KEY') {
|
|
272
|
+
let fkColumns = constraint.columns;
|
|
273
|
+
let refTable = constraint.referencedTable || constraint.references?.table;
|
|
274
|
+
let refColumns = constraint.referencedColumns || constraint.references?.columns;
|
|
275
|
+
let onDelete = constraint.onDelete || constraint.references?.onDelete;
|
|
276
|
+
let onUpdate = constraint.onUpdate || constraint.references?.onUpdate;
|
|
277
|
+
let constraintName = constraint.name;
|
|
278
|
+
if (!refTable && constraint.definition) {
|
|
279
|
+
const parsed = parseFKDefinition(constraint.definition);
|
|
280
|
+
if (parsed) {
|
|
281
|
+
fkColumns = parsed.columns;
|
|
282
|
+
refTable = parsed.referencedTable;
|
|
283
|
+
refColumns = parsed.referencedColumns;
|
|
284
|
+
onDelete = parsed.onDelete;
|
|
285
|
+
onUpdate = parsed.onUpdate;
|
|
286
|
+
if (!constraintName) {
|
|
287
|
+
constraintName = parsed.constraintName;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const isSingleColumn = fkColumns && fkColumns.length === 1;
|
|
292
|
+
let hasExplicitName = false;
|
|
293
|
+
if (constraintName && fkColumns && fkColumns.length > 0) {
|
|
294
|
+
const colName = fkColumns[0];
|
|
295
|
+
const expectedAutoName = `${table.name}_${colName}_fkey`;
|
|
296
|
+
hasExplicitName = constraintName.toLowerCase() !== expectedAutoName.toLowerCase();
|
|
297
|
+
}
|
|
298
|
+
if (isSingleColumn && refTable) {
|
|
299
|
+
const colName = fkColumns[0];
|
|
300
|
+
inlineFKs.set(colName, {
|
|
301
|
+
table: refTable,
|
|
302
|
+
column: refColumns?.[0],
|
|
303
|
+
onDelete: onDelete,
|
|
304
|
+
onUpdate: onUpdate,
|
|
305
|
+
constraintName: hasExplicitName ? constraintName : undefined,
|
|
306
|
+
});
|
|
307
|
+
skipFKIndices.add(index);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
180
312
|
const columnLines = [];
|
|
181
313
|
for (const col of table.columns) {
|
|
182
|
-
|
|
314
|
+
const isInlinePk = singleColumnPkName === col.name;
|
|
315
|
+
const skipInlineUnique = compositeUniqueColumns.has(col.name);
|
|
316
|
+
const inlineFKInfo = inlineFKs.get(col.name);
|
|
317
|
+
columnLines.push(generateColumnSQL(col, { inlinePrimaryKey: isInlinePk, skipInlineUnique, inlineForeignKey: inlineFKInfo }));
|
|
183
318
|
}
|
|
184
319
|
if (includeConstraints && table.constraints) {
|
|
185
|
-
for (
|
|
320
|
+
for (let i = 0; i < table.constraints.length; i++) {
|
|
321
|
+
const constraint = table.constraints[i];
|
|
322
|
+
if (constraint.type === 'PRIMARY KEY' && singleColumnPkName) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (constraint.type === 'FOREIGN KEY' && skipFKIndices.has(i)) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
186
328
|
const constraintSQL = generateInlineConstraintSQL(constraint);
|
|
187
329
|
if (constraintSQL) {
|
|
188
330
|
columnLines.push(constraintSQL);
|
|
@@ -201,9 +343,21 @@ export function generateTableSQL(table, options = {}) {
|
|
|
201
343
|
}
|
|
202
344
|
return lines.join('\n');
|
|
203
345
|
}
|
|
204
|
-
function generateColumnSQL(col) {
|
|
346
|
+
function generateColumnSQL(col, options = {}) {
|
|
347
|
+
const { inlinePrimaryKey = false, skipInlineUnique = false, inlineForeignKey } = options;
|
|
205
348
|
const parts = [];
|
|
206
|
-
|
|
349
|
+
let normalizedType = normalizePgType(col.dataType);
|
|
350
|
+
const baseType = normalizedType.replace('[]', '').toLowerCase();
|
|
351
|
+
if ((baseType === 'varchar' || baseType === 'character varying') && col.maxLength) {
|
|
352
|
+
normalizedType = `varchar(${col.maxLength})${normalizedType.endsWith('[]') ? '[]' : ''}`;
|
|
353
|
+
}
|
|
354
|
+
else if ((baseType === 'char' || baseType === 'character' || baseType === 'bpchar') && col.maxLength) {
|
|
355
|
+
normalizedType = `char(${col.maxLength})${normalizedType.endsWith('[]') ? '[]' : ''}`;
|
|
356
|
+
}
|
|
357
|
+
else if (baseType === 'numeric' && col.precision) {
|
|
358
|
+
const precisionStr = col.scale ? `${col.precision},${col.scale}` : `${col.precision}`;
|
|
359
|
+
normalizedType = `numeric(${precisionStr})${normalizedType.endsWith('[]') ? '[]' : ''}`;
|
|
360
|
+
}
|
|
207
361
|
parts.push(`${quoteIdentifier(col.name).padEnd(28)}${normalizedType}`);
|
|
208
362
|
if (col.dataType.endsWith('[]')) {
|
|
209
363
|
}
|
|
@@ -214,44 +368,68 @@ function generateColumnSQL(col) {
|
|
|
214
368
|
if (col.identityGeneration) {
|
|
215
369
|
parts.push(`GENERATED ${col.identityGeneration} AS IDENTITY`);
|
|
216
370
|
}
|
|
217
|
-
if (!col.isNullable && !col.
|
|
371
|
+
if (!col.isNullable && !col.identityGeneration && !inlinePrimaryKey) {
|
|
218
372
|
parts.push('NOT NULL');
|
|
219
373
|
}
|
|
220
374
|
if (col.defaultValue !== null && col.defaultValue !== undefined && !col.isGenerated) {
|
|
221
375
|
parts.push(`DEFAULT ${col.defaultValue}`);
|
|
222
376
|
}
|
|
223
|
-
if (
|
|
377
|
+
if (inlinePrimaryKey) {
|
|
378
|
+
parts.push('PRIMARY KEY');
|
|
379
|
+
}
|
|
380
|
+
if (col.isUnique && !col.isPrimaryKey && !inlinePrimaryKey && !skipInlineUnique) {
|
|
224
381
|
parts.push('UNIQUE');
|
|
225
382
|
}
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
383
|
+
if (inlineForeignKey) {
|
|
384
|
+
let refPart = '';
|
|
385
|
+
if (inlineForeignKey.constraintName) {
|
|
386
|
+
refPart += `CONSTRAINT ${quoteIdentifier(inlineForeignKey.constraintName)} `;
|
|
387
|
+
}
|
|
388
|
+
refPart += `REFERENCES ${quoteIdentifier(inlineForeignKey.table)}`;
|
|
389
|
+
if (inlineForeignKey.column && inlineForeignKey.column.toLowerCase() !== 'id') {
|
|
390
|
+
refPart += `(${quoteIdentifier(inlineForeignKey.column)})`;
|
|
231
391
|
}
|
|
232
|
-
if (
|
|
233
|
-
|
|
392
|
+
if (inlineForeignKey.onDelete && inlineForeignKey.onDelete !== 'NO ACTION') {
|
|
393
|
+
refPart += ` ON DELETE ${inlineForeignKey.onDelete}`;
|
|
234
394
|
}
|
|
235
|
-
|
|
395
|
+
if (inlineForeignKey.onUpdate && inlineForeignKey.onUpdate !== 'NO ACTION') {
|
|
396
|
+
refPart += ` ON UPDATE ${inlineForeignKey.onUpdate}`;
|
|
397
|
+
}
|
|
398
|
+
parts.push(refPart);
|
|
236
399
|
}
|
|
237
400
|
if (col.check) {
|
|
238
401
|
parts.push(`CHECK (${col.check})`);
|
|
239
402
|
}
|
|
240
403
|
return parts.join(' ');
|
|
241
404
|
}
|
|
405
|
+
function unquoteIdentifiers(sql) {
|
|
406
|
+
return sql.replace(/"([a-z_][a-z0-9_]*)"/g, (match, identifier) => {
|
|
407
|
+
if (RESERVED_WORDS.has(identifier.toLowerCase())) {
|
|
408
|
+
return match;
|
|
409
|
+
}
|
|
410
|
+
return identifier;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
242
413
|
function generateInlineConstraintSQL(constraint) {
|
|
243
414
|
if (constraint.definition) {
|
|
244
|
-
|
|
415
|
+
let def = constraint.definition.trim();
|
|
416
|
+
def = unquoteIdentifiers(def);
|
|
417
|
+
if (constraint.type === 'PRIMARY KEY') {
|
|
418
|
+
const pkMatch = def.match(/PRIMARY\s+KEY\s*\([^)]+\)/i);
|
|
419
|
+
if (pkMatch) {
|
|
420
|
+
return pkMatch[0];
|
|
421
|
+
}
|
|
422
|
+
}
|
|
245
423
|
if (def.toUpperCase().startsWith('CONSTRAINT')) {
|
|
246
424
|
return def;
|
|
247
425
|
}
|
|
248
|
-
if (constraint.name) {
|
|
426
|
+
if (constraint.name && constraint.type !== 'PRIMARY KEY') {
|
|
249
427
|
return `CONSTRAINT ${quoteIdentifier(constraint.name)} ${def}`;
|
|
250
428
|
}
|
|
251
429
|
return def;
|
|
252
430
|
}
|
|
253
431
|
const parts = [];
|
|
254
|
-
if (constraint.name) {
|
|
432
|
+
if (constraint.name && constraint.type !== 'PRIMARY KEY') {
|
|
255
433
|
parts.push(`CONSTRAINT ${quoteIdentifier(constraint.name)}`);
|
|
256
434
|
}
|
|
257
435
|
switch (constraint.type) {
|
|
@@ -272,9 +450,20 @@ function generateInlineConstraintSQL(constraint) {
|
|
|
272
450
|
}
|
|
273
451
|
break;
|
|
274
452
|
case 'FOREIGN KEY':
|
|
275
|
-
|
|
453
|
+
const refTable = constraint.referencedTable || constraint.references?.table;
|
|
454
|
+
const refColumns = constraint.referencedColumns || constraint.references?.columns;
|
|
455
|
+
const onDelete = constraint.onDelete || constraint.references?.onDelete;
|
|
456
|
+
const onUpdate = constraint.onUpdate || constraint.references?.onUpdate;
|
|
457
|
+
if (constraint.columns && refTable && refColumns) {
|
|
276
458
|
parts.push(`FOREIGN KEY (${constraint.columns.map(c => quoteIdentifier(c)).join(', ')})`);
|
|
277
|
-
|
|
459
|
+
let refPart = `REFERENCES ${quoteIdentifier(refTable)}(${refColumns.map((c) => quoteIdentifier(c)).join(', ')})`;
|
|
460
|
+
if (onDelete && onDelete !== 'NO ACTION') {
|
|
461
|
+
refPart += ` ON DELETE ${onDelete}`;
|
|
462
|
+
}
|
|
463
|
+
if (onUpdate && onUpdate !== 'NO ACTION') {
|
|
464
|
+
refPart += ` ON UPDATE ${onUpdate}`;
|
|
465
|
+
}
|
|
466
|
+
parts.push(refPart);
|
|
278
467
|
}
|
|
279
468
|
else {
|
|
280
469
|
return null;
|
|
@@ -309,32 +498,64 @@ export function generatePartitionSQL(partition) {
|
|
|
309
498
|
}
|
|
310
499
|
return parts.join(' ') + ';';
|
|
311
500
|
}
|
|
312
|
-
export function generateIndexSQL(index, tableName) {
|
|
313
|
-
if (index.definition) {
|
|
314
|
-
let def = index.definition.trim();
|
|
315
|
-
if (!def.endsWith(';')) {
|
|
316
|
-
def += ';';
|
|
317
|
-
}
|
|
318
|
-
return def;
|
|
319
|
-
}
|
|
501
|
+
export function generateIndexSQL(index, tableName, options = {}) {
|
|
320
502
|
const parts = [];
|
|
321
503
|
parts.push('CREATE');
|
|
322
504
|
if (index.isUnique) {
|
|
323
505
|
parts.push('UNIQUE');
|
|
324
506
|
}
|
|
325
507
|
parts.push('INDEX');
|
|
508
|
+
if (options.ifNotExists) {
|
|
509
|
+
parts.push('IF NOT EXISTS');
|
|
510
|
+
}
|
|
326
511
|
parts.push(quoteIdentifier(index.name));
|
|
327
512
|
parts.push('ON');
|
|
328
|
-
|
|
513
|
+
if (options.tableOnly) {
|
|
514
|
+
parts.push('ONLY');
|
|
515
|
+
}
|
|
516
|
+
if (options.includeSchema && options.schema) {
|
|
517
|
+
parts.push(`${quoteIdentifier(options.schema)}.${quoteIdentifier(tableName)}`);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
parts.push(quoteIdentifier(tableName));
|
|
521
|
+
}
|
|
329
522
|
if (index.type && index.type.toLowerCase() !== 'btree') {
|
|
330
|
-
parts.push(`USING ${index.type.
|
|
523
|
+
parts.push(`USING ${index.type.toLowerCase()}`);
|
|
524
|
+
}
|
|
525
|
+
let columnSpec = null;
|
|
526
|
+
if (index.definition) {
|
|
527
|
+
const defMatch = index.definition.match(/\bON\s+(?:ONLY\s+)?[^\s(]+\s+(?:USING\s+\w+\s+)?\(([^)]+)\)/i);
|
|
528
|
+
if (defMatch) {
|
|
529
|
+
columnSpec = defMatch[1].trim();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (columnSpec) {
|
|
533
|
+
parts.push(`(${columnSpec})`);
|
|
331
534
|
}
|
|
332
|
-
if (index.expression) {
|
|
535
|
+
else if (index.expression) {
|
|
333
536
|
parts.push(`(${index.expression})`);
|
|
334
537
|
}
|
|
335
|
-
else if (index.columns
|
|
336
|
-
|
|
337
|
-
|
|
538
|
+
else if (index.columns) {
|
|
539
|
+
let columnsArray;
|
|
540
|
+
if (Array.isArray(index.columns)) {
|
|
541
|
+
columnsArray = index.columns;
|
|
542
|
+
}
|
|
543
|
+
else if (typeof index.columns === 'string') {
|
|
544
|
+
const str = index.columns;
|
|
545
|
+
if (str.startsWith('{') && str.endsWith('}')) {
|
|
546
|
+
columnsArray = str.slice(1, -1).split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
columnsArray = [str];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
columnsArray = [];
|
|
554
|
+
}
|
|
555
|
+
if (columnsArray.length > 0) {
|
|
556
|
+
const cols = columnsArray.map(c => quoteColumnRef(c)).join(', ');
|
|
557
|
+
parts.push(`(${cols})`);
|
|
558
|
+
}
|
|
338
559
|
}
|
|
339
560
|
if (index.whereClause) {
|
|
340
561
|
parts.push(`WHERE ${index.whereClause}`);
|
|
@@ -423,7 +644,7 @@ export function generateCommentSQL(comment) {
|
|
|
423
644
|
}
|
|
424
645
|
}
|
|
425
646
|
export function generateFullSchemaSQL(schema, options = {}) {
|
|
426
|
-
const { includeExtensions = true, includeEnums = true, includeDomains = true, includeCompositeTypes = true, includeSequences = true, includeTables = true, includePartitions = true, includeIndexes = true, includeConstraints = true, includeFunctions = true, includeTriggers = true, headerComment, } = options;
|
|
647
|
+
const { includeExtensions = true, includeEnums = true, includeDomains = true, includeCompositeTypes = true, includeSequences = true, includeTables = true, includePartitions = true, includeIndexes = true, includeConstraints = true, includeFunctions = true, includeTriggers = true, includeComments = true, headerComment, ifNotExists = false, } = options;
|
|
427
648
|
const sections = [];
|
|
428
649
|
if (headerComment) {
|
|
429
650
|
sections.push(`-- ${headerComment}\n`);
|
|
@@ -464,45 +685,113 @@ export function generateFullSchemaSQL(schema, options = {}) {
|
|
|
464
685
|
sections.push('');
|
|
465
686
|
}
|
|
466
687
|
if (includeTables && schema.tables && schema.tables.length > 0) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
sections.push(generatePartitionSQL(partition));
|
|
688
|
+
const partitionsByParent = new Map();
|
|
689
|
+
if (includePartitions && schema.partitions) {
|
|
690
|
+
for (const partition of schema.partitions) {
|
|
691
|
+
const parent = partition.parentTable;
|
|
692
|
+
if (!partitionsByParent.has(parent)) {
|
|
693
|
+
partitionsByParent.set(parent, []);
|
|
694
|
+
}
|
|
695
|
+
partitionsByParent.get(parent).push(partition);
|
|
696
|
+
}
|
|
477
697
|
}
|
|
478
|
-
sections.push('');
|
|
479
|
-
}
|
|
480
|
-
if (includeIndexes && schema.tables) {
|
|
481
|
-
const indexLines = [];
|
|
482
698
|
for (const table of schema.tables) {
|
|
483
|
-
|
|
699
|
+
const tableSection = [];
|
|
700
|
+
tableSection.push('-- ==================================================================');
|
|
701
|
+
tableSection.push(`-- TABLE: ${table.name}`);
|
|
702
|
+
tableSection.push('-- ==================================================================');
|
|
703
|
+
tableSection.push('');
|
|
704
|
+
tableSection.push('-- Table');
|
|
705
|
+
tableSection.push(generateTableSQL(table, { includeConstraints, ifNotExists }));
|
|
706
|
+
const tablePartitions = partitionsByParent.get(table.name);
|
|
707
|
+
if (tablePartitions && tablePartitions.length > 0) {
|
|
708
|
+
tableSection.push('');
|
|
709
|
+
tableSection.push(`-- Partitions for ${table.name}`);
|
|
710
|
+
for (const partition of tablePartitions) {
|
|
711
|
+
tableSection.push(generatePartitionSQL(partition));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
if (includeIndexes && table.indexes && table.indexes.length > 0) {
|
|
715
|
+
const uniqueConstraintNames = new Set((table.constraints || [])
|
|
716
|
+
.filter(c => c.type === 'UNIQUE')
|
|
717
|
+
.map(c => c.name));
|
|
718
|
+
const indexLines = [];
|
|
484
719
|
for (const index of table.indexes) {
|
|
485
|
-
if (
|
|
486
|
-
|
|
720
|
+
if (index.isPrimary)
|
|
721
|
+
continue;
|
|
722
|
+
if (index.isUnique && uniqueConstraintNames.has(index.name))
|
|
723
|
+
continue;
|
|
724
|
+
if (index.isUnique && (index.name.endsWith('_key') ||
|
|
725
|
+
index.name.startsWith('unique_') ||
|
|
726
|
+
uniqueConstraintNames.has(index.name.replace(/_key$/, ''))))
|
|
727
|
+
continue;
|
|
728
|
+
indexLines.push(generateIndexSQL(index, table.name, { ifNotExists }));
|
|
729
|
+
}
|
|
730
|
+
if (indexLines.length > 0) {
|
|
731
|
+
tableSection.push('');
|
|
732
|
+
tableSection.push(`-- Indexes for ${table.name}`);
|
|
733
|
+
tableSection.push(indexLines.join('\n'));
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (includeComments) {
|
|
737
|
+
const commentLines = [];
|
|
738
|
+
if (table.comment) {
|
|
739
|
+
commentLines.push(generateCommentSQL({
|
|
740
|
+
objectType: 'table',
|
|
741
|
+
objectName: table.name,
|
|
742
|
+
comment: table.comment,
|
|
743
|
+
}));
|
|
744
|
+
}
|
|
745
|
+
if (table.columns) {
|
|
746
|
+
for (const col of table.columns) {
|
|
747
|
+
if (col.comment) {
|
|
748
|
+
commentLines.push(generateCommentSQL({
|
|
749
|
+
objectType: 'column',
|
|
750
|
+
objectName: table.name,
|
|
751
|
+
subObjectName: col.name,
|
|
752
|
+
comment: col.comment,
|
|
753
|
+
}));
|
|
754
|
+
}
|
|
487
755
|
}
|
|
488
756
|
}
|
|
757
|
+
if (table.indexes) {
|
|
758
|
+
for (const idx of table.indexes) {
|
|
759
|
+
if (idx.comment) {
|
|
760
|
+
commentLines.push(generateCommentSQL({
|
|
761
|
+
objectType: 'index',
|
|
762
|
+
objectName: idx.name,
|
|
763
|
+
comment: idx.comment,
|
|
764
|
+
}));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (commentLines.length > 0) {
|
|
769
|
+
tableSection.push('');
|
|
770
|
+
tableSection.push(`-- Comments for ${table.name}`);
|
|
771
|
+
tableSection.push(commentLines.join('\n'));
|
|
772
|
+
}
|
|
489
773
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
sections.push('
|
|
493
|
-
sections.push(indexLines.join('\n'));
|
|
774
|
+
sections.push(tableSection.join('\n'));
|
|
775
|
+
sections.push('');
|
|
776
|
+
sections.push('');
|
|
494
777
|
sections.push('');
|
|
495
778
|
}
|
|
496
779
|
}
|
|
497
780
|
if (includeFunctions && schema.functions && schema.functions.length > 0) {
|
|
498
|
-
sections.push('--
|
|
781
|
+
sections.push('-- ==================================================================');
|
|
782
|
+
sections.push('-- FUNCTIONS');
|
|
783
|
+
sections.push('-- ==================================================================');
|
|
784
|
+
sections.push('');
|
|
499
785
|
for (const func of schema.functions) {
|
|
500
786
|
sections.push(generateFunctionSQL(func));
|
|
501
787
|
sections.push('');
|
|
502
788
|
}
|
|
503
789
|
}
|
|
504
790
|
if (includeTriggers && schema.triggers && schema.triggers.length > 0) {
|
|
505
|
-
sections.push('--
|
|
791
|
+
sections.push('-- ==================================================================');
|
|
792
|
+
sections.push('-- TRIGGERS');
|
|
793
|
+
sections.push('-- ==================================================================');
|
|
794
|
+
sections.push('');
|
|
506
795
|
for (const trigger of schema.triggers) {
|
|
507
796
|
sections.push(generateTriggerSQL(trigger));
|
|
508
797
|
}
|