@@ -155,12 +155,18 @@ static CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
155
155
156
156
static void quoteOneName (char * buffer , const char * name );
157
157
static void quoteRelationName (char * buffer , Relation rel );
158
- static void ri_ReportViolation (const ConstraintInfo * riinfo , Relation fk_rel );
158
+ static void ri_ReportViolation (const ConstraintInfo * riinfo ,
159
+ Relation rel ,
160
+ Datum key ,
161
+ Oid keytype ,
162
+ bool onfk );
159
163
static SPIPlanPtr FetchPreparedPlan (QueryKey * key );
160
164
static void BuildQueryKey (QueryKey * key , Oid relid , Oid partid );
161
165
static void SavePreparedPlan (QueryKey * key , SPIPlanPtr plan );
162
166
static Oid transformFkeyCheckAttrs (Relation pkrel , int16 attnum , Oid * opclass );
163
- static void create_fk_constraint_internal (Oid fk_table , AttrNumber fk_attnum , Oid pk_table , AttrNumber pk_attnum );
167
+ static void create_fk_constraint_internal (Oid fk_table ,
168
+ AttrNumber fk_attnum ,
169
+ Oid pk_table );
164
170
static void createForeignKeyTriggers (Relation rel , Oid refRelOid ,
165
171
Oid constraintOid , Oid indexOid );
166
172
static HeapTuple get_index_for_key (Relation rel , AttrNumber attnum , Oid * index_id );
@@ -1013,21 +1019,10 @@ ri_PlanCheck(const char *querystr,
1013
1019
bool cache_plan )
1014
1020
{
1015
1021
SPIPlanPtr qplan ;
1016
- // Relation query_rel;
1017
1022
Oid save_userid ;
1018
1023
int save_sec_context ;
1019
1024
Oid argtypes [1 ] = {argtype };
1020
1025
1021
- /*
1022
- * Use the query type code to determine whether the query is run against
1023
- * the PK or FK table; we'll do the check as that table's owner
1024
- */
1025
- // if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
1026
- // query_rel = pk_rel;
1027
- // else
1028
- // query_rel = fk_rel;
1029
- // query_rel = pk_rel;
1030
-
1031
1026
/* Switch to proper UID to perform check as */
1032
1027
GetUserIdAndSecContext (& save_userid , & save_sec_context );
1033
1028
SetUserIdAndSecContext (RelationGetForm (query_rel )-> relowner ,
@@ -1056,12 +1051,6 @@ ri_PlanCheck(const char *querystr,
1056
1051
/*
1057
1052
* Perform a query to enforce an RI restriction
1058
1053
*/
1059
- // static bool
1060
- // ri_PerformCheck(const RI_ConstraintInfo *riinfo,
1061
- // RI_QueryKey *qkey, SPIPlanPtr qplan,
1062
- // Relation fk_rel, Relation pk_rel,
1063
- // HeapTuple old_tuple, HeapTuple new_tuple,
1064
- // bool detectNewRows, int expect_OK)
1065
1054
static bool
1066
1055
ri_PerformCheck (const ConstraintInfo * riinfo ,
1067
1056
SPIPlanPtr qplan ,
@@ -1078,14 +1067,12 @@ ri_PerformCheck(const ConstraintInfo *riinfo,
1078
1067
int spi_result ;
1079
1068
Oid save_userid ;
1080
1069
int save_sec_context ;
1081
- // Datum vals[RI_MAX_NUMKEYS * 2];
1082
- // char nulls[RI_MAX_NUMKEYS * 2];
1083
- Datum vals [1 ];
1070
+ Datum key [1 ];
1084
1071
char nulls [1 ];
1072
+ Oid keytype ;
1085
1073
bool isnull = false;
1086
1074
AttrNumber attnum ;
1087
1075
1088
-
1089
1076
if (source_is_pk )
1090
1077
{
1091
1078
query_rel = pk_rel ;
@@ -1099,9 +1086,12 @@ ri_PerformCheck(const ConstraintInfo *riinfo,
1099
1086
attnum = riinfo -> fk_attnum ;
1100
1087
}
1101
1088
1102
- vals [0 ] = heap_getattr (tuple , attnum , source_rel -> rd_att , & isnull );
1089
+ key [0 ] = heap_getattr (tuple , attnum , source_rel -> rd_att , & isnull );
1103
1090
nulls [0 ] = isnull ? 'n' : ' ' ;
1104
1091
1092
+ Assert (attnum <= source_rel -> rd_att -> natts );
1093
+ keytype = source_rel -> rd_att -> attrs [attnum - 1 ]-> atttypid ;
1094
+
1105
1095
/*
1106
1096
* In READ COMMITTED mode, we just need to use an up-to-date regular
1107
1097
* snapshot, and we will see all rows that could be interesting. But in
@@ -1142,7 +1132,7 @@ ri_PerformCheck(const ConstraintInfo *riinfo,
1142
1132
1143
1133
/* Finally we can run the query. */
1144
1134
spi_result = SPI_execute_snapshot (qplan ,
1145
- vals , nulls ,
1135
+ key , nulls ,
1146
1136
test_snapshot , crosscheck_snapshot ,
1147
1137
false, false, limit );
1148
1138
@@ -1160,7 +1150,7 @@ ri_PerformCheck(const ConstraintInfo *riinfo,
1160
1150
/* XXX wouldn't it be clearer to do this part at the caller? */
1161
1151
if ((SPI_processed == 0 ) != source_is_pk )
1162
1152
/* TODO: write a correct table name on DELETE and UPDATE from PK table */
1163
- ri_ReportViolation (riinfo , fk_rel );
1153
+ ri_ReportViolation (riinfo , source_rel , key [ 0 ], keytype , ! source_is_pk );
1164
1154
1165
1155
return SPI_processed != 0 ;
1166
1156
}
@@ -1201,13 +1191,32 @@ quoteRelationName(char *buffer, Relation rel)
1201
1191
}
1202
1192
1203
1193
static void
1204
- ri_ReportViolation (const ConstraintInfo * riinfo , Relation fk_rel )
1194
+ ri_ReportViolation (const ConstraintInfo * riinfo ,
1195
+ Relation rel ,
1196
+ Datum key ,
1197
+ Oid keytype ,
1198
+ bool onfk )
1205
1199
{
1206
- ereport (ERROR ,
1207
- (errcode (ERRCODE_FOREIGN_KEY_VIOLATION ),
1208
- errmsg ("insert or update on table \"%s\" violates foreign key constraint \"%s\"" ,
1209
- RelationGetRelationName (fk_rel ),
1210
- NameStr (riinfo -> conname ))));
1200
+ if (onfk )
1201
+ ereport (ERROR ,
1202
+ (errcode (ERRCODE_FOREIGN_KEY_VIOLATION ),
1203
+ errmsg ("insert or update on table \"%s\" violates foreign key constraint \"%s\"" ,
1204
+ RelationGetRelationName (rel ),
1205
+ NameStr (riinfo -> conname )),
1206
+ errdetail ("Key (%s)=(%s) is not present in table \"%s\"." ,
1207
+ get_attname (riinfo -> pk_relid , riinfo -> pk_attnum ),
1208
+ datum_to_cstring (key , keytype ),
1209
+ RelationGetRelationName (rel ))));
1210
+ else
1211
+ ereport (ERROR ,
1212
+ (errcode (ERRCODE_FOREIGN_KEY_VIOLATION ),
1213
+ errmsg ("insert or update on table \"%s\" violates foreign key constraint \"%s\"" ,
1214
+ RelationGetRelationName (rel ),
1215
+ NameStr (riinfo -> conname )),
1216
+ errdetail ("Key (%s)=(%s) is still referenced from table \"%s\"." ,
1217
+ get_attname (riinfo -> fk_relid , riinfo -> fk_attnum ),
1218
+ datum_to_cstring (key , keytype ),
1219
+ RelationGetRelationName (rel ))));
1211
1220
}
1212
1221
1213
1222
static void
@@ -1244,18 +1253,19 @@ create_fk_constraint(PG_FUNCTION_ARGS)
1244
1253
Oid fk_table = PG_GETARG_OID (0 );
1245
1254
Oid pk_table = PG_GETARG_OID (2 );
1246
1255
char * fk_attr = TextDatumGetCString (PG_GETARG_TEXT_P (1 ));
1247
- char * pk_attr = TextDatumGetCString (PG_GETARG_TEXT_P (3 ));
1248
1256
AttrNumber fk_attnum = get_attnum (fk_table , fk_attr );
1249
- AttrNumber pk_attnum = get_attnum (pk_table , pk_attr );
1250
1257
1251
- create_fk_constraint_internal (fk_table , fk_attnum , pk_table , pk_attnum );
1258
+ create_fk_constraint_internal (fk_table , fk_attnum , pk_table );
1252
1259
1253
1260
PG_RETURN_VOID ();
1254
1261
}
1255
1262
1256
1263
static void
1257
- create_fk_constraint_internal (Oid fk_table , AttrNumber fk_attnum , Oid pk_table , AttrNumber pk_attnum )
1264
+ create_fk_constraint_internal (Oid fk_table ,
1265
+ AttrNumber fk_attnum ,
1266
+ Oid pk_table )
1258
1267
{
1268
+ const PartRelationInfo * prel ;
1259
1269
Oid indexOid ;
1260
1270
Oid opclass ;
1261
1271
Oid fktypoid = get_atttype (fk_table , fk_attnum );
@@ -1276,19 +1286,23 @@ create_fk_constraint_internal(Oid fk_table, AttrNumber fk_attnum, Oid pk_table,
1276
1286
Oid opfamily ;
1277
1287
Oid opcintype ;
1278
1288
1279
-
1280
1289
fkrel = heap_open (fk_table , ShareRowExclusiveLock );
1281
1290
pkrel = heap_open (pk_table , ShareRowExclusiveLock );
1282
1291
1292
+ prel = get_pathman_relation_info (pk_table );
1293
+ if (!prel )
1294
+ elog (ERROR ,
1295
+ "table %s isn't partitioned by pg_pathman" ,
1296
+ get_rel_name (pk_table ));
1297
+
1283
1298
fkname = ChooseConstraintName (RelationGetRelationName (fkrel ),
1284
1299
get_attname (fk_table , fk_attnum ),
1285
1300
"fkey" ,
1286
1301
RelationGetNamespace (fkrel ),
1287
1302
NIL );
1288
1303
1289
- indexOid = transformFkeyCheckAttrs (pkrel , pk_attnum , & opclass );
1304
+ indexOid = transformFkeyCheckAttrs (pkrel , prel -> attnum , & opclass );
1290
1305
1291
- /* TODO: if index isn't exist then we should raise an exception! */
1292
1306
/* TODO: check permissions */
1293
1307
1294
1308
/* We need several fields out of the pg_opclass entry */
@@ -1350,7 +1364,7 @@ create_fk_constraint_internal(Oid fk_table, AttrNumber fk_attnum, Oid pk_table,
1350
1364
* constraint */
1351
1365
indexOid ,
1352
1366
pk_table ,
1353
- & pk_attnum ,
1367
+ & prel -> attnum ,
1354
1368
& pfeqop ,
1355
1369
& ppeqop ,
1356
1370
& ffeqop ,
0 commit comments