mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Second round of fmgr changes: triggers are now invoked in new style,
CurrentTriggerData is history.
This commit is contained in:
@ -56,7 +56,8 @@ sub-string will fit.
|
||||
|
||||
The create the function that contains the trigger::
|
||||
|
||||
create function fti() returns opaque as '/path/to/fti.so' language 'C';
|
||||
create function fti() returns opaque as
|
||||
'/path/to/fti.so' language 'newC';
|
||||
|
||||
And finally define the trigger on the 'cds' table:
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
Example:
|
||||
|
||||
create function fti() returns opaque as
|
||||
'/home/boekhold/src/postgresql-6.2/contrib/fti/fti.so' language 'c';
|
||||
'/home/boekhold/src/postgresql-6.2/contrib/fti/fti.so' language 'newC';
|
||||
|
||||
create table title_fti (string varchar(25), id oid);
|
||||
create index title_fti_idx on title_fti (string);
|
||||
@ -61,11 +61,11 @@ select p.* from product p, title_fti f1, title_fti f2 where
|
||||
that can build the final query automatigally?
|
||||
*/
|
||||
|
||||
HeapTuple fti(void);
|
||||
char *breakup(char *, char *);
|
||||
bool is_stopword(char *);
|
||||
extern Datum fti(PG_FUNCTION_ARGS);
|
||||
static char *breakup(char *, char *);
|
||||
static bool is_stopword(char *);
|
||||
|
||||
bool new_tuple = false;
|
||||
static bool new_tuple = false;
|
||||
|
||||
|
||||
/* THIS LIST MUST BE IN SORTED ORDER, A BINARY SEARCH IS USED!!!! */
|
||||
@ -93,9 +93,10 @@ static int nDeletePlans = 0;
|
||||
static EPlan *find_plan(char *ident, EPlan ** eplan, int *nplans);
|
||||
|
||||
/***********************************************************************/
|
||||
HeapTuple
|
||||
fti()
|
||||
Datum
|
||||
fti(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
int nargs; /* # of arguments */
|
||||
char **args; /* arguments */
|
||||
@ -119,32 +120,29 @@ fti()
|
||||
* function\n"); fflush(debug);
|
||||
*/
|
||||
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "Full Text Indexing: triggers are not initialized");
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "Full Text Indexing: not fired by trigger manager");
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "Full Text Indexing: can't process STATEMENT events");
|
||||
if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
||||
elog(ERROR, "Full Text Indexing: must be fired AFTER event");
|
||||
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
isinsert = true;
|
||||
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
{
|
||||
isdelete = true;
|
||||
isinsert = true;
|
||||
}
|
||||
if (TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
||||
isdelete = true;
|
||||
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
trigger = trigdata->tg_trigger;
|
||||
rel = trigdata->tg_relation;
|
||||
relname = SPI_getrelname(rel);
|
||||
rettuple = CurrentTriggerData->tg_trigtuple;
|
||||
rettuple = trigdata->tg_trigtuple;
|
||||
if (isdelete && isinsert) /* is an UPDATE */
|
||||
rettuple = CurrentTriggerData->tg_newtuple;
|
||||
|
||||
CurrentTriggerData = NULL; /* invalidate 'normal' calls to this
|
||||
* function */
|
||||
rettuple = trigdata->tg_newtuple;
|
||||
|
||||
if ((ret = SPI_connect()) < 0)
|
||||
elog(ERROR, "Full Text Indexing: SPI_connect failed, returned %d\n", ret);
|
||||
@ -289,10 +287,10 @@ fti()
|
||||
}
|
||||
|
||||
SPI_finish();
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
|
||||
char *
|
||||
static char *
|
||||
breakup(char *string, char *substring)
|
||||
{
|
||||
static char *last_start;
|
||||
@ -342,7 +340,7 @@ breakup(char *string, char *substring)
|
||||
}
|
||||
|
||||
/* copied from src/backend/parser/keywords.c and adjusted for our situation*/
|
||||
bool
|
||||
static bool
|
||||
is_stopword(char *text)
|
||||
{
|
||||
char **StopLow; /* for list of stop-words */
|
||||
|
@ -29,7 +29,7 @@
|
||||
#
|
||||
# create function fti() returns opaque as
|
||||
# '/path/to/fti/file/fti.so'
|
||||
# language 'C';
|
||||
# language 'newC';
|
||||
#
|
||||
# create trigger my_fti_trigger after update or insert or delete
|
||||
# on mytable
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* PostgreSQL type definitions for managed LargeObjects.
|
||||
*
|
||||
* $Id: lo.c,v 1.2 1999/05/25 16:05:45 momjian Exp $
|
||||
* $Id: lo.c,v 1.3 2000/05/29 01:59:02 tgl Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
@ -37,7 +37,7 @@ Blob *lo_in(char *str); /* Create from String */
|
||||
char *lo_out(Blob * addr);/* Output oid as String */
|
||||
Oid lo_oid(Blob * addr);/* Return oid as an oid */
|
||||
Blob *lo(Oid oid); /* Return Blob based on oid */
|
||||
HeapTuple lo_manage(void); /* Trigger handler */
|
||||
Datum lo_manage(PG_FUNCTION_ARGS); /* Trigger handler */
|
||||
|
||||
/*
|
||||
* This creates a large object, and set's its OID to the value in the
|
||||
@ -139,9 +139,10 @@ lo(Oid oid)
|
||||
/*
|
||||
* This handles the trigger that protects us from orphaned large objects
|
||||
*/
|
||||
HeapTuple
|
||||
lo_manage(void)
|
||||
Datum
|
||||
lo_manage(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
int attnum; /* attribute number to monitor */
|
||||
char **args; /* Args containing attr name */
|
||||
TupleDesc tupdesc; /* Tuple Descriptor */
|
||||
@ -150,28 +151,25 @@ lo_manage(void)
|
||||
HeapTuple newtuple = NULL;/* The new value for tuple */
|
||||
HeapTuple trigtuple; /* The original value of tuple */
|
||||
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "lo: triggers are not initialized");
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "lo: not fired by trigger manager");
|
||||
|
||||
/*
|
||||
* Fetch some values from CurrentTriggerData
|
||||
* Fetch some values from trigdata
|
||||
*/
|
||||
newtuple = CurrentTriggerData->tg_newtuple;
|
||||
trigtuple = CurrentTriggerData->tg_trigtuple;
|
||||
tupdesc = CurrentTriggerData->tg_relation->rd_att;
|
||||
args = CurrentTriggerData->tg_trigger->tgargs;
|
||||
newtuple = trigdata->tg_newtuple;
|
||||
trigtuple = trigdata->tg_trigtuple;
|
||||
tupdesc = trigdata->tg_relation->rd_att;
|
||||
args = trigdata->tg_trigger->tgargs;
|
||||
|
||||
/* tuple to return to Executor */
|
||||
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
rettuple = newtuple;
|
||||
else
|
||||
rettuple = trigtuple;
|
||||
|
||||
/* Are we deleting the row? */
|
||||
isdelete = TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event);
|
||||
|
||||
/* Were done with it */
|
||||
CurrentTriggerData = NULL;
|
||||
isdelete = TRIGGER_FIRED_BY_DELETE(trigdata->tg_event);
|
||||
|
||||
/* Get the column were interested in */
|
||||
attnum = SPI_fnumber(tupdesc, args[0]);
|
||||
@ -214,5 +212,5 @@ lo_manage(void)
|
||||
}
|
||||
}
|
||||
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
--
|
||||
-- PostgreSQL code for LargeObjects
|
||||
--
|
||||
-- $Id: lo.sql.in,v 1.1 1998/06/16 07:07:11 momjian Exp $
|
||||
-- $Id: lo.sql.in,v 1.2 2000/05/29 01:59:02 tgl Exp $
|
||||
--
|
||||
|
||||
load '_OBJWD_/lo_DLSUFFIX_';
|
||||
@ -47,7 +47,7 @@ create function lo(oid)
|
||||
create function lo_manage()
|
||||
returns opaque
|
||||
as '_OBJWD_/lo_DLSUFFIX_'
|
||||
language 'c';
|
||||
language 'newC';
|
||||
|
||||
-- This allows us to map lo to oid
|
||||
--
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "commands/trigger.h" /* -"- and triggers */
|
||||
#include <ctype.h> /* tolower () */
|
||||
|
||||
HeapTuple noup(void);
|
||||
extern Datum noup(PG_FUNCTION_ARGS);
|
||||
|
||||
/*
|
||||
* noup () -- revoke permission on column
|
||||
@ -16,9 +16,10 @@ HeapTuple noup(void);
|
||||
* EXECUTE PROCEDURE noup ('col').
|
||||
*/
|
||||
|
||||
HeapTuple /* have to return HeapTuple to Executor */
|
||||
noup()
|
||||
Datum
|
||||
noup(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
int nargs; /* # of args specified in CREATE TRIGGER */
|
||||
char **args; /* arguments: column names and table name */
|
||||
@ -36,42 +37,35 @@ noup()
|
||||
*/
|
||||
|
||||
/* Called by trigger manager ? */
|
||||
if (!CurrentTriggerData)
|
||||
elog(WARN, "noup: triggers are not initialized");
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "noup: not fired by trigger manager");
|
||||
|
||||
/* Should be called for ROW trigger */
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
elog(WARN, "noup: can't process STATEMENT events");
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "noup: can't process STATEMENT events");
|
||||
|
||||
/* Not should be called for INSERT */
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
elog(WARN, "noup: can't process INSERT events");
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
elog(ERROR, "noup: can't process INSERT events");
|
||||
|
||||
/* Not should be called for DELETE */
|
||||
else if (TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event))
|
||||
elog(WARN, "noup: can't process DELETE events");
|
||||
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
||||
elog(ERROR, "noup: can't process DELETE events");
|
||||
|
||||
/* check new Tuple */
|
||||
tuple = CurrentTriggerData->tg_newtuple;
|
||||
tuple = trigdata->tg_newtuple;
|
||||
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
trigger = trigdata->tg_trigger;
|
||||
nargs = trigger->tgnargs;
|
||||
args = trigger->tgargs;
|
||||
|
||||
nkeys = nargs;
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
rel = trigdata->tg_relation;
|
||||
tupdesc = rel->rd_att;
|
||||
|
||||
/*
|
||||
* Setting CurrentTriggerData to NULL prevents direct calls to trigger
|
||||
* functions in queries. Normally, trigger functions have to be called
|
||||
* by trigger manager code only.
|
||||
*/
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
/* Connect to SPI manager */
|
||||
if ((ret = SPI_connect()) < 0)
|
||||
elog(WARN, "noup: SPI_connect returned %d", ret);
|
||||
elog(ERROR, "noup: SPI_connect returned %d", ret);
|
||||
|
||||
/*
|
||||
* We use SPI plan preparation feature, so allocate space to place key
|
||||
@ -87,7 +81,7 @@ noup()
|
||||
|
||||
/* Bad guys may give us un-existing column in CREATE TRIGGER */
|
||||
if (fnumber < 0)
|
||||
elog(WARN, "noup: there is no attribute %s in relation %s",
|
||||
elog(ERROR, "noup: there is no attribute %s in relation %s",
|
||||
args[i], SPI_getrelname(rel));
|
||||
|
||||
/* Well, get binary (in internal format) value of column */
|
||||
@ -99,13 +93,13 @@ noup()
|
||||
if (!isnull)
|
||||
{
|
||||
|
||||
elog(WARN, "%s: update not allowed", args[i]);
|
||||
elog(NOTICE, "%s: update not allowed", args[i]);
|
||||
SPI_finish();
|
||||
return NULL;
|
||||
return PointerGetDatum(NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SPI_finish();
|
||||
return (tuple);
|
||||
return PointerGetDatum(tuple);
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ DROP FUNCTION noup ();
|
||||
CREATE FUNCTION noup ()
|
||||
RETURNS opaque
|
||||
AS '_OBJWD_/noup_DLSUFFIX_'
|
||||
LANGUAGE 'c'
|
||||
LANGUAGE 'newC'
|
||||
;
|
||||
|
@ -2,13 +2,13 @@
|
||||
#include "executor/spi.h" /* this is what you need to work with SPI */
|
||||
#include "commands/trigger.h" /* -"- and triggers */
|
||||
|
||||
HeapTuple autoinc(void);
|
||||
|
||||
extern Datum autoinc(PG_FUNCTION_ARGS);
|
||||
extern int4 nextval(struct varlena * seqin);
|
||||
|
||||
HeapTuple
|
||||
autoinc()
|
||||
Datum
|
||||
autoinc(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
int nargs; /* # of arguments */
|
||||
int *chattrs; /* attnums of attributes to change */
|
||||
@ -22,24 +22,24 @@ autoinc()
|
||||
bool isnull;
|
||||
int i;
|
||||
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "autoinc: triggers are not initialized");
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "autoinc: not fired by trigger manager");
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "autoinc: can't process STATEMENT events");
|
||||
if (TRIGGER_FIRED_AFTER(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
|
||||
elog(ERROR, "autoinc: must be fired before event");
|
||||
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
rettuple = CurrentTriggerData->tg_trigtuple;
|
||||
else if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
rettuple = CurrentTriggerData->tg_newtuple;
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
rettuple = trigdata->tg_trigtuple;
|
||||
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
rettuple = trigdata->tg_newtuple;
|
||||
else
|
||||
elog(ERROR, "autoinc: can't process DELETE events");
|
||||
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
rel = trigdata->tg_relation;
|
||||
relname = SPI_getrelname(rel);
|
||||
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
trigger = trigdata->tg_trigger;
|
||||
|
||||
nargs = trigger->tgnargs;
|
||||
if (nargs <= 0 || nargs % 2 != 0)
|
||||
@ -48,8 +48,6 @@ autoinc()
|
||||
args = trigger->tgargs;
|
||||
tupdesc = rel->rd_att;
|
||||
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
chattrs = (int *) palloc(nargs / 2 * sizeof(int));
|
||||
newvals = (Datum *) palloc(nargs / 2 * sizeof(Datum));
|
||||
|
||||
@ -96,5 +94,5 @@ autoinc()
|
||||
pfree(chattrs);
|
||||
pfree(newvals);
|
||||
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ DROP FUNCTION autoinc();
|
||||
CREATE FUNCTION autoinc()
|
||||
RETURNS opaque
|
||||
AS '_OBJWD_/autoinc_DLSUFFIX_'
|
||||
LANGUAGE 'c';
|
||||
LANGUAGE 'newC';
|
||||
|
@ -10,11 +10,12 @@
|
||||
#include "commands/trigger.h" /* -"- and triggers */
|
||||
#include "miscadmin.h" /* for GetPgUserName() */
|
||||
|
||||
HeapTuple insert_username(void);
|
||||
extern Datum insert_username(PG_FUNCTION_ARGS);
|
||||
|
||||
HeapTuple
|
||||
insert_username()
|
||||
Datum
|
||||
insert_username(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
int nargs; /* # of arguments */
|
||||
Datum newval; /* new value of column */
|
||||
@ -26,24 +27,24 @@ insert_username()
|
||||
int attnum;
|
||||
|
||||
/* sanity checks from autoinc.c */
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "insert_username: triggers are not initialized");
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "insert_username: not fired by trigger manager");
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "insert_username: can't process STATEMENT events");
|
||||
if (TRIGGER_FIRED_AFTER(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
|
||||
elog(ERROR, "insert_username: must be fired before event");
|
||||
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
rettuple = CurrentTriggerData->tg_trigtuple;
|
||||
else if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
rettuple = CurrentTriggerData->tg_newtuple;
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
rettuple = trigdata->tg_trigtuple;
|
||||
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
rettuple = trigdata->tg_newtuple;
|
||||
else
|
||||
elog(ERROR, "insert_username: can't process DELETE events");
|
||||
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
rel = trigdata->tg_relation;
|
||||
relname = SPI_getrelname(rel);
|
||||
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
trigger = trigdata->tg_trigger;
|
||||
|
||||
nargs = trigger->tgnargs;
|
||||
if (nargs != 1)
|
||||
@ -52,8 +53,6 @@ insert_username()
|
||||
args = trigger->tgargs;
|
||||
tupdesc = rel->rd_att;
|
||||
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
attnum = SPI_fnumber(tupdesc, args[0]);
|
||||
|
||||
if (attnum < 0)
|
||||
@ -73,5 +72,5 @@ insert_username()
|
||||
|
||||
pfree(relname);
|
||||
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ DROP FUNCTION insert_username();
|
||||
CREATE FUNCTION insert_username()
|
||||
RETURNS opaque
|
||||
AS '_OBJWD_/insert_username_DLSUFFIX_'
|
||||
LANGUAGE 'c';
|
||||
LANGUAGE 'newC';
|
||||
|
@ -8,18 +8,19 @@ a modification datetime stamp in a record when that record is UPDATEd.
|
||||
Credits
|
||||
This is 95%+ based on autoinc.c, which I used as a starting point as I do
|
||||
not really know what I am doing. I also had help from
|
||||
Jan Wieck <jwieck@debis.com> who told me about the datetime_in("now") function.
|
||||
Jan Wieck <jwieck@debis.com> who told me about the timestamp_in("now") function.
|
||||
OH, me, I'm Terry Mackintosh <terry@terrym.com>
|
||||
*/
|
||||
|
||||
#include "executor/spi.h" /* this is what you need to work with SPI */
|
||||
#include "commands/trigger.h" /* -"- and triggers */
|
||||
|
||||
HeapTuple moddatetime(void);
|
||||
extern Datum moddatetime(PG_FUNCTION_ARGS);
|
||||
|
||||
HeapTuple
|
||||
moddatetime()
|
||||
Datum
|
||||
moddatetime(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
int nargs; /* # of arguments */
|
||||
int attnum; /* positional number of field to change */
|
||||
@ -30,26 +31,26 @@ moddatetime()
|
||||
HeapTuple rettuple = NULL;
|
||||
TupleDesc tupdesc; /* tuple description */
|
||||
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "moddatetime: triggers are not initialized.");
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "moddatetime: not fired by trigger manager.");
|
||||
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "moddatetime: can't process STATEMENT events.");
|
||||
|
||||
if (TRIGGER_FIRED_AFTER(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
|
||||
elog(ERROR, "moddatetime: must be fired before event.");
|
||||
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
elog(ERROR, "moddatetime: must be fired before event.");
|
||||
else if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
rettuple = CurrentTriggerData->tg_newtuple;
|
||||
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
rettuple = trigdata->tg_newtuple;
|
||||
else
|
||||
elog(ERROR, "moddatetime: can't process DELETE events.");
|
||||
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
rel = trigdata->tg_relation;
|
||||
relname = SPI_getrelname(rel);
|
||||
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
trigger = trigdata->tg_trigger;
|
||||
|
||||
nargs = trigger->tgnargs;
|
||||
|
||||
@ -60,11 +61,8 @@ moddatetime()
|
||||
/* must be the field layout? */
|
||||
tupdesc = rel->rd_att;
|
||||
|
||||
/* Why do this? */
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
/* Get the current datetime. */
|
||||
newdt = datetime_in("now");
|
||||
newdt = (Datum) timestamp_in("now");
|
||||
|
||||
/*
|
||||
* This gets the position in the turple of the field we want. args[0]
|
||||
@ -82,12 +80,12 @@ moddatetime()
|
||||
args[0]);
|
||||
|
||||
/*
|
||||
* OK, this is where we make sure the datetime field that we are
|
||||
* modifying is really a datetime field. Hay, error checking, what a
|
||||
* OK, this is where we make sure the timestamp field that we are
|
||||
* modifying is really a timestamp field. Hay, error checking, what a
|
||||
* novel idea !-)
|
||||
*/
|
||||
if (SPI_gettypeid(tupdesc, attnum) != DATETIMEOID)
|
||||
elog(ERROR, "moddatetime (%s): attribute %s must be of DATETIME type",
|
||||
if (SPI_gettypeid(tupdesc, attnum) != TIMESTAMPOID)
|
||||
elog(ERROR, "moddatetime (%s): attribute %s must be of TIMESTAMP type",
|
||||
relname, args[0]);
|
||||
|
||||
/* 1 is the number of items in the arrays attnum and newdt.
|
||||
@ -106,5 +104,5 @@ moddatetime()
|
||||
/* Clean up */
|
||||
pfree(relname);
|
||||
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ DROP FUNCTION moddatetime();
|
||||
CREATE FUNCTION moddatetime()
|
||||
RETURNS opaque
|
||||
AS '_OBJWD_/moddatetime_DLSUFFIX_'
|
||||
LANGUAGE 'c';
|
||||
LANGUAGE 'newC';
|
||||
|
@ -8,10 +8,8 @@
|
||||
#include <ctype.h> /* tolower () */
|
||||
|
||||
|
||||
|
||||
|
||||
HeapTuple check_primary_key(void);
|
||||
HeapTuple check_foreign_key(void);
|
||||
extern Datum check_primary_key(PG_FUNCTION_ARGS);
|
||||
extern Datum check_foreign_key(PG_FUNCTION_ARGS);
|
||||
|
||||
|
||||
typedef struct
|
||||
@ -38,9 +36,10 @@ static EPlan *find_plan(char *ident, EPlan ** eplan, int *nplans);
|
||||
* check_primary_key ('Fkey1', 'Fkey2', 'Ptable', 'Pkey1', 'Pkey2').
|
||||
*/
|
||||
|
||||
HeapTuple /* have to return HeapTuple to Executor */
|
||||
check_primary_key()
|
||||
Datum
|
||||
check_primary_key(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
int nargs; /* # of args specified in CREATE TRIGGER */
|
||||
char **args; /* arguments: column names and table name */
|
||||
@ -57,33 +56,35 @@ check_primary_key()
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Some checks first...
|
||||
*/
|
||||
#ifdef DEBUG_QUERY
|
||||
elog(NOTICE, "Check_primary_key Enter Function");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Some checks first...
|
||||
*/
|
||||
|
||||
/* Called by trigger manager ? */
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "check_primary_key: triggers are not initialized");
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "check_primary_key: not fired by trigger manager");
|
||||
|
||||
/* Should be called for ROW trigger */
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "check_primary_key: can't process STATEMENT events");
|
||||
|
||||
/* If INSERTion then must check Tuple to being inserted */
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
tuple = CurrentTriggerData->tg_trigtuple;
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
tuple = trigdata->tg_trigtuple;
|
||||
|
||||
/* Not should be called for DELETE */
|
||||
else if (TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event))
|
||||
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
||||
elog(ERROR, "check_primary_key: can't process DELETE events");
|
||||
|
||||
/* If UPDATion the must check new Tuple, not old one */
|
||||
else
|
||||
tuple = CurrentTriggerData->tg_newtuple;
|
||||
tuple = trigdata->tg_newtuple;
|
||||
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
trigger = trigdata->tg_trigger;
|
||||
nargs = trigger->tgnargs;
|
||||
args = trigger->tgargs;
|
||||
|
||||
@ -92,16 +93,9 @@ check_primary_key()
|
||||
|
||||
nkeys = nargs / 2;
|
||||
relname = args[nkeys];
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
rel = trigdata->tg_relation;
|
||||
tupdesc = rel->rd_att;
|
||||
|
||||
/*
|
||||
* Setting CurrentTriggerData to NULL prevents direct calls to trigger
|
||||
* functions in queries. Normally, trigger functions have to be called
|
||||
* by trigger manager code only.
|
||||
*/
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
/* Connect to SPI manager */
|
||||
if ((ret = SPI_connect()) < 0)
|
||||
elog(ERROR, "check_primary_key: SPI_connect returned %d", ret);
|
||||
@ -145,7 +139,7 @@ check_primary_key()
|
||||
if (isnull)
|
||||
{
|
||||
SPI_finish();
|
||||
return (tuple);
|
||||
return PointerGetDatum(tuple);
|
||||
}
|
||||
|
||||
if (plan->nplans <= 0) /* Get typeId of column */
|
||||
@ -207,7 +201,7 @@ check_primary_key()
|
||||
|
||||
SPI_finish();
|
||||
|
||||
return (tuple);
|
||||
return PointerGetDatum(tuple);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -222,9 +216,10 @@ check_primary_key()
|
||||
* 'Ftable1', 'Fkey11', 'Fkey12', 'Ftable2', 'Fkey21', 'Fkey22').
|
||||
*/
|
||||
|
||||
HeapTuple /* have to return HeapTuple to Executor */
|
||||
check_foreign_key()
|
||||
Datum
|
||||
check_foreign_key(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
int nargs; /* # of args specified in CREATE TRIGGER */
|
||||
char **args; /* arguments: as described above */
|
||||
@ -258,19 +253,19 @@ check_foreign_key()
|
||||
*/
|
||||
|
||||
/* Called by trigger manager ? */
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "check_foreign_key: triggers are not initialized");
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "check_foreign_key: not fired by trigger manager");
|
||||
|
||||
/* Should be called for ROW trigger */
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "check_foreign_key: can't process STATEMENT events");
|
||||
|
||||
/* Not should be called for INSERT */
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
elog(ERROR, "check_foreign_key: can't process INSERT events");
|
||||
|
||||
/* Have to check tg_trigtuple - tuple being deleted */
|
||||
trigtuple = CurrentTriggerData->tg_trigtuple;
|
||||
trigtuple = trigdata->tg_trigtuple;
|
||||
|
||||
/*
|
||||
* But if this is UPDATE then we have to return tg_newtuple. Also, if
|
||||
@ -278,12 +273,12 @@ check_foreign_key()
|
||||
* do.
|
||||
*/
|
||||
is_update = 0;
|
||||
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
{
|
||||
newtuple = CurrentTriggerData->tg_newtuple;
|
||||
newtuple = trigdata->tg_newtuple;
|
||||
is_update = 1;
|
||||
}
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
trigger = trigdata->tg_trigger;
|
||||
nargs = trigger->tgnargs;
|
||||
args = trigger->tgargs;
|
||||
|
||||
@ -304,16 +299,9 @@ check_foreign_key()
|
||||
elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
|
||||
nargs + 2, nrefs);
|
||||
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
rel = trigdata->tg_relation;
|
||||
tupdesc = rel->rd_att;
|
||||
|
||||
/*
|
||||
* Setting CurrentTriggerData to NULL prevents direct calls to trigger
|
||||
* functions in queries. Normally, trigger functions have to be called
|
||||
* by trigger manager code only.
|
||||
*/
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
/* Connect to SPI manager */
|
||||
if ((ret = SPI_connect()) < 0)
|
||||
elog(ERROR, "check_foreign_key: SPI_connect returned %d", ret);
|
||||
@ -364,7 +352,7 @@ check_foreign_key()
|
||||
if (isnull)
|
||||
{
|
||||
SPI_finish();
|
||||
return ((newtuple == NULL) ? trigtuple : newtuple);
|
||||
return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -527,7 +515,7 @@ check_foreign_key()
|
||||
if (newtuple != NULL && isequal)
|
||||
{
|
||||
SPI_finish();
|
||||
return (newtuple);
|
||||
return PointerGetDatum(newtuple);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -571,7 +559,7 @@ check_foreign_key()
|
||||
|
||||
SPI_finish();
|
||||
|
||||
return ((newtuple == NULL) ? trigtuple : newtuple);
|
||||
return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
|
||||
}
|
||||
|
||||
static EPlan *
|
||||
|
@ -4,11 +4,11 @@ DROP FUNCTION check_foreign_key ();
|
||||
CREATE FUNCTION check_primary_key ()
|
||||
RETURNS opaque
|
||||
AS '_OBJWD_/refint_DLSUFFIX_'
|
||||
LANGUAGE 'c'
|
||||
LANGUAGE 'newC'
|
||||
;
|
||||
|
||||
CREATE FUNCTION check_foreign_key ()
|
||||
RETURNS opaque
|
||||
AS '_OBJWD_/refint_DLSUFFIX_'
|
||||
LANGUAGE 'c'
|
||||
LANGUAGE 'newC'
|
||||
;
|
||||
|
@ -10,8 +10,8 @@
|
||||
#define ABSTIMEOID 702 /* it should be in pg_type.h */
|
||||
|
||||
AbsoluteTime currabstime(void);
|
||||
HeapTuple timetravel(void);
|
||||
int32 set_timetravel(Name relname, int32 on);
|
||||
Datum timetravel(PG_FUNCTION_ARGS);
|
||||
Datum set_timetravel(PG_FUNCTION_ARGS);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -47,9 +47,10 @@ static EPlan *find_plan(char *ident, EPlan ** eplan, int *nplans);
|
||||
* timetravel ('date_on', 'date_off').
|
||||
*/
|
||||
|
||||
HeapTuple /* have to return HeapTuple to Executor */
|
||||
timetravel()
|
||||
Datum
|
||||
timetravel(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
Trigger *trigger; /* to get trigger name */
|
||||
char **args; /* arguments */
|
||||
int attnum[2]; /* fnumbers of start/stop columns */
|
||||
@ -78,27 +79,27 @@ timetravel()
|
||||
*/
|
||||
|
||||
/* Called by trigger manager ? */
|
||||
if (!CurrentTriggerData)
|
||||
elog(ERROR, "timetravel: triggers are not initialized");
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "timetravel: not fired by trigger manager");
|
||||
|
||||
/* Should be called for ROW trigger */
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||
elog(ERROR, "timetravel: can't process STATEMENT events");
|
||||
|
||||
/* Should be called BEFORE */
|
||||
if (TRIGGER_FIRED_AFTER(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
|
||||
elog(ERROR, "timetravel: must be fired before event");
|
||||
|
||||
/* INSERT ? */
|
||||
if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
isinsert = true;
|
||||
|
||||
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
newtuple = CurrentTriggerData->tg_newtuple;
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
newtuple = trigdata->tg_newtuple;
|
||||
|
||||
trigtuple = CurrentTriggerData->tg_trigtuple;
|
||||
trigtuple = trigdata->tg_trigtuple;
|
||||
|
||||
rel = CurrentTriggerData->tg_relation;
|
||||
rel = trigdata->tg_relation;
|
||||
relname = SPI_getrelname(rel);
|
||||
|
||||
/* check if TT is OFF for this relation */
|
||||
@ -108,10 +109,10 @@ timetravel()
|
||||
if (i < nTTOff) /* OFF - nothing to do */
|
||||
{
|
||||
pfree(relname);
|
||||
return ((newtuple != NULL) ? newtuple : trigtuple);
|
||||
return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
|
||||
}
|
||||
|
||||
trigger = CurrentTriggerData->tg_trigger;
|
||||
trigger = trigdata->tg_trigger;
|
||||
|
||||
if (trigger->tgnargs != 2)
|
||||
elog(ERROR, "timetravel (%s): invalid (!= 2) number of arguments %d",
|
||||
@ -121,13 +122,6 @@ timetravel()
|
||||
tupdesc = rel->rd_att;
|
||||
natts = tupdesc->natts;
|
||||
|
||||
/*
|
||||
* Setting CurrentTriggerData to NULL prevents direct calls to trigger
|
||||
* functions in queries. Normally, trigger functions have to be called
|
||||
* by trigger manager code only.
|
||||
*/
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
attnum[i] = SPI_fnumber(tupdesc, args[i]);
|
||||
@ -175,11 +169,11 @@ timetravel()
|
||||
|
||||
pfree(relname);
|
||||
if (chnattrs <= 0)
|
||||
return (trigtuple);
|
||||
return PointerGetDatum(trigtuple);
|
||||
|
||||
rettuple = SPI_modifytuple(rel, trigtuple, chnattrs,
|
||||
chattrs, newvals, NULL);
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
|
||||
oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull);
|
||||
@ -210,13 +204,13 @@ timetravel()
|
||||
if (newoff != NOEND_ABSTIME)
|
||||
{
|
||||
pfree(relname); /* allocated in upper executor context */
|
||||
return (NULL);
|
||||
return PointerGetDatum(NULL);
|
||||
}
|
||||
}
|
||||
else if (oldoff != NOEND_ABSTIME) /* DELETE */
|
||||
{
|
||||
pfree(relname);
|
||||
return (NULL);
|
||||
return PointerGetDatum(NULL);
|
||||
}
|
||||
|
||||
newoff = GetCurrentAbsoluteTime();
|
||||
@ -325,16 +319,18 @@ timetravel()
|
||||
|
||||
pfree(relname);
|
||||
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
|
||||
/*
|
||||
* set_timetravel () --
|
||||
* set_timetravel (relname, on) --
|
||||
* turn timetravel for specified relation ON/OFF
|
||||
*/
|
||||
int32
|
||||
set_timetravel(Name relname, int32 on)
|
||||
Datum
|
||||
set_timetravel(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Name relname = PG_GETARG_NAME(0);
|
||||
int32 on = PG_GETARG_INT32(1);
|
||||
char *rname;
|
||||
char *d;
|
||||
char *s;
|
||||
@ -347,7 +343,7 @@ set_timetravel(Name relname, int32 on)
|
||||
if (i < nTTOff) /* OFF currently */
|
||||
{
|
||||
if (on == 0)
|
||||
return (0);
|
||||
PG_RETURN_INT32(0);
|
||||
|
||||
/* turn ON */
|
||||
free(TTOff[i]);
|
||||
@ -360,12 +356,12 @@ set_timetravel(Name relname, int32 on)
|
||||
TTOff = realloc(TTOff, (nTTOff - 1) * sizeof(char *));
|
||||
}
|
||||
nTTOff--;
|
||||
return (0);
|
||||
PG_RETURN_INT32(0);
|
||||
}
|
||||
|
||||
/* ON currently */
|
||||
if (on != 0)
|
||||
return (1);
|
||||
PG_RETURN_INT32(1);
|
||||
|
||||
/* turn OFF */
|
||||
if (nTTOff == 0)
|
||||
@ -380,8 +376,7 @@ set_timetravel(Name relname, int32 on)
|
||||
pfree(rname);
|
||||
nTTOff++;
|
||||
|
||||
return (1);
|
||||
|
||||
PG_RETURN_INT32(1);
|
||||
}
|
||||
|
||||
AbsoluteTime
|
||||
|
@ -4,9 +4,9 @@ DROP FUNCTION set_timetravel(name, int4);
|
||||
CREATE FUNCTION timetravel()
|
||||
RETURNS opaque
|
||||
AS '_OBJWD_/timetravel_DLSUFFIX_'
|
||||
LANGUAGE 'c';
|
||||
LANGUAGE 'newC';
|
||||
|
||||
CREATE FUNCTION set_timetravel(name, int4)
|
||||
RETURNS int4
|
||||
AS '_OBJWD_/timetravel_DLSUFFIX_'
|
||||
LANGUAGE 'c';
|
||||
LANGUAGE 'newC' WITH (isStrict);
|
||||
|
Reference in New Issue
Block a user