Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
b7b6d4bf
Commit
b7b6d4bf
authored
Jan 06, 2000
by
Jan Wieck
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Changed "triggered data change violation" detection code
in trigger manager. Jan
parent
88016a56
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
427 additions
and
294 deletions
+427
-294
src/backend/commands/trigger.c
src/backend/commands/trigger.c
+161
-165
src/backend/parser/analyze.c
src/backend/parser/analyze.c
+125
-125
src/backend/utils/adt/ri_triggers.c
src/backend/utils/adt/ri_triggers.c
+124
-1
src/include/catalog/pg_proc.h
src/include/catalog/pg_proc.h
+5
-1
src/include/commands/trigger.h
src/include/commands/trigger.h
+9
-1
src/include/utils/builtins.h
src/include/utils/builtins.h
+3
-1
No files found.
src/backend/commands/trigger.c
View file @
b7b6d4bf
...
...
@@ -1054,20 +1054,6 @@ deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid)
}
/* ----------
* deferredTriggerCancelEvent()
*
* Mark an event in the eventlist as cancelled because it isn't
* required anymore (replaced by anoter event).
* ----------
*/
static
void
deferredTriggerCancelEvent
(
DeferredTriggerEvent
event
)
{
event
->
dte_event
|=
TRIGGER_DEFERRED_CANCELED
;
}
/* ----------
* deferredTriggerExecute()
*
...
...
@@ -1112,10 +1098,11 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno)
* Setup the trigger information
* ----------
*/
SaveTriggerData
.
tg_event
=
event
->
dte_event
|
TRIGGER_EVENT_ROW
;
SaveTriggerData
.
tg_event
=
(
event
->
dte_event
&
TRIGGER_EVENT_OPMASK
)
|
TRIGGER_EVENT_ROW
;
SaveTriggerData
.
tg_relation
=
rel
;
switch
(
event
->
dte_event
)
switch
(
event
->
dte_event
&
TRIGGER_EVENT_OPMASK
)
{
case
TRIGGER_EVENT_INSERT
:
SaveTriggerData
.
tg_trigtuple
=
&
newtuple
;
...
...
@@ -1656,13 +1643,13 @@ DeferredTriggerSaveEvent(Relation rel, int event,
MemoryContext
oldcxt
;
DeferredTriggerEvent
new_event
;
DeferredTriggerEvent
prev_event
;
bool
prev_done
=
false
;
int
new_size
;
int
i
;
int
ntriggers
;
Trigger
**
triggers
;
ItemPointerData
oldctid
;
ItemPointerData
newctid
;
TriggerData
SaveTriggerData
;
if
(
deftrig_cxt
==
NULL
)
elog
(
ERROR
,
...
...
@@ -1694,203 +1681,212 @@ DeferredTriggerSaveEvent(Relation rel, int event,
ItemPointerSetInvalid
(
&
(
newctid
));
/* ----------
*
Eventually modify the event and do some general RI violation checks
*
Create a new event
* ----------
*/
switch
(
event
)
oldcxt
=
MemoryContextSwitchTo
((
MemoryContext
)
deftrig_cxt
);
ntriggers
=
rel
->
trigdesc
->
n_after_row
[
event
];
triggers
=
rel
->
trigdesc
->
tg_after_row
[
event
];
new_size
=
sizeof
(
DeferredTriggerEventData
)
+
ntriggers
*
sizeof
(
DeferredTriggerEventItem
);
new_event
=
(
DeferredTriggerEvent
)
palloc
(
new_size
);
new_event
->
dte_event
=
event
&
TRIGGER_EVENT_OPMASK
;
new_event
->
dte_relid
=
rel
->
rd_id
;
ItemPointerCopy
(
&
oldctid
,
&
(
new_event
->
dte_oldctid
));
ItemPointerCopy
(
&
newctid
,
&
(
new_event
->
dte_newctid
));
new_event
->
dte_n_items
=
ntriggers
;
new_event
->
dte_item
[
ntriggers
].
dti_state
=
new_size
;
for
(
i
=
0
;
i
<
ntriggers
;
i
++
)
{
new_event
->
dte_item
[
i
].
dti_tgoid
=
triggers
[
i
]
->
tgoid
;
new_event
->
dte_item
[
i
].
dti_state
=
((
triggers
[
i
]
->
tgdeferrable
)
?
TRIGGER_DEFERRED_DEFERRABLE
:
0
)
|
((
triggers
[
i
]
->
tginitdeferred
)
?
TRIGGER_DEFERRED_INITDEFERRED
:
0
)
|
((
rel
->
trigdesc
->
n_before_row
[
event
]
>
0
)
?
TRIGGER_DEFERRED_HAS_BEFORE
:
0
);
}
MemoryContextSwitchTo
(
oldcxt
);
switch
(
event
&
TRIGGER_EVENT_OPMASK
)
{
case
TRIGGER_EVENT_INSERT
:
/* ----------
* Don't know how to (surely) check if another tuple with
* this meaning (from all FK's point of view) got deleted
* in the same transaction. Thus not handled yet.
* ----------
*/
new_event
->
dte_event
|=
TRIGGER_DEFERRED_ROW_INSERTED
;
new_event
->
dte_event
|=
TRIGGER_DEFERRED_KEY_CHANGED
;
break
;
case
TRIGGER_EVENT_UPDATE
:
/* ----------
* On UPDATE check if the tuple updated is a result
* of the same transaction.
* On UPDATE check if the tuple updated has been inserted
* or a foreign referenced key value that's changing now
* has been updated once before in this transaction.
* ----------
*/
if
(
oldtup
->
t_data
->
t_xmin
!=
GetCurrentTransactionId
())
break
;
prev_event
=
NULL
;
else
prev_event
=
deferredTriggerGetPreviousEvent
(
rel
->
rd_id
,
&
oldctid
);
/* ----------
* Look at the previous event to the same tuple if
* any of its triggers has already been executed.
* Such a situation would potentially violate RI
* so we abort the transaction.
* Now check if one of the referenced keys is changed.
* ----------
*/
prev_event
=
deferredTriggerGetPreviousEvent
(
rel
->
rd_id
,
&
oldctid
);
if
(
prev_event
->
dte_event
&
TRIGGER_DEFERRED_HAS_BEFORE
||
(
prev_event
->
dte_n_items
!=
0
&&
prev_event
->
dte_event
&
TRIGGER_DEFERRED_DONE
))
prev_done
=
true
;
else
for
(
i
=
0
;
i
<
prev_event
->
dte_n_items
;
i
++
)
for
(
i
=
0
;
i
<
ntriggers
;
i
++
)
{
bool
is_ri_trigger
;
bool
key_unchanged
;
/* ----------
* We are interested in RI_FKEY triggers only.
* ----------
*/
switch
(
triggers
[
i
]
->
tgfoid
)
{
if
(
prev_event
->
dte_item
[
i
].
dti_state
&
TRIGGER_DEFERRED_DONE
)
{
prev_done
=
true
;
case
F_RI_FKEY_NOACTION_UPD
:
is_ri_trigger
=
true
;
new_event
->
dte_item
[
i
].
dti_state
|=
TRIGGER_DEFERRED_DONE
;
break
;
}
}
if
(
prev_done
)
{
elog
(
NOTICE
,
"UPDATE of row inserted/updated in same "
"transaction violates"
);
elog
(
NOTICE
,
"referential integrity semantics. Other "
"triggers or IMMEDIATE "
);
elog
(
ERROR
,
" constraints have already been executed."
);
}
case
F_RI_FKEY_CASCADE_UPD
:
case
F_RI_FKEY_RESTRICT_UPD
:
case
F_RI_FKEY_SETNULL_UPD
:
case
F_RI_FKEY_SETDEFAULT_UPD
:
is_ri_trigger
=
true
;
break
;
default:
is_ri_trigger
=
false
;
break
;
}
if
(
!
is_ri_trigger
)
continue
;
SaveTriggerData
.
tg_event
=
TRIGGER_EVENT_UPDATE
;
SaveTriggerData
.
tg_relation
=
rel
;
SaveTriggerData
.
tg_trigtuple
=
oldtup
;
SaveTriggerData
.
tg_newtuple
=
newtup
;
SaveTriggerData
.
tg_trigger
=
triggers
[
i
];
CurrentTriggerData
=
&
SaveTriggerData
;
key_unchanged
=
RI_FKey_keyequal_upd
();
CurrentTriggerData
=
NULL
;
if
(
key_unchanged
)
{
/* ----------
* The key hasn't changed, so no need later to invoke
* the trigger at all. But remember other states from
* the possible earlier event.
* ----------
*/
new_event
->
dte_item
[
i
].
dti_state
|=
TRIGGER_DEFERRED_DONE
;
/* ----------
* Anything's fine so far - i.e. none of the previous
* events triggers has been executed up to now. Let's
* the REAL event that happened so far.
* ----------
*/
switch
(
prev_event
->
dte_event
&
TRIGGER_EVENT_OPMASK
)
{
case
TRIGGER_EVENT_INSERT
:
if
(
prev_event
)
{
if
(
prev_event
->
dte_event
&
TRIGGER_DEFERRED_ROW_INSERTED
)
{
/* ----------
* This is a row inserted during our transaction.
* So any key value is considered changed.
* ----------
*/
new_event
->
dte_event
|=
TRIGGER_DEFERRED_ROW_INSERTED
;
new_event
->
dte_event
|=
TRIGGER_DEFERRED_KEY_CHANGED
;
new_event
->
dte_item
[
i
].
dti_state
|=
TRIGGER_DEFERRED_KEY_CHANGED
;
}
else
{
/* ----------
* This is a row, previously updated. So
* if this key has been changed before, we
* still remember that it happened.
* ----------
*/
if
(
prev_event
->
dte_item
[
i
].
dti_state
&
TRIGGER_DEFERRED_KEY_CHANGED
)
{
new_event
->
dte_item
[
i
].
dti_state
|=
TRIGGER_DEFERRED_KEY_CHANGED
;
new_event
->
dte_event
|=
TRIGGER_DEFERRED_KEY_CHANGED
;
}
}
}
}
else
{
/* ----------
* The previous operation was an insert.
* So the REAL new event is an INSERT of
* the new tuple.
* Bomb out if this key has been changed before.
* Otherwise remember that we do so.
* ----------
*/
event
=
TRIGGER_EVENT_INSERT
;
ItemPointerSetInvalid
(
&
oldctid
);
deferredTriggerCancelEvent
(
prev_event
);
break
;
if
(
prev_event
)
{
if
(
prev_event
->
dte_event
&
TRIGGER_DEFERRED_ROW_INSERTED
)
elog
(
ERROR
,
"triggered data change violation "
"on relation
\"
%s
\"
"
,
nameout
(
&
(
rel
->
rd_rel
->
relname
)));
if
(
prev_event
->
dte_item
[
i
].
dti_state
&
TRIGGER_DEFERRED_KEY_CHANGED
)
elog
(
ERROR
,
"triggered data change violation "
"on relation
\"
%s
\"
"
,
nameout
(
&
(
rel
->
rd_rel
->
relname
)));
}
case
TRIGGER_EVENT_UPDATE
:
/* ----------
* The previous operation was an UPDATE.
* So the REAL new event is still an UPDATE
* but from the original tuple to this new one.
* This is the first change to this key, so let
* it happen.
* ----------
*/
event
=
TRIGGER_EVENT_UPDATE
;
ItemPointerCopy
(
&
(
prev_event
->
dte_oldctid
),
&
oldctid
)
;
deferredTriggerCancelEvent
(
prev_event
)
;
break
;
new_event
->
dte_item
[
i
].
dti_state
|=
TRIGGER_DEFERRED_KEY_CHANGED
;
new_event
->
dte_event
|=
TRIGGER_DEFERRED_KEY_CHANGED
;
}
}
break
;
case
TRIGGER_EVENT_DELETE
:
/* ----------
* On DELETE check if the tuple updated is a result
* of the same transaction.
* On DELETE check if the tuple deleted has been inserted
* or a possibly referenced key value has changed in this
* transaction.
* ----------
*/
if
(
oldtup
->
t_data
->
t_xmin
!=
GetCurrentTransactionId
())
break
;
/* ----------
* Look at the previous event to the same tuple if
* any of its triggers has already been executed.
* Such a situation would potentially violate RI
* so we abort the transaction.
* Look at the previous event to the same tuple.
* ----------
*/
prev_event
=
deferredTriggerGetPreviousEvent
(
rel
->
rd_id
,
&
oldctid
);
if
(
prev_event
->
dte_event
&
TRIGGER_DEFERRED_HAS_BEFORE
||
(
prev_event
->
dte_n_items
!=
0
&&
prev_event
->
dte_event
&
TRIGGER_DEFERRED_DONE
))
prev_done
=
true
;
else
for
(
i
=
0
;
i
<
prev_event
->
dte_n_items
;
i
++
)
{
if
(
prev_event
->
dte_item
[
i
].
dti_state
&
TRIGGER_DEFERRED_DONE
)
{
prev_done
=
true
;
break
;
}
}
if
(
prev_done
)
{
elog
(
NOTICE
,
"DELETE of row inserted/updated in same "
"transaction violates"
);
elog
(
NOTICE
,
"referential integrity semantics. Other "
"triggers or IMMEDIATE "
);
elog
(
ERROR
,
" constraints have already been executed."
);
}
/* ----------
* Anything's fine so far - i.e. none of the previous
* events triggers has been executed up to now. Let's
* the REAL event that happened so far.
* ----------
*/
switch
(
prev_event
->
dte_event
&
TRIGGER_EVENT_OPMASK
)
{
case
TRIGGER_EVENT_INSERT
:
/* ----------
* The previous operation was an insert.
* So the REAL new event is nothing.
* ----------
*/
deferredTriggerCancelEvent
(
prev_event
);
return
;
case
TRIGGER_EVENT_UPDATE
:
/* ----------
* The previous operation was an UPDATE.
* So the REAL new event is a DELETE
* but from the original tuple.
* ----------
*/
event
=
TRIGGER_EVENT_DELETE
;
ItemPointerCopy
(
&
(
prev_event
->
dte_oldctid
),
&
oldctid
);
deferredTriggerCancelEvent
(
prev_event
);
break
;
}
if
(
prev_event
->
dte_event
&
TRIGGER_DEFERRED_KEY_CHANGED
)
elog
(
ERROR
,
"triggered data change violation "
"on relation
\"
%s
\"
"
,
nameout
(
&
(
rel
->
rd_rel
->
relname
)));
break
;
}
/* ----------
*
Create a new event and save it
.
*
Anything's fine up to here. Add the new event to the queue
.
* ----------
*/
oldcxt
=
MemoryContextSwitchTo
((
MemoryContext
)
deftrig_cxt
);
ntriggers
=
rel
->
trigdesc
->
n_after_row
[
event
];
triggers
=
rel
->
trigdesc
->
tg_after_row
[
event
];
new_size
=
sizeof
(
DeferredTriggerEventData
)
+
ntriggers
*
sizeof
(
DeferredTriggerEventItem
);
new_event
=
(
DeferredTriggerEvent
)
palloc
(
new_size
);
new_event
->
dte_event
=
event
;
new_event
->
dte_relid
=
rel
->
rd_id
;
ItemPointerCopy
(
&
oldctid
,
&
(
new_event
->
dte_oldctid
));
ItemPointerCopy
(
&
newctid
,
&
(
new_event
->
dte_newctid
));
new_event
->
dte_n_items
=
ntriggers
;
new_event
->
dte_item
[
ntriggers
].
dti_state
=
new_size
;
for
(
i
=
0
;
i
<
ntriggers
;
i
++
)
{
new_event
->
dte_item
[
i
].
dti_tgoid
=
triggers
[
i
]
->
tgoid
;
new_event
->
dte_item
[
i
].
dti_state
=
((
triggers
[
i
]
->
tgdeferrable
)
?
TRIGGER_DEFERRED_DEFERRABLE
:
0
)
|
((
triggers
[
i
]
->
tginitdeferred
)
?
TRIGGER_DEFERRED_INITDEFERRED
:
0
)
|
((
rel
->
trigdesc
->
n_before_row
[
event
]
>
0
)
?
TRIGGER_DEFERRED_HAS_BEFORE
:
0
);
}
deferredTriggerAddEvent
(
new_event
);
MemoryContextSwitchTo
(
oldcxt
);
return
;
...
...
src/backend/parser/analyze.c
View file @
b7b6d4bf
...
...
@@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: analyze.c,v 1.12
6 1999/12/10 07:37:35 tgl
Exp $
* $Id: analyze.c,v 1.12
7 2000/01/06 20:46:49 wieck
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -1015,141 +1015,141 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
extras_after
=
lappend
(
extras_after
,
(
Node
*
)
fk_trigger
);
if
((
fkconstraint
->
actions
&
FKCONSTR_ON_DELETE_MASK
)
!=
0
)
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the
* ON DELETE action fired on the PK table !!!
*
*/
fk_trigger
=
(
CreateTrigStmt
*
)
makeNode
(
CreateTrigStmt
);
fk_trigger
->
trigname
=
fkconstraint
->
constr_name
;
fk_trigger
->
relname
=
fkconstraint
->
pktable_name
;
switch
((
fkconstraint
->
actions
&
FKCONSTR_ON_DELETE_MASK
)
>>
FKCONSTR_ON_DELETE_SHIFT
)
{
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the
* ON DELETE action fired on the PK table !!!
*
*/
fk_trigger
=
(
CreateTrigStmt
*
)
makeNode
(
CreateTrigStmt
);
fk_trigger
->
trigname
=
fkconstraint
->
constr_name
;
fk_trigger
->
relname
=
fkconstraint
->
pktable_name
;
switch
((
fkconstraint
->
actions
&
FKCONSTR_ON_DELETE_MASK
)
>>
FKCONSTR_ON_DELETE_SHIFT
)
{
case
FKCONSTR_ON_KEY_RESTRICT
:
fk_trigger
->
funcname
=
"RI_FKey_restrict_del"
;
break
;
case
FKCONSTR_ON_KEY_CASCADE
:
fk_trigger
->
funcname
=
"RI_FKey_cascade_del"
;
break
;
case
FKCONSTR_ON_KEY_SETNULL
:
fk_trigger
->
funcname
=
"RI_FKey_setnull_del"
;
break
;
case
FKCONSTR_ON_KEY_SETDEFAULT
:
fk_trigger
->
funcname
=
"RI_FKey_setdefault_del"
;
break
;
default:
elog
(
ERROR
,
"Only one ON DELETE action can be specified for FOREIGN KEY constraint"
);
break
;
}
fk_trigger
->
before
=
false
;
fk_trigger
->
row
=
true
;
fk_trigger
->
actions
[
0
]
=
'd'
;
fk_trigger
->
actions
[
1
]
=
'\0'
;
fk_trigger
->
lang
=
NULL
;
fk_trigger
->
text
=
NULL
;
fk_trigger
->
attr
=
NIL
;
fk_trigger
->
when
=
NULL
;
fk_trigger
->
isconstraint
=
true
;
fk_trigger
->
deferrable
=
fkconstraint
->
deferrable
;
fk_trigger
->
initdeferred
=
fkconstraint
->
initdeferred
;
fk_trigger
->
constrrelname
=
stmt
->
relname
;
fk_trigger
->
args
=
NIL
;
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
constr_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
stmt
->
relname
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
pktable_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
match_type
);
fk_attr
=
fkconstraint
->
fk_attrs
;
pk_attr
=
fkconstraint
->
pk_attrs
;
while
(
fk_attr
!=
NIL
)
{
id
=
(
Ident
*
)
lfirst
(
fk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
case
FKCONSTR_ON_KEY_NOACTION
:
fk_trigger
->
funcname
=
"RI_FKey_noaction_del"
;
break
;
case
FKCONSTR_ON_KEY_RESTRICT
:
fk_trigger
->
funcname
=
"RI_FKey_restrict_del"
;
break
;
case
FKCONSTR_ON_KEY_CASCADE
:
fk_trigger
->
funcname
=
"RI_FKey_cascade_del"
;
break
;
case
FKCONSTR_ON_KEY_SETNULL
:
fk_trigger
->
funcname
=
"RI_FKey_setnull_del"
;
break
;
case
FKCONSTR_ON_KEY_SETDEFAULT
:
fk_trigger
->
funcname
=
"RI_FKey_setdefault_del"
;
break
;
default:
elog
(
ERROR
,
"Only one ON DELETE action can be specified for FOREIGN KEY constraint"
);
break
;
}
fk_trigger
->
before
=
false
;
fk_trigger
->
row
=
true
;
fk_trigger
->
actions
[
0
]
=
'd'
;
fk_trigger
->
actions
[
1
]
=
'\0'
;
fk_trigger
->
lang
=
NULL
;
fk_trigger
->
text
=
NULL
;
fk_trigger
->
attr
=
NIL
;
fk_trigger
->
when
=
NULL
;
fk_trigger
->
isconstraint
=
true
;
fk_trigger
->
deferrable
=
fkconstraint
->
deferrable
;
fk_trigger
->
initdeferred
=
fkconstraint
->
initdeferred
;
fk_trigger
->
constrrelname
=
stmt
->
relname
;
id
=
(
Ident
*
)
lfirst
(
pk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
fk_trigger
->
args
=
NIL
;
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
constr_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
stmt
->
relname
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
pktable_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
match_type
);
fk_attr
=
fkconstraint
->
fk_attrs
;
pk_attr
=
fkconstraint
->
pk_attrs
;
while
(
fk_attr
!=
NIL
)
{
id
=
(
Ident
*
)
lfirst
(
fk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
fk_attr
=
lnext
(
fk_attr
);
pk_attr
=
lnext
(
pk_attr
);
}
id
=
(
Ident
*
)
lfirst
(
pk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
extras_after
=
lappend
(
extras_after
,
(
Node
*
)
fk_trigger
);
fk_attr
=
lnext
(
fk_attr
);
pk_attr
=
lnext
(
pk_attr
);
}
if
((
fkconstraint
->
actions
&
FKCONSTR_ON_UPDATE_MASK
)
!=
0
)
extras_after
=
lappend
(
extras_after
,
(
Node
*
)
fk_trigger
);
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the
* ON UPDATE action fired on the PK table !!!
*
*/
fk_trigger
=
(
CreateTrigStmt
*
)
makeNode
(
CreateTrigStmt
);
fk_trigger
->
trigname
=
fkconstraint
->
constr_name
;
fk_trigger
->
relname
=
fkconstraint
->
pktable_name
;
switch
((
fkconstraint
->
actions
&
FKCONSTR_ON_UPDATE_MASK
)
>>
FKCONSTR_ON_UPDATE_SHIFT
)
{
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the
* ON UPDATE action fired on the PK table !!!
*
*/
fk_trigger
=
(
CreateTrigStmt
*
)
makeNode
(
CreateTrigStmt
);
fk_trigger
->
trigname
=
fkconstraint
->
constr_name
;
fk_trigger
->
relname
=
fkconstraint
->
pktable_name
;
switch
((
fkconstraint
->
actions
&
FKCONSTR_ON_UPDATE_MASK
)
>>
FKCONSTR_ON_UPDATE_SHIFT
)
{
case
FKCONSTR_ON_KEY_RESTRICT
:
fk_trigger
->
funcname
=
"RI_FKey_restrict_upd"
;
break
;
case
FKCONSTR_ON_KEY_CASCADE
:
fk_trigger
->
funcname
=
"RI_FKey_cascade_upd"
;
break
;
case
FKCONSTR_ON_KEY_SETNULL
:
fk_trigger
->
funcname
=
"RI_FKey_setnull_upd"
;
break
;
case
FKCONSTR_ON_KEY_SETDEFAULT
:
fk_trigger
->
funcname
=
"RI_FKey_setdefault_upd"
;
break
;
default:
elog
(
ERROR
,
"Only one ON UPDATE action can be specified for FOREIGN KEY constraint"
);
break
;
}
fk_trigger
->
before
=
false
;
fk_trigger
->
row
=
true
;
fk_trigger
->
actions
[
0
]
=
'u'
;
fk_trigger
->
actions
[
1
]
=
'\0'
;
fk_trigger
->
lang
=
NULL
;
fk_trigger
->
text
=
NULL
;
fk_trigger
->
attr
=
NIL
;
fk_trigger
->
when
=
NULL
;
fk_trigger
->
isconstraint
=
true
;
fk_trigger
->
deferrable
=
fkconstraint
->
deferrable
;
fk_trigger
->
initdeferred
=
fkconstraint
->
initdeferred
;
fk_trigger
->
constrrelname
=
stmt
->
relname
;
fk_trigger
->
args
=
NIL
;
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
constr_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
stmt
->
relname
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
pktable_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
match_type
);
fk_attr
=
fkconstraint
->
fk_attrs
;
pk_attr
=
fkconstraint
->
pk_attrs
;
while
(
fk_attr
!=
NIL
)
{
id
=
(
Ident
*
)
lfirst
(
fk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
case
FKCONSTR_ON_KEY_NOACTION
:
fk_trigger
->
funcname
=
"RI_FKey_noaction_upd"
;
break
;
case
FKCONSTR_ON_KEY_RESTRICT
:
fk_trigger
->
funcname
=
"RI_FKey_restrict_upd"
;
break
;
case
FKCONSTR_ON_KEY_CASCADE
:
fk_trigger
->
funcname
=
"RI_FKey_cascade_upd"
;
break
;
case
FKCONSTR_ON_KEY_SETNULL
:
fk_trigger
->
funcname
=
"RI_FKey_setnull_upd"
;
break
;
case
FKCONSTR_ON_KEY_SETDEFAULT
:
fk_trigger
->
funcname
=
"RI_FKey_setdefault_upd"
;
break
;
default:
elog
(
ERROR
,
"Only one ON UPDATE action can be specified for FOREIGN KEY constraint"
);
break
;
}
fk_trigger
->
before
=
false
;
fk_trigger
->
row
=
true
;
fk_trigger
->
actions
[
0
]
=
'u'
;
fk_trigger
->
actions
[
1
]
=
'\0'
;
fk_trigger
->
lang
=
NULL
;
fk_trigger
->
text
=
NULL
;
fk_trigger
->
attr
=
NIL
;
fk_trigger
->
when
=
NULL
;
fk_trigger
->
isconstraint
=
true
;
fk_trigger
->
deferrable
=
fkconstraint
->
deferrable
;
fk_trigger
->
initdeferred
=
fkconstraint
->
initdeferred
;
fk_trigger
->
constrrelname
=
stmt
->
relname
;
id
=
(
Ident
*
)
lfirst
(
pk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
fk_trigger
->
args
=
NIL
;
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
constr_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
stmt
->
relname
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
pktable_name
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
fkconstraint
->
match_type
);
fk_attr
=
fkconstraint
->
fk_attrs
;
pk_attr
=
fkconstraint
->
pk_attrs
;
while
(
fk_attr
!=
NIL
)
{
id
=
(
Ident
*
)
lfirst
(
fk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
fk_attr
=
lnext
(
fk_attr
);
pk_attr
=
lnext
(
pk_attr
);
}
id
=
(
Ident
*
)
lfirst
(
pk_attr
);
fk_trigger
->
args
=
lappend
(
fk_trigger
->
args
,
id
->
name
);
extras_after
=
lappend
(
extras_after
,
(
Node
*
)
fk_trigger
);
fk_attr
=
lnext
(
fk_attr
);
pk_attr
=
lnext
(
pk_attr
);
}
extras_after
=
lappend
(
extras_after
,
(
Node
*
)
fk_trigger
);
}
}
...
...
src/backend/utils/adt/ri_triggers.c
View file @
b7b6d4bf
...
...
@@ -6,7 +6,7 @@
*
* 1999 Jan Wieck
*
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.1
1 2000/01/06 16:30:43
wieck Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.1
2 2000/01/06 20:46:51
wieck Exp $
*
* ----------
*/
...
...
@@ -466,6 +466,34 @@ RI_FKey_check_upd (FmgrInfo *proinfo)
}
/* ----------
* RI_FKey_noaction_del -
*
* This is here only to let the trigger manager trace for
* "triggered data change violation"
* ----------
*/
HeapTuple
RI_FKey_noaction_del
(
FmgrInfo
*
proinfo
)
{
return
NULL
;
}
/* ----------
* RI_FKey_noaction_upd -
*
* This is here only to let the trigger manager trace for
* "triggered data change violation"
* ----------
*/
HeapTuple
RI_FKey_noaction_upd
(
FmgrInfo
*
proinfo
)
{
return
NULL
;
}
/* ----------
* RI_FKey_cascade_del -
*
...
...
@@ -2252,6 +2280,101 @@ RI_FKey_setdefault_upd (FmgrInfo *proinfo)
}
/* ----------
* RI_FKey_keyequal_upd -
*
* Check if we have a key change on update.
*
* This is no real trigger procedure. It is used by the deferred
* trigger queue manager to detect "triggered data change violation".
* ----------
*/
bool
RI_FKey_keyequal_upd
(
void
)
{
TriggerData
*
trigdata
;
int
tgnargs
;
char
**
tgargs
;
Relation
fk_rel
;
Relation
pk_rel
;
HeapTuple
new_row
;
HeapTuple
old_row
;
RI_QueryKey
qkey
;
trigdata
=
CurrentTriggerData
;
CurrentTriggerData
=
NULL
;
/* ----------
* Check for the correct # of call arguments
* ----------
*/
tgnargs
=
trigdata
->
tg_trigger
->
tgnargs
;
tgargs
=
trigdata
->
tg_trigger
->
tgargs
;
if
(
tgnargs
<
4
||
(
tgnargs
%
2
)
!=
0
)
elog
(
ERROR
,
"wrong # of arguments in call to RI_FKey_keyequal_upd()"
);
if
(
tgnargs
>
RI_MAX_ARGUMENTS
)
elog
(
ERROR
,
"too many keys (%d max) in call to RI_FKey_keyequal_upd()"
,
RI_MAX_NUMKEYS
);
/* ----------
* Nothing to do if no column names to compare given
* ----------
*/
if
(
tgnargs
==
4
)
return
true
;
/* ----------
* Get the relation descriptors of the FK and PK tables and
* the new and old tuple.
* ----------
*/
fk_rel
=
heap_openr
(
tgargs
[
RI_FK_RELNAME_ARGNO
],
NoLock
);
pk_rel
=
trigdata
->
tg_relation
;
new_row
=
trigdata
->
tg_newtuple
;
old_row
=
trigdata
->
tg_trigtuple
;
switch
(
ri_DetermineMatchType
(
tgargs
[
RI_MATCH_TYPE_ARGNO
]))
{
/* ----------
* MATCH <UNSPECIFIED>
* ----------
*/
case
RI_MATCH_TYPE_UNSPECIFIED
:
elog
(
ERROR
,
"MATCH <unspecified> not implemented yet"
);
break
;
case
RI_MATCH_TYPE_FULL
:
ri_BuildQueryKeyFull
(
&
qkey
,
trigdata
->
tg_trigger
->
tgoid
,
0
,
fk_rel
,
pk_rel
,
tgnargs
,
tgargs
);
heap_close
(
fk_rel
,
NoLock
);
/* ----------
* Return if key's are equal
* ----------
*/
return
ri_KeysEqual
(
pk_rel
,
old_row
,
new_row
,
&
qkey
,
RI_KEYPAIR_PK_IDX
);
/* ----------
* Handle MATCH PARTIAL set null delete.
* ----------
*/
case
RI_MATCH_TYPE_PARTIAL
:
elog
(
ERROR
,
"MATCH PARTIAL not yet supported"
);
break
;
}
/* ----------
* Never reached
* ----------
*/
elog
(
ERROR
,
"internal error #9 in ri_triggers.c"
);
return
false
;
}
...
...
src/include/catalog/pg_proc.h
View file @
b7b6d4bf
...
...
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_proc.h,v 1.11
0 1999/12/28 13:40:50
wieck Exp $
* $Id: pg_proc.h,v 1.11
1 2000/01/06 20:46:54
wieck Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
...
...
@@ -2142,6 +2142,10 @@ DATA(insert OID = 1652 ( RI_FKey_setdefault_del PGUID 11 f t f 0 f 0 "" 100 0 0
DESCR
(
"referential integrity ON DELETE SET DEFAULT"
);
DATA
(
insert
OID
=
1653
(
RI_FKey_setdefault_upd
PGUID
11
f
t
f
0
f
0
""
100
0
0
100
RI_FKey_setdefault_upd
-
));
DESCR
(
"referential integrity ON UPDATE SET DEFAULT"
);
DATA
(
insert
OID
=
1654
(
RI_FKey_noaction_del
PGUID
11
f
t
f
0
f
0
""
100
0
0
100
RI_FKey_setdefault_del
-
));
DESCR
(
"referential integrity ON DELETE NO ACTION"
);
DATA
(
insert
OID
=
1655
(
RI_FKey_noaction_upd
PGUID
11
f
t
f
0
f
0
""
100
0
0
100
RI_FKey_setdefault_upd
-
));
DESCR
(
"referential integrity ON UPDATE NO ACTION"
);
/* for mac type support */
DATA
(
insert
OID
=
436
(
macaddr_in
PGUID
11
f
t
t
1
f
829
"0"
100
0
0
100
macaddr_in
-
));
...
...
src/include/commands/trigger.h
View file @
b7b6d4bf
...
...
@@ -37,7 +37,9 @@ extern DLLIMPORT TriggerData *CurrentTriggerData;
#define TRIGGER_DEFERRED_DEFERRABLE 0x00000040
#define TRIGGER_DEFERRED_INITDEFERRED 0x00000080
#define TRIGGER_DEFERRED_HAS_BEFORE 0x00000100
#define TRIGGER_DEFERRED_MASK 0x000001F0
#define TRIGGER_DEFERRED_ROW_INSERTED 0x00000200
#define TRIGGER_DEFERRED_KEY_CHANGED 0x00000400
#define TRIGGER_DEFERRED_MASK 0x000007F0
#define TRIGGER_FIRED_BY_INSERT(event) \
(((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \
...
...
@@ -117,4 +119,10 @@ extern void DeferredTriggerSaveEvent(Relation rel, int event,
HeapTuple
oldtup
,
HeapTuple
newtup
);
/*
* in utils/adt/ri_triggers.c
*
*/
extern
bool
RI_FKey_keyequal_upd
(
void
);
#endif
/* TRIGGER_H */
src/include/utils/builtins.h
View file @
b7b6d4bf
...
...
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: builtins.h,v 1.9
3 1999/12/28 13:40:52
wieck Exp $
* $Id: builtins.h,v 1.9
4 2000/01/06 20:47:01
wieck Exp $
*
* NOTES
* This should normally only be included by fmgr.h.
...
...
@@ -623,6 +623,8 @@ float64 numeric_float8(Numeric num);
/* ri_triggers.c */
HeapTuple
RI_FKey_check_ins
(
FmgrInfo
*
proinfo
);
HeapTuple
RI_FKey_check_upd
(
FmgrInfo
*
proinfo
);
HeapTuple
RI_FKey_noaction_del
(
FmgrInfo
*
proinfo
);
HeapTuple
RI_FKey_noaction_upd
(
FmgrInfo
*
proinfo
);
HeapTuple
RI_FKey_cascade_del
(
FmgrInfo
*
proinfo
);
HeapTuple
RI_FKey_cascade_upd
(
FmgrInfo
*
proinfo
);
HeapTuple
RI_FKey_restrict_del
(
FmgrInfo
*
proinfo
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment