17
17
*
18
18
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
19
19
*
20
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.39 2002/06/21 02:59:38 momjian Exp $
20
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.40 2002/07/30 16:33:21 momjian Exp $
21
21
*
22
22
* ----------
23
23
*/
@@ -130,19 +130,24 @@ static void ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id,
130
130
int32 constr_queryno ,
131
131
Relation fk_rel , Relation pk_rel ,
132
132
int argc , char * * argv );
133
+ static void ri_BuildQueryKeyPkCheck (RI_QueryKey * key , Oid constr_id ,
134
+ int32 constr_queryno ,
135
+ Relation pk_rel ,
136
+ int argc , char * * argv );
133
137
static bool ri_KeysEqual (Relation rel , HeapTuple oldtup , HeapTuple newtup ,
134
138
RI_QueryKey * key , int pairidx );
135
139
static bool ri_AllKeysUnequal (Relation rel , HeapTuple oldtup , HeapTuple newtup ,
136
140
RI_QueryKey * key , int pairidx );
137
141
static bool ri_OneKeyEqual (Relation rel , int column , HeapTuple oldtup ,
138
142
HeapTuple newtup , RI_QueryKey * key , int pairidx );
139
143
static bool ri_AttributesEqual (Oid typeid , Datum oldvalue , Datum newvalue );
144
+ static bool ri_Check_Pk_Match (Relation pk_rel , HeapTuple old_row ,
145
+ Oid tgoid , int match_type , int tgnargs , char * * tgargs );
140
146
141
147
static void ri_InitHashTables (void );
142
148
static void * ri_FetchPreparedPlan (RI_QueryKey * key );
143
149
static void ri_HashPreparedPlan (RI_QueryKey * key , void * plan );
144
150
145
-
146
151
/* ----------
147
152
* RI_FKey_check -
148
153
*
@@ -385,6 +390,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
385
390
if (SPI_connect () != SPI_OK_CONNECT )
386
391
elog (WARNING , "SPI_connect() failed in RI_FKey_check()" );
387
392
393
+
388
394
/*
389
395
* Fetch or prepare a saved plan for the real check
390
396
*/
@@ -512,6 +518,161 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
512
518
}
513
519
514
520
521
+ /* ----------
522
+ * ri_Check_Pk_Match
523
+ *
524
+ * Check for matching value of old pk row in current state for
525
+ * noaction triggers. Returns false if no row was found and a fk row
526
+ * could potentially be referencing this row, true otherwise.
527
+ * ----------
528
+ */
529
+ static bool
530
+ ri_Check_Pk_Match (Relation pk_rel , HeapTuple old_row , Oid tgoid , int match_type , int tgnargs , char * * tgargs ) {
531
+ void * qplan ;
532
+ RI_QueryKey qkey ;
533
+ bool isnull ;
534
+ Datum check_values [RI_MAX_NUMKEYS ];
535
+ char check_nulls [RI_MAX_NUMKEYS + 1 ];
536
+ int i ;
537
+ Oid save_uid ;
538
+ bool result ;
539
+ save_uid = GetUserId ();
540
+
541
+ ri_BuildQueryKeyPkCheck (& qkey , tgoid ,
542
+ RI_PLAN_CHECK_LOOKUPPK , pk_rel ,
543
+ tgnargs , tgargs );
544
+
545
+ switch (ri_NullCheck (pk_rel , old_row , & qkey , RI_KEYPAIR_PK_IDX ))
546
+ {
547
+ case RI_KEYS_ALL_NULL :
548
+ /*
549
+ * No check - nothing could have been referencing this row anyway.
550
+ */
551
+ return true;
552
+
553
+ case RI_KEYS_SOME_NULL :
554
+
555
+ /*
556
+ * This is the only case that differs between the three kinds
557
+ * of MATCH.
558
+ */
559
+ switch (match_type )
560
+ {
561
+ case RI_MATCH_TYPE_FULL :
562
+ case RI_MATCH_TYPE_UNSPECIFIED :
563
+
564
+ /*
565
+ * MATCH <unspecified>/FULL - if ANY column is null, we
566
+ * can't be matching to this row already.
567
+ */
568
+ return true;
569
+
570
+ case RI_MATCH_TYPE_PARTIAL :
571
+
572
+ /*
573
+ * MATCH PARTIAL - all non-null columns must match.
574
+ * (not implemented, can be done by modifying the
575
+ * query below to only include non-null columns, or by
576
+ * writing a special version here)
577
+ */
578
+ elog (ERROR , "MATCH PARTIAL not yet implemented" );
579
+ break ;
580
+ }
581
+
582
+ case RI_KEYS_NONE_NULL :
583
+
584
+ /*
585
+ * Have a full qualified key - continue below for all three
586
+ * kinds of MATCH.
587
+ */
588
+ break ;
589
+ }
590
+
591
+ if (SPI_connect () != SPI_OK_CONNECT )
592
+ elog (WARNING , "SPI_connect() failed in RI_FKey_check()" );
593
+
594
+
595
+ /*
596
+ * Fetch or prepare a saved plan for the real check
597
+ */
598
+ if ((qplan = ri_FetchPreparedPlan (& qkey )) == NULL )
599
+ {
600
+ char querystr [MAX_QUOTED_REL_NAME_LEN + 100 +
601
+ (MAX_QUOTED_NAME_LEN + 32 ) * RI_MAX_NUMKEYS ];
602
+ char pkrelname [MAX_QUOTED_REL_NAME_LEN ];
603
+ char attname [MAX_QUOTED_NAME_LEN ];
604
+ const char * querysep ;
605
+ Oid queryoids [RI_MAX_NUMKEYS ];
606
+
607
+ /* ----------
608
+ * The query string built is
609
+ * SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...]
610
+ * The type id's for the $ parameters are those of the
611
+ * corresponding FK attributes. Thus, SPI_prepare could
612
+ * eventually fail if the parser cannot identify some way
613
+ * how to compare these two types by '='.
614
+ * ----------
615
+ */
616
+ quoteRelationName (pkrelname , pk_rel );
617
+ sprintf (querystr , "SELECT 1 FROM ONLY %s x" , pkrelname );
618
+ querysep = "WHERE" ;
619
+ for (i = 0 ; i < qkey .nkeypairs ; i ++ )
620
+ {
621
+ quoteOneName (attname ,
622
+ tgargs [RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_PK_IDX ]);
623
+ sprintf (querystr + strlen (querystr ), " %s %s = $%d" ,
624
+ querysep , attname , i + 1 );
625
+ querysep = "AND" ;
626
+ queryoids [i ] = SPI_gettypeid (pk_rel -> rd_att ,
627
+ qkey .keypair [i ][RI_KEYPAIR_PK_IDX ]);
628
+ }
629
+ strcat (querystr , " FOR UPDATE OF x" );
630
+
631
+ /*
632
+ * Prepare, save and remember the new plan.
633
+ */
634
+ qplan = SPI_prepare (querystr , qkey .nkeypairs , queryoids );
635
+ qplan = SPI_saveplan (qplan );
636
+ ri_HashPreparedPlan (& qkey , qplan );
637
+ }
638
+
639
+ /*
640
+ * We have a plan now. Build up the arguments for SPI_execp() from the
641
+ * key values in the new FK tuple.
642
+ */
643
+ for (i = 0 ; i < qkey .nkeypairs ; i ++ )
644
+ {
645
+ check_values [i ] = SPI_getbinval (old_row ,
646
+ pk_rel -> rd_att ,
647
+ qkey .keypair [i ][RI_KEYPAIR_PK_IDX ],
648
+ & isnull );
649
+ if (isnull )
650
+ check_nulls [i ] = 'n' ;
651
+ else
652
+ check_nulls [i ] = ' ' ;
653
+ }
654
+ check_nulls [i ] = '\0' ;
655
+
656
+ /*
657
+ * Now check that foreign key exists in PK table
658
+ */
659
+
660
+ SetUserId (RelationGetForm (pk_rel )-> relowner );
661
+
662
+ if (SPI_execp (qplan , check_values , check_nulls , 1 ) != SPI_OK_SELECT )
663
+ elog (ERROR , "SPI_execp() failed in ri_Check_Pk_Match()" );
664
+
665
+ SetUserId (save_uid );
666
+
667
+ result = (SPI_processed != 0 );
668
+
669
+ if (SPI_finish () != SPI_OK_FINISH )
670
+ elog (WARNING , "SPI_finish() failed in ri_Check_Pk_Match()" );
671
+
672
+ return result ;
673
+ }
674
+
675
+
515
676
/* ----------
516
677
* RI_FKey_noaction_del -
517
678
*
@@ -535,6 +696,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
535
696
char del_nulls [RI_MAX_NUMKEYS + 1 ];
536
697
bool isnull ;
537
698
int i ;
699
+ int match_type ;
538
700
Oid save_uid ;
539
701
540
702
save_uid = GetUserId ();
@@ -581,7 +743,18 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
581
743
pk_rel = trigdata -> tg_relation ;
582
744
old_row = trigdata -> tg_trigtuple ;
583
745
584
- switch (ri_DetermineMatchType (tgargs [RI_MATCH_TYPE_ARGNO ]))
746
+ match_type = ri_DetermineMatchType (tgargs [RI_MATCH_TYPE_ARGNO ]);
747
+ if (ri_Check_Pk_Match (pk_rel , old_row , trigdata -> tg_trigger -> tgoid ,
748
+ match_type , tgnargs , tgargs )) {
749
+ /*
750
+ * There's either another row, or no row could match this
751
+ * one. In either case, we don't need to do the check.
752
+ */
753
+ heap_close (fk_rel , RowShareLock );
754
+ return PointerGetDatum (NULL );
755
+ }
756
+
757
+ switch (match_type )
585
758
{
586
759
/* ----------
587
760
* SQL3 11.9 <referential constraint definition>
@@ -746,6 +919,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
746
919
char upd_nulls [RI_MAX_NUMKEYS + 1 ];
747
920
bool isnull ;
748
921
int i ;
922
+ int match_type ;
749
923
Oid save_uid ;
750
924
751
925
save_uid = GetUserId ();
@@ -793,7 +967,18 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
793
967
new_row = trigdata -> tg_newtuple ;
794
968
old_row = trigdata -> tg_trigtuple ;
795
969
796
- switch (ri_DetermineMatchType (tgargs [RI_MATCH_TYPE_ARGNO ]))
970
+ match_type = ri_DetermineMatchType (tgargs [RI_MATCH_TYPE_ARGNO ]);
971
+ if (ri_Check_Pk_Match (pk_rel , old_row , trigdata -> tg_trigger -> tgoid ,
972
+ match_type , tgnargs , tgargs )) {
973
+ /*
974
+ * There's either another row, or no row could match this
975
+ * one. In either case, we don't need to do the check.
976
+ */
977
+ heap_close (fk_rel , RowShareLock );
978
+ return PointerGetDatum (NULL );
979
+ }
980
+
981
+ switch (match_type )
797
982
{
798
983
/* ----------
799
984
* SQL3 11.9 <referential constraint definition>
@@ -3042,6 +3227,59 @@ ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
3042
3227
}
3043
3228
}
3044
3229
3230
+ /* ----------
3231
+ * ri_BuildQueryKeyPkCheck -
3232
+ *
3233
+ * Build up a new hashtable key for a prepared SPI plan of a
3234
+ * check for PK rows in noaction triggers.
3235
+ *
3236
+ * constr_type is FULL
3237
+ * constr_id is the OID of the pg_trigger row that invoked us
3238
+ * constr_queryno is an internal number of the query inside the proc
3239
+ * pk_relid is the OID of referenced relation
3240
+ * nkeypairs is the number of keypairs
3241
+ * following are the attribute number keypairs of the trigger invocation
3242
+ *
3243
+ * At least for MATCH FULL this builds a unique key per plan.
3244
+ * ----------
3245
+ */
3246
+ static void
3247
+ ri_BuildQueryKeyPkCheck (RI_QueryKey * key , Oid constr_id , int32 constr_queryno ,
3248
+ Relation pk_rel ,
3249
+ int argc , char * * argv )
3250
+ {
3251
+ int i ;
3252
+ int j ;
3253
+ int fno ;
3254
+
3255
+ /*
3256
+ * Initialize the key and fill in type, oid's and number of keypairs
3257
+ */
3258
+ memset ((void * ) key , 0 , sizeof (RI_QueryKey ));
3259
+ key -> constr_type = RI_MATCH_TYPE_FULL ;
3260
+ key -> constr_id = constr_id ;
3261
+ key -> constr_queryno = constr_queryno ;
3262
+ key -> fk_relid = 0 ;
3263
+ key -> pk_relid = pk_rel -> rd_id ;
3264
+ key -> nkeypairs = (argc - RI_FIRST_ATTNAME_ARGNO ) / 2 ;
3265
+
3266
+ /*
3267
+ * Lookup the attribute numbers of the arguments to the trigger call
3268
+ * and fill in the keypairs.
3269
+ */
3270
+ for (i = 0 , j = RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX ; j < argc ; i ++ , j += 2 )
3271
+ {
3272
+ fno = SPI_fnumber (pk_rel -> rd_att , argv [j ]);
3273
+ if (fno == SPI_ERROR_NOATTRIBUTE )
3274
+ elog (ERROR , "constraint %s: table %s does not have an attribute %s" ,
3275
+ argv [RI_CONSTRAINT_NAME_ARGNO ],
3276
+ RelationGetRelationName (pk_rel ),
3277
+ argv [j + 1 ]);
3278
+ key -> keypair [i ][RI_KEYPAIR_PK_IDX ] = fno ;
3279
+ key -> keypair [i ][RI_KEYPAIR_FK_IDX ] = 0 ;
3280
+ }
3281
+ }
3282
+
3045
3283
3046
3284
/* ----------
3047
3285
* ri_NullCheck -
@@ -3378,3 +3616,4 @@ ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue)
3378
3616
return DatumGetBool (FunctionCall2 (& (entry -> oprfmgrinfo ),
3379
3617
oldvalue , newvalue ));
3380
3618
}
3619
+
0 commit comments