@prisma-next/target-postgres 0.3.0-dev.113 → 0.3.0-dev.114
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/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/target-postgres",
|
|
3
|
-
"version": "0.3.0-dev.
|
|
3
|
+
"version": "0.3.0-dev.114",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Postgres target pack for Prisma Next",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"arktype": "^2.0.0",
|
|
9
|
-
"@prisma-next/cli": "0.3.0-dev.
|
|
10
|
-
"@prisma-next/
|
|
11
|
-
"@prisma-next/
|
|
12
|
-
"@prisma-next/
|
|
13
|
-
"@prisma-next/
|
|
14
|
-
"@prisma-next/sql-
|
|
15
|
-
"@prisma-next/
|
|
16
|
-
"@prisma-next/sql-runtime": "0.3.0-dev.
|
|
17
|
-
"@prisma-next/
|
|
18
|
-
"@prisma-next/sql-relational-core": "0.3.0-dev.
|
|
19
|
-
"@prisma-next/
|
|
9
|
+
"@prisma-next/cli": "0.3.0-dev.114",
|
|
10
|
+
"@prisma-next/contract": "0.3.0-dev.114",
|
|
11
|
+
"@prisma-next/core-execution-plane": "0.3.0-dev.114",
|
|
12
|
+
"@prisma-next/family-sql": "0.3.0-dev.114",
|
|
13
|
+
"@prisma-next/sql-contract": "0.3.0-dev.114",
|
|
14
|
+
"@prisma-next/sql-errors": "0.3.0-dev.114",
|
|
15
|
+
"@prisma-next/core-control-plane": "0.3.0-dev.114",
|
|
16
|
+
"@prisma-next/sql-runtime": "0.3.0-dev.114",
|
|
17
|
+
"@prisma-next/sql-schema-ir": "0.3.0-dev.114",
|
|
18
|
+
"@prisma-next/sql-relational-core": "0.3.0-dev.114",
|
|
19
|
+
"@prisma-next/utils": "0.3.0-dev.114"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsdown": "0.18.4",
|
|
23
23
|
"typescript": "5.9.3",
|
|
24
24
|
"vitest": "4.0.17",
|
|
25
|
-
"@prisma-next/
|
|
26
|
-
"@prisma-next/
|
|
27
|
-
"@prisma-next/extension-pgvector": "0.3.0-dev.113",
|
|
25
|
+
"@prisma-next/extension-pgvector": "0.3.0-dev.114",
|
|
26
|
+
"@prisma-next/adapter-postgres": "0.3.0-dev.114",
|
|
28
27
|
"@prisma-next/test-utils": "0.0.1",
|
|
29
28
|
"@prisma-next/tsconfig": "0.0.0",
|
|
30
|
-
"@prisma-next/tsdown": "0.0.0"
|
|
29
|
+
"@prisma-next/tsdown": "0.0.0",
|
|
30
|
+
"@prisma-next/driver-postgres": "0.3.0-dev.114"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"dist",
|
|
@@ -7,12 +7,17 @@ import type {
|
|
|
7
7
|
SqlPlannerConflict,
|
|
8
8
|
} from '@prisma-next/family-sql/control';
|
|
9
9
|
import type { SqlContract, SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
|
|
10
|
+
import { invariant } from '@prisma-next/utils/assertions';
|
|
10
11
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
11
12
|
import type { PlanningMode, PostgresPlanTargetDetails } from './planner';
|
|
12
13
|
import {
|
|
14
|
+
buildColumnDefaultSql,
|
|
13
15
|
buildColumnTypeSql,
|
|
16
|
+
buildExpectedFormatType,
|
|
17
|
+
columnDefaultExistsCheck,
|
|
14
18
|
columnExistsCheck,
|
|
15
19
|
columnNullabilityCheck,
|
|
20
|
+
columnTypeCheck,
|
|
16
21
|
constraintExistsCheck,
|
|
17
22
|
qualifyTableName,
|
|
18
23
|
toRegclassLiteral,
|
|
@@ -189,11 +194,72 @@ function buildReconciliationOperationFromIssue(options: {
|
|
|
189
194
|
);
|
|
190
195
|
}
|
|
191
196
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
case 'default_missing': {
|
|
198
|
+
if (!issue.table || !issue.column) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const contractColMissing = getContractColumn(contract, issue.table, issue.column);
|
|
202
|
+
if (!contractColMissing) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
// NOTE: Being in the `default_missing` case means the verifier found the contract expects a default, so it should exist here. We must still narrow.
|
|
206
|
+
invariant(
|
|
207
|
+
contractColMissing.default !== undefined,
|
|
208
|
+
`default_missing issue for "${issue.table}"."${issue.column}" but contract column has no default`,
|
|
209
|
+
);
|
|
210
|
+
return buildDefaultOperation(
|
|
211
|
+
schemaName,
|
|
212
|
+
issue.table,
|
|
213
|
+
issue.column,
|
|
214
|
+
contractColMissing,
|
|
215
|
+
contractColMissing.default,
|
|
216
|
+
'additive',
|
|
217
|
+
'Set',
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
case 'default_mismatch': {
|
|
222
|
+
if (!issue.table || !issue.column) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
if (!mode.allowWidening) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
const contractColMismatch = getContractColumn(contract, issue.table, issue.column);
|
|
229
|
+
if (!contractColMismatch) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
// NOTE: Being in the `default_mismatch` case means the verifier found the contract expects a different default, so it should exist here. We must still narrow.
|
|
233
|
+
invariant(
|
|
234
|
+
contractColMismatch.default !== undefined,
|
|
235
|
+
`default_mismatch issue for "${issue.table}"."${issue.column}" but contract column has no default`,
|
|
236
|
+
);
|
|
237
|
+
return buildDefaultOperation(
|
|
238
|
+
schemaName,
|
|
239
|
+
issue.table,
|
|
240
|
+
issue.column,
|
|
241
|
+
contractColMismatch,
|
|
242
|
+
contractColMismatch.default,
|
|
243
|
+
'widening',
|
|
244
|
+
'Change',
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
case 'extra_default': {
|
|
249
|
+
if (!issue.table || !issue.column) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
if (!mode.allowDestructive) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return buildDropDefaultOperation(schemaName, issue.table, issue.column);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Remaining issue kinds (primary_key_mismatch, unique_constraint_mismatch,
|
|
259
|
+
// index_mismatch, foreign_key_mismatch) do not yet have reconciliation operation
|
|
260
|
+
// builders. They fall through to the caller, which converts them to conflicts via
|
|
261
|
+
// convertIssueToConflict. When a new SchemaIssue kind is added, add a case here if
|
|
262
|
+
// the planner can emit an operation for it; otherwise it becomes a conflict.
|
|
197
263
|
default:
|
|
198
264
|
return null;
|
|
199
265
|
}
|
|
@@ -338,7 +404,7 @@ function buildDropConstraintOperation(
|
|
|
338
404
|
precheck: [
|
|
339
405
|
{
|
|
340
406
|
description: `ensure constraint "${constraintName}" exists`,
|
|
341
|
-
sql: constraintExistsCheck({ constraintName, schema: schemaName }),
|
|
407
|
+
sql: constraintExistsCheck({ constraintName, schema: schemaName, table: tableName }),
|
|
342
408
|
},
|
|
343
409
|
],
|
|
344
410
|
execute: [
|
|
@@ -351,7 +417,12 @@ DROP CONSTRAINT ${quoteIdentifier(constraintName)}`,
|
|
|
351
417
|
postcheck: [
|
|
352
418
|
{
|
|
353
419
|
description: `verify constraint "${constraintName}" is removed`,
|
|
354
|
-
sql: constraintExistsCheck({
|
|
420
|
+
sql: constraintExistsCheck({
|
|
421
|
+
constraintName,
|
|
422
|
+
schema: schemaName,
|
|
423
|
+
table: tableName,
|
|
424
|
+
exists: false,
|
|
425
|
+
}),
|
|
355
426
|
},
|
|
356
427
|
],
|
|
357
428
|
};
|
|
@@ -488,10 +559,106 @@ USING ${quoteIdentifier(columnName)}::${expectedType}`,
|
|
|
488
559
|
],
|
|
489
560
|
postcheck: [
|
|
490
561
|
{
|
|
491
|
-
description: `verify column "${columnName}"
|
|
562
|
+
description: `verify column "${columnName}" has type ${expectedType}`,
|
|
563
|
+
sql: columnTypeCheck({
|
|
564
|
+
schema: schemaName,
|
|
565
|
+
table: tableName,
|
|
566
|
+
column: columnName,
|
|
567
|
+
expectedType: buildExpectedFormatType(column, codecHooks),
|
|
568
|
+
}),
|
|
569
|
+
},
|
|
570
|
+
],
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function buildDefaultOperation(
|
|
575
|
+
schemaName: string,
|
|
576
|
+
tableName: string,
|
|
577
|
+
columnName: string,
|
|
578
|
+
column: Omit<StorageColumn, 'default'>,
|
|
579
|
+
columnDefault: NonNullable<StorageColumn['default']>,
|
|
580
|
+
operationClass: 'additive' | 'widening',
|
|
581
|
+
verb: 'Set' | 'Change',
|
|
582
|
+
): SqlMigrationPlanOperation<PostgresPlanTargetDetails> | null {
|
|
583
|
+
const qualified = qualifyTableName(schemaName, tableName);
|
|
584
|
+
const defaultClause = buildColumnDefaultSql(columnDefault, column);
|
|
585
|
+
// autoincrement defaults are handled by SERIAL types — buildColumnDefaultSql returns ''
|
|
586
|
+
// for them. Until the IR is enriched to distinguish autoincrement (TML-2107), skip.
|
|
587
|
+
if (!defaultClause) return null;
|
|
588
|
+
const verbLower = verb.toLowerCase();
|
|
589
|
+
return {
|
|
590
|
+
id: `setDefault.${tableName}.${columnName}`,
|
|
591
|
+
label: `${verb} default for ${columnName} on ${tableName}`,
|
|
592
|
+
summary: `${verb}s default on column ${columnName} of table ${tableName}`,
|
|
593
|
+
operationClass,
|
|
594
|
+
target: {
|
|
595
|
+
id: 'postgres',
|
|
596
|
+
details: buildTargetDetails('column', columnName, schemaName, tableName),
|
|
597
|
+
},
|
|
598
|
+
precheck: [
|
|
599
|
+
{
|
|
600
|
+
description: `ensure column "${columnName}" exists`,
|
|
492
601
|
sql: columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
|
|
493
602
|
},
|
|
494
603
|
],
|
|
604
|
+
execute: [
|
|
605
|
+
{
|
|
606
|
+
description: `${verbLower} default on "${columnName}"`,
|
|
607
|
+
sql: `ALTER TABLE ${qualified}\nALTER COLUMN ${quoteIdentifier(columnName)} SET ${defaultClause}`,
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
postcheck: [
|
|
611
|
+
{
|
|
612
|
+
description: `verify column "${columnName}" has a default`,
|
|
613
|
+
sql: columnDefaultExistsCheck({
|
|
614
|
+
schema: schemaName,
|
|
615
|
+
table: tableName,
|
|
616
|
+
column: columnName,
|
|
617
|
+
exists: true,
|
|
618
|
+
}),
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function buildDropDefaultOperation(
|
|
625
|
+
schemaName: string,
|
|
626
|
+
tableName: string,
|
|
627
|
+
columnName: string,
|
|
628
|
+
): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
|
|
629
|
+
const qualified = qualifyTableName(schemaName, tableName);
|
|
630
|
+
return {
|
|
631
|
+
id: `dropDefault.${tableName}.${columnName}`,
|
|
632
|
+
label: `Drop default for ${columnName} on ${tableName}`,
|
|
633
|
+
summary: `Drops default on column ${columnName} of table ${tableName}`,
|
|
634
|
+
operationClass: 'destructive',
|
|
635
|
+
target: {
|
|
636
|
+
id: 'postgres',
|
|
637
|
+
details: buildTargetDetails('column', columnName, schemaName, tableName),
|
|
638
|
+
},
|
|
639
|
+
precheck: [
|
|
640
|
+
{
|
|
641
|
+
description: `ensure column "${columnName}" exists`,
|
|
642
|
+
sql: columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
|
|
643
|
+
},
|
|
644
|
+
],
|
|
645
|
+
execute: [
|
|
646
|
+
{
|
|
647
|
+
description: `drop default on "${columnName}"`,
|
|
648
|
+
sql: `ALTER TABLE ${qualified}\nALTER COLUMN ${quoteIdentifier(columnName)} DROP DEFAULT`,
|
|
649
|
+
},
|
|
650
|
+
],
|
|
651
|
+
postcheck: [
|
|
652
|
+
{
|
|
653
|
+
description: `verify column "${columnName}" has no default`,
|
|
654
|
+
sql: columnDefaultExistsCheck({
|
|
655
|
+
schema: schemaName,
|
|
656
|
+
table: tableName,
|
|
657
|
+
column: columnName,
|
|
658
|
+
exists: false,
|
|
659
|
+
}),
|
|
660
|
+
},
|
|
661
|
+
],
|
|
495
662
|
};
|
|
496
663
|
}
|
|
497
664
|
|
|
@@ -507,6 +674,7 @@ function convertIssueToConflict(issue: SchemaIssue): SqlPlannerConflict | null {
|
|
|
507
674
|
return buildConflict('nullabilityConflict', issue);
|
|
508
675
|
case 'default_missing':
|
|
509
676
|
case 'default_mismatch':
|
|
677
|
+
case 'extra_default':
|
|
510
678
|
case 'extra_table':
|
|
511
679
|
case 'extra_column':
|
|
512
680
|
case 'extra_primary_key':
|
|
@@ -126,7 +126,7 @@ function renderParameterizedTypeSql(
|
|
|
126
126
|
return expanded !== column.nativeType ? expanded : null;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
function buildColumnDefaultSql(
|
|
129
|
+
export function buildColumnDefaultSql(
|
|
130
130
|
columnDefault: PostgresColumnDefault | undefined,
|
|
131
131
|
column?: StorageColumn,
|
|
132
132
|
): string {
|
|
@@ -192,10 +192,12 @@ export function toRegclassLiteral(schema: string, name: string): string {
|
|
|
192
192
|
export function constraintExistsCheck({
|
|
193
193
|
constraintName,
|
|
194
194
|
schema,
|
|
195
|
+
table,
|
|
195
196
|
exists = true,
|
|
196
197
|
}: {
|
|
197
198
|
constraintName: string;
|
|
198
199
|
schema: string;
|
|
200
|
+
table: string;
|
|
199
201
|
exists?: boolean;
|
|
200
202
|
}): string {
|
|
201
203
|
const existsClause = exists ? 'EXISTS' : 'NOT EXISTS';
|
|
@@ -204,6 +206,7 @@ export function constraintExistsCheck({
|
|
|
204
206
|
JOIN pg_namespace n ON c.connamespace = n.oid
|
|
205
207
|
WHERE c.conname = '${escapeLiteral(constraintName)}'
|
|
206
208
|
AND n.nspname = '${escapeLiteral(schema)}'
|
|
209
|
+
AND c.conrelid = to_regclass(${toRegclassLiteral(schema, table)})
|
|
207
210
|
)`;
|
|
208
211
|
}
|
|
209
212
|
|
|
@@ -250,6 +253,111 @@ export function columnNullabilityCheck({
|
|
|
250
253
|
)`;
|
|
251
254
|
}
|
|
252
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Maps contract native type names to the display form returned by PostgreSQL's
|
|
258
|
+
* `format_type()`. Base types use short names in the contract (e.g., `int4`)
|
|
259
|
+
* but `format_type()` returns SQL-standard names (e.g., `integer`).
|
|
260
|
+
*
|
|
261
|
+
* NOTE: The inverse mapping lives in `normalizeFormattedType` in control-adapter.ts.
|
|
262
|
+
* These two maps must stay in sync. A shared bidirectional map in
|
|
263
|
+
* @prisma-next/adapter-postgres would eliminate the drift risk.
|
|
264
|
+
*/
|
|
265
|
+
const FORMAT_TYPE_DISPLAY: ReadonlyMap<string, string> = new Map([
|
|
266
|
+
['int2', 'smallint'],
|
|
267
|
+
['int4', 'integer'],
|
|
268
|
+
['int8', 'bigint'],
|
|
269
|
+
['float4', 'real'],
|
|
270
|
+
['float8', 'double precision'],
|
|
271
|
+
['bool', 'boolean'],
|
|
272
|
+
['timestamp', 'timestamp without time zone'],
|
|
273
|
+
['timestamptz', 'timestamp with time zone'],
|
|
274
|
+
['time', 'time without time zone'],
|
|
275
|
+
['timetz', 'time with time zone'],
|
|
276
|
+
]);
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Builds the string that `format_type(atttypid, atttypmod)` would return for a
|
|
280
|
+
* contract column. Used for postchecks — separate from `buildColumnTypeSql` which
|
|
281
|
+
* produces DDL-safe strings (e.g., quoted identifiers, SERIAL).
|
|
282
|
+
*/
|
|
283
|
+
export function buildExpectedFormatType(
|
|
284
|
+
column: StorageColumn,
|
|
285
|
+
codecHooks: Map<string, CodecControlHooks>,
|
|
286
|
+
): string {
|
|
287
|
+
// Parameterized types: expand with typeParams.
|
|
288
|
+
// format_type() returns the same form (e.g., 'character varying(255)').
|
|
289
|
+
if (column.typeParams && column.codecId) {
|
|
290
|
+
const hooks = codecHooks.get(column.codecId);
|
|
291
|
+
if (hooks?.expandNativeType) {
|
|
292
|
+
return hooks.expandNativeType({
|
|
293
|
+
nativeType: column.nativeType,
|
|
294
|
+
codecId: column.codecId,
|
|
295
|
+
typeParams: column.typeParams,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// User-defined types (enums, composites): format_type() double-quotes names
|
|
301
|
+
// that contain uppercase characters (e.g., "StatusType") but returns lowercase
|
|
302
|
+
// names bare (e.g., status_type). We can't use quoteIdentifier() here because
|
|
303
|
+
// it always quotes, which would break the lowercase case.
|
|
304
|
+
if (column.typeRef) {
|
|
305
|
+
const needsQuoting = column.nativeType !== column.nativeType.toLowerCase();
|
|
306
|
+
return needsQuoting ? `"${column.nativeType}"` : column.nativeType;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Base types: map contract short names to format_type() display names.
|
|
310
|
+
return FORMAT_TYPE_DISPLAY.get(column.nativeType) ?? column.nativeType;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Checks that the column's full type (including typmods) matches the expected type via `format_type()`. */
|
|
314
|
+
export function columnTypeCheck({
|
|
315
|
+
schema,
|
|
316
|
+
table,
|
|
317
|
+
column,
|
|
318
|
+
expectedType,
|
|
319
|
+
}: {
|
|
320
|
+
schema: string;
|
|
321
|
+
table: string;
|
|
322
|
+
column: string;
|
|
323
|
+
expectedType: string;
|
|
324
|
+
}): string {
|
|
325
|
+
return `SELECT EXISTS (
|
|
326
|
+
SELECT 1
|
|
327
|
+
FROM pg_attribute a
|
|
328
|
+
JOIN pg_class c ON c.oid = a.attrelid
|
|
329
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
330
|
+
WHERE n.nspname = '${escapeLiteral(schema)}'
|
|
331
|
+
AND c.relname = '${escapeLiteral(table)}'
|
|
332
|
+
AND a.attname = '${escapeLiteral(column)}'
|
|
333
|
+
AND format_type(a.atttypid, a.atttypmod) = '${escapeLiteral(expectedType)}'
|
|
334
|
+
AND NOT a.attisdropped
|
|
335
|
+
)`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Checks that a column default exists (or does not exist) via `information_schema.columns.column_default`. */
|
|
339
|
+
export function columnDefaultExistsCheck({
|
|
340
|
+
schema,
|
|
341
|
+
table,
|
|
342
|
+
column,
|
|
343
|
+
exists = true,
|
|
344
|
+
}: {
|
|
345
|
+
schema: string;
|
|
346
|
+
table: string;
|
|
347
|
+
column: string;
|
|
348
|
+
exists?: boolean;
|
|
349
|
+
}): string {
|
|
350
|
+
const nullCheck = exists ? 'IS NOT NULL' : 'IS NULL';
|
|
351
|
+
return `SELECT EXISTS (
|
|
352
|
+
SELECT 1
|
|
353
|
+
FROM information_schema.columns
|
|
354
|
+
WHERE table_schema = '${escapeLiteral(schema)}'
|
|
355
|
+
AND table_name = '${escapeLiteral(table)}'
|
|
356
|
+
AND column_name = '${escapeLiteral(column)}'
|
|
357
|
+
AND column_default ${nullCheck}
|
|
358
|
+
)`;
|
|
359
|
+
}
|
|
360
|
+
|
|
253
361
|
export function tableIsEmptyCheck(qualifiedTableName: string): string {
|
|
254
362
|
return `SELECT NOT EXISTS (SELECT 1 FROM ${qualifiedTableName} LIMIT 1)`;
|
|
255
363
|
}
|
|
@@ -525,7 +525,12 @@ PRIMARY KEY (${table.primaryKey.columns.map(quoteIdentifier).join(', ')})`,
|
|
|
525
525
|
precheck: [
|
|
526
526
|
{
|
|
527
527
|
description: `ensure unique constraint "${constraintName}" is missing`,
|
|
528
|
-
sql: constraintExistsCheck({
|
|
528
|
+
sql: constraintExistsCheck({
|
|
529
|
+
constraintName,
|
|
530
|
+
schema: schemaName,
|
|
531
|
+
table: tableName,
|
|
532
|
+
exists: false,
|
|
533
|
+
}),
|
|
529
534
|
},
|
|
530
535
|
],
|
|
531
536
|
execute: [
|
|
@@ -539,7 +544,7 @@ UNIQUE (${unique.columns.map(quoteIdentifier).join(', ')})`,
|
|
|
539
544
|
postcheck: [
|
|
540
545
|
{
|
|
541
546
|
description: `verify unique constraint "${constraintName}" exists`,
|
|
542
|
-
sql: constraintExistsCheck({ constraintName, schema: schemaName }),
|
|
547
|
+
sql: constraintExistsCheck({ constraintName, schema: schemaName, table: tableName }),
|
|
543
548
|
},
|
|
544
549
|
],
|
|
545
550
|
});
|
|
@@ -685,6 +690,7 @@ UNIQUE (${unique.columns.map(quoteIdentifier).join(', ')})`,
|
|
|
685
690
|
sql: constraintExistsCheck({
|
|
686
691
|
constraintName: fkName,
|
|
687
692
|
schema: schemaName,
|
|
693
|
+
table: tableName,
|
|
688
694
|
exists: false,
|
|
689
695
|
}),
|
|
690
696
|
},
|
|
@@ -698,7 +704,11 @@ UNIQUE (${unique.columns.map(quoteIdentifier).join(', ')})`,
|
|
|
698
704
|
postcheck: [
|
|
699
705
|
{
|
|
700
706
|
description: `verify foreign key "${fkName}" exists`,
|
|
701
|
-
sql: constraintExistsCheck({
|
|
707
|
+
sql: constraintExistsCheck({
|
|
708
|
+
constraintName: fkName,
|
|
709
|
+
schema: schemaName,
|
|
710
|
+
table: tableName,
|
|
711
|
+
}),
|
|
702
712
|
},
|
|
703
713
|
],
|
|
704
714
|
});
|