@prisma-next/adapter-postgres 0.15.0-dev.9 → 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.
@@ -1,4 +1,8 @@
1
- import type { ContractMarkerRecord, LedgerEntryRecord } from '@prisma-next/contract/types';
1
+ import type {
2
+ ColumnDefault,
3
+ ContractMarkerRecord,
4
+ LedgerEntryRecord,
5
+ } from '@prisma-next/contract/types';
2
6
  import {
3
7
  parseMarkerRowSafely,
4
8
  rethrowMarkerReadError,
@@ -712,6 +716,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
712
716
  numeric_scale: number | null;
713
717
  column_default: string | null;
714
718
  formatted_type: string | null;
719
+ attidentity: string;
715
720
  }>(
716
721
  `SELECT
717
722
  c.table_name,
@@ -723,7 +728,8 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
723
728
  numeric_precision,
724
729
  numeric_scale,
725
730
  column_default,
726
- format_type(a.atttypid, a.atttypmod) AS formatted_type
731
+ format_type(a.atttypid, a.atttypmod) AS formatted_type,
732
+ a.attidentity
727
733
  FROM information_schema.columns c
728
734
  JOIN pg_catalog.pg_class cl
729
735
  ON cl.relname = c.table_name
@@ -739,7 +745,13 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
739
745
  ORDER BY c.table_name, c.ordinal_position`,
740
746
  [schema],
741
747
  );
742
- // 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.
743
755
  const pkResult = await driver.query<{
744
756
  table_name: string;
745
757
  constraint_name: string;
@@ -747,24 +759,29 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
747
759
  ordinal_position: number;
748
760
  }>(
749
761
  `SELECT
750
- tc.table_name,
751
- tc.constraint_name,
752
- kcu.column_name,
753
- kcu.ordinal_position
754
- FROM information_schema.table_constraints tc
755
- JOIN information_schema.key_column_usage kcu
756
- ON tc.constraint_name = kcu.constraint_name
757
- AND tc.table_schema = kcu.table_schema
758
- AND tc.table_name = kcu.table_name
759
- WHERE tc.table_schema = $1
760
- AND tc.constraint_type = 'PRIMARY KEY'
761
- ORDER BY tc.table_name, kcu.ordinal_position`,
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`,
762
776
  [schema],
763
777
  );
764
- // Query all foreign keys for all tables in schema, including referential actions.
765
- // Uses pg_catalog for correct positional pairing of composite FK columns
766
- // (information_schema.constraint_column_usage lacks ordinal_position,
767
- // which causes Cartesian products for multi-column FKs).
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.
768
785
  const fkResult = await driver.query<{
769
786
  table_name: string;
770
787
  constraint_name: string;
@@ -777,41 +794,48 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
777
794
  update_rule: string;
778
795
  }>(
779
796
  `SELECT
780
- tc.table_name,
781
- tc.constraint_name,
782
- kcu.column_name,
783
- kcu.ordinal_position,
797
+ cl.relname AS table_name,
798
+ con.conname AS constraint_name,
799
+ a.attname AS column_name,
800
+ k.ord AS ordinal_position,
784
801
  ref_ns.nspname AS referenced_table_schema,
785
802
  ref_cl.relname AS referenced_table_name,
786
803
  ref_att.attname AS referenced_column_name,
787
- rc.delete_rule,
788
- rc.update_rule
789
- FROM information_schema.table_constraints tc
790
- JOIN information_schema.key_column_usage kcu
791
- ON tc.constraint_name = kcu.constraint_name
792
- AND tc.table_schema = kcu.table_schema
793
- AND tc.table_name = kcu.table_name
794
- JOIN pg_catalog.pg_constraint pgc
795
- ON pgc.conname = tc.constraint_name
796
- AND pgc.connamespace = (
797
- SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = tc.table_schema
798
- )
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
799
825
  JOIN pg_catalog.pg_class ref_cl
800
- ON ref_cl.oid = pgc.confrelid
826
+ ON ref_cl.oid = con.confrelid
801
827
  JOIN pg_catalog.pg_namespace ref_ns
802
828
  ON ref_ns.oid = ref_cl.relnamespace
803
829
  JOIN pg_catalog.pg_attribute ref_att
804
- ON ref_att.attrelid = pgc.confrelid
805
- AND ref_att.attnum = pgc.confkey[kcu.ordinal_position]
806
- JOIN information_schema.referential_constraints rc
807
- ON rc.constraint_name = tc.constraint_name
808
- AND rc.constraint_schema = tc.table_schema
809
- WHERE tc.table_schema = $1
810
- AND tc.constraint_type = 'FOREIGN KEY'
811
- 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`,
812
835
  [schema],
813
836
  );
814
- // 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).
815
839
  const uniqueResult = await driver.query<{
816
840
  table_name: string;
817
841
  constraint_name: string;
@@ -819,18 +843,20 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
819
843
  ordinal_position: number;
820
844
  }>(
821
845
  `SELECT
822
- tc.table_name,
823
- tc.constraint_name,
824
- kcu.column_name,
825
- kcu.ordinal_position
826
- FROM information_schema.table_constraints tc
827
- JOIN information_schema.key_column_usage kcu
828
- ON tc.constraint_name = kcu.constraint_name
829
- AND tc.table_schema = kcu.table_schema
830
- AND tc.table_name = kcu.table_name
831
- WHERE tc.table_schema = $1
832
- AND tc.constraint_type = 'UNIQUE'
833
- ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position`,
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`,
834
860
  [schema],
835
861
  );
836
862
  // Query all indexes for all tables in schema (excluding constraints).
@@ -875,10 +901,9 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
875
901
  WHERE i.schemaname = $1
876
902
  AND NOT EXISTS (
877
903
  SELECT 1
878
- FROM information_schema.table_constraints tc
879
- WHERE tc.table_schema = $1
880
- AND tc.table_name = i.tablename
881
- 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')
882
907
  )
883
908
  ORDER BY i.tablename, i.indexname, k.ord`,
884
909
  [schema],
@@ -988,6 +1013,21 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
988
1013
  // normalizes them itself.
989
1014
  const resolvedNativeType = `${normalizeSchemaNativeType(nativeType)}${many ? '[]' : ''}`;
990
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;
991
1031
  columns[colRow.column_name] = {
992
1032
  name: colRow.column_name,
993
1033
  nativeType,
@@ -995,12 +1035,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
995
1035
  ...ifDefined('default', rawDefault),
996
1036
  ...ifDefined('many', many),
997
1037
  resolvedNativeType,
998
- ...ifDefined(
999
- 'resolvedDefault',
1000
- rawDefault !== undefined
1001
- ? parsePostgresDefault(rawDefault, resolvedNativeType)
1002
- : undefined,
1003
- ),
1038
+ ...ifDefined('resolvedDefault', resolvedDefault),
1004
1039
  };
1005
1040
  }
1006
1041