relq 1.0.5 → 1.0.7
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 +257 -17
- package/dist/cjs/cli/commands/commit.cjs +13 -2
- 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/config/config.cjs +29 -10
- package/dist/cjs/core/relq-client.cjs +22 -6
- package/dist/cjs/schema-definition/column-types.cjs +149 -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 +147 -2
- package/dist/esm/cli/commands/add.js +255 -18
- package/dist/esm/cli/commands/commit.js +13 -2
- 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/config/config.js +29 -10
- package/dist/esm/core/relq-client.js +23 -7
- package/dist/esm/schema-definition/column-types.js +146 -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
|
@@ -15,11 +15,11 @@ exports.generateTriggerSQL = generateTriggerSQL;
|
|
|
15
15
|
exports.generateCommentSQL = generateCommentSQL;
|
|
16
16
|
exports.generateFullSchemaSQL = generateFullSchemaSQL;
|
|
17
17
|
const RESERVED_WORDS = new Set([
|
|
18
|
-
'
|
|
18
|
+
'order', 'check', 'table', 'index', 'column', 'constraint',
|
|
19
19
|
'primary', 'foreign', 'key', 'references', 'unique', 'default',
|
|
20
20
|
'null', 'not', 'and', 'or', 'in', 'like', 'between', 'case',
|
|
21
21
|
'when', 'then', 'else', 'end', 'select', 'from', 'where', 'group',
|
|
22
|
-
'having', '
|
|
22
|
+
'having', 'by', 'limit', 'offset', 'join', 'left', 'right',
|
|
23
23
|
'inner', 'outer', 'cross', 'on', 'as', 'into', 'values', 'insert',
|
|
24
24
|
'update', 'delete', 'create', 'alter', 'drop', 'grant', 'revoke',
|
|
25
25
|
'all', 'any', 'some', 'exists', 'true', 'false', 'boolean', 'int',
|
|
@@ -61,7 +61,7 @@ const RESERVED_WORDS = new Set([
|
|
|
61
61
|
'off', 'oids', 'old', 'only', 'operator', 'option', 'options',
|
|
62
62
|
'ordinality', 'others', 'out', 'over', 'overlaps', 'overlay',
|
|
63
63
|
'overriding', 'owned', 'owner', 'parallel', 'parser', 'partial',
|
|
64
|
-
'partition', 'passing', '
|
|
64
|
+
'partition', 'passing', 'placing', 'plans', 'policy',
|
|
65
65
|
'position', 'preceding', 'prepare', 'prepared', 'preserve', 'prior',
|
|
66
66
|
'privileges', 'procedural', 'program', 'publication', 'quote',
|
|
67
67
|
'range', 'read', 'reassign', 'recheck', 'refresh', 'reindex',
|
|
@@ -97,6 +97,34 @@ function quoteIdentifier(name) {
|
|
|
97
97
|
}
|
|
98
98
|
return name;
|
|
99
99
|
}
|
|
100
|
+
function parseFKDefinition(definition) {
|
|
101
|
+
if (!definition)
|
|
102
|
+
return null;
|
|
103
|
+
const constraintNameMatch = definition.match(/^CONSTRAINT\s+"?([^"]+)"?\s+/i);
|
|
104
|
+
const constraintName = constraintNameMatch?.[1];
|
|
105
|
+
const fkPart = constraintName
|
|
106
|
+
? definition.substring(constraintNameMatch[0].length)
|
|
107
|
+
: definition;
|
|
108
|
+
const fkMatch = fkPart.match(/FOREIGN\s+KEY\s*\(\s*([^)]+)\s*\)\s*REFERENCES\s+"?([^"\s(]+)"?\s*(?:\(\s*([^)]+)\s*\))?/i);
|
|
109
|
+
if (!fkMatch)
|
|
110
|
+
return null;
|
|
111
|
+
const parseColumnList = (str) => {
|
|
112
|
+
return str.split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
113
|
+
};
|
|
114
|
+
const columns = parseColumnList(fkMatch[1]);
|
|
115
|
+
const referencedTable = fkMatch[2].replace(/^"|"$/g, '');
|
|
116
|
+
const referencedColumns = fkMatch[3] ? parseColumnList(fkMatch[3]) : ['id'];
|
|
117
|
+
const onDeleteMatch = definition.match(/ON\s+DELETE\s+(CASCADE|SET\s+NULL|SET\s+DEFAULT|RESTRICT|NO\s+ACTION)/i);
|
|
118
|
+
const onUpdateMatch = definition.match(/ON\s+UPDATE\s+(CASCADE|SET\s+NULL|SET\s+DEFAULT|RESTRICT|NO\s+ACTION)/i);
|
|
119
|
+
return {
|
|
120
|
+
constraintName,
|
|
121
|
+
columns,
|
|
122
|
+
referencedTable,
|
|
123
|
+
referencedColumns,
|
|
124
|
+
onDelete: onDeleteMatch?.[1]?.toUpperCase().replace(/\s+/g, ' '),
|
|
125
|
+
onUpdate: onUpdateMatch?.[1]?.toUpperCase().replace(/\s+/g, ' '),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
100
128
|
function quoteColumnRef(name) {
|
|
101
129
|
const lowerName = name.toLowerCase();
|
|
102
130
|
if (RESERVED_WORDS.has(lowerName)) {
|
|
@@ -107,12 +135,25 @@ function quoteColumnRef(name) {
|
|
|
107
135
|
}
|
|
108
136
|
return name;
|
|
109
137
|
}
|
|
138
|
+
const PG_TYPE_MAP = {
|
|
139
|
+
'int2': 'smallint',
|
|
140
|
+
'int4': 'integer',
|
|
141
|
+
'int8': 'bigint',
|
|
142
|
+
'float4': 'real',
|
|
143
|
+
'float8': 'double precision',
|
|
144
|
+
'bool': 'boolean',
|
|
145
|
+
'timestamptz': 'timestamp with time zone',
|
|
146
|
+
'timetz': 'time with time zone',
|
|
147
|
+
'bpchar': 'character',
|
|
148
|
+
'varbit': 'bit varying',
|
|
149
|
+
};
|
|
110
150
|
function normalizePgType(pgType) {
|
|
111
151
|
if (pgType.startsWith('_')) {
|
|
112
152
|
const baseType = pgType.slice(1);
|
|
113
|
-
|
|
153
|
+
const friendlyBase = PG_TYPE_MAP[baseType] || baseType;
|
|
154
|
+
return `${friendlyBase}[]`;
|
|
114
155
|
}
|
|
115
|
-
return pgType;
|
|
156
|
+
return PG_TYPE_MAP[pgType] || pgType;
|
|
116
157
|
}
|
|
117
158
|
function generateExtensionSQL(extensionName) {
|
|
118
159
|
const quotedName = extensionName.includes('-') ? `"${extensionName}"` : extensionName;
|
|
@@ -190,15 +231,116 @@ function generateSequenceSQL(seq) {
|
|
|
190
231
|
return parts.join(' ') + ';';
|
|
191
232
|
}
|
|
192
233
|
function generateTableSQL(table, options = {}) {
|
|
193
|
-
const { includeConstraints = true } = options;
|
|
234
|
+
const { includeConstraints = true, ifNotExists = false } = options;
|
|
194
235
|
const lines = [];
|
|
195
|
-
|
|
236
|
+
const ifNotExistsClause = ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
237
|
+
lines.push(`-- DEBUG: generateTableSQL running for ${table.name}`);
|
|
238
|
+
lines.push(`CREATE TABLE ${ifNotExistsClause}${quoteIdentifier(table.name)} (`);
|
|
239
|
+
let singleColumnPkName = null;
|
|
240
|
+
let pkConstraintIndex = -1;
|
|
241
|
+
const compositeUniqueColumns = new Set();
|
|
242
|
+
const inlineFKs = new Map();
|
|
243
|
+
const skipFKIndices = new Set();
|
|
244
|
+
if (table.constraints) {
|
|
245
|
+
const pkIndex = table.constraints.findIndex(c => c.type === 'PRIMARY KEY');
|
|
246
|
+
if (pkIndex !== -1) {
|
|
247
|
+
const pkConstraint = table.constraints[pkIndex];
|
|
248
|
+
pkConstraintIndex = pkIndex;
|
|
249
|
+
if (pkConstraint.columns && pkConstraint.columns.length === 1) {
|
|
250
|
+
singleColumnPkName = pkConstraint.columns[0];
|
|
251
|
+
}
|
|
252
|
+
else if (pkConstraint.definition) {
|
|
253
|
+
const match = pkConstraint.definition.match(/PRIMARY\s+KEY\s*\(\s*"?([^",\s)]+)"?\s*\)/i);
|
|
254
|
+
if (match && !pkConstraint.definition.includes(',')) {
|
|
255
|
+
singleColumnPkName = match[1];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const constraint of table.constraints) {
|
|
260
|
+
if (constraint.type === 'UNIQUE') {
|
|
261
|
+
let cols = [];
|
|
262
|
+
if (constraint.columns && constraint.columns.length > 0) {
|
|
263
|
+
if (Array.isArray(constraint.columns)) {
|
|
264
|
+
cols = constraint.columns;
|
|
265
|
+
}
|
|
266
|
+
else if (typeof constraint.columns === 'string') {
|
|
267
|
+
const str = constraint.columns;
|
|
268
|
+
if (str.startsWith('{') && str.endsWith('}')) {
|
|
269
|
+
cols = str.slice(1, -1).split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
else if (constraint.definition) {
|
|
274
|
+
const match = constraint.definition.match(/UNIQUE\s*\(([^)]+)\)/i);
|
|
275
|
+
if (match) {
|
|
276
|
+
cols = match[1].split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (cols.length > 1) {
|
|
280
|
+
for (const col of cols) {
|
|
281
|
+
compositeUniqueColumns.add(col);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
table.constraints.forEach((constraint, index) => {
|
|
287
|
+
if (constraint.type === 'FOREIGN KEY') {
|
|
288
|
+
let fkColumns = constraint.columns;
|
|
289
|
+
let refTable = constraint.referencedTable || constraint.references?.table;
|
|
290
|
+
let refColumns = constraint.referencedColumns || constraint.references?.columns;
|
|
291
|
+
let onDelete = constraint.onDelete || constraint.references?.onDelete;
|
|
292
|
+
let onUpdate = constraint.onUpdate || constraint.references?.onUpdate;
|
|
293
|
+
let constraintName = constraint.name;
|
|
294
|
+
if (!refTable && constraint.definition) {
|
|
295
|
+
const parsed = parseFKDefinition(constraint.definition);
|
|
296
|
+
if (parsed) {
|
|
297
|
+
fkColumns = parsed.columns;
|
|
298
|
+
refTable = parsed.referencedTable;
|
|
299
|
+
refColumns = parsed.referencedColumns;
|
|
300
|
+
onDelete = parsed.onDelete;
|
|
301
|
+
onUpdate = parsed.onUpdate;
|
|
302
|
+
if (!constraintName) {
|
|
303
|
+
constraintName = parsed.constraintName;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const isSingleColumn = fkColumns && fkColumns.length === 1;
|
|
308
|
+
let hasExplicitName = false;
|
|
309
|
+
if (constraintName && fkColumns && fkColumns.length > 0) {
|
|
310
|
+
const colName = fkColumns[0];
|
|
311
|
+
const expectedAutoName = `${table.name}_${colName}_fkey`;
|
|
312
|
+
hasExplicitName = constraintName.toLowerCase() !== expectedAutoName.toLowerCase();
|
|
313
|
+
}
|
|
314
|
+
if (isSingleColumn && refTable) {
|
|
315
|
+
const colName = fkColumns[0];
|
|
316
|
+
inlineFKs.set(colName, {
|
|
317
|
+
table: refTable,
|
|
318
|
+
column: refColumns?.[0],
|
|
319
|
+
onDelete: onDelete,
|
|
320
|
+
onUpdate: onUpdate,
|
|
321
|
+
constraintName: hasExplicitName ? constraintName : undefined,
|
|
322
|
+
});
|
|
323
|
+
skipFKIndices.add(index);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
196
328
|
const columnLines = [];
|
|
197
329
|
for (const col of table.columns) {
|
|
198
|
-
|
|
330
|
+
const isInlinePk = singleColumnPkName === col.name;
|
|
331
|
+
const skipInlineUnique = compositeUniqueColumns.has(col.name);
|
|
332
|
+
const inlineFKInfo = inlineFKs.get(col.name);
|
|
333
|
+
columnLines.push(generateColumnSQL(col, { inlinePrimaryKey: isInlinePk, skipInlineUnique, inlineForeignKey: inlineFKInfo }));
|
|
199
334
|
}
|
|
200
335
|
if (includeConstraints && table.constraints) {
|
|
201
|
-
for (
|
|
336
|
+
for (let i = 0; i < table.constraints.length; i++) {
|
|
337
|
+
const constraint = table.constraints[i];
|
|
338
|
+
if (constraint.type === 'PRIMARY KEY' && singleColumnPkName) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (constraint.type === 'FOREIGN KEY' && skipFKIndices.has(i)) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
202
344
|
const constraintSQL = generateInlineConstraintSQL(constraint);
|
|
203
345
|
if (constraintSQL) {
|
|
204
346
|
columnLines.push(constraintSQL);
|
|
@@ -217,9 +359,21 @@ function generateTableSQL(table, options = {}) {
|
|
|
217
359
|
}
|
|
218
360
|
return lines.join('\n');
|
|
219
361
|
}
|
|
220
|
-
function generateColumnSQL(col) {
|
|
362
|
+
function generateColumnSQL(col, options = {}) {
|
|
363
|
+
const { inlinePrimaryKey = false, skipInlineUnique = false, inlineForeignKey } = options;
|
|
221
364
|
const parts = [];
|
|
222
|
-
|
|
365
|
+
let normalizedType = normalizePgType(col.dataType);
|
|
366
|
+
const baseType = normalizedType.replace('[]', '').toLowerCase();
|
|
367
|
+
if ((baseType === 'varchar' || baseType === 'character varying') && col.maxLength) {
|
|
368
|
+
normalizedType = `varchar(${col.maxLength})${normalizedType.endsWith('[]') ? '[]' : ''}`;
|
|
369
|
+
}
|
|
370
|
+
else if ((baseType === 'char' || baseType === 'character' || baseType === 'bpchar') && col.maxLength) {
|
|
371
|
+
normalizedType = `char(${col.maxLength})${normalizedType.endsWith('[]') ? '[]' : ''}`;
|
|
372
|
+
}
|
|
373
|
+
else if (baseType === 'numeric' && col.precision) {
|
|
374
|
+
const precisionStr = col.scale ? `${col.precision},${col.scale}` : `${col.precision}`;
|
|
375
|
+
normalizedType = `numeric(${precisionStr})${normalizedType.endsWith('[]') ? '[]' : ''}`;
|
|
376
|
+
}
|
|
223
377
|
parts.push(`${quoteIdentifier(col.name).padEnd(28)}${normalizedType}`);
|
|
224
378
|
if (col.dataType.endsWith('[]')) {
|
|
225
379
|
}
|
|
@@ -230,44 +384,68 @@ function generateColumnSQL(col) {
|
|
|
230
384
|
if (col.identityGeneration) {
|
|
231
385
|
parts.push(`GENERATED ${col.identityGeneration} AS IDENTITY`);
|
|
232
386
|
}
|
|
233
|
-
if (!col.isNullable && !col.
|
|
387
|
+
if (!col.isNullable && !col.identityGeneration && !inlinePrimaryKey) {
|
|
234
388
|
parts.push('NOT NULL');
|
|
235
389
|
}
|
|
236
390
|
if (col.defaultValue !== null && col.defaultValue !== undefined && !col.isGenerated) {
|
|
237
391
|
parts.push(`DEFAULT ${col.defaultValue}`);
|
|
238
392
|
}
|
|
239
|
-
if (
|
|
393
|
+
if (inlinePrimaryKey) {
|
|
394
|
+
parts.push('PRIMARY KEY');
|
|
395
|
+
}
|
|
396
|
+
if (col.isUnique && !col.isPrimaryKey && !inlinePrimaryKey && !skipInlineUnique) {
|
|
240
397
|
parts.push('UNIQUE');
|
|
241
398
|
}
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
399
|
+
if (inlineForeignKey) {
|
|
400
|
+
let refPart = '';
|
|
401
|
+
if (inlineForeignKey.constraintName) {
|
|
402
|
+
refPart += `CONSTRAINT ${quoteIdentifier(inlineForeignKey.constraintName)} `;
|
|
403
|
+
}
|
|
404
|
+
refPart += `REFERENCES ${quoteIdentifier(inlineForeignKey.table)}`;
|
|
405
|
+
if (inlineForeignKey.column && inlineForeignKey.column.toLowerCase() !== 'id') {
|
|
406
|
+
refPart += `(${quoteIdentifier(inlineForeignKey.column)})`;
|
|
247
407
|
}
|
|
248
|
-
if (
|
|
249
|
-
|
|
408
|
+
if (inlineForeignKey.onDelete && inlineForeignKey.onDelete !== 'NO ACTION') {
|
|
409
|
+
refPart += ` ON DELETE ${inlineForeignKey.onDelete}`;
|
|
250
410
|
}
|
|
251
|
-
|
|
411
|
+
if (inlineForeignKey.onUpdate && inlineForeignKey.onUpdate !== 'NO ACTION') {
|
|
412
|
+
refPart += ` ON UPDATE ${inlineForeignKey.onUpdate}`;
|
|
413
|
+
}
|
|
414
|
+
parts.push(refPart);
|
|
252
415
|
}
|
|
253
416
|
if (col.check) {
|
|
254
417
|
parts.push(`CHECK (${col.check})`);
|
|
255
418
|
}
|
|
256
419
|
return parts.join(' ');
|
|
257
420
|
}
|
|
421
|
+
function unquoteIdentifiers(sql) {
|
|
422
|
+
return sql.replace(/"([a-z_][a-z0-9_]*)"/g, (match, identifier) => {
|
|
423
|
+
if (RESERVED_WORDS.has(identifier.toLowerCase())) {
|
|
424
|
+
return match;
|
|
425
|
+
}
|
|
426
|
+
return identifier;
|
|
427
|
+
});
|
|
428
|
+
}
|
|
258
429
|
function generateInlineConstraintSQL(constraint) {
|
|
259
430
|
if (constraint.definition) {
|
|
260
|
-
|
|
431
|
+
let def = constraint.definition.trim();
|
|
432
|
+
def = unquoteIdentifiers(def);
|
|
433
|
+
if (constraint.type === 'PRIMARY KEY') {
|
|
434
|
+
const pkMatch = def.match(/PRIMARY\s+KEY\s*\([^)]+\)/i);
|
|
435
|
+
if (pkMatch) {
|
|
436
|
+
return pkMatch[0];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
261
439
|
if (def.toUpperCase().startsWith('CONSTRAINT')) {
|
|
262
440
|
return def;
|
|
263
441
|
}
|
|
264
|
-
if (constraint.name) {
|
|
442
|
+
if (constraint.name && constraint.type !== 'PRIMARY KEY') {
|
|
265
443
|
return `CONSTRAINT ${quoteIdentifier(constraint.name)} ${def}`;
|
|
266
444
|
}
|
|
267
445
|
return def;
|
|
268
446
|
}
|
|
269
447
|
const parts = [];
|
|
270
|
-
if (constraint.name) {
|
|
448
|
+
if (constraint.name && constraint.type !== 'PRIMARY KEY') {
|
|
271
449
|
parts.push(`CONSTRAINT ${quoteIdentifier(constraint.name)}`);
|
|
272
450
|
}
|
|
273
451
|
switch (constraint.type) {
|
|
@@ -288,9 +466,20 @@ function generateInlineConstraintSQL(constraint) {
|
|
|
288
466
|
}
|
|
289
467
|
break;
|
|
290
468
|
case 'FOREIGN KEY':
|
|
291
|
-
|
|
469
|
+
const refTable = constraint.referencedTable || constraint.references?.table;
|
|
470
|
+
const refColumns = constraint.referencedColumns || constraint.references?.columns;
|
|
471
|
+
const onDelete = constraint.onDelete || constraint.references?.onDelete;
|
|
472
|
+
const onUpdate = constraint.onUpdate || constraint.references?.onUpdate;
|
|
473
|
+
if (constraint.columns && refTable && refColumns) {
|
|
292
474
|
parts.push(`FOREIGN KEY (${constraint.columns.map(c => quoteIdentifier(c)).join(', ')})`);
|
|
293
|
-
|
|
475
|
+
let refPart = `REFERENCES ${quoteIdentifier(refTable)}(${refColumns.map((c) => quoteIdentifier(c)).join(', ')})`;
|
|
476
|
+
if (onDelete && onDelete !== 'NO ACTION') {
|
|
477
|
+
refPart += ` ON DELETE ${onDelete}`;
|
|
478
|
+
}
|
|
479
|
+
if (onUpdate && onUpdate !== 'NO ACTION') {
|
|
480
|
+
refPart += ` ON UPDATE ${onUpdate}`;
|
|
481
|
+
}
|
|
482
|
+
parts.push(refPart);
|
|
294
483
|
}
|
|
295
484
|
else {
|
|
296
485
|
return null;
|
|
@@ -325,32 +514,64 @@ function generatePartitionSQL(partition) {
|
|
|
325
514
|
}
|
|
326
515
|
return parts.join(' ') + ';';
|
|
327
516
|
}
|
|
328
|
-
function generateIndexSQL(index, tableName) {
|
|
329
|
-
if (index.definition) {
|
|
330
|
-
let def = index.definition.trim();
|
|
331
|
-
if (!def.endsWith(';')) {
|
|
332
|
-
def += ';';
|
|
333
|
-
}
|
|
334
|
-
return def;
|
|
335
|
-
}
|
|
517
|
+
function generateIndexSQL(index, tableName, options = {}) {
|
|
336
518
|
const parts = [];
|
|
337
519
|
parts.push('CREATE');
|
|
338
520
|
if (index.isUnique) {
|
|
339
521
|
parts.push('UNIQUE');
|
|
340
522
|
}
|
|
341
523
|
parts.push('INDEX');
|
|
524
|
+
if (options.ifNotExists) {
|
|
525
|
+
parts.push('IF NOT EXISTS');
|
|
526
|
+
}
|
|
342
527
|
parts.push(quoteIdentifier(index.name));
|
|
343
528
|
parts.push('ON');
|
|
344
|
-
|
|
529
|
+
if (options.tableOnly) {
|
|
530
|
+
parts.push('ONLY');
|
|
531
|
+
}
|
|
532
|
+
if (options.includeSchema && options.schema) {
|
|
533
|
+
parts.push(`${quoteIdentifier(options.schema)}.${quoteIdentifier(tableName)}`);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
parts.push(quoteIdentifier(tableName));
|
|
537
|
+
}
|
|
345
538
|
if (index.type && index.type.toLowerCase() !== 'btree') {
|
|
346
|
-
parts.push(`USING ${index.type.
|
|
539
|
+
parts.push(`USING ${index.type.toLowerCase()}`);
|
|
540
|
+
}
|
|
541
|
+
let columnSpec = null;
|
|
542
|
+
if (index.definition) {
|
|
543
|
+
const defMatch = index.definition.match(/\bON\s+(?:ONLY\s+)?[^\s(]+\s+(?:USING\s+\w+\s+)?\(([^)]+)\)/i);
|
|
544
|
+
if (defMatch) {
|
|
545
|
+
columnSpec = defMatch[1].trim();
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (columnSpec) {
|
|
549
|
+
parts.push(`(${columnSpec})`);
|
|
347
550
|
}
|
|
348
|
-
if (index.expression) {
|
|
551
|
+
else if (index.expression) {
|
|
349
552
|
parts.push(`(${index.expression})`);
|
|
350
553
|
}
|
|
351
|
-
else if (index.columns
|
|
352
|
-
|
|
353
|
-
|
|
554
|
+
else if (index.columns) {
|
|
555
|
+
let columnsArray;
|
|
556
|
+
if (Array.isArray(index.columns)) {
|
|
557
|
+
columnsArray = index.columns;
|
|
558
|
+
}
|
|
559
|
+
else if (typeof index.columns === 'string') {
|
|
560
|
+
const str = index.columns;
|
|
561
|
+
if (str.startsWith('{') && str.endsWith('}')) {
|
|
562
|
+
columnsArray = str.slice(1, -1).split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
columnsArray = [str];
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
columnsArray = [];
|
|
570
|
+
}
|
|
571
|
+
if (columnsArray.length > 0) {
|
|
572
|
+
const cols = columnsArray.map(c => quoteColumnRef(c)).join(', ');
|
|
573
|
+
parts.push(`(${cols})`);
|
|
574
|
+
}
|
|
354
575
|
}
|
|
355
576
|
if (index.whereClause) {
|
|
356
577
|
parts.push(`WHERE ${index.whereClause}`);
|
|
@@ -439,7 +660,7 @@ function generateCommentSQL(comment) {
|
|
|
439
660
|
}
|
|
440
661
|
}
|
|
441
662
|
function generateFullSchemaSQL(schema, options = {}) {
|
|
442
|
-
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;
|
|
663
|
+
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;
|
|
443
664
|
const sections = [];
|
|
444
665
|
if (headerComment) {
|
|
445
666
|
sections.push(`-- ${headerComment}\n`);
|
|
@@ -480,45 +701,113 @@ function generateFullSchemaSQL(schema, options = {}) {
|
|
|
480
701
|
sections.push('');
|
|
481
702
|
}
|
|
482
703
|
if (includeTables && schema.tables && schema.tables.length > 0) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
sections.push(generatePartitionSQL(partition));
|
|
704
|
+
const partitionsByParent = new Map();
|
|
705
|
+
if (includePartitions && schema.partitions) {
|
|
706
|
+
for (const partition of schema.partitions) {
|
|
707
|
+
const parent = partition.parentTable;
|
|
708
|
+
if (!partitionsByParent.has(parent)) {
|
|
709
|
+
partitionsByParent.set(parent, []);
|
|
710
|
+
}
|
|
711
|
+
partitionsByParent.get(parent).push(partition);
|
|
712
|
+
}
|
|
493
713
|
}
|
|
494
|
-
sections.push('');
|
|
495
|
-
}
|
|
496
|
-
if (includeIndexes && schema.tables) {
|
|
497
|
-
const indexLines = [];
|
|
498
714
|
for (const table of schema.tables) {
|
|
499
|
-
|
|
715
|
+
const tableSection = [];
|
|
716
|
+
tableSection.push('-- ==================================================================');
|
|
717
|
+
tableSection.push(`-- TABLE: ${table.name}`);
|
|
718
|
+
tableSection.push('-- ==================================================================');
|
|
719
|
+
tableSection.push('');
|
|
720
|
+
tableSection.push('-- Table');
|
|
721
|
+
tableSection.push(generateTableSQL(table, { includeConstraints, ifNotExists }));
|
|
722
|
+
const tablePartitions = partitionsByParent.get(table.name);
|
|
723
|
+
if (tablePartitions && tablePartitions.length > 0) {
|
|
724
|
+
tableSection.push('');
|
|
725
|
+
tableSection.push(`-- Partitions for ${table.name}`);
|
|
726
|
+
for (const partition of tablePartitions) {
|
|
727
|
+
tableSection.push(generatePartitionSQL(partition));
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (includeIndexes && table.indexes && table.indexes.length > 0) {
|
|
731
|
+
const uniqueConstraintNames = new Set((table.constraints || [])
|
|
732
|
+
.filter(c => c.type === 'UNIQUE')
|
|
733
|
+
.map(c => c.name));
|
|
734
|
+
const indexLines = [];
|
|
500
735
|
for (const index of table.indexes) {
|
|
501
|
-
if (
|
|
502
|
-
|
|
736
|
+
if (index.isPrimary)
|
|
737
|
+
continue;
|
|
738
|
+
if (index.isUnique && uniqueConstraintNames.has(index.name))
|
|
739
|
+
continue;
|
|
740
|
+
if (index.isUnique && (index.name.endsWith('_key') ||
|
|
741
|
+
index.name.startsWith('unique_') ||
|
|
742
|
+
uniqueConstraintNames.has(index.name.replace(/_key$/, ''))))
|
|
743
|
+
continue;
|
|
744
|
+
indexLines.push(generateIndexSQL(index, table.name, { ifNotExists }));
|
|
745
|
+
}
|
|
746
|
+
if (indexLines.length > 0) {
|
|
747
|
+
tableSection.push('');
|
|
748
|
+
tableSection.push(`-- Indexes for ${table.name}`);
|
|
749
|
+
tableSection.push(indexLines.join('\n'));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (includeComments) {
|
|
753
|
+
const commentLines = [];
|
|
754
|
+
if (table.comment) {
|
|
755
|
+
commentLines.push(generateCommentSQL({
|
|
756
|
+
objectType: 'table',
|
|
757
|
+
objectName: table.name,
|
|
758
|
+
comment: table.comment,
|
|
759
|
+
}));
|
|
760
|
+
}
|
|
761
|
+
if (table.columns) {
|
|
762
|
+
for (const col of table.columns) {
|
|
763
|
+
if (col.comment) {
|
|
764
|
+
commentLines.push(generateCommentSQL({
|
|
765
|
+
objectType: 'column',
|
|
766
|
+
objectName: table.name,
|
|
767
|
+
subObjectName: col.name,
|
|
768
|
+
comment: col.comment,
|
|
769
|
+
}));
|
|
770
|
+
}
|
|
503
771
|
}
|
|
504
772
|
}
|
|
773
|
+
if (table.indexes) {
|
|
774
|
+
for (const idx of table.indexes) {
|
|
775
|
+
if (idx.comment) {
|
|
776
|
+
commentLines.push(generateCommentSQL({
|
|
777
|
+
objectType: 'index',
|
|
778
|
+
objectName: idx.name,
|
|
779
|
+
comment: idx.comment,
|
|
780
|
+
}));
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (commentLines.length > 0) {
|
|
785
|
+
tableSection.push('');
|
|
786
|
+
tableSection.push(`-- Comments for ${table.name}`);
|
|
787
|
+
tableSection.push(commentLines.join('\n'));
|
|
788
|
+
}
|
|
505
789
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
sections.push('
|
|
509
|
-
sections.push(indexLines.join('\n'));
|
|
790
|
+
sections.push(tableSection.join('\n'));
|
|
791
|
+
sections.push('');
|
|
792
|
+
sections.push('');
|
|
510
793
|
sections.push('');
|
|
511
794
|
}
|
|
512
795
|
}
|
|
513
796
|
if (includeFunctions && schema.functions && schema.functions.length > 0) {
|
|
514
|
-
sections.push('--
|
|
797
|
+
sections.push('-- ==================================================================');
|
|
798
|
+
sections.push('-- FUNCTIONS');
|
|
799
|
+
sections.push('-- ==================================================================');
|
|
800
|
+
sections.push('');
|
|
515
801
|
for (const func of schema.functions) {
|
|
516
802
|
sections.push(generateFunctionSQL(func));
|
|
517
803
|
sections.push('');
|
|
518
804
|
}
|
|
519
805
|
}
|
|
520
806
|
if (includeTriggers && schema.triggers && schema.triggers.length > 0) {
|
|
521
|
-
sections.push('--
|
|
807
|
+
sections.push('-- ==================================================================');
|
|
808
|
+
sections.push('-- TRIGGERS');
|
|
809
|
+
sections.push('-- ==================================================================');
|
|
810
|
+
sections.push('');
|
|
522
811
|
for (const trigger of schema.triggers) {
|
|
523
812
|
sections.push(generateTriggerSQL(trigger));
|
|
524
813
|
}
|