mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-09 08:10:09 +08:00
9257f07872
Invent a new function heap_modify_tuple_by_cols() that is functionally equivalent to SPI_modifytuple except that it always allocates its result by simple palloc. I chose however to make the API details a bit more like heap_modify_tuple: pass a tupdesc rather than a Relation, and use bool convention for the isnull array. Use this function in place of SPI_modifytuple at all call sites where the intended behavior is to allocate in current context. (There actually are only two call sites left that depend on the old behavior, which makes me wonder if we should just drop this function rather than keep it.) This new function is easier to use than heap_modify_tuple() for purposes of replacing a single column (or, really, any fixed number of columns). There are a number of places where it would simplify the code to change over, but I resisted that temptation for the moment ... everywhere except in plpgsql's exec_assign_value(); changing that might offer some small performance benefit, so I did it. This is on the way to removing SPI_push/SPI_pop, but it seems like good code cleanup in its own right. Discussion: <9633.1478552022@sss.pgh.pa.us>
93 lines
2.5 KiB
C
93 lines
2.5 KiB
C
/*
|
|
* contrib/spi/insert_username.c
|
|
*
|
|
* insert user name in response to a trigger
|
|
* usage: insert_username (column_name)
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/htup_details.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "commands/trigger.h"
|
|
#include "executor/spi.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/rel.h"
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
PG_FUNCTION_INFO_V1(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 */
|
|
bool newnull; /* null flag */
|
|
char **args; /* arguments */
|
|
char *relname; /* triggered relation name */
|
|
Relation rel; /* triggered relation */
|
|
HeapTuple rettuple = NULL;
|
|
TupleDesc tupdesc; /* tuple description */
|
|
int attnum;
|
|
|
|
/* sanity checks from autoinc.c */
|
|
if (!CALLED_AS_TRIGGER(fcinfo))
|
|
/* internal error */
|
|
elog(ERROR, "insert_username: not fired by trigger manager");
|
|
if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
|
|
/* internal error */
|
|
elog(ERROR, "insert_username: must be fired for row");
|
|
if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
|
/* internal error */
|
|
elog(ERROR, "insert_username: must be fired before event");
|
|
|
|
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
|
|
/* internal error */
|
|
elog(ERROR, "insert_username: cannot process DELETE events");
|
|
|
|
rel = trigdata->tg_relation;
|
|
relname = SPI_getrelname(rel);
|
|
|
|
trigger = trigdata->tg_trigger;
|
|
|
|
nargs = trigger->tgnargs;
|
|
if (nargs != 1)
|
|
/* internal error */
|
|
elog(ERROR, "insert_username (%s): one argument was expected", relname);
|
|
|
|
args = trigger->tgargs;
|
|
tupdesc = rel->rd_att;
|
|
|
|
attnum = SPI_fnumber(tupdesc, args[0]);
|
|
|
|
if (attnum <= 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
|
|
errmsg("\"%s\" has no attribute \"%s\"", relname, args[0])));
|
|
|
|
if (SPI_gettypeid(tupdesc, attnum) != TEXTOID)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
|
|
errmsg("attribute \"%s\" of \"%s\" must be type TEXT",
|
|
args[0], relname)));
|
|
|
|
/* create fields containing name */
|
|
newval = CStringGetTextDatum(GetUserNameFromId(GetUserId(), false));
|
|
newnull = false;
|
|
|
|
/* construct new tuple */
|
|
rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
|
|
1, &attnum, &newval, &newnull);
|
|
|
|
pfree(relname);
|
|
|
|
return PointerGetDatum(rettuple);
|
|
}
|