00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "postgres.h"
00032
00033 #include "access/htup_details.h"
00034 #include "access/sysattr.h"
00035 #include "access/xact.h"
00036 #include "catalog/pg_collation.h"
00037 #include "catalog/pg_constraint.h"
00038 #include "catalog/pg_operator.h"
00039 #include "catalog/pg_type.h"
00040 #include "commands/trigger.h"
00041 #include "executor/executor.h"
00042 #include "executor/spi.h"
00043 #include "parser/parse_coerce.h"
00044 #include "parser/parse_relation.h"
00045 #include "miscadmin.h"
00046 #include "utils/builtins.h"
00047 #include "utils/fmgroids.h"
00048 #include "utils/guc.h"
00049 #include "utils/inval.h"
00050 #include "utils/lsyscache.h"
00051 #include "utils/memutils.h"
00052 #include "utils/rel.h"
00053 #include "utils/snapmgr.h"
00054 #include "utils/syscache.h"
00055 #include "utils/tqual.h"
00056
00057
00058
00059
00060
00061
00062
00063 #define RI_MAX_NUMKEYS INDEX_MAX_KEYS
00064
00065 #define RI_INIT_CONSTRAINTHASHSIZE 64
00066 #define RI_INIT_QUERYHASHSIZE (RI_INIT_CONSTRAINTHASHSIZE * 4)
00067
00068 #define RI_KEYS_ALL_NULL 0
00069 #define RI_KEYS_SOME_NULL 1
00070 #define RI_KEYS_NONE_NULL 2
00071
00072
00073
00074 #define RI_PLAN_CHECK_LOOKUPPK 1
00075 #define RI_PLAN_CHECK_LOOKUPPK_FROM_PK 2
00076 #define RI_PLAN_LAST_ON_PK RI_PLAN_CHECK_LOOKUPPK_FROM_PK
00077
00078 #define RI_PLAN_CASCADE_DEL_DODELETE 3
00079 #define RI_PLAN_CASCADE_UPD_DOUPDATE 4
00080 #define RI_PLAN_RESTRICT_DEL_CHECKREF 5
00081 #define RI_PLAN_RESTRICT_UPD_CHECKREF 6
00082 #define RI_PLAN_SETNULL_DEL_DOUPDATE 7
00083 #define RI_PLAN_SETNULL_UPD_DOUPDATE 8
00084 #define RI_PLAN_SETDEFAULT_DEL_DOUPDATE 9
00085 #define RI_PLAN_SETDEFAULT_UPD_DOUPDATE 10
00086
00087 #define MAX_QUOTED_NAME_LEN (NAMEDATALEN*2+3)
00088 #define MAX_QUOTED_REL_NAME_LEN (MAX_QUOTED_NAME_LEN*2)
00089
00090 #define RIAttName(rel, attnum) NameStr(*attnumAttName(rel, attnum))
00091 #define RIAttType(rel, attnum) attnumTypeId(rel, attnum)
00092 #define RIAttCollation(rel, attnum) attnumCollationId(rel, attnum)
00093
00094 #define RI_TRIGTYPE_INSERT 1
00095 #define RI_TRIGTYPE_UPDATE 2
00096 #define RI_TRIGTYPE_DELETE 3
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106 typedef struct RI_ConstraintInfo
00107 {
00108 Oid constraint_id;
00109 bool valid;
00110 uint32 oidHashValue;
00111 NameData conname;
00112 Oid pk_relid;
00113 Oid fk_relid;
00114 char confupdtype;
00115 char confdeltype;
00116 char confmatchtype;
00117 int nkeys;
00118 int16 pk_attnums[RI_MAX_NUMKEYS];
00119 int16 fk_attnums[RI_MAX_NUMKEYS];
00120 Oid pf_eq_oprs[RI_MAX_NUMKEYS];
00121
00122 Oid pp_eq_oprs[RI_MAX_NUMKEYS];
00123
00124 Oid ff_eq_oprs[RI_MAX_NUMKEYS];
00125
00126 } RI_ConstraintInfo;
00127
00128
00129
00130
00131
00132
00133
00134
00135 typedef struct RI_QueryKey
00136 {
00137 Oid constr_id;
00138 int32 constr_queryno;
00139 } RI_QueryKey;
00140
00141
00142
00143
00144
00145
00146 typedef struct RI_QueryHashEntry
00147 {
00148 RI_QueryKey key;
00149 SPIPlanPtr plan;
00150 } RI_QueryHashEntry;
00151
00152
00153
00154
00155
00156
00157
00158
00159 typedef struct RI_CompareKey
00160 {
00161 Oid eq_opr;
00162 Oid typeid;
00163 } RI_CompareKey;
00164
00165
00166
00167
00168
00169
00170 typedef struct RI_CompareHashEntry
00171 {
00172 RI_CompareKey key;
00173 bool valid;
00174 FmgrInfo eq_opr_finfo;
00175 FmgrInfo cast_func_finfo;
00176 } RI_CompareHashEntry;
00177
00178
00179
00180
00181
00182
00183 static HTAB *ri_constraint_cache = NULL;
00184 static HTAB *ri_query_cache = NULL;
00185 static HTAB *ri_compare_cache = NULL;
00186
00187
00188
00189
00190
00191
00192 static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
00193 HeapTuple old_row,
00194 const RI_ConstraintInfo *riinfo);
00195 static Datum ri_restrict_del(TriggerData *trigdata, bool is_no_action);
00196 static Datum ri_restrict_upd(TriggerData *trigdata, bool is_no_action);
00197 static void quoteOneName(char *buffer, const char *name);
00198 static void quoteRelationName(char *buffer, Relation rel);
00199 static void ri_GenerateQual(StringInfo buf,
00200 const char *sep,
00201 const char *leftop, Oid leftoptype,
00202 Oid opoid,
00203 const char *rightop, Oid rightoptype);
00204 static void ri_add_cast_to(StringInfo buf, Oid typid);
00205 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
00206 static int ri_NullCheck(HeapTuple tup,
00207 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
00208 static void ri_BuildQueryKey(RI_QueryKey *key,
00209 const RI_ConstraintInfo *riinfo,
00210 int32 constr_queryno);
00211 static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
00212 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
00213 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
00214 Datum oldvalue, Datum newvalue);
00215
00216 static void ri_InitHashTables(void);
00217 static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue);
00218 static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
00219 static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
00220 static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
00221
00222 static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
00223 int tgkind);
00224 static const RI_ConstraintInfo *ri_FetchConstraintInfo(Trigger *trigger,
00225 Relation trig_rel, bool rel_is_pk);
00226 static const RI_ConstraintInfo *ri_LoadConstraintInfo(Oid constraintOid);
00227 static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
00228 RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
00229 bool cache_plan);
00230 static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
00231 RI_QueryKey *qkey, SPIPlanPtr qplan,
00232 Relation fk_rel, Relation pk_rel,
00233 HeapTuple old_tuple, HeapTuple new_tuple,
00234 bool detectNewRows, int expect_OK);
00235 static void ri_ExtractValues(Relation rel, HeapTuple tup,
00236 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
00237 Datum *vals, char *nulls);
00238 static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
00239 Relation pk_rel, Relation fk_rel,
00240 HeapTuple violator, TupleDesc tupdesc,
00241 int queryno, bool spi_err);
00242
00243
00244
00245
00246
00247
00248
00249
00250 static Datum
00251 RI_FKey_check(TriggerData *trigdata)
00252 {
00253 const RI_ConstraintInfo *riinfo;
00254 Relation fk_rel;
00255 Relation pk_rel;
00256 HeapTuple new_row;
00257 Buffer new_row_buf;
00258 RI_QueryKey qkey;
00259 SPIPlanPtr qplan;
00260 int i;
00261
00262
00263
00264
00265 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
00266 trigdata->tg_relation, false);
00267
00268 if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00269 {
00270 new_row = trigdata->tg_newtuple;
00271 new_row_buf = trigdata->tg_newtuplebuf;
00272 }
00273 else
00274 {
00275 new_row = trigdata->tg_trigtuple;
00276 new_row_buf = trigdata->tg_trigtuplebuf;
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294 Assert(new_row_buf != InvalidBuffer);
00295 if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
00296 return PointerGetDatum(NULL);
00297
00298
00299
00300
00301
00302
00303
00304 fk_rel = trigdata->tg_relation;
00305 pk_rel = heap_open(riinfo->pk_relid, RowShareLock);
00306
00307 if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL)
00308 ereport(ERROR,
00309 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00310 errmsg("MATCH PARTIAL not yet implemented")));
00311
00312 switch (ri_NullCheck(new_row, riinfo, false))
00313 {
00314 case RI_KEYS_ALL_NULL:
00315
00316
00317
00318
00319
00320 heap_close(pk_rel, RowShareLock);
00321 return PointerGetDatum(NULL);
00322
00323 case RI_KEYS_SOME_NULL:
00324
00325
00326
00327
00328
00329 switch (riinfo->confmatchtype)
00330 {
00331 case FKCONSTR_MATCH_FULL:
00332
00333
00334
00335
00336
00337 ereport(ERROR,
00338 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
00339 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
00340 RelationGetRelationName(fk_rel),
00341 NameStr(riinfo->conname)),
00342 errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
00343 errtableconstraint(fk_rel,
00344 NameStr(riinfo->conname))));
00345 heap_close(pk_rel, RowShareLock);
00346 return PointerGetDatum(NULL);
00347
00348 case FKCONSTR_MATCH_SIMPLE:
00349
00350
00351
00352
00353
00354 heap_close(pk_rel, RowShareLock);
00355 return PointerGetDatum(NULL);
00356
00357 case FKCONSTR_MATCH_PARTIAL:
00358
00359
00360
00361
00362
00363
00364
00365 ereport(ERROR,
00366 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00367 errmsg("MATCH PARTIAL not yet implemented")));
00368 heap_close(pk_rel, RowShareLock);
00369 return PointerGetDatum(NULL);
00370
00371 default:
00372 elog(ERROR, "unrecognized confmatchtype: %d",
00373 riinfo->confmatchtype);
00374 break;
00375 }
00376
00377 case RI_KEYS_NONE_NULL:
00378
00379
00380
00381
00382
00383 break;
00384 }
00385
00386 if (SPI_connect() != SPI_OK_CONNECT)
00387 elog(ERROR, "SPI_connect failed");
00388
00389
00390
00391
00392 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
00393
00394 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00395 {
00396 StringInfoData querybuf;
00397 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
00398 char attname[MAX_QUOTED_NAME_LEN];
00399 char paramname[16];
00400 const char *querysep;
00401 Oid queryoids[RI_MAX_NUMKEYS];
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411 initStringInfo(&querybuf);
00412 quoteRelationName(pkrelname, pk_rel);
00413 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
00414 querysep = "WHERE";
00415 for (i = 0; i < riinfo->nkeys; i++)
00416 {
00417 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00418 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
00419
00420 quoteOneName(attname,
00421 RIAttName(pk_rel, riinfo->pk_attnums[i]));
00422 sprintf(paramname, "$%d", i + 1);
00423 ri_GenerateQual(&querybuf, querysep,
00424 attname, pk_type,
00425 riinfo->pf_eq_oprs[i],
00426 paramname, fk_type);
00427 querysep = "AND";
00428 queryoids[i] = fk_type;
00429 }
00430 appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00431
00432
00433 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00434 &qkey, fk_rel, pk_rel, true);
00435 }
00436
00437
00438
00439
00440 ri_PerformCheck(riinfo, &qkey, qplan,
00441 fk_rel, pk_rel,
00442 NULL, new_row,
00443 false,
00444 SPI_OK_SELECT);
00445
00446 if (SPI_finish() != SPI_OK_FINISH)
00447 elog(ERROR, "SPI_finish failed");
00448
00449 heap_close(pk_rel, RowShareLock);
00450
00451 return PointerGetDatum(NULL);
00452 }
00453
00454
00455
00456
00457
00458
00459
00460
00461 Datum
00462 RI_FKey_check_ins(PG_FUNCTION_ARGS)
00463 {
00464
00465
00466
00467 ri_CheckTrigger(fcinfo, "RI_FKey_check_ins", RI_TRIGTYPE_INSERT);
00468
00469
00470
00471
00472 return RI_FKey_check((TriggerData *) fcinfo->context);
00473 }
00474
00475
00476
00477
00478
00479
00480
00481
00482 Datum
00483 RI_FKey_check_upd(PG_FUNCTION_ARGS)
00484 {
00485
00486
00487
00488 ri_CheckTrigger(fcinfo, "RI_FKey_check_upd", RI_TRIGTYPE_UPDATE);
00489
00490
00491
00492
00493 return RI_FKey_check((TriggerData *) fcinfo->context);
00494 }
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508 static bool
00509 ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
00510 HeapTuple old_row,
00511 const RI_ConstraintInfo *riinfo)
00512 {
00513 SPIPlanPtr qplan;
00514 RI_QueryKey qkey;
00515 int i;
00516 bool result;
00517
00518
00519 Assert(ri_NullCheck(old_row, riinfo, true) == RI_KEYS_NONE_NULL);
00520
00521 if (SPI_connect() != SPI_OK_CONNECT)
00522 elog(ERROR, "SPI_connect failed");
00523
00524
00525
00526
00527
00528 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK_FROM_PK);
00529
00530 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00531 {
00532 StringInfoData querybuf;
00533 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
00534 char attname[MAX_QUOTED_NAME_LEN];
00535 char paramname[16];
00536 const char *querysep;
00537 Oid queryoids[RI_MAX_NUMKEYS];
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547 initStringInfo(&querybuf);
00548 quoteRelationName(pkrelname, pk_rel);
00549 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
00550 querysep = "WHERE";
00551 for (i = 0; i < riinfo->nkeys; i++)
00552 {
00553 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00554
00555 quoteOneName(attname,
00556 RIAttName(pk_rel, riinfo->pk_attnums[i]));
00557 sprintf(paramname, "$%d", i + 1);
00558 ri_GenerateQual(&querybuf, querysep,
00559 attname, pk_type,
00560 riinfo->pp_eq_oprs[i],
00561 paramname, pk_type);
00562 querysep = "AND";
00563 queryoids[i] = pk_type;
00564 }
00565 appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00566
00567
00568 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00569 &qkey, fk_rel, pk_rel, true);
00570 }
00571
00572
00573
00574
00575 result = ri_PerformCheck(riinfo, &qkey, qplan,
00576 fk_rel, pk_rel,
00577 old_row, NULL,
00578 true,
00579 SPI_OK_SELECT);
00580
00581 if (SPI_finish() != SPI_OK_FINISH)
00582 elog(ERROR, "SPI_finish failed");
00583
00584 return result;
00585 }
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596 Datum
00597 RI_FKey_noaction_del(PG_FUNCTION_ARGS)
00598 {
00599
00600
00601
00602 ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
00603
00604
00605
00606
00607 return ri_restrict_del((TriggerData *) fcinfo->context, true);
00608 }
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621 Datum
00622 RI_FKey_restrict_del(PG_FUNCTION_ARGS)
00623 {
00624
00625
00626
00627 ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
00628
00629
00630
00631
00632 return ri_restrict_del((TriggerData *) fcinfo->context, false);
00633 }
00634
00635
00636
00637
00638
00639
00640
00641 static Datum
00642 ri_restrict_del(TriggerData *trigdata, bool is_no_action)
00643 {
00644 const RI_ConstraintInfo *riinfo;
00645 Relation fk_rel;
00646 Relation pk_rel;
00647 HeapTuple old_row;
00648 RI_QueryKey qkey;
00649 SPIPlanPtr qplan;
00650 int i;
00651
00652
00653
00654
00655 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
00656 trigdata->tg_relation, true);
00657
00658
00659
00660
00661
00662
00663
00664 fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
00665 pk_rel = trigdata->tg_relation;
00666 old_row = trigdata->tg_trigtuple;
00667
00668 switch (riinfo->confmatchtype)
00669 {
00670
00671
00672
00673
00674
00675
00676
00677 case FKCONSTR_MATCH_SIMPLE:
00678 case FKCONSTR_MATCH_FULL:
00679 switch (ri_NullCheck(old_row, riinfo, true))
00680 {
00681 case RI_KEYS_ALL_NULL:
00682 case RI_KEYS_SOME_NULL:
00683
00684
00685
00686
00687
00688 heap_close(fk_rel, RowShareLock);
00689 return PointerGetDatum(NULL);
00690
00691 case RI_KEYS_NONE_NULL:
00692
00693
00694
00695
00696 break;
00697 }
00698
00699
00700
00701
00702
00703
00704
00705 if (is_no_action &&
00706 ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
00707 {
00708 heap_close(fk_rel, RowShareLock);
00709 return PointerGetDatum(NULL);
00710 }
00711
00712 if (SPI_connect() != SPI_OK_CONNECT)
00713 elog(ERROR, "SPI_connect failed");
00714
00715
00716
00717
00718 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_DEL_CHECKREF);
00719
00720 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00721 {
00722 StringInfoData querybuf;
00723 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
00724 char attname[MAX_QUOTED_NAME_LEN];
00725 char paramname[16];
00726 const char *querysep;
00727 Oid queryoids[RI_MAX_NUMKEYS];
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737 initStringInfo(&querybuf);
00738 quoteRelationName(fkrelname, fk_rel);
00739 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
00740 fkrelname);
00741 querysep = "WHERE";
00742 for (i = 0; i < riinfo->nkeys; i++)
00743 {
00744 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00745 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
00746
00747 quoteOneName(attname,
00748 RIAttName(fk_rel, riinfo->fk_attnums[i]));
00749 sprintf(paramname, "$%d", i + 1);
00750 ri_GenerateQual(&querybuf, querysep,
00751 paramname, pk_type,
00752 riinfo->pf_eq_oprs[i],
00753 attname, fk_type);
00754 querysep = "AND";
00755 queryoids[i] = pk_type;
00756 }
00757 appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00758
00759
00760 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00761 &qkey, fk_rel, pk_rel, true);
00762 }
00763
00764
00765
00766
00767 ri_PerformCheck(riinfo, &qkey, qplan,
00768 fk_rel, pk_rel,
00769 old_row, NULL,
00770 true,
00771 SPI_OK_SELECT);
00772
00773 if (SPI_finish() != SPI_OK_FINISH)
00774 elog(ERROR, "SPI_finish failed");
00775
00776 heap_close(fk_rel, RowShareLock);
00777
00778 return PointerGetDatum(NULL);
00779
00780
00781
00782
00783 case FKCONSTR_MATCH_PARTIAL:
00784 ereport(ERROR,
00785 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00786 errmsg("MATCH PARTIAL not yet implemented")));
00787 return PointerGetDatum(NULL);
00788
00789 default:
00790 elog(ERROR, "unrecognized confmatchtype: %d",
00791 riinfo->confmatchtype);
00792 break;
00793 }
00794
00795
00796 return PointerGetDatum(NULL);
00797 }
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808 Datum
00809 RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
00810 {
00811
00812
00813
00814 ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
00815
00816
00817
00818
00819 return ri_restrict_upd((TriggerData *) fcinfo->context, true);
00820 }
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833 Datum
00834 RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
00835 {
00836
00837
00838
00839 ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
00840
00841
00842
00843
00844 return ri_restrict_upd((TriggerData *) fcinfo->context, false);
00845 }
00846
00847
00848
00849
00850
00851
00852
00853 static Datum
00854 ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
00855 {
00856 const RI_ConstraintInfo *riinfo;
00857 Relation fk_rel;
00858 Relation pk_rel;
00859 HeapTuple new_row;
00860 HeapTuple old_row;
00861 RI_QueryKey qkey;
00862 SPIPlanPtr qplan;
00863 int i;
00864
00865
00866
00867
00868 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
00869 trigdata->tg_relation, true);
00870
00871
00872
00873
00874
00875
00876
00877
00878 fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
00879 pk_rel = trigdata->tg_relation;
00880 new_row = trigdata->tg_newtuple;
00881 old_row = trigdata->tg_trigtuple;
00882
00883 switch (riinfo->confmatchtype)
00884 {
00885
00886
00887
00888
00889
00890
00891
00892 case FKCONSTR_MATCH_SIMPLE:
00893 case FKCONSTR_MATCH_FULL:
00894 switch (ri_NullCheck(old_row, riinfo, true))
00895 {
00896 case RI_KEYS_ALL_NULL:
00897 case RI_KEYS_SOME_NULL:
00898
00899
00900
00901
00902
00903 heap_close(fk_rel, RowShareLock);
00904 return PointerGetDatum(NULL);
00905
00906 case RI_KEYS_NONE_NULL:
00907
00908
00909
00910
00911 break;
00912 }
00913
00914
00915
00916
00917 if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
00918 {
00919 heap_close(fk_rel, RowShareLock);
00920 return PointerGetDatum(NULL);
00921 }
00922
00923
00924
00925
00926
00927
00928
00929 if (is_no_action &&
00930 ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
00931 {
00932 heap_close(fk_rel, RowShareLock);
00933 return PointerGetDatum(NULL);
00934 }
00935
00936 if (SPI_connect() != SPI_OK_CONNECT)
00937 elog(ERROR, "SPI_connect failed");
00938
00939
00940
00941
00942 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_UPD_CHECKREF);
00943
00944 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00945 {
00946 StringInfoData querybuf;
00947 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
00948 char attname[MAX_QUOTED_NAME_LEN];
00949 char paramname[16];
00950 const char *querysep;
00951 Oid queryoids[RI_MAX_NUMKEYS];
00952
00953
00954
00955
00956
00957
00958
00959
00960 initStringInfo(&querybuf);
00961 quoteRelationName(fkrelname, fk_rel);
00962 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
00963 fkrelname);
00964 querysep = "WHERE";
00965 for (i = 0; i < riinfo->nkeys; i++)
00966 {
00967 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00968 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
00969
00970 quoteOneName(attname,
00971 RIAttName(fk_rel, riinfo->fk_attnums[i]));
00972 sprintf(paramname, "$%d", i + 1);
00973 ri_GenerateQual(&querybuf, querysep,
00974 paramname, pk_type,
00975 riinfo->pf_eq_oprs[i],
00976 attname, fk_type);
00977 querysep = "AND";
00978 queryoids[i] = pk_type;
00979 }
00980 appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00981
00982
00983 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00984 &qkey, fk_rel, pk_rel, true);
00985 }
00986
00987
00988
00989
00990 ri_PerformCheck(riinfo, &qkey, qplan,
00991 fk_rel, pk_rel,
00992 old_row, NULL,
00993 true,
00994 SPI_OK_SELECT);
00995
00996 if (SPI_finish() != SPI_OK_FINISH)
00997 elog(ERROR, "SPI_finish failed");
00998
00999 heap_close(fk_rel, RowShareLock);
01000
01001 return PointerGetDatum(NULL);
01002
01003
01004
01005
01006 case FKCONSTR_MATCH_PARTIAL:
01007 ereport(ERROR,
01008 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01009 errmsg("MATCH PARTIAL not yet implemented")));
01010 return PointerGetDatum(NULL);
01011
01012 default:
01013 elog(ERROR, "unrecognized confmatchtype: %d",
01014 riinfo->confmatchtype);
01015 break;
01016 }
01017
01018
01019 return PointerGetDatum(NULL);
01020 }
01021
01022
01023
01024
01025
01026
01027
01028
01029 Datum
01030 RI_FKey_cascade_del(PG_FUNCTION_ARGS)
01031 {
01032 TriggerData *trigdata = (TriggerData *) fcinfo->context;
01033 const RI_ConstraintInfo *riinfo;
01034 Relation fk_rel;
01035 Relation pk_rel;
01036 HeapTuple old_row;
01037 RI_QueryKey qkey;
01038 SPIPlanPtr qplan;
01039 int i;
01040
01041
01042
01043
01044 ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
01045
01046
01047
01048
01049 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01050 trigdata->tg_relation, true);
01051
01052
01053
01054
01055
01056
01057
01058 fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01059 pk_rel = trigdata->tg_relation;
01060 old_row = trigdata->tg_trigtuple;
01061
01062 switch (riinfo->confmatchtype)
01063 {
01064
01065
01066
01067
01068
01069
01070
01071 case FKCONSTR_MATCH_SIMPLE:
01072 case FKCONSTR_MATCH_FULL:
01073 switch (ri_NullCheck(old_row, riinfo, true))
01074 {
01075 case RI_KEYS_ALL_NULL:
01076 case RI_KEYS_SOME_NULL:
01077
01078
01079
01080
01081
01082 heap_close(fk_rel, RowExclusiveLock);
01083 return PointerGetDatum(NULL);
01084
01085 case RI_KEYS_NONE_NULL:
01086
01087
01088
01089
01090 break;
01091 }
01092
01093 if (SPI_connect() != SPI_OK_CONNECT)
01094 elog(ERROR, "SPI_connect failed");
01095
01096
01097
01098
01099 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
01100
01101 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01102 {
01103 StringInfoData querybuf;
01104 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
01105 char attname[MAX_QUOTED_NAME_LEN];
01106 char paramname[16];
01107 const char *querysep;
01108 Oid queryoids[RI_MAX_NUMKEYS];
01109
01110
01111
01112
01113
01114
01115
01116
01117 initStringInfo(&querybuf);
01118 quoteRelationName(fkrelname, fk_rel);
01119 appendStringInfo(&querybuf, "DELETE FROM ONLY %s", fkrelname);
01120 querysep = "WHERE";
01121 for (i = 0; i < riinfo->nkeys; i++)
01122 {
01123 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01124 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01125
01126 quoteOneName(attname,
01127 RIAttName(fk_rel, riinfo->fk_attnums[i]));
01128 sprintf(paramname, "$%d", i + 1);
01129 ri_GenerateQual(&querybuf, querysep,
01130 paramname, pk_type,
01131 riinfo->pf_eq_oprs[i],
01132 attname, fk_type);
01133 querysep = "AND";
01134 queryoids[i] = pk_type;
01135 }
01136
01137
01138 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01139 &qkey, fk_rel, pk_rel, true);
01140 }
01141
01142
01143
01144
01145
01146 ri_PerformCheck(riinfo, &qkey, qplan,
01147 fk_rel, pk_rel,
01148 old_row, NULL,
01149 true,
01150 SPI_OK_DELETE);
01151
01152 if (SPI_finish() != SPI_OK_FINISH)
01153 elog(ERROR, "SPI_finish failed");
01154
01155 heap_close(fk_rel, RowExclusiveLock);
01156
01157 return PointerGetDatum(NULL);
01158
01159
01160
01161
01162 case FKCONSTR_MATCH_PARTIAL:
01163 ereport(ERROR,
01164 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01165 errmsg("MATCH PARTIAL not yet implemented")));
01166 return PointerGetDatum(NULL);
01167
01168 default:
01169 elog(ERROR, "unrecognized confmatchtype: %d",
01170 riinfo->confmatchtype);
01171 break;
01172 }
01173
01174
01175 return PointerGetDatum(NULL);
01176 }
01177
01178
01179
01180
01181
01182
01183
01184
01185 Datum
01186 RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
01187 {
01188 TriggerData *trigdata = (TriggerData *) fcinfo->context;
01189 const RI_ConstraintInfo *riinfo;
01190 Relation fk_rel;
01191 Relation pk_rel;
01192 HeapTuple new_row;
01193 HeapTuple old_row;
01194 RI_QueryKey qkey;
01195 SPIPlanPtr qplan;
01196 int i;
01197 int j;
01198
01199
01200
01201
01202 ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
01203
01204
01205
01206
01207 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01208 trigdata->tg_relation, true);
01209
01210
01211
01212
01213
01214
01215
01216
01217 fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01218 pk_rel = trigdata->tg_relation;
01219 new_row = trigdata->tg_newtuple;
01220 old_row = trigdata->tg_trigtuple;
01221
01222 switch (riinfo->confmatchtype)
01223 {
01224
01225
01226
01227
01228
01229
01230
01231 case FKCONSTR_MATCH_SIMPLE:
01232 case FKCONSTR_MATCH_FULL:
01233 switch (ri_NullCheck(old_row, riinfo, true))
01234 {
01235 case RI_KEYS_ALL_NULL:
01236 case RI_KEYS_SOME_NULL:
01237
01238
01239
01240
01241
01242 heap_close(fk_rel, RowExclusiveLock);
01243 return PointerGetDatum(NULL);
01244
01245 case RI_KEYS_NONE_NULL:
01246
01247
01248
01249
01250 break;
01251 }
01252
01253
01254
01255
01256 if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
01257 {
01258 heap_close(fk_rel, RowExclusiveLock);
01259 return PointerGetDatum(NULL);
01260 }
01261
01262 if (SPI_connect() != SPI_OK_CONNECT)
01263 elog(ERROR, "SPI_connect failed");
01264
01265
01266
01267
01268 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
01269
01270 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01271 {
01272 StringInfoData querybuf;
01273 StringInfoData qualbuf;
01274 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
01275 char attname[MAX_QUOTED_NAME_LEN];
01276 char paramname[16];
01277 const char *querysep;
01278 const char *qualsep;
01279 Oid queryoids[RI_MAX_NUMKEYS * 2];
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291 initStringInfo(&querybuf);
01292 initStringInfo(&qualbuf);
01293 quoteRelationName(fkrelname, fk_rel);
01294 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01295 querysep = "";
01296 qualsep = "WHERE";
01297 for (i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
01298 {
01299 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01300 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01301
01302 quoteOneName(attname,
01303 RIAttName(fk_rel, riinfo->fk_attnums[i]));
01304 appendStringInfo(&querybuf,
01305 "%s %s = $%d",
01306 querysep, attname, i + 1);
01307 sprintf(paramname, "$%d", j + 1);
01308 ri_GenerateQual(&qualbuf, qualsep,
01309 paramname, pk_type,
01310 riinfo->pf_eq_oprs[i],
01311 attname, fk_type);
01312 querysep = ",";
01313 qualsep = "AND";
01314 queryoids[i] = pk_type;
01315 queryoids[j] = pk_type;
01316 }
01317 appendStringInfoString(&querybuf, qualbuf.data);
01318
01319
01320 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
01321 &qkey, fk_rel, pk_rel, true);
01322 }
01323
01324
01325
01326
01327 ri_PerformCheck(riinfo, &qkey, qplan,
01328 fk_rel, pk_rel,
01329 old_row, new_row,
01330 true,
01331 SPI_OK_UPDATE);
01332
01333 if (SPI_finish() != SPI_OK_FINISH)
01334 elog(ERROR, "SPI_finish failed");
01335
01336 heap_close(fk_rel, RowExclusiveLock);
01337
01338 return PointerGetDatum(NULL);
01339
01340
01341
01342
01343 case FKCONSTR_MATCH_PARTIAL:
01344 ereport(ERROR,
01345 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01346 errmsg("MATCH PARTIAL not yet implemented")));
01347 return PointerGetDatum(NULL);
01348
01349 default:
01350 elog(ERROR, "unrecognized confmatchtype: %d",
01351 riinfo->confmatchtype);
01352 break;
01353 }
01354
01355
01356 return PointerGetDatum(NULL);
01357 }
01358
01359
01360
01361
01362
01363
01364
01365
01366 Datum
01367 RI_FKey_setnull_del(PG_FUNCTION_ARGS)
01368 {
01369 TriggerData *trigdata = (TriggerData *) fcinfo->context;
01370 const RI_ConstraintInfo *riinfo;
01371 Relation fk_rel;
01372 Relation pk_rel;
01373 HeapTuple old_row;
01374 RI_QueryKey qkey;
01375 SPIPlanPtr qplan;
01376 int i;
01377
01378
01379
01380
01381 ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
01382
01383
01384
01385
01386 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01387 trigdata->tg_relation, true);
01388
01389
01390
01391
01392
01393
01394
01395 fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01396 pk_rel = trigdata->tg_relation;
01397 old_row = trigdata->tg_trigtuple;
01398
01399 switch (riinfo->confmatchtype)
01400 {
01401
01402
01403
01404
01405
01406
01407
01408 case FKCONSTR_MATCH_SIMPLE:
01409 case FKCONSTR_MATCH_FULL:
01410 switch (ri_NullCheck(old_row, riinfo, true))
01411 {
01412 case RI_KEYS_ALL_NULL:
01413 case RI_KEYS_SOME_NULL:
01414
01415
01416
01417
01418
01419 heap_close(fk_rel, RowExclusiveLock);
01420 return PointerGetDatum(NULL);
01421
01422 case RI_KEYS_NONE_NULL:
01423
01424
01425
01426
01427 break;
01428 }
01429
01430 if (SPI_connect() != SPI_OK_CONNECT)
01431 elog(ERROR, "SPI_connect failed");
01432
01433
01434
01435
01436 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_DEL_DOUPDATE);
01437
01438 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01439 {
01440 StringInfoData querybuf;
01441 StringInfoData qualbuf;
01442 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
01443 char attname[MAX_QUOTED_NAME_LEN];
01444 char paramname[16];
01445 const char *querysep;
01446 const char *qualsep;
01447 Oid queryoids[RI_MAX_NUMKEYS];
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457 initStringInfo(&querybuf);
01458 initStringInfo(&qualbuf);
01459 quoteRelationName(fkrelname, fk_rel);
01460 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01461 querysep = "";
01462 qualsep = "WHERE";
01463 for (i = 0; i < riinfo->nkeys; i++)
01464 {
01465 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01466 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01467
01468 quoteOneName(attname,
01469 RIAttName(fk_rel, riinfo->fk_attnums[i]));
01470 appendStringInfo(&querybuf,
01471 "%s %s = NULL",
01472 querysep, attname);
01473 sprintf(paramname, "$%d", i + 1);
01474 ri_GenerateQual(&qualbuf, qualsep,
01475 paramname, pk_type,
01476 riinfo->pf_eq_oprs[i],
01477 attname, fk_type);
01478 querysep = ",";
01479 qualsep = "AND";
01480 queryoids[i] = pk_type;
01481 }
01482 appendStringInfoString(&querybuf, qualbuf.data);
01483
01484
01485 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01486 &qkey, fk_rel, pk_rel, true);
01487 }
01488
01489
01490
01491
01492 ri_PerformCheck(riinfo, &qkey, qplan,
01493 fk_rel, pk_rel,
01494 old_row, NULL,
01495 true,
01496 SPI_OK_UPDATE);
01497
01498 if (SPI_finish() != SPI_OK_FINISH)
01499 elog(ERROR, "SPI_finish failed");
01500
01501 heap_close(fk_rel, RowExclusiveLock);
01502
01503 return PointerGetDatum(NULL);
01504
01505
01506
01507
01508 case FKCONSTR_MATCH_PARTIAL:
01509 ereport(ERROR,
01510 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01511 errmsg("MATCH PARTIAL not yet implemented")));
01512 return PointerGetDatum(NULL);
01513
01514 default:
01515 elog(ERROR, "unrecognized confmatchtype: %d",
01516 riinfo->confmatchtype);
01517 break;
01518 }
01519
01520
01521 return PointerGetDatum(NULL);
01522 }
01523
01524
01525
01526
01527
01528
01529
01530
01531 Datum
01532 RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
01533 {
01534 TriggerData *trigdata = (TriggerData *) fcinfo->context;
01535 const RI_ConstraintInfo *riinfo;
01536 Relation fk_rel;
01537 Relation pk_rel;
01538 HeapTuple new_row;
01539 HeapTuple old_row;
01540 RI_QueryKey qkey;
01541 SPIPlanPtr qplan;
01542 int i;
01543
01544
01545
01546
01547 ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
01548
01549
01550
01551
01552 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01553 trigdata->tg_relation, true);
01554
01555
01556
01557
01558
01559
01560
01561 fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01562 pk_rel = trigdata->tg_relation;
01563 new_row = trigdata->tg_newtuple;
01564 old_row = trigdata->tg_trigtuple;
01565
01566 switch (riinfo->confmatchtype)
01567 {
01568
01569
01570
01571
01572
01573
01574
01575 case FKCONSTR_MATCH_SIMPLE:
01576 case FKCONSTR_MATCH_FULL:
01577 switch (ri_NullCheck(old_row, riinfo, true))
01578 {
01579 case RI_KEYS_ALL_NULL:
01580 case RI_KEYS_SOME_NULL:
01581
01582
01583
01584
01585
01586 heap_close(fk_rel, RowExclusiveLock);
01587 return PointerGetDatum(NULL);
01588
01589 case RI_KEYS_NONE_NULL:
01590
01591
01592
01593
01594 break;
01595 }
01596
01597
01598
01599
01600 if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
01601 {
01602 heap_close(fk_rel, RowExclusiveLock);
01603 return PointerGetDatum(NULL);
01604 }
01605
01606 if (SPI_connect() != SPI_OK_CONNECT)
01607 elog(ERROR, "SPI_connect failed");
01608
01609
01610
01611
01612 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_UPD_DOUPDATE);
01613
01614 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01615 {
01616 StringInfoData querybuf;
01617 StringInfoData qualbuf;
01618 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
01619 char attname[MAX_QUOTED_NAME_LEN];
01620 char paramname[16];
01621 const char *querysep;
01622 const char *qualsep;
01623 Oid queryoids[RI_MAX_NUMKEYS];
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633 initStringInfo(&querybuf);
01634 initStringInfo(&qualbuf);
01635 quoteRelationName(fkrelname, fk_rel);
01636 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01637 querysep = "";
01638 qualsep = "WHERE";
01639 for (i = 0; i < riinfo->nkeys; i++)
01640 {
01641 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01642 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01643
01644 quoteOneName(attname,
01645 RIAttName(fk_rel, riinfo->fk_attnums[i]));
01646 appendStringInfo(&querybuf,
01647 "%s %s = NULL",
01648 querysep, attname);
01649 sprintf(paramname, "$%d", i + 1);
01650 ri_GenerateQual(&qualbuf, qualsep,
01651 paramname, pk_type,
01652 riinfo->pf_eq_oprs[i],
01653 attname, fk_type);
01654 querysep = ",";
01655 qualsep = "AND";
01656 queryoids[i] = pk_type;
01657 }
01658 appendStringInfoString(&querybuf, qualbuf.data);
01659
01660
01661 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01662 &qkey, fk_rel, pk_rel, true);
01663 }
01664
01665
01666
01667
01668 ri_PerformCheck(riinfo, &qkey, qplan,
01669 fk_rel, pk_rel,
01670 old_row, NULL,
01671 true,
01672 SPI_OK_UPDATE);
01673
01674 if (SPI_finish() != SPI_OK_FINISH)
01675 elog(ERROR, "SPI_finish failed");
01676
01677 heap_close(fk_rel, RowExclusiveLock);
01678
01679 return PointerGetDatum(NULL);
01680
01681
01682
01683
01684 case FKCONSTR_MATCH_PARTIAL:
01685 ereport(ERROR,
01686 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01687 errmsg("MATCH PARTIAL not yet implemented")));
01688 return PointerGetDatum(NULL);
01689
01690 default:
01691 elog(ERROR, "unrecognized confmatchtype: %d",
01692 riinfo->confmatchtype);
01693 break;
01694 }
01695
01696
01697 return PointerGetDatum(NULL);
01698 }
01699
01700
01701
01702
01703
01704
01705
01706
01707 Datum
01708 RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
01709 {
01710 TriggerData *trigdata = (TriggerData *) fcinfo->context;
01711 const RI_ConstraintInfo *riinfo;
01712 Relation fk_rel;
01713 Relation pk_rel;
01714 HeapTuple old_row;
01715 RI_QueryKey qkey;
01716 SPIPlanPtr qplan;
01717
01718
01719
01720
01721 ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
01722
01723
01724
01725
01726 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01727 trigdata->tg_relation, true);
01728
01729
01730
01731
01732
01733
01734
01735 fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01736 pk_rel = trigdata->tg_relation;
01737 old_row = trigdata->tg_trigtuple;
01738
01739 switch (riinfo->confmatchtype)
01740 {
01741
01742
01743
01744
01745
01746
01747
01748 case FKCONSTR_MATCH_SIMPLE:
01749 case FKCONSTR_MATCH_FULL:
01750 switch (ri_NullCheck(old_row, riinfo, true))
01751 {
01752 case RI_KEYS_ALL_NULL:
01753 case RI_KEYS_SOME_NULL:
01754
01755
01756
01757
01758
01759 heap_close(fk_rel, RowExclusiveLock);
01760 return PointerGetDatum(NULL);
01761
01762 case RI_KEYS_NONE_NULL:
01763
01764
01765
01766
01767 break;
01768 }
01769
01770 if (SPI_connect() != SPI_OK_CONNECT)
01771 elog(ERROR, "SPI_connect failed");
01772
01773
01774
01775
01776
01777 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_DEL_DOUPDATE);
01778
01779 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01780 {
01781 StringInfoData querybuf;
01782 StringInfoData qualbuf;
01783 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
01784 char attname[MAX_QUOTED_NAME_LEN];
01785 char paramname[16];
01786 const char *querysep;
01787 const char *qualsep;
01788 Oid queryoids[RI_MAX_NUMKEYS];
01789 int i;
01790
01791
01792
01793
01794
01795
01796
01797
01798
01799 initStringInfo(&querybuf);
01800 initStringInfo(&qualbuf);
01801 quoteRelationName(fkrelname, fk_rel);
01802 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01803 querysep = "";
01804 qualsep = "WHERE";
01805 for (i = 0; i < riinfo->nkeys; i++)
01806 {
01807 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01808 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01809
01810 quoteOneName(attname,
01811 RIAttName(fk_rel, riinfo->fk_attnums[i]));
01812 appendStringInfo(&querybuf,
01813 "%s %s = DEFAULT",
01814 querysep, attname);
01815 sprintf(paramname, "$%d", i + 1);
01816 ri_GenerateQual(&qualbuf, qualsep,
01817 paramname, pk_type,
01818 riinfo->pf_eq_oprs[i],
01819 attname, fk_type);
01820 querysep = ",";
01821 qualsep = "AND";
01822 queryoids[i] = pk_type;
01823 }
01824 appendStringInfoString(&querybuf, qualbuf.data);
01825
01826
01827 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01828 &qkey, fk_rel, pk_rel, true);
01829 }
01830
01831
01832
01833
01834 ri_PerformCheck(riinfo, &qkey, qplan,
01835 fk_rel, pk_rel,
01836 old_row, NULL,
01837 true,
01838 SPI_OK_UPDATE);
01839
01840 if (SPI_finish() != SPI_OK_FINISH)
01841 elog(ERROR, "SPI_finish failed");
01842
01843 heap_close(fk_rel, RowExclusiveLock);
01844
01845
01846
01847
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857 RI_FKey_noaction_del(fcinfo);
01858
01859 return PointerGetDatum(NULL);
01860
01861
01862
01863
01864 case FKCONSTR_MATCH_PARTIAL:
01865 ereport(ERROR,
01866 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01867 errmsg("MATCH PARTIAL not yet implemented")));
01868 return PointerGetDatum(NULL);
01869
01870 default:
01871 elog(ERROR, "unrecognized confmatchtype: %d",
01872 riinfo->confmatchtype);
01873 break;
01874 }
01875
01876
01877 return PointerGetDatum(NULL);
01878 }
01879
01880
01881
01882
01883
01884
01885
01886
01887 Datum
01888 RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
01889 {
01890 TriggerData *trigdata = (TriggerData *) fcinfo->context;
01891 const RI_ConstraintInfo *riinfo;
01892 Relation fk_rel;
01893 Relation pk_rel;
01894 HeapTuple new_row;
01895 HeapTuple old_row;
01896 RI_QueryKey qkey;
01897 SPIPlanPtr qplan;
01898
01899
01900
01901
01902 ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
01903
01904
01905
01906
01907 riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01908 trigdata->tg_relation, true);
01909
01910
01911
01912
01913
01914
01915
01916 fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01917 pk_rel = trigdata->tg_relation;
01918 new_row = trigdata->tg_newtuple;
01919 old_row = trigdata->tg_trigtuple;
01920
01921 switch (riinfo->confmatchtype)
01922 {
01923
01924
01925
01926
01927
01928
01929
01930 case FKCONSTR_MATCH_SIMPLE:
01931 case FKCONSTR_MATCH_FULL:
01932 switch (ri_NullCheck(old_row, riinfo, true))
01933 {
01934 case RI_KEYS_ALL_NULL:
01935 case RI_KEYS_SOME_NULL:
01936
01937
01938
01939
01940
01941 heap_close(fk_rel, RowExclusiveLock);
01942 return PointerGetDatum(NULL);
01943
01944 case RI_KEYS_NONE_NULL:
01945
01946
01947
01948
01949 break;
01950 }
01951
01952
01953
01954
01955 if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
01956 {
01957 heap_close(fk_rel, RowExclusiveLock);
01958 return PointerGetDatum(NULL);
01959 }
01960
01961 if (SPI_connect() != SPI_OK_CONNECT)
01962 elog(ERROR, "SPI_connect failed");
01963
01964
01965
01966
01967
01968 ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_UPD_DOUPDATE);
01969
01970 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01971 {
01972 StringInfoData querybuf;
01973 StringInfoData qualbuf;
01974 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
01975 char attname[MAX_QUOTED_NAME_LEN];
01976 char paramname[16];
01977 const char *querysep;
01978 const char *qualsep;
01979 Oid queryoids[RI_MAX_NUMKEYS];
01980 int i;
01981
01982
01983
01984
01985
01986
01987
01988
01989
01990 initStringInfo(&querybuf);
01991 initStringInfo(&qualbuf);
01992 quoteRelationName(fkrelname, fk_rel);
01993 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01994 querysep = "";
01995 qualsep = "WHERE";
01996 for (i = 0; i < riinfo->nkeys; i++)
01997 {
01998 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01999 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
02000
02001 quoteOneName(attname,
02002 RIAttName(fk_rel, riinfo->fk_attnums[i]));
02003 appendStringInfo(&querybuf,
02004 "%s %s = DEFAULT",
02005 querysep, attname);
02006 sprintf(paramname, "$%d", i + 1);
02007 ri_GenerateQual(&qualbuf, qualsep,
02008 paramname, pk_type,
02009 riinfo->pf_eq_oprs[i],
02010 attname, fk_type);
02011 querysep = ",";
02012 qualsep = "AND";
02013 queryoids[i] = pk_type;
02014 }
02015 appendStringInfoString(&querybuf, qualbuf.data);
02016
02017
02018 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
02019 &qkey, fk_rel, pk_rel, true);
02020 }
02021
02022
02023
02024
02025 ri_PerformCheck(riinfo, &qkey, qplan,
02026 fk_rel, pk_rel,
02027 old_row, NULL,
02028 true,
02029 SPI_OK_UPDATE);
02030
02031 if (SPI_finish() != SPI_OK_FINISH)
02032 elog(ERROR, "SPI_finish failed");
02033
02034 heap_close(fk_rel, RowExclusiveLock);
02035
02036
02037
02038
02039
02040
02041
02042
02043
02044
02045
02046
02047
02048 RI_FKey_noaction_upd(fcinfo);
02049
02050 return PointerGetDatum(NULL);
02051
02052
02053
02054
02055 case FKCONSTR_MATCH_PARTIAL:
02056 ereport(ERROR,
02057 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02058 errmsg("MATCH PARTIAL not yet implemented")));
02059 return PointerGetDatum(NULL);
02060
02061 default:
02062 elog(ERROR, "unrecognized confmatchtype: %d",
02063 riinfo->confmatchtype);
02064 break;
02065 }
02066
02067
02068 return PointerGetDatum(NULL);
02069 }
02070
02071
02072
02073
02074
02075
02076
02077
02078
02079
02080
02081
02082 bool
02083 RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
02084 HeapTuple old_row, HeapTuple new_row)
02085 {
02086 const RI_ConstraintInfo *riinfo;
02087
02088
02089
02090
02091 riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true);
02092
02093 switch (riinfo->confmatchtype)
02094 {
02095 case FKCONSTR_MATCH_SIMPLE:
02096 case FKCONSTR_MATCH_FULL:
02097
02098
02099
02100
02101
02102 if (ri_NullCheck(old_row, riinfo, true) != RI_KEYS_NONE_NULL)
02103 return false;
02104
02105
02106 if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
02107 return false;
02108
02109
02110 return true;
02111
02112
02113 case FKCONSTR_MATCH_PARTIAL:
02114 ereport(ERROR,
02115 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02116 errmsg("MATCH PARTIAL not yet implemented")));
02117 break;
02118
02119 default:
02120 elog(ERROR, "unrecognized confmatchtype: %d",
02121 riinfo->confmatchtype);
02122 break;
02123 }
02124
02125
02126 return false;
02127 }
02128
02129
02130
02131
02132
02133
02134
02135
02136
02137
02138
02139 bool
02140 RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
02141 HeapTuple old_row, HeapTuple new_row)
02142 {
02143 const RI_ConstraintInfo *riinfo;
02144
02145
02146
02147
02148 riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
02149
02150 switch (riinfo->confmatchtype)
02151 {
02152 case FKCONSTR_MATCH_SIMPLE:
02153
02154
02155
02156
02157 if (ri_NullCheck(new_row, riinfo, false) != RI_KEYS_NONE_NULL)
02158 return false;
02159
02160
02161
02162
02163
02164
02165
02166
02167
02168 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
02169 return true;
02170
02171
02172 if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
02173 return false;
02174
02175
02176 return true;
02177
02178 case FKCONSTR_MATCH_FULL:
02179
02180
02181
02182
02183
02184
02185
02186
02187 switch (ri_NullCheck(new_row, riinfo, false))
02188 {
02189 case RI_KEYS_ALL_NULL:
02190 return false;
02191 case RI_KEYS_SOME_NULL:
02192 return true;
02193 case RI_KEYS_NONE_NULL:
02194 break;
02195 }
02196
02197
02198
02199
02200
02201
02202
02203
02204
02205 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
02206 return true;
02207
02208
02209 if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
02210 return false;
02211
02212
02213 return true;
02214
02215
02216 case FKCONSTR_MATCH_PARTIAL:
02217 ereport(ERROR,
02218 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02219 errmsg("MATCH PARTIAL not yet implemented")));
02220 break;
02221
02222 default:
02223 elog(ERROR, "unrecognized confmatchtype: %d",
02224 riinfo->confmatchtype);
02225 break;
02226 }
02227
02228
02229 return false;
02230 }
02231
02232
02233
02234
02235
02236
02237
02238
02239
02240
02241
02242
02243
02244
02245
02246
02247
02248
02249
02250 bool
02251 RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
02252 {
02253 const RI_ConstraintInfo *riinfo;
02254 StringInfoData querybuf;
02255 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
02256 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
02257 char pkattname[MAX_QUOTED_NAME_LEN + 3];
02258 char fkattname[MAX_QUOTED_NAME_LEN + 3];
02259 RangeTblEntry *pkrte;
02260 RangeTblEntry *fkrte;
02261 const char *sep;
02262 int i;
02263 int save_nestlevel;
02264 char workmembuf[32];
02265 int spi_result;
02266 SPIPlanPtr qplan;
02267
02268
02269 riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
02270
02271
02272
02273
02274
02275
02276
02277
02278 pkrte = makeNode(RangeTblEntry);
02279 pkrte->rtekind = RTE_RELATION;
02280 pkrte->relid = RelationGetRelid(pk_rel);
02281 pkrte->relkind = pk_rel->rd_rel->relkind;
02282 pkrte->requiredPerms = ACL_SELECT;
02283
02284 fkrte = makeNode(RangeTblEntry);
02285 fkrte->rtekind = RTE_RELATION;
02286 fkrte->relid = RelationGetRelid(fk_rel);
02287 fkrte->relkind = fk_rel->rd_rel->relkind;
02288 fkrte->requiredPerms = ACL_SELECT;
02289
02290 for (i = 0; i < riinfo->nkeys; i++)
02291 {
02292 int attno;
02293
02294 attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
02295 pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno);
02296
02297 attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
02298 fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno);
02299 }
02300
02301 if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false))
02302 return false;
02303
02304
02305
02306
02307
02308
02309
02310
02311
02312
02313
02314
02315
02316
02317
02318
02319 initStringInfo(&querybuf);
02320 appendStringInfo(&querybuf, "SELECT ");
02321 sep = "";
02322 for (i = 0; i < riinfo->nkeys; i++)
02323 {
02324 quoteOneName(fkattname,
02325 RIAttName(fk_rel, riinfo->fk_attnums[i]));
02326 appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
02327 sep = ", ";
02328 }
02329
02330 quoteRelationName(pkrelname, pk_rel);
02331 quoteRelationName(fkrelname, fk_rel);
02332 appendStringInfo(&querybuf,
02333 " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
02334 fkrelname, pkrelname);
02335
02336 strcpy(pkattname, "pk.");
02337 strcpy(fkattname, "fk.");
02338 sep = "(";
02339 for (i = 0; i < riinfo->nkeys; i++)
02340 {
02341 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
02342 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
02343 Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
02344 Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
02345
02346 quoteOneName(pkattname + 3,
02347 RIAttName(pk_rel, riinfo->pk_attnums[i]));
02348 quoteOneName(fkattname + 3,
02349 RIAttName(fk_rel, riinfo->fk_attnums[i]));
02350 ri_GenerateQual(&querybuf, sep,
02351 pkattname, pk_type,
02352 riinfo->pf_eq_oprs[i],
02353 fkattname, fk_type);
02354 if (pk_coll != fk_coll)
02355 ri_GenerateQualCollation(&querybuf, pk_coll);
02356 sep = "AND";
02357 }
02358
02359
02360
02361
02362
02363 quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
02364 appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
02365
02366 sep = "";
02367 for (i = 0; i < riinfo->nkeys; i++)
02368 {
02369 quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
02370 appendStringInfo(&querybuf,
02371 "%sfk.%s IS NOT NULL",
02372 sep, fkattname);
02373 switch (riinfo->confmatchtype)
02374 {
02375 case FKCONSTR_MATCH_SIMPLE:
02376 sep = " AND ";
02377 break;
02378 case FKCONSTR_MATCH_FULL:
02379 sep = " OR ";
02380 break;
02381 case FKCONSTR_MATCH_PARTIAL:
02382 ereport(ERROR,
02383 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02384 errmsg("MATCH PARTIAL not yet implemented")));
02385 break;
02386 default:
02387 elog(ERROR, "unrecognized confmatchtype: %d",
02388 riinfo->confmatchtype);
02389 break;
02390 }
02391 }
02392 appendStringInfo(&querybuf, ")");
02393
02394
02395
02396
02397
02398
02399
02400
02401
02402
02403
02404
02405
02406 save_nestlevel = NewGUCNestLevel();
02407
02408 snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
02409 (void) set_config_option("work_mem", workmembuf,
02410 PGC_USERSET, PGC_S_SESSION,
02411 GUC_ACTION_SAVE, true, 0);
02412
02413 if (SPI_connect() != SPI_OK_CONNECT)
02414 elog(ERROR, "SPI_connect failed");
02415
02416
02417
02418
02419
02420 qplan = SPI_prepare(querybuf.data, 0, NULL);
02421
02422 if (qplan == NULL)
02423 elog(ERROR, "SPI_prepare returned %d for %s",
02424 SPI_result, querybuf.data);
02425
02426
02427
02428
02429
02430
02431
02432
02433 spi_result = SPI_execute_snapshot(qplan,
02434 NULL, NULL,
02435 GetLatestSnapshot(),
02436 InvalidSnapshot,
02437 true, false, 1);
02438
02439
02440 if (spi_result != SPI_OK_SELECT)
02441 elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
02442
02443
02444 if (SPI_processed > 0)
02445 {
02446 HeapTuple tuple = SPI_tuptable->vals[0];
02447 TupleDesc tupdesc = SPI_tuptable->tupdesc;
02448 RI_ConstraintInfo fake_riinfo;
02449
02450
02451
02452
02453
02454
02455
02456
02457
02458
02459 memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
02460 for (i = 0; i < fake_riinfo.nkeys; i++)
02461 fake_riinfo.fk_attnums[i] = i + 1;
02462
02463
02464
02465
02466
02467
02468 if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
02469 ri_NullCheck(tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
02470 ereport(ERROR,
02471 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
02472 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
02473 RelationGetRelationName(fk_rel),
02474 NameStr(fake_riinfo.conname)),
02475 errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
02476 errtableconstraint(fk_rel,
02477 NameStr(fake_riinfo.conname))));
02478
02479
02480
02481
02482
02483
02484 ri_ReportViolation(&fake_riinfo,
02485 pk_rel, fk_rel,
02486 tuple, tupdesc,
02487 RI_PLAN_CHECK_LOOKUPPK, false);
02488 }
02489
02490 if (SPI_finish() != SPI_OK_FINISH)
02491 elog(ERROR, "SPI_finish failed");
02492
02493
02494
02495
02496 AtEOXact_GUC(true, save_nestlevel);
02497
02498 return true;
02499 }
02500
02501
02502
02503
02504
02505
02506
02507
02508
02509
02510
02511
02512
02513 static void
02514 quoteOneName(char *buffer, const char *name)
02515 {
02516
02517 *buffer++ = '"';
02518 while (*name)
02519 {
02520 if (*name == '"')
02521 *buffer++ = '"';
02522 *buffer++ = *name++;
02523 }
02524 *buffer++ = '"';
02525 *buffer = '\0';
02526 }
02527
02528
02529
02530
02531
02532
02533 static void
02534 quoteRelationName(char *buffer, Relation rel)
02535 {
02536 quoteOneName(buffer, get_namespace_name(RelationGetNamespace(rel)));
02537 buffer += strlen(buffer);
02538 *buffer++ = '.';
02539 quoteOneName(buffer, RelationGetRelationName(rel));
02540 }
02541
02542
02543
02544
02545
02546
02547
02548
02549
02550
02551
02552
02553 static void
02554 ri_GenerateQual(StringInfo buf,
02555 const char *sep,
02556 const char *leftop, Oid leftoptype,
02557 Oid opoid,
02558 const char *rightop, Oid rightoptype)
02559 {
02560 HeapTuple opertup;
02561 Form_pg_operator operform;
02562 char *oprname;
02563 char *nspname;
02564
02565 opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
02566 if (!HeapTupleIsValid(opertup))
02567 elog(ERROR, "cache lookup failed for operator %u", opoid);
02568 operform = (Form_pg_operator) GETSTRUCT(opertup);
02569 Assert(operform->oprkind == 'b');
02570 oprname = NameStr(operform->oprname);
02571
02572 nspname = get_namespace_name(operform->oprnamespace);
02573
02574 appendStringInfo(buf, " %s %s", sep, leftop);
02575 if (leftoptype != operform->oprleft)
02576 ri_add_cast_to(buf, operform->oprleft);
02577 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
02578 appendStringInfoString(buf, oprname);
02579 appendStringInfo(buf, ") %s", rightop);
02580 if (rightoptype != operform->oprright)
02581 ri_add_cast_to(buf, operform->oprright);
02582
02583 ReleaseSysCache(opertup);
02584 }
02585
02586
02587
02588
02589
02590
02591
02592
02593
02594 static void
02595 ri_add_cast_to(StringInfo buf, Oid typid)
02596 {
02597 HeapTuple typetup;
02598 Form_pg_type typform;
02599 char *typname;
02600 char *nspname;
02601
02602 typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
02603 if (!HeapTupleIsValid(typetup))
02604 elog(ERROR, "cache lookup failed for type %u", typid);
02605 typform = (Form_pg_type) GETSTRUCT(typetup);
02606
02607 typname = NameStr(typform->typname);
02608 nspname = get_namespace_name(typform->typnamespace);
02609
02610 appendStringInfo(buf, "::%s.%s",
02611 quote_identifier(nspname), quote_identifier(typname));
02612
02613 ReleaseSysCache(typetup);
02614 }
02615
02616
02617
02618
02619
02620
02621
02622
02623
02624
02625
02626
02627
02628
02629
02630
02631
02632
02633 static void
02634 ri_GenerateQualCollation(StringInfo buf, Oid collation)
02635 {
02636 HeapTuple tp;
02637 Form_pg_collation colltup;
02638 char *collname;
02639 char onename[MAX_QUOTED_NAME_LEN];
02640
02641
02642 if (!OidIsValid(collation))
02643 return;
02644
02645 tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
02646 if (!HeapTupleIsValid(tp))
02647 elog(ERROR, "cache lookup failed for collation %u", collation);
02648 colltup = (Form_pg_collation) GETSTRUCT(tp);
02649 collname = NameStr(colltup->collname);
02650
02651
02652
02653
02654
02655 quoteOneName(onename, get_namespace_name(colltup->collnamespace));
02656 appendStringInfo(buf, " COLLATE %s", onename);
02657 quoteOneName(onename, collname);
02658 appendStringInfo(buf, ".%s", onename);
02659
02660 ReleaseSysCache(tp);
02661 }
02662
02663
02664
02665
02666
02667
02668
02669
02670
02671
02672
02673
02674 static void
02675 ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
02676 int32 constr_queryno)
02677 {
02678
02679
02680
02681
02682 key->constr_id = riinfo->constraint_id;
02683 key->constr_queryno = constr_queryno;
02684 }
02685
02686
02687
02688
02689 static void
02690 ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
02691 {
02692 TriggerData *trigdata = (TriggerData *) fcinfo->context;
02693
02694 if (!CALLED_AS_TRIGGER(fcinfo))
02695 ereport(ERROR,
02696 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02697 errmsg("function \"%s\" was not called by trigger manager", funcname)));
02698
02699
02700
02701
02702 if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
02703 !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
02704 ereport(ERROR,
02705 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02706 errmsg("function \"%s\" must be fired AFTER ROW", funcname)));
02707
02708 switch (tgkind)
02709 {
02710 case RI_TRIGTYPE_INSERT:
02711 if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
02712 ereport(ERROR,
02713 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02714 errmsg("function \"%s\" must be fired for INSERT", funcname)));
02715 break;
02716 case RI_TRIGTYPE_UPDATE:
02717 if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
02718 ereport(ERROR,
02719 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02720 errmsg("function \"%s\" must be fired for UPDATE", funcname)));
02721 break;
02722 case RI_TRIGTYPE_DELETE:
02723 if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
02724 ereport(ERROR,
02725 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02726 errmsg("function \"%s\" must be fired for DELETE", funcname)));
02727 break;
02728 }
02729 }
02730
02731
02732
02733
02734
02735 static const RI_ConstraintInfo *
02736 ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk)
02737 {
02738 Oid constraintOid = trigger->tgconstraint;
02739 const RI_ConstraintInfo *riinfo;
02740
02741
02742
02743
02744
02745
02746 if (!OidIsValid(constraintOid))
02747 ereport(ERROR,
02748 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
02749 errmsg("no pg_constraint entry for trigger \"%s\" on table \"%s\"",
02750 trigger->tgname, RelationGetRelationName(trig_rel)),
02751 errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
02752
02753
02754 riinfo = ri_LoadConstraintInfo(constraintOid);
02755
02756
02757 if (rel_is_pk)
02758 {
02759 if (riinfo->fk_relid != trigger->tgconstrrelid ||
02760 riinfo->pk_relid != RelationGetRelid(trig_rel))
02761 elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
02762 trigger->tgname, RelationGetRelationName(trig_rel));
02763 }
02764 else
02765 {
02766 if (riinfo->fk_relid != RelationGetRelid(trig_rel) ||
02767 riinfo->pk_relid != trigger->tgconstrrelid)
02768 elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
02769 trigger->tgname, RelationGetRelationName(trig_rel));
02770 }
02771
02772 return riinfo;
02773 }
02774
02775
02776
02777
02778 static const RI_ConstraintInfo *
02779 ri_LoadConstraintInfo(Oid constraintOid)
02780 {
02781 RI_ConstraintInfo *riinfo;
02782 bool found;
02783 HeapTuple tup;
02784 Form_pg_constraint conForm;
02785 Datum adatum;
02786 bool isNull;
02787 ArrayType *arr;
02788 int numkeys;
02789
02790
02791
02792
02793 if (!ri_constraint_cache)
02794 ri_InitHashTables();
02795
02796
02797
02798
02799 riinfo = (RI_ConstraintInfo *) hash_search(ri_constraint_cache,
02800 (void *) &constraintOid,
02801 HASH_ENTER, &found);
02802 if (!found)
02803 riinfo->valid = false;
02804 else if (riinfo->valid)
02805 return riinfo;
02806
02807
02808
02809
02810 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
02811 if (!HeapTupleIsValid(tup))
02812 elog(ERROR, "cache lookup failed for constraint %u", constraintOid);
02813 conForm = (Form_pg_constraint) GETSTRUCT(tup);
02814
02815 if (conForm->contype != CONSTRAINT_FOREIGN)
02816 elog(ERROR, "constraint %u is not a foreign key constraint",
02817 constraintOid);
02818
02819
02820 Assert(riinfo->constraint_id == constraintOid);
02821 riinfo->oidHashValue = GetSysCacheHashValue1(CONSTROID,
02822 ObjectIdGetDatum(constraintOid));
02823 memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
02824 riinfo->pk_relid = conForm->confrelid;
02825 riinfo->fk_relid = conForm->conrelid;
02826 riinfo->confupdtype = conForm->confupdtype;
02827 riinfo->confdeltype = conForm->confdeltype;
02828 riinfo->confmatchtype = conForm->confmatchtype;
02829
02830
02831
02832
02833
02834
02835 adatum = SysCacheGetAttr(CONSTROID, tup,
02836 Anum_pg_constraint_conkey, &isNull);
02837 if (isNull)
02838 elog(ERROR, "null conkey for constraint %u", constraintOid);
02839 arr = DatumGetArrayTypeP(adatum);
02840 if (ARR_NDIM(arr) != 1 ||
02841 ARR_HASNULL(arr) ||
02842 ARR_ELEMTYPE(arr) != INT2OID)
02843 elog(ERROR, "conkey is not a 1-D smallint array");
02844 numkeys = ARR_DIMS(arr)[0];
02845 if (numkeys <= 0 || numkeys > RI_MAX_NUMKEYS)
02846 elog(ERROR, "foreign key constraint cannot have %d columns", numkeys);
02847 riinfo->nkeys = numkeys;
02848 memcpy(riinfo->fk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
02849 if ((Pointer) arr != DatumGetPointer(adatum))
02850 pfree(arr);
02851
02852 adatum = SysCacheGetAttr(CONSTROID, tup,
02853 Anum_pg_constraint_confkey, &isNull);
02854 if (isNull)
02855 elog(ERROR, "null confkey for constraint %u", constraintOid);
02856 arr = DatumGetArrayTypeP(adatum);
02857 if (ARR_NDIM(arr) != 1 ||
02858 ARR_DIMS(arr)[0] != numkeys ||
02859 ARR_HASNULL(arr) ||
02860 ARR_ELEMTYPE(arr) != INT2OID)
02861 elog(ERROR, "confkey is not a 1-D smallint array");
02862 memcpy(riinfo->pk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
02863 if ((Pointer) arr != DatumGetPointer(adatum))
02864 pfree(arr);
02865
02866 adatum = SysCacheGetAttr(CONSTROID, tup,
02867 Anum_pg_constraint_conpfeqop, &isNull);
02868 if (isNull)
02869 elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
02870 arr = DatumGetArrayTypeP(adatum);
02871
02872 if (ARR_NDIM(arr) != 1 ||
02873 ARR_DIMS(arr)[0] != numkeys ||
02874 ARR_HASNULL(arr) ||
02875 ARR_ELEMTYPE(arr) != OIDOID)
02876 elog(ERROR, "conpfeqop is not a 1-D Oid array");
02877 memcpy(riinfo->pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
02878 if ((Pointer) arr != DatumGetPointer(adatum))
02879 pfree(arr);
02880
02881 adatum = SysCacheGetAttr(CONSTROID, tup,
02882 Anum_pg_constraint_conppeqop, &isNull);
02883 if (isNull)
02884 elog(ERROR, "null conppeqop for constraint %u", constraintOid);
02885 arr = DatumGetArrayTypeP(adatum);
02886 if (ARR_NDIM(arr) != 1 ||
02887 ARR_DIMS(arr)[0] != numkeys ||
02888 ARR_HASNULL(arr) ||
02889 ARR_ELEMTYPE(arr) != OIDOID)
02890 elog(ERROR, "conppeqop is not a 1-D Oid array");
02891 memcpy(riinfo->pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
02892 if ((Pointer) arr != DatumGetPointer(adatum))
02893 pfree(arr);
02894
02895 adatum = SysCacheGetAttr(CONSTROID, tup,
02896 Anum_pg_constraint_conffeqop, &isNull);
02897 if (isNull)
02898 elog(ERROR, "null conffeqop for constraint %u", constraintOid);
02899 arr = DatumGetArrayTypeP(adatum);
02900 if (ARR_NDIM(arr) != 1 ||
02901 ARR_DIMS(arr)[0] != numkeys ||
02902 ARR_HASNULL(arr) ||
02903 ARR_ELEMTYPE(arr) != OIDOID)
02904 elog(ERROR, "conffeqop is not a 1-D Oid array");
02905 memcpy(riinfo->ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
02906 if ((Pointer) arr != DatumGetPointer(adatum))
02907 pfree(arr);
02908
02909 ReleaseSysCache(tup);
02910
02911 riinfo->valid = true;
02912
02913 return riinfo;
02914 }
02915
02916
02917
02918
02919
02920
02921
02922
02923
02924 static void
02925 InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
02926 {
02927 HASH_SEQ_STATUS status;
02928 RI_ConstraintInfo *hentry;
02929
02930 Assert(ri_constraint_cache != NULL);
02931
02932 hash_seq_init(&status, ri_constraint_cache);
02933 while ((hentry = (RI_ConstraintInfo *) hash_seq_search(&status)) != NULL)
02934 {
02935 if (hashvalue == 0 || hentry->oidHashValue == hashvalue)
02936 hentry->valid = false;
02937 }
02938 }
02939
02940
02941
02942
02943
02944
02945
02946
02947 static SPIPlanPtr
02948 ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
02949 RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
02950 bool cache_plan)
02951 {
02952 SPIPlanPtr qplan;
02953 Relation query_rel;
02954 Oid save_userid;
02955 int save_sec_context;
02956
02957
02958
02959
02960
02961 if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
02962 query_rel = pk_rel;
02963 else
02964 query_rel = fk_rel;
02965
02966
02967 GetUserIdAndSecContext(&save_userid, &save_sec_context);
02968 SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
02969 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
02970
02971
02972 qplan = SPI_prepare(querystr, nargs, argtypes);
02973
02974 if (qplan == NULL)
02975 elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
02976
02977
02978 SetUserIdAndSecContext(save_userid, save_sec_context);
02979
02980
02981 if (cache_plan)
02982 {
02983 SPI_keepplan(qplan);
02984 ri_HashPreparedPlan(qkey, qplan);
02985 }
02986
02987 return qplan;
02988 }
02989
02990
02991
02992
02993 static bool
02994 ri_PerformCheck(const RI_ConstraintInfo *riinfo,
02995 RI_QueryKey *qkey, SPIPlanPtr qplan,
02996 Relation fk_rel, Relation pk_rel,
02997 HeapTuple old_tuple, HeapTuple new_tuple,
02998 bool detectNewRows, int expect_OK)
02999 {
03000 Relation query_rel,
03001 source_rel;
03002 bool source_is_pk;
03003 Snapshot test_snapshot;
03004 Snapshot crosscheck_snapshot;
03005 int limit;
03006 int spi_result;
03007 Oid save_userid;
03008 int save_sec_context;
03009 Datum vals[RI_MAX_NUMKEYS * 2];
03010 char nulls[RI_MAX_NUMKEYS * 2];
03011
03012
03013
03014
03015
03016 if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
03017 query_rel = pk_rel;
03018 else
03019 query_rel = fk_rel;
03020
03021
03022
03023
03024
03025
03026
03027
03028 if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)
03029 {
03030 source_rel = fk_rel;
03031 source_is_pk = false;
03032 }
03033 else
03034 {
03035 source_rel = pk_rel;
03036 source_is_pk = true;
03037 }
03038
03039
03040 if (new_tuple)
03041 {
03042 ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
03043 vals, nulls);
03044 if (old_tuple)
03045 ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
03046 vals + riinfo->nkeys, nulls + riinfo->nkeys);
03047 }
03048 else
03049 {
03050 ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
03051 vals, nulls);
03052 }
03053
03054
03055
03056
03057
03058
03059
03060
03061
03062
03063
03064
03065 if (IsolationUsesXactSnapshot() && detectNewRows)
03066 {
03067 CommandCounterIncrement();
03068 test_snapshot = GetLatestSnapshot();
03069 crosscheck_snapshot = GetTransactionSnapshot();
03070 }
03071 else
03072 {
03073
03074 test_snapshot = InvalidSnapshot;
03075 crosscheck_snapshot = InvalidSnapshot;
03076 }
03077
03078
03079
03080
03081
03082
03083
03084 limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
03085
03086
03087 GetUserIdAndSecContext(&save_userid, &save_sec_context);
03088 SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
03089 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
03090
03091
03092 spi_result = SPI_execute_snapshot(qplan,
03093 vals, nulls,
03094 test_snapshot, crosscheck_snapshot,
03095 false, false, limit);
03096
03097
03098 SetUserIdAndSecContext(save_userid, save_sec_context);
03099
03100
03101 if (spi_result < 0)
03102 elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
03103
03104 if (expect_OK >= 0 && spi_result != expect_OK)
03105 ri_ReportViolation(riinfo,
03106 pk_rel, fk_rel,
03107 new_tuple ? new_tuple : old_tuple,
03108 NULL,
03109 qkey->constr_queryno, true);
03110
03111
03112 if (qkey->constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK &&
03113 expect_OK == SPI_OK_SELECT &&
03114 (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
03115 ri_ReportViolation(riinfo,
03116 pk_rel, fk_rel,
03117 new_tuple ? new_tuple : old_tuple,
03118 NULL,
03119 qkey->constr_queryno, false);
03120
03121 return SPI_processed != 0;
03122 }
03123
03124
03125
03126
03127 static void
03128 ri_ExtractValues(Relation rel, HeapTuple tup,
03129 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
03130 Datum *vals, char *nulls)
03131 {
03132 TupleDesc tupdesc = rel->rd_att;
03133 const int16 *attnums;
03134 int i;
03135 bool isnull;
03136
03137 if (rel_is_pk)
03138 attnums = riinfo->pk_attnums;
03139 else
03140 attnums = riinfo->fk_attnums;
03141
03142 for (i = 0; i < riinfo->nkeys; i++)
03143 {
03144 vals[i] = heap_getattr(tup, attnums[i], tupdesc,
03145 &isnull);
03146 nulls[i] = isnull ? 'n' : ' ';
03147 }
03148 }
03149
03150
03151
03152
03153
03154
03155
03156
03157
03158
03159 static void
03160 ri_ReportViolation(const RI_ConstraintInfo *riinfo,
03161 Relation pk_rel, Relation fk_rel,
03162 HeapTuple violator, TupleDesc tupdesc,
03163 int queryno, bool spi_err)
03164 {
03165 StringInfoData key_names;
03166 StringInfoData key_values;
03167 bool onfk;
03168 const int16 *attnums;
03169 int idx;
03170
03171 if (spi_err)
03172 ereport(ERROR,
03173 (errcode(ERRCODE_INTERNAL_ERROR),
03174 errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
03175 RelationGetRelationName(pk_rel),
03176 NameStr(riinfo->conname),
03177 RelationGetRelationName(fk_rel)),
03178 errhint("This is most likely due to a rule having rewritten the query.")));
03179
03180
03181
03182
03183
03184 onfk = (queryno == RI_PLAN_CHECK_LOOKUPPK);
03185 if (onfk)
03186 {
03187 attnums = riinfo->fk_attnums;
03188 if (tupdesc == NULL)
03189 tupdesc = fk_rel->rd_att;
03190 }
03191 else
03192 {
03193 attnums = riinfo->pk_attnums;
03194 if (tupdesc == NULL)
03195 tupdesc = pk_rel->rd_att;
03196 }
03197
03198
03199 initStringInfo(&key_names);
03200 initStringInfo(&key_values);
03201 for (idx = 0; idx < riinfo->nkeys; idx++)
03202 {
03203 int fnum = attnums[idx];
03204 char *name,
03205 *val;
03206
03207 name = SPI_fname(tupdesc, fnum);
03208 val = SPI_getvalue(violator, tupdesc, fnum);
03209 if (!val)
03210 val = "null";
03211
03212 if (idx > 0)
03213 {
03214 appendStringInfoString(&key_names, ", ");
03215 appendStringInfoString(&key_values, ", ");
03216 }
03217 appendStringInfoString(&key_names, name);
03218 appendStringInfoString(&key_values, val);
03219 }
03220
03221 if (onfk)
03222 ereport(ERROR,
03223 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
03224 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
03225 RelationGetRelationName(fk_rel),
03226 NameStr(riinfo->conname)),
03227 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
03228 key_names.data, key_values.data,
03229 RelationGetRelationName(pk_rel)),
03230 errtableconstraint(fk_rel, NameStr(riinfo->conname))));
03231 else
03232 ereport(ERROR,
03233 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
03234 errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
03235 RelationGetRelationName(pk_rel),
03236 NameStr(riinfo->conname),
03237 RelationGetRelationName(fk_rel)),
03238 errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
03239 key_names.data, key_values.data,
03240 RelationGetRelationName(fk_rel)),
03241 errtableconstraint(fk_rel, NameStr(riinfo->conname))));
03242 }
03243
03244
03245
03246
03247
03248
03249
03250
03251
03252
03253 static int
03254 ri_NullCheck(HeapTuple tup,
03255 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
03256 {
03257 const int16 *attnums;
03258 int i;
03259 bool allnull = true;
03260 bool nonenull = true;
03261
03262 if (rel_is_pk)
03263 attnums = riinfo->pk_attnums;
03264 else
03265 attnums = riinfo->fk_attnums;
03266
03267 for (i = 0; i < riinfo->nkeys; i++)
03268 {
03269 if (heap_attisnull(tup, attnums[i]))
03270 nonenull = false;
03271 else
03272 allnull = false;
03273 }
03274
03275 if (allnull)
03276 return RI_KEYS_ALL_NULL;
03277
03278 if (nonenull)
03279 return RI_KEYS_NONE_NULL;
03280
03281 return RI_KEYS_SOME_NULL;
03282 }
03283
03284
03285
03286
03287
03288
03289
03290
03291 static void
03292 ri_InitHashTables(void)
03293 {
03294 HASHCTL ctl;
03295
03296 memset(&ctl, 0, sizeof(ctl));
03297 ctl.keysize = sizeof(Oid);
03298 ctl.entrysize = sizeof(RI_ConstraintInfo);
03299 ctl.hash = oid_hash;
03300 ri_constraint_cache = hash_create("RI constraint cache",
03301 RI_INIT_CONSTRAINTHASHSIZE,
03302 &ctl, HASH_ELEM | HASH_FUNCTION);
03303
03304
03305 CacheRegisterSyscacheCallback(CONSTROID,
03306 InvalidateConstraintCacheCallBack,
03307 (Datum) 0);
03308
03309 memset(&ctl, 0, sizeof(ctl));
03310 ctl.keysize = sizeof(RI_QueryKey);
03311 ctl.entrysize = sizeof(RI_QueryHashEntry);
03312 ctl.hash = tag_hash;
03313 ri_query_cache = hash_create("RI query cache",
03314 RI_INIT_QUERYHASHSIZE,
03315 &ctl, HASH_ELEM | HASH_FUNCTION);
03316
03317 memset(&ctl, 0, sizeof(ctl));
03318 ctl.keysize = sizeof(RI_CompareKey);
03319 ctl.entrysize = sizeof(RI_CompareHashEntry);
03320 ctl.hash = tag_hash;
03321 ri_compare_cache = hash_create("RI compare cache",
03322 RI_INIT_QUERYHASHSIZE,
03323 &ctl, HASH_ELEM | HASH_FUNCTION);
03324 }
03325
03326
03327
03328
03329
03330
03331
03332
03333
03334 static SPIPlanPtr
03335 ri_FetchPreparedPlan(RI_QueryKey *key)
03336 {
03337 RI_QueryHashEntry *entry;
03338 SPIPlanPtr plan;
03339
03340
03341
03342
03343 if (!ri_query_cache)
03344 ri_InitHashTables();
03345
03346
03347
03348
03349 entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
03350 (void *) key,
03351 HASH_FIND, NULL);
03352 if (entry == NULL)
03353 return NULL;
03354
03355
03356
03357
03358
03359
03360
03361
03362
03363
03364
03365 plan = entry->plan;
03366 if (plan && SPI_plan_is_valid(plan))
03367 return plan;
03368
03369
03370
03371
03372
03373 entry->plan = NULL;
03374 if (plan)
03375 SPI_freeplan(plan);
03376
03377 return NULL;
03378 }
03379
03380
03381
03382
03383
03384
03385
03386
03387 static void
03388 ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
03389 {
03390 RI_QueryHashEntry *entry;
03391 bool found;
03392
03393
03394
03395
03396 if (!ri_query_cache)
03397 ri_InitHashTables();
03398
03399
03400
03401
03402
03403 entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
03404 (void *) key,
03405 HASH_ENTER, &found);
03406 Assert(!found || entry->plan == NULL);
03407 entry->plan = plan;
03408 }
03409
03410
03411
03412
03413
03414
03415
03416
03417
03418
03419
03420
03421
03422 static bool
03423 ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
03424 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
03425 {
03426 TupleDesc tupdesc = RelationGetDescr(rel);
03427 const int16 *attnums;
03428 const Oid *eq_oprs;
03429 int i;
03430
03431 if (rel_is_pk)
03432 {
03433 attnums = riinfo->pk_attnums;
03434 eq_oprs = riinfo->pp_eq_oprs;
03435 }
03436 else
03437 {
03438 attnums = riinfo->fk_attnums;
03439 eq_oprs = riinfo->ff_eq_oprs;
03440 }
03441
03442 for (i = 0; i < riinfo->nkeys; i++)
03443 {
03444 Datum oldvalue;
03445 Datum newvalue;
03446 bool isnull;
03447
03448
03449
03450
03451 oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
03452 if (isnull)
03453 return false;
03454
03455
03456
03457
03458 newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
03459 if (isnull)
03460 return false;
03461
03462
03463
03464
03465 if (!ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
03466 oldvalue, newvalue))
03467 return false;
03468 }
03469
03470 return true;
03471 }
03472
03473
03474
03475
03476
03477
03478
03479
03480
03481
03482 static bool
03483 ri_AttributesEqual(Oid eq_opr, Oid typeid,
03484 Datum oldvalue, Datum newvalue)
03485 {
03486 RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
03487
03488
03489 if (OidIsValid(entry->cast_func_finfo.fn_oid))
03490 {
03491 oldvalue = FunctionCall3(&entry->cast_func_finfo,
03492 oldvalue,
03493 Int32GetDatum(-1),
03494 BoolGetDatum(false));
03495 newvalue = FunctionCall3(&entry->cast_func_finfo,
03496 newvalue,
03497 Int32GetDatum(-1),
03498 BoolGetDatum(false));
03499 }
03500
03501
03502
03503
03504
03505 return DatumGetBool(FunctionCall2(&entry->eq_opr_finfo,
03506 oldvalue, newvalue));
03507 }
03508
03509
03510
03511
03512
03513
03514
03515
03516 static RI_CompareHashEntry *
03517 ri_HashCompareOp(Oid eq_opr, Oid typeid)
03518 {
03519 RI_CompareKey key;
03520 RI_CompareHashEntry *entry;
03521 bool found;
03522
03523
03524
03525
03526 if (!ri_compare_cache)
03527 ri_InitHashTables();
03528
03529
03530
03531
03532
03533 key.eq_opr = eq_opr;
03534 key.typeid = typeid;
03535 entry = (RI_CompareHashEntry *) hash_search(ri_compare_cache,
03536 (void *) &key,
03537 HASH_ENTER, &found);
03538 if (!found)
03539 entry->valid = false;
03540
03541
03542
03543
03544
03545
03546 if (!entry->valid)
03547 {
03548 Oid lefttype,
03549 righttype,
03550 castfunc;
03551 CoercionPathType pathtype;
03552
03553
03554 fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
03555 TopMemoryContext);
03556
03557
03558
03559
03560
03561
03562
03563
03564
03565
03566
03567
03568
03569 op_input_types(eq_opr, &lefttype, &righttype);
03570 Assert(lefttype == righttype);
03571 if (typeid == lefttype)
03572 castfunc = InvalidOid;
03573 else
03574 {
03575 pathtype = find_coercion_pathway(lefttype, typeid,
03576 COERCION_IMPLICIT,
03577 &castfunc);
03578 if (pathtype != COERCION_PATH_FUNC &&
03579 pathtype != COERCION_PATH_RELABELTYPE)
03580 {
03581
03582
03583
03584
03585
03586
03587 if (!IsPolymorphicType(lefttype) &&
03588 !IsBinaryCoercible(typeid, lefttype))
03589 elog(ERROR, "no conversion function from %s to %s",
03590 format_type_be(typeid),
03591 format_type_be(lefttype));
03592 }
03593 }
03594 if (OidIsValid(castfunc))
03595 fmgr_info_cxt(castfunc, &entry->cast_func_finfo,
03596 TopMemoryContext);
03597 else
03598 entry->cast_func_finfo.fn_oid = InvalidOid;
03599 entry->valid = true;
03600 }
03601
03602 return entry;
03603 }
03604
03605
03606
03607
03608
03609
03610 int
03611 RI_FKey_trigger_type(Oid tgfoid)
03612 {
03613 switch (tgfoid)
03614 {
03615 case F_RI_FKEY_CASCADE_DEL:
03616 case F_RI_FKEY_CASCADE_UPD:
03617 case F_RI_FKEY_RESTRICT_DEL:
03618 case F_RI_FKEY_RESTRICT_UPD:
03619 case F_RI_FKEY_SETNULL_DEL:
03620 case F_RI_FKEY_SETNULL_UPD:
03621 case F_RI_FKEY_SETDEFAULT_DEL:
03622 case F_RI_FKEY_SETDEFAULT_UPD:
03623 case F_RI_FKEY_NOACTION_DEL:
03624 case F_RI_FKEY_NOACTION_UPD:
03625 return RI_TRIGGER_PK;
03626
03627 case F_RI_FKEY_CHECK_INS:
03628 case F_RI_FKEY_CHECK_UPD:
03629 return RI_TRIGGER_FK;
03630 }
03631
03632 return RI_TRIGGER_NONE;
03633 }