diff --git a/contrib/spi/Makefile b/contrib/spi/Makefile index c9c9ff8d480..5d85b2638e0 100644 --- a/contrib/spi/Makefile +++ b/contrib/spi/Makefile @@ -3,34 +3,23 @@ SRCDIR= ../../src include $(SRCDIR)/Makefile.global -CONTRIBDIR=$(LIBDIR)/modules - CFLAGS+= $(CFLAGS_SL) -I$(SRCDIR)/include ifdef REFINT_VERBOSE CFLAGS+= -DREFINT_VERBOSE endif -TARGETS= refint$(DLSUFFIX) refint.sql timetravel$(DLSUFFIX) timetravel.sql \ - autoinc$(DLSUFFIX) autoinc.sql moddatetime$(DLSUFFIX) moddatetime.sql \ - insert_username$(DLSUFFIX) insert_username.sql +TARGETS= refint$(DLSUFFIX) refint.sql CLEANFILES+= $(TARGETS) all:: $(TARGETS) -install:: all $(CONTRIBDIR) - $(INSTALL) -c README $(CONTRIBDIR)/README.spi - for f in *.example *.sql *$(DLSUFFIX); do $(INSTALL) -c $$f $(CONTRIBDIR)/$$f; done - -$(CONTRIBDIR): - mkdir -p $(CONTRIBDIR) - %.sql: %.source rm -f $@; \ C=`pwd`; \ - sed -e "s:_OBJWD_:$(CONTRIBDIR):g" \ + sed -e "s:_OBJWD_:$$C:g" \ -e "s:_DLSUFFIX_:$(DLSUFFIX):g" < $< > $@ clean: - rm -f $(TARGETS) *.o + rm -f $(TARGETS) diff --git a/contrib/spi/README b/contrib/spi/README index a1ab46cb2bf..65868f0fc7a 100644 --- a/contrib/spi/README +++ b/contrib/spi/README @@ -8,8 +8,8 @@ table/field names (as described below) while creating a trigger. check_primary_key () is to used for foreign keys of a table. - You have to create trigger (BEFORE INSERT OR UPDATE) using this -function on a table referencing another table. You have to specify + You are to create trigger (BEFORE INSERT OR UPDATE) using this +function on a table referencing another table. You are to specify as function arguments: triggered table column names which correspond to foreign key, referenced table name and column names in referenced table which correspond to primary/unique key. @@ -18,8 +18,8 @@ one reference. check_foreign_key () is to used for primary/unique keys of a table. - You have to create trigger (BEFORE DELETE OR UPDATE) using this -function on a table referenced by another table(s). You have to specify + You are to create trigger (BEFORE DELETE OR UPDATE) using this +function on a table referenced by another table(s). You are to specify as function arguments: number of references for which function has to performe checking, action if referencing key found ('cascade' - to delete corresponding foreign key, 'restrict' - to abort transaction if foreign keys @@ -42,26 +42,20 @@ refint.source). Old internally supported time-travel (TT) used insert/delete transaction commit times. To get the same feature using triggers -you have to add to a table two columns of abstime type to store +you are to add to a table two columns of abstime type to store date when a tuple was inserted (start_date) and changed/deleted (stop_date): CREATE TABLE XXX ( ... ... - date_on abstime, - date_off abstime + date_on abstime default currabstime(), + date_off abstime default 'infinity' ... ... ); -CREATE TRIGGER timetravel - BEFORE INSERT OR DELETE OR UPDATE ON tttest - FOR EACH ROW - EXECUTE PROCEDURE - timetravel (date_on, date_off); - - Tuples being inserted with NULLs in date_on/date_off will get current -date in date_on (name of start_date column in XXX) and INFINITY in date_off -(name of stop_date column in XXX). +- so, tuples being inserted with NULLs in date_on/date_off will get +_current_date_ in date_on (name of start_date column in XXX) and INFINITY in +date_off (name of stop_date column in XXX). Tuples with stop_date equal INFINITY are "valid now": when trigger will be fired for UPDATE/DELETE of a tuple with stop_date NOT equal INFINITY then @@ -78,7 +72,7 @@ DELETE: new tuple will be inserted with stop_date setted to current date (and with the same data in other columns as in tuple being deleted). NOTE: -1. To get tuples "valid now" you have to add _stop_date_ = 'infinity' +1. To get tuples "valid now" you are to add _stop_date_ = 'infinity' to WHERE. Internally supported TT allowed to avoid this... Fixed rewriting RULEs could help here... As work arround you may use VIEWs... @@ -89,9 +83,12 @@ DELETE: new tuple will be inserted with stop_date setted to current date timetravel() is general trigger function. - You have to create trigger BEFORE (!!!) INSERT OR UPDATE OR DELETE using -this function on a time-traveled table. You have to specify two arguments: -name of start_date column and name of stop_date column in triggered table. + You are to create trigger BEFORE (!!!) UPDATE OR DELETE using this +function on a time-traveled table. You are to specify two arguments: name of +start_date column and name of stop_date column in triggered table. + +currabstime() may be used in DEFAULT for start_date column to get +current date. set_timetravel() allows you turn time-travel ON/OFF for a table: @@ -99,51 +96,9 @@ set_timetravel() allows you turn time-travel ON/OFF for a table: old status). set_timetravel('XXX', 0) will turn TT OFF for table XXX (-"-). -Turning TT OFF allows you do with a table ALL what you want! +Turning TT OFF allows you do with a table ALL what you want. There is example in timetravel.example. To CREATE FUNCTIONs use timetravel.sql (will be made by gmake from timetravel.source). - - -3. autoinc.c - function for implementing AUTOINCREMENT/IDENTITY feature. - - You have to create BEFORE INSERT OR UPDATE trigger using function -autoinc(). You have to specify as function arguments: column name -(of int4 type) for which you want to get this feature and name of -SEQUENCE from which next value has to be fetched when NULL or 0 -value is being inserted into column (, ... - you are able to specify -as many column/sequence pairs as you need). - - There is example in autoinc.example. - - To CREATE FUNCTION use autoinc.sql (will be made by gmake from -autoinc.source). - - -4. insert_username.c - function for inserting user names. - - You have to create BEFORE INSERT OR UPDATE trigger using the function -insert_username(). You have to specify as a function argument: the column -name (of text type) in which user names will be inserted. Note that user -names will be inserted irregardless of the initial value of the field, so -that users cannot bypass this functionality by simply defining the field to -be NOT NULL. - - There is an example in insert_username.example. - - To CREATE FUNCTION use insert_username.sql (will be made by gmake from -insert_username.source). - - -5. moddatetime.c - function for maintaining a modification datetime stamp. - - You have to create a BEFORE UPDATE trigger using the function moddatetime(). -One argument must be given, that is the name of the field that is of type -datetime that is to be used as the modification time stamp. - - There is an example in moddatetime.example. - - To CREATE FUNCTION use moddatetime.sql ( will be made by gmake from -moddatetime.source). diff --git a/contrib/spi/new_example.sql b/contrib/spi/new_example.sql new file mode 100644 index 00000000000..02049078941 --- /dev/null +++ b/contrib/spi/new_example.sql @@ -0,0 +1,68 @@ +--Column ID of table A is primary key: + +CREATE TABLE A ( + ID int4 not null, + id1 int4 not null, +primary key (ID,ID1) +); + +--Columns REFB of table B and REFC of C are foreign keys referenting ID of A: + +CREATE TABLE B ( + REFB int4, + REFB1 INT4 +); +CREATE INDEX BI ON B (REFB); + +CREATE TABLE C ( + REFC int4, + REFC1 int4 +); +CREATE INDEX CI ON C (REFC); + +--Trigger for table A: + +CREATE TRIGGER AT BEFORE DELETE ON A FOR EACH ROW +EXECUTE PROCEDURE +check_foreign_key (2, 'cascade', 'ID','id1', 'B', 'REFB','REFB1', 'C', 'REFC','REFC1'); + + +CREATE TRIGGER AT1 AFTER UPDATE ON A FOR EACH ROW +EXECUTE PROCEDURE +check_foreign_key (2, 'cascade', 'ID','id1', 'B', 'REFB','REFB1', 'C', 'REFC','REFC1'); + + +CREATE TRIGGER BT BEFORE INSERT OR UPDATE ON B FOR EACH ROW +EXECUTE PROCEDURE +check_primary_key ('REFB','REFB1', 'A', 'ID','ID1'); + +CREATE TRIGGER CT BEFORE INSERT OR UPDATE ON C FOR EACH ROW +EXECUTE PROCEDURE +check_primary_key ('REFC','REFC1', 'A', 'ID','ID1'); + + + +-- Now try + +INSERT INTO A VALUES (10,10); +INSERT INTO A VALUES (20,20); +INSERT INTO A VALUES (30,30); +INSERT INTO A VALUES (40,41); +INSERT INTO A VALUES (50,50); + +INSERT INTO B VALUES (1); -- invalid reference +INSERT INTO B VALUES (10,10); +INSERT INTO B VALUES (30,30); +INSERT INTO B VALUES (30,30); + +INSERT INTO C VALUES (11); -- invalid reference +INSERT INTO C VALUES (20,20); +INSERT INTO C VALUES (20,21); +INSERT INTO C VALUES (30,30); + +-- now update work well +update A set ID = 100 , ID1 = 199 where ID=30 ; + +SELECT * FROM A; +SELECT * FROM B; +SELECT * FROM C; diff --git a/contrib/spi/preprocessor/README.MAX b/contrib/spi/preprocessor/README.MAX new file mode 100644 index 00000000000..7969438c602 --- /dev/null +++ b/contrib/spi/preprocessor/README.MAX @@ -0,0 +1,76 @@ + +Here are general trigger functions provided as workable examples +of using SPI and triggers. "General" means that functions may be +used for defining triggers for any tables but you have to specify +table/field names (as described below) while creating a trigger. + +1. refint.c - functions for implementing referential integrity. + +check_primary_key () is to used for foreign keys of a table. + + You are to create trigger (BEFORE INSERT OR UPDATE) using this +function on a table referencing another table. You are to specify +as function arguments: triggered table column names which correspond +to foreign key, referenced table name and column names in referenced +table which correspond to primary/unique key. + You may create as many triggers as you need - one trigger for +one reference. + +check_foreign_key () is to used for primary/unique keys of a table. + + You are to create trigger (BEFORE DELETE OR UPDATE) using this +function on a table referenced by another table(s). You are to specify +as function arguments: number of references for which function has to +performe checking, action if referencing key found ('cascade' - to delete +corresponding foreign key, 'restrict' - to abort transaction if foreign keys +exist, 'setnull' - to set foreign key referencing primary/unique key +being deleted to null), triggered table column names which correspond +to primary/unique key, referencing table name and column names corresponding +to foreign key (, ... - as many referencing tables/keys as specified +by first argument). + Note, that NOT NULL constraint and unique index have to be defined by +youself. + + There are examples in refint.example and regression tests +(sql/triggers.sql). + + To CREATE FUNCTIONs use refint.sql (will be made by gmake from +refint.source). + +# Excuse me for my bad english. Massimo Lambertini +# +# +# New check foreign key +# +I think that cascade mode is to be considered like that the operation over +main table is to be made also in referenced table . +When i Delete , i must delete from referenced table , +but when i update , i update referenced table and not delete like unmodified refint.c . + +I made a patch that when i update it check the type of modified key ( if is a text , char() i +added '') and then create a update query that do the right think . + +For my point of view that policy is helpfull because i do not have in referenced table +loss of information . + + +In preprocessor subdir i have placed a little utility that from a SQL92 table definition, +it create all trigger for foreign key . + + +the schema that i use to analyze the problem is this + +create table +A +( key int4 not null primary key ,...) ; +create table +REFERENCED_B +( key int 4 , ... , +foreign key ( key ) references A -- +); + + + + + + diff --git a/contrib/spi/preprocessor/example.sql b/contrib/spi/preprocessor/example.sql new file mode 100644 index 00000000000..7ecd6ad7e5c --- /dev/null +++ b/contrib/spi/preprocessor/example.sql @@ -0,0 +1,37 @@ +-- Note the syntax is strict because i have no time to write better perl filter. +-- +-- [blank] is 1 blank +-- at the end of an interesting line must be a [,] or [--] +-- [ending] must be a , or -- +-- +-- foreign[blank]key[blank]([blank]keyname,..,keyname[blank])[blank]references[blank]table[blank][ending] +-- +-- step1.e < example.sql | step2.pl > foreign_key_triggers.sql +-- +-- step1.e is a simple program that UPPERCASE ALL . I know that is simple implementing in Perl +-- bu i haven't time + + +CREATE TABLE +gruppo +( +codice_gruppo int4 NOT NULL, +descrizione varchar(32) NOT NULL +primary key ( codice_gruppo ) + +) ; + +-- +-- fa_parte : Appartenenza di una Azienda Conatto o Cliente ad un certo GRUPPO +-- + +CREATE TABLE +fa_parte +( +codice_gruppo int4 NOT NULL, +codice_contatto int4 NOT NULL, + +primary key ( codice_gruppo,codice_contatto ) , +foreign key ( codice_gruppo ) references gruppo -- +); + diff --git a/contrib/spi/preprocessor/step1.c b/contrib/spi/preprocessor/step1.c new file mode 100644 index 00000000000..9d59c47edd8 --- /dev/null +++ b/contrib/spi/preprocessor/step1.c @@ -0,0 +1,24 @@ +#include + +char *strtoupper(char *string) +{ + int i ; + for (i=0;i ) +{ + chop; + $str=$_ ; + + if ($is_create == 1) { + $table_name=$str; + $is_create=2; + } + if ( $str =~ /^CREATE TABLE/ ){ + $is_create=1; + } + if ($is_create == 2) { + if ($str =~ /^FOREIGN KEY/){ + ($d1,$d2,$d3,$columns,$d4,$d5,$references_table,$d6) = split (/ /,$str,8); + #printf "Table $table_name $columns $references_table\n"; + + if ($table_name ne $old_name ){ + printf "--\n-- Trigger for $table_name\n--\n\n"; + } + + foreach $i ( split(/,/ , $columns ) ){ + print "CREATE INDEX I_$table_name"; + print "_$i ON $table_name ( $i ) ;\n"; + } + + printf "\nCREATE TRIGGER T_P_$table_name"; + printf "_$references_table BEFORE INSERT OR UPDATE ON $table_name FOR EACH ROW\n" ; + printf "EXECUTE PROCEDURE\n"; + printf "check_primary_key("; + $val=0; + foreach $i ( split(/,/ , $columns ) ){ + print "'$i',"; + $val=$val+1 ; + } + print "'$references_table',"; + + $t=1; + foreach $i ( split(/,/,$columns ) ){ + print "'$i'"; + if ( $t < $val ) { + printf ","; + } + $t=$t+1; + } + print " );\n\n"; + + printf "CREATE TRIGGER T_F_D_$references_table"; + printf "_$table_name BEFORE DELETE ON $references_table FOR EACH ROW\n" ; + printf "EXECUTE PROCEDURE\n"; + printf "check_foreign_key(1,'cascade',"; + $val=0; + foreach $i ( split(/,/ , $columns ) ){ + print "'$i',"; + $val=$val+1 ; + } + print "'$table_name',"; + + $t=1; + foreach $i ( split(/,/,$columns ) ){ + print "'$i'"; + if ( $t < $val ) { + printf ","; + } + $t=$t+1; + } + print " );\n\n"; + + printf "CREATE TRIGGER T_F_U_$references_table"; + printf "_$table_name AFTER UPDATE ON $references_table FOR EACH ROW\n" ; + printf "EXECUTE PROCEDURE\n"; + printf "check_foreign_key(1,'cascade',"; + $val=0; + foreach $i ( split(/,/ , $columns ) ){ + print "'$i',"; + $val=$val+1 ; + } + print "'$table_name',"; + + $t=1; + foreach $i ( split(/,/,$columns ) ){ + print "'$i'"; + if ( $t < $val ) { + printf ","; + } + $t=$t+1; + } + print " );\n\n"; + + if ($table_name ne $old_name ){ + printf "-- ********************************\n\n\n"; + } + $old_name=$table_name ; + + + + + } + } + if ($str =~ /^\)\;/ ) { + $is_create = 0 ; + } + +} + + + + + + diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c index ca491192975..de5065f5391 100644 --- a/contrib/spi/refint.c +++ b/contrib/spi/refint.c @@ -3,10 +3,15 @@ * constraints using general triggers. */ +#define DEBUG_QUERY 1 + #include "executor/spi.h" /* this is what you need to work with SPI */ #include "commands/trigger.h" /* -"- and triggers */ #include /* tolower () */ + + + HeapTuple check_primary_key(void); HeapTuple check_foreign_key(void); @@ -30,9 +35,9 @@ static EPlan *find_plan(char *ident, EPlan ** eplan, int *nplans); * references existing tuple in "primary" table. * Though it's called without args You have to specify referenced * table/keys while creating trigger: key field names in triggered table, - * referenced table name, referenced key field names,type of action [automatic|dependent]: + * referenced table name, referenced key field names: * EXECUTE PROCEDURE - * check_primary_key ('Fkey1', 'Fkey2', 'Ptable', 'Pkey1', 'Pkey2','[automatic|dependent]'). + * check_primary_key ('Fkey1', 'Fkey2', 'Ptable', 'Pkey1', 'Pkey2'). */ HeapTuple /* have to return HeapTuple to Executor */ @@ -41,10 +46,9 @@ check_primary_key() Trigger *trigger; /* to get trigger name */ int nargs; /* # of args specified in CREATE TRIGGER */ char **args; /* arguments: column names and table name */ - int nkeys; /* # of key columns (= (nargs-1) / 2) */ + int nkeys; /* # of key columns (= nargs / 2) */ Datum *kvals; /* key values */ char *relname; /* referenced relation name */ - char *action; /* action on insert or update*/ Relation rel; /* triggered relation */ HeapTuple tuple = NULL; /* tuple to return */ TupleDesc tupdesc; /* tuple description */ @@ -58,7 +62,9 @@ check_primary_key() /* * Some checks first... */ - +#ifdef DEBUG_QUERY + elog(NOTICE,"Check_primary_key Enter Function"); +#endif /* Called by trigger manager ? */ if (!CurrentTriggerData) elog(ERROR, "check_primary_key: triggers are not initialized"); @@ -69,12 +75,10 @@ check_primary_key() /* If INSERTion then must check Tuple to being inserted */ if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event)) - tuple = CurrentTriggerData->tg_trigtuple; /* Not should be called for DELETE */ else if (TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event)) - elog(ERROR, "check_primary_key: can't process DELETE events"); /* If UPDATion the must check new Tuple, not old one */ @@ -85,14 +89,10 @@ check_primary_key() nargs = trigger->tgnargs; args = trigger->tgargs; - if ((nargs-1) % 2 != 1) /* odd number of arguments! */ - elog(ERROR, "check_primary_key: even number of arguments should be specified"); + if (nargs % 2 != 1) /* odd number of arguments! */ + elog(ERROR, "check_primary_key: odd number of arguments should be specified"); - nkeys = (nargs-1) / 2; - action=args[nargs -1]; - if (strcmp(action,"automatic") && strcmp(action,"dependent")) - elog(ERROR,"check_primary_key: unknown action"); - nargs=nargs-1; + nkeys = nargs / 2; relname = args[nkeys]; rel = CurrentTriggerData->tg_relation; tupdesc = rel->rd_att; @@ -203,62 +203,9 @@ check_primary_key() /* * If there are no tuples returned by SELECT then ... */ - if (SPI_processed == 0 && strcmp(action,"dependent")==0) + if (SPI_processed == 0) elog(ERROR, "%s: tuple references non-existing key in %s", trigger->tgname, relname); - else if (strcmp(action,"automatic")==0) - { - /* insert tuple in parent with only primary keys */ - /* prepare plan */ - void *pplan; - char sql[8192]; - - /* - * Construct query:INSERT INTO relname (Pkey1[,Pkey2]*) values ($1,$2..); - */ - sprintf(sql, "insert into %s ( ", relname); - for (i = 0; i < nkeys; i++) - { - sprintf(sql + strlen(sql), "%s%s ", args[i + nkeys + 1],(isplan = (void **) malloc(sizeof(void *)); - *(plan->splan) = pplan; - plan->nplans = 1; - /* - * Ok, execute prepared plan. - */ - ret = SPI_execp(*(plan->splan), kvals, NULL, 1); - /* we have no NULLs - so we pass ^^^^ here */ - - if (ret < 0) - elog(ERROR, "check_primary_key: SPI_execp returned %d", ret); - - /* - * If there are no tuples returned by INSERT then ... - */ - if (SPI_processed == 0) - elog(ERROR, "error: can't enter automatically in %s",relname); - } SPI_finish(); @@ -283,6 +230,7 @@ check_foreign_key() Trigger *trigger; /* to get trigger name */ int nargs; /* # of args specified in CREATE TRIGGER */ char **args; /* arguments: as described above */ + char **args_temp ; int nrefs; /* number of references (== # of plans) */ char action; /* 'R'estrict | 'S'etnull | 'C'ascade */ int nkeys; /* # of key columns */ @@ -298,10 +246,13 @@ check_foreign_key() bool isequal = true; /* are keys in both tuples equal (in * UPDATE) */ char ident[2 * NAMEDATALEN]; /* to identify myself */ + int is_update=0; int ret; int i, r; - +#ifdef DEBUG_QUERY + elog(NOTICE,"Check_foreign_key Enter Function"); +#endif /* * Some checks first... */ @@ -316,7 +267,6 @@ check_foreign_key() /* Not should be called for INSERT */ if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event)) - elog(ERROR, "check_foreign_key: can't process INSERT events"); /* Have to check tg_trigtuple - tuple being deleted */ @@ -327,9 +277,12 @@ check_foreign_key() * key in tg_newtuple is the same as in tg_trigtuple then nothing to * do. */ + is_update=0; if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event)) + { newtuple = CurrentTriggerData->tg_newtuple; - + is_update=1; + } trigger = CurrentTriggerData->tg_trigger; nargs = trigger->tgnargs; args = trigger->tgargs; @@ -337,6 +290,7 @@ check_foreign_key() if (nargs < 5) /* nrefs, action, key, Relation, key - at * least */ elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs); + nrefs = pg_atoi(args[0], sizeof(int), 0); if (nrefs < 1) elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs); @@ -434,6 +388,7 @@ check_foreign_key() if (plan->nplans <= 0) /* Get typeId of column */ argtypes[i] = SPI_gettypeid(tupdesc, fnumber); } + args_temp = args; nargs -= nkeys; args += nkeys; @@ -444,14 +399,13 @@ check_foreign_key() { void *pplan; char sql[8192]; - char **args2 = args; + char **args2 = args ; plan->splan = (void **) malloc(nrefs * sizeof(void *)); for (r = 0; r < nrefs; r++) { relname = args2[0]; - /* * For 'R'estrict action we construct SELECT query - SELECT 1 * FROM _referencing_relation_ WHERE Fkey1 = $1 [AND Fkey2 = @@ -465,11 +419,50 @@ check_foreign_key() * For 'C'ascade action we construct DELETE query - DELETE * FROM _referencing_relation_ WHERE Fkey1 = $1 [AND Fkey2 = * $2 [...]] - to delete all referencing tuples. - */ - else if (action == 'c') - + */ + /*Max : Cascade with UPDATE query i create update query that + updates new key values in referenced tables + */ + + + else if (action == 'c'){ + if (is_update == 1) + { + int fn; + char *nv; + int k ; + sprintf(sql, "update %s set ", relname); + for (k = 1; k <= nkeys; k++) + { + int is_char_type =0; + char *type; + + fn = SPI_fnumber(tupdesc, args_temp[k-1]); + nv = SPI_getvalue(newtuple, tupdesc, fn); + type=SPI_gettype(tupdesc,fn); + + if ( (strcmp(type,"text") && strcmp (type,"varchar") && + strcmp(type,"char") && strcmp (type,"bpchar") && + strcmp(type,"date") && strcmp (type,"datetime")) == 0 ) + is_char_type=1; +#ifdef DEBUG_QUERY + elog(NOTICE,"Check_foreign_key Debug value %s type %s %d", + nv,type,is_char_type); +#endif + /* is_char_type =1 i set ' ' for define a new value + */ + sprintf(sql + strlen(sql), " %s = %s%s%s %s ", + args2[k], (is_char_type>0) ? "'" :"" , + nv, (is_char_type >0) ? "'" :"",(k < nkeys) ? ", " : ""); + is_char_type=0; + } + strcat(sql, " where "); + + } + else /* DELETE */ sprintf(sql, "delete from %s where ", relname); - + + } /* * For 'S'etnull action we construct UPDATE query - UPDATE * _referencing_relation_ SET Fkey1 null [, Fkey2 null [...]] @@ -509,12 +502,15 @@ check_foreign_key() elog(ERROR, "check_foreign_key: SPI_saveplan returned %d", SPI_result); plan->splan[r] = pplan; - + args2 += nkeys + 1; /* to the next relation */ } plan->nplans = nrefs; +#ifdef DEBUG_QUERY + elog(NOTICE,"Check_foreign_key Debug Query is : %s ", sql); +#endif } - + /* * If UPDATE and key is not changed ... */