@prisma-next/adapter-postgres 0.15.0 → 0.16.0-dev.1
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-BaZsfXGA.mjs} +2 -2
- package/dist/{adapter-CssyjkaR.mjs.map → adapter-BaZsfXGA.mjs.map} +1 -1
- package/dist/adapter.d.mts +1 -2
- package/dist/adapter.d.mts.map +1 -1
- package/dist/adapter.mjs +1 -1
- package/dist/column-types.d.mts +0 -1
- package/dist/column-types.d.mts.map +1 -1
- package/dist/{control-adapter-w2BFxQ--.mjs → control-adapter-B6IM_oTR.mjs} +125 -62
- package/dist/control-adapter-B6IM_oTR.mjs.map +1 -0
- package/dist/control.d.mts +0 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +1 -1
- package/dist/operation-types.d.mts +0 -1
- package/dist/operation-types.d.mts.map +1 -1
- package/dist/runtime.d.mts +1 -2
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/{types-DkGeLfUp.d.mts → types-DyNEvnRB.d.mts} +1 -2
- package/dist/types-DyNEvnRB.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/package.json +24 -24
- package/src/core/control-adapter.ts +154 -68
- package/dist/control-adapter-w2BFxQ--.mjs.map +0 -1
- package/dist/types-DkGeLfUp.d.mts.map +0 -1
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ColumnDefault,
|
|
3
|
+
ContractMarkerRecord,
|
|
4
|
+
LedgerEntryRecord,
|
|
5
|
+
} from '@prisma-next/contract/types';
|
|
2
6
|
import {
|
|
3
7
|
parseMarkerRowSafely,
|
|
4
8
|
rethrowMarkerReadError,
|
|
@@ -7,7 +11,7 @@ import {
|
|
|
7
11
|
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
8
12
|
import { parseContractMarkerRow } from '@prisma-next/family-sql/verify';
|
|
9
13
|
import type { CodecLookup, CodecRegistry } from '@prisma-next/framework-components/codec';
|
|
10
|
-
import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
|
|
14
|
+
import { APP_SPACE_ID, type SchemaNodeRef } from '@prisma-next/framework-components/control';
|
|
11
15
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
12
16
|
import { ledgerOriginFromStored } from '@prisma-next/migration-tools/ledger-origin';
|
|
13
17
|
import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
|
|
@@ -36,6 +40,7 @@ import type {
|
|
|
36
40
|
SqlReferentialAction,
|
|
37
41
|
SqlUniqueIRInput,
|
|
38
42
|
} from '@prisma-next/sql-schema-ir/types';
|
|
43
|
+
import { RelationalSchemaNodeKind } from '@prisma-next/sql-schema-ir/types';
|
|
39
44
|
import {
|
|
40
45
|
buildControlTableBootstrapQueries,
|
|
41
46
|
buildSignMarkerBootstrapQueries,
|
|
@@ -66,6 +71,7 @@ import {
|
|
|
66
71
|
PostgresNativeEnumSchemaNode,
|
|
67
72
|
PostgresPolicySchemaNode,
|
|
68
73
|
PostgresRoleSchemaNode,
|
|
74
|
+
PostgresSchemaNodeKind,
|
|
69
75
|
PostgresTableSchemaNode,
|
|
70
76
|
} from '@prisma-next/target-postgres/types';
|
|
71
77
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
@@ -710,6 +716,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
710
716
|
numeric_scale: number | null;
|
|
711
717
|
column_default: string | null;
|
|
712
718
|
formatted_type: string | null;
|
|
719
|
+
attidentity: string;
|
|
713
720
|
}>(
|
|
714
721
|
`SELECT
|
|
715
722
|
c.table_name,
|
|
@@ -721,7 +728,8 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
721
728
|
numeric_precision,
|
|
722
729
|
numeric_scale,
|
|
723
730
|
column_default,
|
|
724
|
-
format_type(a.atttypid, a.atttypmod) AS formatted_type
|
|
731
|
+
format_type(a.atttypid, a.atttypmod) AS formatted_type,
|
|
732
|
+
a.attidentity
|
|
725
733
|
FROM information_schema.columns c
|
|
726
734
|
JOIN pg_catalog.pg_class cl
|
|
727
735
|
ON cl.relname = c.table_name
|
|
@@ -737,7 +745,13 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
737
745
|
ORDER BY c.table_name, c.ordinal_position`,
|
|
738
746
|
[schema],
|
|
739
747
|
);
|
|
740
|
-
// Query all primary keys for all tables in schema
|
|
748
|
+
// Query all primary keys for all tables in schema. Reads pg_catalog, not
|
|
749
|
+
// information_schema: `information_schema.table_constraints` hides
|
|
750
|
+
// constraints on tables where the connecting role's only privilege is
|
|
751
|
+
// SELECT (its filter grants visibility for INSERT/UPDATE/DELETE/TRUNCATE/
|
|
752
|
+
// REFERENCES/TRIGGER — not SELECT), so a SELECT-only table introspects
|
|
753
|
+
// with all its columns but zero constraints and verify reports every
|
|
754
|
+
// declared constraint as missing. pg_catalog is not privilege-filtered.
|
|
741
755
|
const pkResult = await driver.query<{
|
|
742
756
|
table_name: string;
|
|
743
757
|
constraint_name: string;
|
|
@@ -745,24 +759,29 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
745
759
|
ordinal_position: number;
|
|
746
760
|
}>(
|
|
747
761
|
`SELECT
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
FROM
|
|
753
|
-
JOIN
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
AND
|
|
759
|
-
|
|
762
|
+
cl.relname AS table_name,
|
|
763
|
+
con.conname AS constraint_name,
|
|
764
|
+
a.attname AS column_name,
|
|
765
|
+
k.ord AS ordinal_position
|
|
766
|
+
FROM pg_catalog.pg_constraint con
|
|
767
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
768
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
769
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
770
|
+
JOIN pg_catalog.pg_attribute a
|
|
771
|
+
ON a.attrelid = con.conrelid
|
|
772
|
+
AND a.attnum = k.attnum
|
|
773
|
+
WHERE ns.nspname = $1
|
|
774
|
+
AND con.contype = 'p'
|
|
775
|
+
ORDER BY cl.relname, k.ord`,
|
|
760
776
|
[schema],
|
|
761
777
|
);
|
|
762
|
-
// Query all foreign keys for all tables in schema, including referential
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
//
|
|
778
|
+
// Query all foreign keys for all tables in schema, including referential
|
|
779
|
+
// actions. Reads pg_catalog only (see the primary-key query above for why
|
|
780
|
+
// information_schema is unusable here); `unnest(conkey) WITH ORDINALITY`
|
|
781
|
+
// pairs source and referenced columns positionally, so composite FKs
|
|
782
|
+
// round-trip in declared order. The referential-action CASEs mirror
|
|
783
|
+
// pg_constraint's confdeltype/confupdtype encoding and produce the same
|
|
784
|
+
// rule strings information_schema.referential_constraints reports.
|
|
766
785
|
const fkResult = await driver.query<{
|
|
767
786
|
table_name: string;
|
|
768
787
|
constraint_name: string;
|
|
@@ -775,41 +794,48 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
775
794
|
update_rule: string;
|
|
776
795
|
}>(
|
|
777
796
|
`SELECT
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
797
|
+
cl.relname AS table_name,
|
|
798
|
+
con.conname AS constraint_name,
|
|
799
|
+
a.attname AS column_name,
|
|
800
|
+
k.ord AS ordinal_position,
|
|
782
801
|
ref_ns.nspname AS referenced_table_schema,
|
|
783
802
|
ref_cl.relname AS referenced_table_name,
|
|
784
803
|
ref_att.attname AS referenced_column_name,
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
804
|
+
CASE con.confdeltype
|
|
805
|
+
WHEN 'a' THEN 'NO ACTION'
|
|
806
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
807
|
+
WHEN 'c' THEN 'CASCADE'
|
|
808
|
+
WHEN 'n' THEN 'SET NULL'
|
|
809
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
810
|
+
END AS delete_rule,
|
|
811
|
+
CASE con.confupdtype
|
|
812
|
+
WHEN 'a' THEN 'NO ACTION'
|
|
813
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
814
|
+
WHEN 'c' THEN 'CASCADE'
|
|
815
|
+
WHEN 'n' THEN 'SET NULL'
|
|
816
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
817
|
+
END AS update_rule
|
|
818
|
+
FROM pg_catalog.pg_constraint con
|
|
819
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
820
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
821
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
822
|
+
JOIN pg_catalog.pg_attribute a
|
|
823
|
+
ON a.attrelid = con.conrelid
|
|
824
|
+
AND a.attnum = k.attnum
|
|
797
825
|
JOIN pg_catalog.pg_class ref_cl
|
|
798
|
-
ON ref_cl.oid =
|
|
826
|
+
ON ref_cl.oid = con.confrelid
|
|
799
827
|
JOIN pg_catalog.pg_namespace ref_ns
|
|
800
828
|
ON ref_ns.oid = ref_cl.relnamespace
|
|
801
829
|
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`,
|
|
830
|
+
ON ref_att.attrelid = con.confrelid
|
|
831
|
+
AND ref_att.attnum = con.confkey[k.ord]
|
|
832
|
+
WHERE ns.nspname = $1
|
|
833
|
+
AND con.contype = 'f'
|
|
834
|
+
ORDER BY cl.relname, con.conname, k.ord`,
|
|
810
835
|
[schema],
|
|
811
836
|
);
|
|
812
|
-
// Query all unique constraints for all tables in schema (excluding PKs)
|
|
837
|
+
// Query all unique constraints for all tables in schema (excluding PKs).
|
|
838
|
+
// Reads pg_catalog (see the primary-key query above).
|
|
813
839
|
const uniqueResult = await driver.query<{
|
|
814
840
|
table_name: string;
|
|
815
841
|
constraint_name: string;
|
|
@@ -817,18 +843,20 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
817
843
|
ordinal_position: number;
|
|
818
844
|
}>(
|
|
819
845
|
`SELECT
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
FROM
|
|
825
|
-
JOIN
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
AND
|
|
831
|
-
|
|
846
|
+
cl.relname AS table_name,
|
|
847
|
+
con.conname AS constraint_name,
|
|
848
|
+
a.attname AS column_name,
|
|
849
|
+
k.ord AS ordinal_position
|
|
850
|
+
FROM pg_catalog.pg_constraint con
|
|
851
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
852
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
853
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
854
|
+
JOIN pg_catalog.pg_attribute a
|
|
855
|
+
ON a.attrelid = con.conrelid
|
|
856
|
+
AND a.attnum = k.attnum
|
|
857
|
+
WHERE ns.nspname = $1
|
|
858
|
+
AND con.contype = 'u'
|
|
859
|
+
ORDER BY cl.relname, con.conname, k.ord`,
|
|
832
860
|
[schema],
|
|
833
861
|
);
|
|
834
862
|
// Query all indexes for all tables in schema (excluding constraints).
|
|
@@ -873,10 +901,9 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
873
901
|
WHERE i.schemaname = $1
|
|
874
902
|
AND NOT EXISTS (
|
|
875
903
|
SELECT 1
|
|
876
|
-
FROM
|
|
877
|
-
WHERE
|
|
878
|
-
AND
|
|
879
|
-
AND tc.constraint_name = i.indexname
|
|
904
|
+
FROM pg_catalog.pg_constraint con
|
|
905
|
+
WHERE con.conindid = ic.oid
|
|
906
|
+
AND con.contype IN ('p', 'u', 'x')
|
|
880
907
|
)
|
|
881
908
|
ORDER BY i.tablename, i.indexname, k.ord`,
|
|
882
909
|
[schema],
|
|
@@ -986,6 +1013,21 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
986
1013
|
// normalizes them itself.
|
|
987
1014
|
const resolvedNativeType = `${normalizeSchemaNativeType(nativeType)}${many ? '[]' : ''}`;
|
|
988
1015
|
const rawDefault = colRow.column_default ?? undefined;
|
|
1016
|
+
// `GENERATED ALWAYS AS IDENTITY` ('a') and `GENERATED BY DEFAULT AS
|
|
1017
|
+
// IDENTITY` ('d') both report a NULL column_default — Postgres tracks
|
|
1018
|
+
// generation via attidentity, not a default expression — so neither
|
|
1019
|
+
// variant is visible in `rawDefault` at all. The contract has no
|
|
1020
|
+
// syntax to distinguish the two, so both resolve directly to the same
|
|
1021
|
+
// `autoincrement()` a `serial` column's `nextval(...)` default
|
|
1022
|
+
// already maps to. This is the only place identity is recognized —
|
|
1023
|
+
// there is no `SqlColumnIR.identity` field; every consumer compares
|
|
1024
|
+
// `resolvedDefault` instead.
|
|
1025
|
+
const isIdentityColumn = colRow.attidentity === 'a' || colRow.attidentity === 'd';
|
|
1026
|
+
const resolvedDefault: ColumnDefault | undefined = isIdentityColumn
|
|
1027
|
+
? { kind: 'function', expression: 'autoincrement()' }
|
|
1028
|
+
: rawDefault !== undefined
|
|
1029
|
+
? parsePostgresDefault(rawDefault, resolvedNativeType)
|
|
1030
|
+
: undefined;
|
|
989
1031
|
columns[colRow.column_name] = {
|
|
990
1032
|
name: colRow.column_name,
|
|
991
1033
|
nativeType,
|
|
@@ -993,12 +1035,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
993
1035
|
...ifDefined('default', rawDefault),
|
|
994
1036
|
...ifDefined('many', many),
|
|
995
1037
|
resolvedNativeType,
|
|
996
|
-
...ifDefined(
|
|
997
|
-
'resolvedDefault',
|
|
998
|
-
rawDefault !== undefined
|
|
999
|
-
? parsePostgresDefault(rawDefault, resolvedNativeType)
|
|
1000
|
-
: undefined,
|
|
1001
|
-
),
|
|
1038
|
+
...ifDefined('resolvedDefault', resolvedDefault),
|
|
1002
1039
|
};
|
|
1003
1040
|
}
|
|
1004
1041
|
|
|
@@ -1012,6 +1049,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1012
1049
|
? {
|
|
1013
1050
|
columns: primaryKeyColumns,
|
|
1014
1051
|
...(pkRows[0]?.constraint_name ? { name: pkRows[0].constraint_name } : {}),
|
|
1052
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, primaryKeyColumns),
|
|
1015
1053
|
}
|
|
1016
1054
|
: undefined;
|
|
1017
1055
|
|
|
@@ -1054,6 +1092,10 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1054
1092
|
name: fk.name,
|
|
1055
1093
|
...ifDefined('onDelete', mapReferentialAction(fk.deleteRule)),
|
|
1056
1094
|
...ifDefined('onUpdate', mapReferentialAction(fk.updateRule)),
|
|
1095
|
+
dependsOn: [
|
|
1096
|
+
postgresTableDependsOn(fk.referencedSchema, fk.referencedTable),
|
|
1097
|
+
...postgresColumnDependsOn(schema, tableName, fk.columns),
|
|
1098
|
+
],
|
|
1057
1099
|
}),
|
|
1058
1100
|
);
|
|
1059
1101
|
|
|
@@ -1078,6 +1120,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1078
1120
|
const uniques: readonly SqlUniqueIRInput[] = Array.from(uniquesMap.values()).map((uq) => ({
|
|
1079
1121
|
columns: Object.freeze([...uq.columns]) as readonly string[],
|
|
1080
1122
|
name: uq.name,
|
|
1123
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, uq.columns),
|
|
1081
1124
|
}));
|
|
1082
1125
|
|
|
1083
1126
|
// Process indexes
|
|
@@ -1159,6 +1202,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1159
1202
|
unique: idx.unique,
|
|
1160
1203
|
...(idx.type !== undefined && { type: idx.type }),
|
|
1161
1204
|
...(idx.options !== undefined && { options: idx.options }),
|
|
1205
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, idx.columns),
|
|
1162
1206
|
}),
|
|
1163
1207
|
);
|
|
1164
1208
|
|
|
@@ -1241,6 +1285,10 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
1241
1285
|
...(row.qual !== null ? { using: row.qual } : {}),
|
|
1242
1286
|
...(row.with_check !== null ? { withCheck: row.with_check } : {}),
|
|
1243
1287
|
permissive,
|
|
1288
|
+
dependsOn: [
|
|
1289
|
+
postgresTableDependsOn(row.schemaname, row.tablename),
|
|
1290
|
+
...policyRoles.map(postgresRoleDependsOn),
|
|
1291
|
+
],
|
|
1244
1292
|
});
|
|
1245
1293
|
const list = policiesByTable.get(row.tablename) ?? [];
|
|
1246
1294
|
list.push(policy);
|
|
@@ -1502,6 +1550,44 @@ function mapReferentialAction(rule: string): SqlReferentialAction | undefined {
|
|
|
1502
1550
|
return mapped;
|
|
1503
1551
|
}
|
|
1504
1552
|
|
|
1553
|
+
/**
|
|
1554
|
+
* A FK/policy dependency chain, mirroring the shape the expected-side
|
|
1555
|
+
* derivation stamps (`contractToPostgresDatabaseSchemaNode`): the database
|
|
1556
|
+
* root's fixed sentinel id, then the namespace, then the table.
|
|
1557
|
+
*/
|
|
1558
|
+
function postgresTableDependsOn(namespaceId: string, tableName: string): SchemaNodeRef {
|
|
1559
|
+
return [
|
|
1560
|
+
{ nodeKind: PostgresSchemaNodeKind.database, id: 'database' },
|
|
1561
|
+
{ nodeKind: PostgresSchemaNodeKind.namespace, id: namespaceId },
|
|
1562
|
+
{ nodeKind: PostgresSchemaNodeKind.table, id: tableName },
|
|
1563
|
+
];
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/** A policy's dependency chain onto one of the roles it grants to. */
|
|
1567
|
+
function postgresRoleDependsOn(role: string): SchemaNodeRef {
|
|
1568
|
+
return [
|
|
1569
|
+
{ nodeKind: PostgresSchemaNodeKind.database, id: 'database' },
|
|
1570
|
+
{ nodeKind: PostgresSchemaNodeKind.role, id: role },
|
|
1571
|
+
];
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/**
|
|
1575
|
+
* The chains from a table-child object (foreign key, index, unique, primary
|
|
1576
|
+
* key) to each of the own columns it is built on — the introspection-side
|
|
1577
|
+
* mirror of `contractToPostgresDatabaseSchemaNode`'s `columnDependsOn`. An
|
|
1578
|
+
* object is dropped before the columns it covers.
|
|
1579
|
+
*/
|
|
1580
|
+
function postgresColumnDependsOn(
|
|
1581
|
+
namespaceId: string,
|
|
1582
|
+
tableName: string,
|
|
1583
|
+
columns: readonly string[],
|
|
1584
|
+
): SchemaNodeRef[] {
|
|
1585
|
+
return columns.map((column) => [
|
|
1586
|
+
...postgresTableDependsOn(namespaceId, tableName),
|
|
1587
|
+
{ nodeKind: RelationalSchemaNodeKind.column, id: `column:${column}` },
|
|
1588
|
+
]);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1505
1591
|
/**
|
|
1506
1592
|
* Groups an array of objects by a specified key.
|
|
1507
1593
|
* Returns a Map for O(1) lookup by group key.
|