1997-08-31 13:42:21 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* trigger.h
|
2000-05-29 03:59:17 +02:00
|
|
|
* Declarations for trigger handling.
|
1997-08-31 13:42:21 +02:00
|
|
|
*
|
2015-01-06 17:43:47 +01:00
|
|
|
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
|
2000-01-31 05:35:57 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/include/commands/trigger.h
|
1997-08-31 13:42:21 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#ifndef TRIGGER_H
|
|
|
|
#define TRIGGER_H
|
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
#include "catalog/objectaddress.h"
|
1998-12-15 13:47:01 +01:00
|
|
|
#include "nodes/execnodes.h"
|
2011-09-04 07:13:16 +02:00
|
|
|
#include "nodes/parsenodes.h"
|
1997-09-01 10:10:12 +02:00
|
|
|
|
2000-05-29 03:59:17 +02:00
|
|
|
/*
|
|
|
|
* TriggerData is the node type that is passed as fmgr "context" info
|
|
|
|
* when a function is called by the trigger manager.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define CALLED_AS_TRIGGER(fcinfo) \
|
|
|
|
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
typedef uint32 TriggerEvent;
|
1997-09-01 10:10:12 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
typedef struct TriggerData
|
|
|
|
{
|
2000-05-29 03:59:17 +02:00
|
|
|
NodeTag type;
|
1997-09-08 04:41:22 +02:00
|
|
|
TriggerEvent tg_event;
|
|
|
|
Relation tg_relation;
|
|
|
|
HeapTuple tg_trigtuple;
|
|
|
|
HeapTuple tg_newtuple;
|
|
|
|
Trigger *tg_trigger;
|
2004-10-30 22:53:06 +02:00
|
|
|
Buffer tg_trigtuplebuf;
|
|
|
|
Buffer tg_newtuplebuf;
|
1998-02-26 05:46:47 +01:00
|
|
|
} TriggerData;
|
1997-09-01 10:10:12 +02:00
|
|
|
|
2008-03-28 01:21:56 +01:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* TriggerEvent bit flags
|
2008-03-28 01:21:56 +01:00
|
|
|
*
|
|
|
|
* Note that we assume different event types (INSERT/DELETE/UPDATE/TRUNCATE)
|
|
|
|
* can't be OR'd together in a single TriggerEvent. This is unlike the
|
|
|
|
* situation for pg_trigger rows, so pg_trigger.tgtype uses a different
|
|
|
|
* representation!
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
#define TRIGGER_EVENT_INSERT 0x00000000
|
|
|
|
#define TRIGGER_EVENT_DELETE 0x00000001
|
|
|
|
#define TRIGGER_EVENT_UPDATE 0x00000002
|
2008-03-28 01:21:56 +01:00
|
|
|
#define TRIGGER_EVENT_TRUNCATE 0x00000003
|
1997-09-07 07:04:48 +02:00
|
|
|
#define TRIGGER_EVENT_OPMASK 0x00000003
|
2010-10-10 19:43:33 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#define TRIGGER_EVENT_ROW 0x00000004
|
2010-10-10 19:43:33 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#define TRIGGER_EVENT_BEFORE 0x00000008
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_EVENT_AFTER 0x00000000
|
|
|
|
#define TRIGGER_EVENT_INSTEAD 0x00000010
|
|
|
|
#define TRIGGER_EVENT_TIMINGMASK 0x00000018
|
1997-09-01 10:10:12 +02:00
|
|
|
|
2004-09-10 20:40:09 +02:00
|
|
|
/* More TriggerEvent flags, used only within trigger.c */
|
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define AFTER_TRIGGER_DEFERRABLE 0x00000020
|
|
|
|
#define AFTER_TRIGGER_INITDEFERRED 0x00000040
|
1999-09-29 18:06:40 +02:00
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_FIRED_BY_INSERT(event) \
|
|
|
|
(((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT)
|
1997-09-01 10:10:12 +02:00
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_FIRED_BY_DELETE(event) \
|
|
|
|
(((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE)
|
1997-09-01 10:10:12 +02:00
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_FIRED_BY_UPDATE(event) \
|
|
|
|
(((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE)
|
1997-09-04 15:26:19 +02:00
|
|
|
|
2008-03-28 01:21:56 +01:00
|
|
|
#define TRIGGER_FIRED_BY_TRUNCATE(event) \
|
2010-10-10 19:43:33 +02:00
|
|
|
(((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE)
|
|
|
|
|
|
|
|
#define TRIGGER_FIRED_FOR_ROW(event) \
|
|
|
|
((event) & TRIGGER_EVENT_ROW)
|
2008-03-28 01:21:56 +01:00
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_FIRED_FOR_STATEMENT(event) \
|
|
|
|
(!TRIGGER_FIRED_FOR_ROW(event))
|
1997-09-04 15:26:19 +02:00
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_FIRED_BEFORE(event) \
|
|
|
|
(((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE)
|
1997-09-04 15:26:19 +02:00
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_FIRED_AFTER(event) \
|
|
|
|
(((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER)
|
1997-09-04 15:26:19 +02:00
|
|
|
|
2010-10-10 19:43:33 +02:00
|
|
|
#define TRIGGER_FIRED_INSTEAD(event) \
|
|
|
|
(((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD)
|
1997-09-01 10:10:12 +02:00
|
|
|
|
2007-03-20 00:38:32 +01:00
|
|
|
/*
|
2010-10-10 19:43:33 +02:00
|
|
|
* Definitions for replication role based firing.
|
2007-03-20 00:38:32 +01:00
|
|
|
*/
|
|
|
|
#define SESSION_REPLICATION_ROLE_ORIGIN 0
|
|
|
|
#define SESSION_REPLICATION_ROLE_REPLICA 1
|
|
|
|
#define SESSION_REPLICATION_ROLE_LOCAL 2
|
2009-06-11 16:49:15 +02:00
|
|
|
extern PGDLLIMPORT int SessionReplicationRole;
|
2007-03-20 00:38:32 +01:00
|
|
|
|
2009-01-22 20:16:31 +01:00
|
|
|
/*
|
|
|
|
* States at which a trigger can be fired. These are the
|
|
|
|
* possible values for pg_trigger.tgenabled.
|
|
|
|
*/
|
2007-11-15 22:14:46 +01:00
|
|
|
#define TRIGGER_FIRES_ON_ORIGIN 'O'
|
|
|
|
#define TRIGGER_FIRES_ALWAYS 'A'
|
|
|
|
#define TRIGGER_FIRES_ON_REPLICA 'R'
|
|
|
|
#define TRIGGER_DISABLED 'D'
|
1997-09-01 10:10:12 +02:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
extern ObjectAddress CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
|
Avoid repeated name lookups during table and index DDL.
If the name lookups come to different conclusions due to concurrent
activity, we might perform some parts of the DDL on a different table
than other parts. At least in the case of CREATE INDEX, this can be
used to cause the permissions checks to be performed against a
different table than the index creation, allowing for a privilege
escalation attack.
This changes the calling convention for DefineIndex, CreateTrigger,
transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible
(in 9.2 and newer), and AlterTable (in 9.1 and older). In addition,
CheckRelationOwnership is removed in 9.2 and newer and the calling
convention is changed in older branches. A field has also been added
to the Constraint node (FkConstraint in 8.4). Third-party code calling
these functions or using the Constraint node will require updating.
Report by Andres Freund. Patch by Robert Haas and Andres Freund,
reviewed by Tom Lane.
Security: CVE-2014-0062
2014-02-17 15:33:31 +01:00
|
|
|
Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid,
|
2010-01-17 23:56:23 +01:00
|
|
|
bool isInternal);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
extern void RemoveTriggerById(Oid trigOid);
|
2011-04-10 17:42:00 +02:00
|
|
|
extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok);
|
1997-08-31 13:42:21 +02:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
extern ObjectAddress renametrig(RenameStmt *stmt);
|
2002-04-26 21:29:47 +02:00
|
|
|
|
2005-08-24 00:40:47 +02:00
|
|
|
extern void EnableDisableTrigger(Relation rel, const char *tgname,
|
2007-03-20 00:38:32 +01:00
|
|
|
char fires_when, bool skip_system);
|
2005-08-24 00:40:47 +02:00
|
|
|
|
2000-01-31 05:35:57 +01:00
|
|
|
extern void RelationBuildTriggers(Relation relation);
|
|
|
|
|
2002-10-14 18:51:30 +02:00
|
|
|
extern TriggerDesc *CopyTriggerDesc(TriggerDesc *trigdesc);
|
2000-01-31 05:35:57 +01:00
|
|
|
|
2002-10-14 18:51:30 +02:00
|
|
|
extern void FreeTriggerDesc(TriggerDesc *trigdesc);
|
2000-01-31 05:35:57 +01:00
|
|
|
|
2002-11-23 04:59:09 +01:00
|
|
|
extern void ExecBSInsertTriggers(EState *estate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo);
|
2002-11-23 04:59:09 +01:00
|
|
|
extern void ExecASInsertTriggers(EState *estate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo);
|
2011-02-22 03:18:04 +01:00
|
|
|
extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
ResultRelInfo *relinfo,
|
2011-02-22 03:18:04 +01:00
|
|
|
TupleTableSlot *slot);
|
2001-01-22 01:50:07 +01:00
|
|
|
extern void ExecARInsertTriggers(EState *estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
ResultRelInfo *relinfo,
|
2009-07-29 22:56:21 +02:00
|
|
|
HeapTuple trigtuple,
|
|
|
|
List *recheckIndexes);
|
2011-02-22 03:18:04 +01:00
|
|
|
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
|
2010-10-10 19:43:33 +02:00
|
|
|
ResultRelInfo *relinfo,
|
2011-02-22 03:18:04 +01:00
|
|
|
TupleTableSlot *slot);
|
2002-11-23 04:59:09 +01:00
|
|
|
extern void ExecBSDeleteTriggers(EState *estate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo);
|
2002-11-23 04:59:09 +01:00
|
|
|
extern void ExecASDeleteTriggers(EState *estate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo);
|
2001-06-01 04:41:36 +02:00
|
|
|
extern bool ExecBRDeleteTriggers(EState *estate,
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
2009-10-26 03:26:45 +01:00
|
|
|
EPQState *epqstate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo,
|
2014-03-23 07:16:34 +01:00
|
|
|
ItemPointer tupleid,
|
|
|
|
HeapTuple fdw_trigtuple);
|
2001-06-01 04:41:36 +02:00
|
|
|
extern void ExecARDeleteTriggers(EState *estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
ResultRelInfo *relinfo,
|
2014-03-23 07:16:34 +01:00
|
|
|
ItemPointer tupleid,
|
|
|
|
HeapTuple fdw_trigtuple);
|
2010-10-10 19:43:33 +02:00
|
|
|
extern bool ExecIRDeleteTriggers(EState *estate,
|
|
|
|
ResultRelInfo *relinfo,
|
|
|
|
HeapTuple trigtuple);
|
2002-11-23 04:59:09 +01:00
|
|
|
extern void ExecBSUpdateTriggers(EState *estate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo);
|
2002-11-23 04:59:09 +01:00
|
|
|
extern void ExecASUpdateTriggers(EState *estate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo);
|
2011-02-22 03:18:04 +01:00
|
|
|
extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
2009-10-26 03:26:45 +01:00
|
|
|
EPQState *epqstate,
|
2003-08-04 02:43:34 +02:00
|
|
|
ResultRelInfo *relinfo,
|
|
|
|
ItemPointer tupleid,
|
2014-03-23 07:16:34 +01:00
|
|
|
HeapTuple fdw_trigtuple,
|
2011-02-22 03:18:04 +01:00
|
|
|
TupleTableSlot *slot);
|
2001-06-01 04:41:36 +02:00
|
|
|
extern void ExecARUpdateTriggers(EState *estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
ResultRelInfo *relinfo,
|
|
|
|
ItemPointer tupleid,
|
2014-03-23 07:16:34 +01:00
|
|
|
HeapTuple fdw_trigtuple,
|
2009-07-29 22:56:21 +02:00
|
|
|
HeapTuple newtuple,
|
|
|
|
List *recheckIndexes);
|
2011-02-22 03:18:04 +01:00
|
|
|
extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate,
|
2010-10-10 19:43:33 +02:00
|
|
|
ResultRelInfo *relinfo,
|
2011-02-22 03:18:04 +01:00
|
|
|
HeapTuple trigtuple,
|
|
|
|
TupleTableSlot *slot);
|
2008-03-28 01:21:56 +01:00
|
|
|
extern void ExecBSTruncateTriggers(EState *estate,
|
2009-06-11 16:49:15 +02:00
|
|
|
ResultRelInfo *relinfo);
|
2008-03-28 01:21:56 +01:00
|
|
|
extern void ExecASTruncateTriggers(EState *estate,
|
2009-06-11 16:49:15 +02:00
|
|
|
ResultRelInfo *relinfo);
|
1999-09-29 18:06:40 +02:00
|
|
|
|
2004-09-10 20:40:09 +02:00
|
|
|
extern void AfterTriggerBeginXact(void);
|
|
|
|
extern void AfterTriggerBeginQuery(void);
|
2005-03-25 22:58:00 +01:00
|
|
|
extern void AfterTriggerEndQuery(EState *estate);
|
2005-04-11 21:51:16 +02:00
|
|
|
extern void AfterTriggerFireDeferred(void);
|
|
|
|
extern void AfterTriggerEndXact(bool isCommit);
|
2004-09-10 20:40:09 +02:00
|
|
|
extern void AfterTriggerBeginSubXact(void);
|
|
|
|
extern void AfterTriggerEndSubXact(bool isCommit);
|
|
|
|
extern void AfterTriggerSetState(ConstraintsSetStmt *stmt);
|
2008-01-03 00:34:42 +01:00
|
|
|
extern bool AfterTriggerPendingOnRel(Oid relid);
|
1999-09-29 18:06:40 +02:00
|
|
|
|
|
|
|
|
2000-01-06 21:47:01 +01:00
|
|
|
/*
|
|
|
|
* in utils/adt/ri_triggers.c
|
|
|
|
*/
|
2012-06-20 02:07:08 +02:00
|
|
|
extern bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
|
|
|
|
HeapTuple old_row, HeapTuple new_row);
|
|
|
|
extern bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
|
|
|
|
HeapTuple old_row, HeapTuple new_row);
|
2007-02-14 02:58:58 +01:00
|
|
|
extern bool RI_Initial_Check(Trigger *trigger,
|
2007-11-15 22:14:46 +01:00
|
|
|
Relation fk_rel, Relation pk_rel);
|
2001-10-28 07:26:15 +01:00
|
|
|
|
2007-02-14 02:58:58 +01:00
|
|
|
/* result values for RI_FKey_trigger_type: */
|
2005-05-30 09:20:59 +02:00
|
|
|
#define RI_TRIGGER_PK 1 /* is a trigger on the PK relation */
|
|
|
|
#define RI_TRIGGER_FK 2 /* is a trigger on the FK relation */
|
|
|
|
#define RI_TRIGGER_NONE 0 /* is not an RI trigger function */
|
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
extern int RI_FKey_trigger_type(Oid tgfoid);
|
2005-05-30 09:20:59 +02:00
|
|
|
|
2012-01-25 17:15:29 +01:00
|
|
|
extern Datum pg_trigger_depth(PG_FUNCTION_ARGS);
|
|
|
|
|
2001-11-05 18:46:40 +01:00
|
|
|
#endif /* TRIGGER_H */
|