@@ -69,9 +69,51 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
6969 return res . rows . map ( row => ( { name : row . column_name , sampleValue : sampleRow [ row . column_name ] } ) ) ;
7070 }
7171
72+ private async getPgFkCascadeMap (
73+ tableName : string ,
74+ schema = 'public'
75+ ) : Promise < Record < string , { cascade : boolean ; targetTable : string } > > {
76+ const res = await this . client . query (
77+ `
78+ SELECT
79+ att.attname AS column_name,
80+ rel.relname AS child_table,
81+ p.relname AS parent_table,
82+ con.confdeltype AS confdeltype
83+ FROM pg_constraint con
84+ JOIN pg_class rel ON rel.oid = con.conrelid
85+ JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
86+ JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON TRUE
87+ JOIN pg_attribute att
88+ ON att.attrelid = con.conrelid AND att.attnum = k.attnum
89+ JOIN pg_class p ON p.oid = con.confrelid
90+ WHERE con.contype = 'f'
91+ AND nsp.nspname = $2
92+ AND p.relname = $1
93+ ` ,
94+ [ tableName , schema ]
95+ ) ;
96+
97+ const fkMap : Record < string , { cascade : boolean ; targetTable : string } > = { } ;
98+
99+ for ( const row of res . rows ) {
100+ fkMap [ row . column_name . toLowerCase ( ) ] = {
101+ cascade : row . confdeltype === 'c' ,
102+ targetTable : row . parent_table ,
103+ } ;
104+ }
105+ return fkMap ;
106+ }
107+
72108 async discoverFields ( resource ) {
73109
74110 const tableName = resource . table ;
111+ const fkMap = await this . getPgFkCascadeMap ( tableName ) ;
112+ const hasCascade = Object . values ( fkMap ) . some ( fk => fk . cascade ) ;
113+ const cascadeWarningShownMap : Record < string , boolean > = { } ;
114+ if ( hasCascade && ! cascadeWarningShownMap [ tableName ] ) {
115+ afLogger . warn ( `The database has ON DELETE CASCADE, which may conflict with adminForth cascade deletion and upload logic. Please remove it.` ) ;
116+ }
75117 const stmt = await this . client . query ( `
76118 SELECT
77119 a.attname AS name,
0 commit comments