@prisma-next/adapter-postgres 0.15.0-dev.1 → 0.15.0-dev.10
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/{adapter-CssyjkaR.mjs → adapter-Bh8GA6sC.mjs} +2 -2
- package/dist/{adapter-CssyjkaR.mjs.map → adapter-Bh8GA6sC.mjs.map} +1 -1
- package/dist/adapter.mjs +1 -1
- package/dist/{control-adapter-w2BFxQ--.mjs → control-adapter-aFfSI2bK.mjs} +118 -60
- package/dist/control-adapter-aFfSI2bK.mjs.map +1 -0
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +1 -1
- package/dist/runtime.mjs +1 -1
- package/package.json +23 -23
- package/src/core/control-adapter.ts +130 -60
- package/dist/control-adapter-w2BFxQ--.mjs.map +0 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
8
8
|
import { parseContractMarkerRow } from '@prisma-next/family-sql/verify';
|
|
9
9
|
import type { CodecLookup, CodecRegistry } from '@prisma-next/framework-components/codec';
|
|
10
|
-
import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
|
|
10
|
+
import { APP_SPACE_ID, type SchemaNodeRef } from '@prisma-next/framework-components/control';
|
|
11
11
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
12
12
|
import { ledgerOriginFromStored } from '@prisma-next/migration-tools/ledger-origin';
|
|
13
13
|
import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
|
|
@@ -36,6 +36,7 @@ import type {
|
|
|
36
36
|
SqlReferentialAction,
|
|
37
37
|
SqlUniqueIRInput,
|
|
38
38
|
} from '@prisma-next/sql-schema-ir/types';
|
|
39
|
+
import { RelationalSchemaNodeKind } from '@prisma-next/sql-schema-ir/types';
|
|
39
40
|
import {
|
|
40
41
|
buildControlTableBootstrapQueries,
|
|
41
42
|
buildSignMarkerBootstrapQueries,
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
PostgresNativeEnumSchemaNode,
|
|
67
68
|
PostgresPolicySchemaNode,
|
|
68
69
|
PostgresRoleSchemaNode,
|
|
70
|
+
PostgresSchemaNodeKind,
|
|
69
71
|
PostgresTableSchemaNode,
|
|
70
72
|
} from '@prisma-next/target-postgres/types';
|
|
71
73
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
@@ -737,7 +739,13 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
737
739
|
ORDER BY c.table_name, c.ordinal_position`,
|
|
738
740
|
[schema],
|
|
739
741
|
);
|
|
740
|
-
// Query all primary keys for all tables in schema
|
|
742
|
+
// Query all primary keys for all tables in schema. Reads pg_catalog, not
|
|
743
|
+
// information_schema: `information_schema.table_constraints` hides
|
|
744
|
+
// constraints on tables where the connecting role's only privilege is
|
|
745
|
+
// SELECT (its filter grants visibility for INSERT/UPDATE/DELETE/TRUNCATE/
|
|
746
|
+
// REFERENCES/TRIGGER — not SELECT), so a SELECT-only table introspects
|
|
747
|
+
// with all its columns but zero constraints and verify reports every
|
|
748
|
+
// declared constraint as missing. pg_catalog is not privilege-filtered.
|
|
741
749
|
const pkResult = await driver.query<{
|
|
742
750
|
table_name: string;
|
|
743
751
|
constraint_name: string;
|
|
@@ -745,24 +753,29 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
745
753
|
ordinal_position: number;
|
|
746
754
|
}>(
|
|
747
755
|
`SELECT
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
FROM
|
|
753
|
-
JOIN
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
AND
|
|
759
|
-
|
|
756
|
+
cl.relname AS table_name,
|
|
757
|
+
con.conname AS constraint_name,
|
|
758
|
+
a.attname AS column_name,
|
|
759
|
+
k.ord AS ordinal_position
|
|
760
|
+
FROM pg_catalog.pg_constraint con
|
|
761
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
762
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
763
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
764
|
+
JOIN pg_catalog.pg_attribute a
|
|
765
|
+
ON a.attrelid = con.conrelid
|
|
766
|
+
AND a.attnum = k.attnum
|
|
767
|
+
WHERE ns.nspname = $1
|
|
768
|
+
AND con.contype = 'p'
|
|
769
|
+
ORDER BY cl.relname, k.ord`,
|
|
760
770
|
[schema],
|
|
761
771
|
);
|
|
762
|
-
// Query all foreign keys for all tables in schema, including referential
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
//
|
|
772
|
+
// Query all foreign keys for all tables in schema, including referential
|
|
773
|
+
// actions. Reads pg_catalog only (see the primary-key query above for why
|
|
774
|
+
// information_schema is unusable here); `unnest(conkey) WITH ORDINALITY`
|
|
775
|
+
// pairs source and referenced columns positionally, so composite FKs
|
|
776
|
+
// round-trip in declared order. The referential-action CASEs mirror
|
|
777
|
+
// pg_constraint's confdeltype/confupdtype encoding and produce the same
|
|
778
|
+
// rule strings information_schema.referential_constraints reports.
|
|
766
779
|
const fkResult = await driver.query<{
|
|
767
780
|
table_name: string;
|
|
768
781
|
constraint_name: string;
|
|
@@ -775,41 +788,48 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
775
788
|
update_rule: string;
|
|
776
789
|
}>(
|
|
777
790
|
`SELECT
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
791
|
+
cl.relname AS table_name,
|
|
792
|
+
con.conname AS constraint_name,
|
|
793
|
+
a.attname AS column_name,
|
|
794
|
+
k.ord AS ordinal_position,
|
|
782
795
|
ref_ns.nspname AS referenced_table_schema,
|
|
783
796
|
ref_cl.relname AS referenced_table_name,
|
|
784
797
|
ref_att.attname AS referenced_column_name,
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
798
|
+
CASE con.confdeltype
|
|
799
|
+
WHEN 'a' THEN 'NO ACTION'
|
|
800
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
801
|
+
WHEN 'c' THEN 'CASCADE'
|
|
802
|
+
WHEN 'n' THEN 'SET NULL'
|
|
803
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
804
|
+
END AS delete_rule,
|
|
805
|
+
CASE con.confupdtype
|
|
806
|
+
WHEN 'a' THEN 'NO ACTION'
|
|
807
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
808
|
+
WHEN 'c' THEN 'CASCADE'
|
|
809
|
+
WHEN 'n' THEN 'SET NULL'
|
|
810
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
811
|
+
END AS update_rule
|
|
812
|
+
FROM pg_catalog.pg_constraint con
|
|
813
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
814
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
815
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
816
|
+
JOIN pg_catalog.pg_attribute a
|
|
817
|
+
ON a.attrelid = con.conrelid
|
|
818
|
+
AND a.attnum = k.attnum
|
|
797
819
|
JOIN pg_catalog.pg_class ref_cl
|
|
798
|
-
ON ref_cl.oid =
|
|
820
|
+
ON ref_cl.oid = con.confrelid
|
|
799
821
|
JOIN pg_catalog.pg_namespace ref_ns
|
|
800
822
|
ON ref_ns.oid = ref_cl.relnamespace
|
|
801
823
|
JOIN pg_catalog.pg_attribute ref_att
|
|
802
|
-
ON ref_att.attrelid =
|
|
803
|
-
AND ref_att.attnum =
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
WHERE tc.table_schema = $1
|
|
808
|
-
AND tc.constraint_type = 'FOREIGN KEY'
|
|
809
|
-
ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position`,
|
|
824
|
+
ON ref_att.attrelid = con.confrelid
|
|
825
|
+
AND ref_att.attnum = con.confkey[k.ord]
|
|
826
|
+
WHERE ns.nspname = $1
|
|
827
|
+
AND con.contype = 'f'
|
|
828
|
+
ORDER BY cl.relname, con.conname, k.ord`,
|
|
810
829
|
[schema],
|
|
811
830
|
);
|
|
812
|
-
// Query all unique constraints for all tables in schema (excluding PKs)
|
|
831
|
+
// Query all unique constraints for all tables in schema (excluding PKs).
|
|
832
|
+
// Reads pg_catalog (see the primary-key query above).
|
|
813
833
|
const uniqueResult = await driver.query<{
|
|
814
834
|
table_name: string;
|
|
815
835
|
constraint_name: string;
|
|
@@ -817,18 +837,20 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
817
837
|
ordinal_position: number;
|
|
818
838
|
}>(
|
|
819
839
|
`SELECT
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
FROM
|
|
825
|
-
JOIN
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
AND
|
|
831
|
-
|
|
840
|
+
cl.relname AS table_name,
|
|
841
|
+
con.conname AS constraint_name,
|
|
842
|
+
a.attname AS column_name,
|
|
843
|
+
k.ord AS ordinal_position
|
|
844
|
+
FROM pg_catalog.pg_constraint con
|
|
845
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
846
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
847
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
848
|
+
JOIN pg_catalog.pg_attribute a
|
|
849
|
+
ON a.attrelid = con.conrelid
|
|
850
|
+
AND a.attnum = k.attnum
|
|
851
|
+
WHERE ns.nspname = $1
|
|
852
|
+
AND con.contype = 'u'
|
|
853
|
+
ORDER BY cl.relname, con.conname, k.ord`,
|
|
832
854
|
[schema],
|
|
833
855
|
);
|
|
834
856
|
// Query all indexes for all tables in schema (excluding constraints).
|
|
@@ -873,10 +895,9 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
873
895
|
WHERE i.schemaname = $1
|
|
874
896
|
AND NOT EXISTS (
|
|
875
897
|
SELECT 1
|
|
876
|
-
FROM
|
|
877
|
-
WHERE
|
|
878
|
-
AND
|
|
879
|
-
AND tc.constraint_name = i.indexname
|
|
898
|
+
FROM pg_catalog.pg_constraint con
|
|
899
|
+
WHERE con.conindid = ic.oid
|
|
900
|
+
AND con.contype IN ('p', 'u', 'x')
|
|
880
901
|
)
|
|
881
902
|
ORDER BY i.tablename, i.indexname, k.ord`,
|
|
882
903
|
[schema],
|
|
@@ -1012,6 +1033,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1012
1033
|
? {
|
|
1013
1034
|
columns: primaryKeyColumns,
|
|
1014
1035
|
...(pkRows[0]?.constraint_name ? { name: pkRows[0].constraint_name } : {}),
|
|
1036
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, primaryKeyColumns),
|
|
1015
1037
|
}
|
|
1016
1038
|
: undefined;
|
|
1017
1039
|
|
|
@@ -1054,6 +1076,10 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1054
1076
|
name: fk.name,
|
|
1055
1077
|
...ifDefined('onDelete', mapReferentialAction(fk.deleteRule)),
|
|
1056
1078
|
...ifDefined('onUpdate', mapReferentialAction(fk.updateRule)),
|
|
1079
|
+
dependsOn: [
|
|
1080
|
+
postgresTableDependsOn(fk.referencedSchema, fk.referencedTable),
|
|
1081
|
+
...postgresColumnDependsOn(schema, tableName, fk.columns),
|
|
1082
|
+
],
|
|
1057
1083
|
}),
|
|
1058
1084
|
);
|
|
1059
1085
|
|
|
@@ -1078,6 +1104,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1078
1104
|
const uniques: readonly SqlUniqueIRInput[] = Array.from(uniquesMap.values()).map((uq) => ({
|
|
1079
1105
|
columns: Object.freeze([...uq.columns]) as readonly string[],
|
|
1080
1106
|
name: uq.name,
|
|
1107
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, uq.columns),
|
|
1081
1108
|
}));
|
|
1082
1109
|
|
|
1083
1110
|
// Process indexes
|
|
@@ -1159,6 +1186,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1159
1186
|
unique: idx.unique,
|
|
1160
1187
|
...(idx.type !== undefined && { type: idx.type }),
|
|
1161
1188
|
...(idx.options !== undefined && { options: idx.options }),
|
|
1189
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, idx.columns),
|
|
1162
1190
|
}),
|
|
1163
1191
|
);
|
|
1164
1192
|
|
|
@@ -1241,6 +1269,10 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1241
1269
|
...(row.qual !== null ? { using: row.qual } : {}),
|
|
1242
1270
|
...(row.with_check !== null ? { withCheck: row.with_check } : {}),
|
|
1243
1271
|
permissive,
|
|
1272
|
+
dependsOn: [
|
|
1273
|
+
postgresTableDependsOn(row.schemaname, row.tablename),
|
|
1274
|
+
...policyRoles.map(postgresRoleDependsOn),
|
|
1275
|
+
],
|
|
1244
1276
|
});
|
|
1245
1277
|
const list = policiesByTable.get(row.tablename) ?? [];
|
|
1246
1278
|
list.push(policy);
|
|
@@ -1502,6 +1534,44 @@ function mapReferentialAction(rule: string): SqlReferentialAction | undefined {
|
|
|
1502
1534
|
return mapped;
|
|
1503
1535
|
}
|
|
1504
1536
|
|
|
1537
|
+
/**
|
|
1538
|
+
* A FK/policy dependency chain, mirroring the shape the expected-side
|
|
1539
|
+
* derivation stamps (`contractToPostgresDatabaseSchemaNode`): the database
|
|
1540
|
+
* root's fixed sentinel id, then the namespace, then the table.
|
|
1541
|
+
*/
|
|
1542
|
+
function postgresTableDependsOn(namespaceId: string, tableName: string): SchemaNodeRef {
|
|
1543
|
+
return [
|
|
1544
|
+
{ nodeKind: PostgresSchemaNodeKind.database, id: 'database' },
|
|
1545
|
+
{ nodeKind: PostgresSchemaNodeKind.namespace, id: namespaceId },
|
|
1546
|
+
{ nodeKind: PostgresSchemaNodeKind.table, id: tableName },
|
|
1547
|
+
];
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/** A policy's dependency chain onto one of the roles it grants to. */
|
|
1551
|
+
function postgresRoleDependsOn(role: string): SchemaNodeRef {
|
|
1552
|
+
return [
|
|
1553
|
+
{ nodeKind: PostgresSchemaNodeKind.database, id: 'database' },
|
|
1554
|
+
{ nodeKind: PostgresSchemaNodeKind.role, id: role },
|
|
1555
|
+
];
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* The chains from a table-child object (foreign key, index, unique, primary
|
|
1560
|
+
* key) to each of the own columns it is built on — the introspection-side
|
|
1561
|
+
* mirror of `contractToPostgresDatabaseSchemaNode`'s `columnDependsOn`. An
|
|
1562
|
+
* object is dropped before the columns it covers.
|
|
1563
|
+
*/
|
|
1564
|
+
function postgresColumnDependsOn(
|
|
1565
|
+
namespaceId: string,
|
|
1566
|
+
tableName: string,
|
|
1567
|
+
columns: readonly string[],
|
|
1568
|
+
): SchemaNodeRef[] {
|
|
1569
|
+
return columns.map((column) => [
|
|
1570
|
+
...postgresTableDependsOn(namespaceId, tableName),
|
|
1571
|
+
{ nodeKind: RelationalSchemaNodeKind.column, id: `column:${column}` },
|
|
1572
|
+
]);
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1505
1575
|
/**
|
|
1506
1576
|
* Groups an array of objects by a specified key.
|
|
1507
1577
|
* Returns a Map for O(1) lookup by group key.
|