提交 d489fdfc 编写于 作者: B Bruce Momjian

I send you a attach of my modified refint.c that

works with a new policy  in cascade mode .

Please Read README.MAX .
I do not know if you are the author of refint.c ,
but if not please tell me who is .


Thank you ( excuse me for my bad english) .
Massimo Lambertini massimo.lambertini@everex.it
上级 9e4e33c9
......@@ -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)
......@@ -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).
--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;
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 --
);
-- 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 --
);
#include <stdio.h>
char *strtoupper(char *string)
{
int i ;
for (i=0;i<strlen(string);i++)
{
string[i]=toupper(string[i]);
}
return string;
}
void main ( char argc , char **argv )
{
char str[250];
int sw = 0 ;
while ( fgets (str,240,stdin) )
{
if ( sw == 0 ) printf("%s",strtoupper(str));
}
}
#!/usr/bin/perl
##
## MAIN
##
$table_name="";
$old_name="";
$references_table="";
$references_column="";
$is_create=0;
while ( <> )
{
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 ;
}
}
......@@ -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 <ctype.h> /* 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],(i<nkeys-1) ? ",":"");
}
sprintf(sql+strlen(sql),") values (");
for (i=0;i<nkeys; i++)
{
sprintf(sql+strlen(sql),"$%d%s ",i+1,(i<nkeys-1) ? ",":"");
}
sprintf(sql+strlen(sql),")");
/* Prepare plan for query */
pplan = SPI_prepare(sql, nkeys, argtypes);
if (pplan == NULL)
elog(ERROR, "check_primary_key: SPI_prepare returned %d", SPI_result);
/*
* Remember that SPI_prepare places plan in current memory context
* - so, we have to save plan in Top memory context for latter
* use.
*/
pplan = SPI_saveplan(pplan);
if (pplan == NULL)
elog(ERROR, "check_primary_key: SPI_saveplan returned %d", SPI_result);
plan->splan = (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 ...
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册