mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Now we are able to CREATE PROCEDURAL LANGUAGE (Thanks, Jan).
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
# Makefile for commands
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.4 1997/08/31 11:40:12 vadim Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.5 1997/10/28 14:54:43 vadim Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -19,7 +19,7 @@ CFLAGS+=$(INCLUDE_OPT)
|
||||
|
||||
OBJS = async.o creatinh.o command.o copy.o defind.o define.o \
|
||||
purge.o remove.o rename.o vacuum.o version.o view.o cluster.o \
|
||||
recipe.o explain.o sequence.o trigger.o
|
||||
recipe.o explain.o sequence.o trigger.o proclang.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.16 1997/09/08 21:42:38 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.17 1997/10/28 14:54:46 vadim Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@ -45,6 +45,7 @@
|
||||
#include <catalog/pg_operator.h>
|
||||
#include <catalog/pg_proc.h>
|
||||
#include <catalog/pg_type.h>
|
||||
#include <catalog/pg_language.h>
|
||||
#include <utils/syscache.h>
|
||||
#include <fmgr.h> /* for fmgr */
|
||||
#include <utils/builtins.h> /* prototype for textin() */
|
||||
@ -239,6 +240,8 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
|
||||
bool canCache;
|
||||
bool returnsSet;
|
||||
|
||||
bool lanisPL = false;
|
||||
|
||||
/* The function returns a set of values, as opposed to a singleton. */
|
||||
|
||||
|
||||
@ -262,19 +265,59 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(WARN,
|
||||
HeapTuple languageTuple;
|
||||
Form_pg_language languageStruct;
|
||||
|
||||
/* Lookup the language in the system cache */
|
||||
languageTuple = SearchSysCacheTuple(LANNAME,
|
||||
PointerGetDatum(languageName),
|
||||
0, 0, 0);
|
||||
|
||||
if (!HeapTupleIsValid(languageTuple)) {
|
||||
|
||||
elog(WARN,
|
||||
"Unrecognized language specified in a CREATE FUNCTION: "
|
||||
"'%s'. Recognized languages are sql, C, and internal.",
|
||||
"'%s'. Recognized languages are sql, C, internal "
|
||||
"and the created procedural languages.",
|
||||
languageName);
|
||||
}
|
||||
|
||||
/* Check that this language is a PL */
|
||||
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
|
||||
if (!(languageStruct->lanispl)) {
|
||||
elog(WARN,
|
||||
"Language '%s' isn't defined as PL", languageName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions in untrusted procedural languages are
|
||||
* restricted to be defined by postgres superusers only
|
||||
*/
|
||||
if (languageStruct->lanpltrusted == false && !superuser()) {
|
||||
elog(WARN, "Only users with Postgres superuser privilege "
|
||||
"are permitted to create a function in the '%s' "
|
||||
"language.",
|
||||
languageName);
|
||||
}
|
||||
|
||||
lanisPL = true;
|
||||
|
||||
/*
|
||||
* These are meaningless
|
||||
*/
|
||||
perbyte_cpu = percall_cpu = 0;
|
||||
byte_pct = outin_ratio = 100;
|
||||
canCache = false;
|
||||
}
|
||||
|
||||
interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str);
|
||||
|
||||
if (strcmp(languageName, "sql") != 0 && !superuser())
|
||||
if (strcmp(languageName, "sql") != 0 && lanisPL == false && !superuser())
|
||||
elog(WARN,
|
||||
"Only users with Postgres superuser privilege are permitted "
|
||||
"to create a function "
|
||||
"in the '%s' language. Others may use the 'sql' language.",
|
||||
"in the '%s' language. Others may use the 'sql' language "
|
||||
"or the created procedural languages.",
|
||||
languageName);
|
||||
/* Above does not return. */
|
||||
else
|
||||
|
205
src/backend/commands/proclang.c
Normal file
205
src/backend/commands/proclang.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* proclang.c--
|
||||
* PostgreSQL PROCEDURAL LANGUAGE support code.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/pg_user.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_language.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "commands/proclang.h"
|
||||
#include "fmgr.h"
|
||||
|
||||
|
||||
static void
|
||||
case_translate_language_name(const char *input, char *output)
|
||||
{
|
||||
/*-------------------------------------------------------------------------
|
||||
Translate the input language name to lower case, except if it's C,
|
||||
translate to upper case.
|
||||
--------------------------------------------------------------------------*/
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NAMEDATALEN && input[i] != '\0'; ++i)
|
||||
output[i] = tolower(input[i]);
|
||||
|
||||
output[i] = '\0';
|
||||
|
||||
if (strcmp(output, "c") == 0)
|
||||
output[0] = 'C';
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* CREATE PROCEDURAL LANGUAGE
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
CreateProceduralLanguage(CreatePLangStmt * stmt)
|
||||
{
|
||||
char languageName[NAMEDATALEN];
|
||||
HeapTuple langTup;
|
||||
HeapTuple procTup;
|
||||
|
||||
Oid typev[8];
|
||||
char nulls[Natts_pg_language];
|
||||
Datum values[Natts_pg_language];
|
||||
Relation rdesc;
|
||||
HeapTuple tup;
|
||||
TupleDesc tupDesc;
|
||||
|
||||
int i;
|
||||
|
||||
/* ----------------
|
||||
* Check permission
|
||||
* ----------------
|
||||
*/
|
||||
if (!superuser())
|
||||
{
|
||||
elog(WARN, "Only users with Postgres superuser privilege are "
|
||||
"permitted to create procedural languages");
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Translate the language name and check that
|
||||
* this language doesn't already exist
|
||||
* ----------------
|
||||
*/
|
||||
case_translate_language_name(stmt->plname, languageName);
|
||||
|
||||
langTup = SearchSysCacheTuple(LANNAME,
|
||||
PointerGetDatum(languageName),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(langTup))
|
||||
{
|
||||
elog(WARN, "Language %s already exists", languageName);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Lookup the PL handler function and check that it is
|
||||
* of return type Opaque
|
||||
* ----------------
|
||||
*/
|
||||
memset(typev, 0, sizeof(typev));
|
||||
procTup = SearchSysCacheTuple(PRONAME,
|
||||
PointerGetDatum(stmt->plhandler),
|
||||
UInt16GetDatum(0),
|
||||
PointerGetDatum(typev),
|
||||
0);
|
||||
if (!HeapTupleIsValid(procTup))
|
||||
{
|
||||
elog(WARN, "PL handler function %s() doesn't exist",
|
||||
stmt->plhandler);
|
||||
}
|
||||
if (((Form_pg_proc) GETSTRUCT(procTup))->prorettype != InvalidOid)
|
||||
{
|
||||
elog(WARN, "PL handler function %s() isn't of return type Opaque",
|
||||
stmt->plhandler);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Insert the new language into pg_language
|
||||
* ----------------
|
||||
*/
|
||||
for (i = 0; i < Natts_pg_language; i++)
|
||||
{
|
||||
nulls[i] = ' ';
|
||||
values[i] = (Datum) NULL;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
values[i++] = PointerGetDatum(languageName);
|
||||
values[i++] = Int8GetDatum((bool) 1);
|
||||
values[i++] = Int8GetDatum(stmt->pltrusted);
|
||||
values[i++] = ObjectIdGetDatum(procTup->t_oid);
|
||||
values[i++] = (Datum) fmgr(TextInRegProcedure, stmt->plcompiler);
|
||||
|
||||
rdesc = heap_openr(LanguageRelationName);
|
||||
|
||||
tupDesc = rdesc->rd_att;
|
||||
tup = heap_formtuple(tupDesc, values, nulls);
|
||||
|
||||
heap_insert(rdesc, tup);
|
||||
|
||||
heap_close(rdesc);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* DROP PROCEDURAL LANGUAGE
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
DropProceduralLanguage(DropPLangStmt * stmt)
|
||||
{
|
||||
char languageName[NAMEDATALEN];
|
||||
HeapTuple langTup;
|
||||
|
||||
Relation rdesc;
|
||||
HeapScanDesc scanDesc;
|
||||
ScanKeyData scanKeyData;
|
||||
HeapTuple tup;
|
||||
|
||||
/* ----------------
|
||||
* Check permission
|
||||
* ----------------
|
||||
*/
|
||||
if (!superuser())
|
||||
{
|
||||
elog(WARN, "Only users with Postgres superuser privilege are "
|
||||
"permitted to drop procedural languages");
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Translate the language name, check that
|
||||
* this language exist and is a PL
|
||||
* ----------------
|
||||
*/
|
||||
case_translate_language_name(stmt->plname, languageName);
|
||||
|
||||
langTup = SearchSysCacheTuple(LANNAME,
|
||||
PointerGetDatum(languageName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(langTup))
|
||||
{
|
||||
elog(WARN, "Language %s doesn't exist", languageName);
|
||||
}
|
||||
|
||||
if (!((Form_pg_language) GETSTRUCT(langTup))->lanispl)
|
||||
{
|
||||
elog(WARN, "Language %s isn't a created procedural language",
|
||||
languageName);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Now scan pg_language and delete the PL tuple
|
||||
* ----------------
|
||||
*/
|
||||
rdesc = heap_openr(LanguageRelationName);
|
||||
|
||||
ScanKeyEntryInitialize(&scanKeyData, 0, Anum_pg_language_lanname,
|
||||
F_NAMEEQ, PointerGetDatum(languageName));
|
||||
|
||||
scanDesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, &scanKeyData);
|
||||
|
||||
tup = heap_getnext(scanDesc, 0, (Buffer *) NULL);
|
||||
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
elog(WARN, "Language with name '%s' not found", languageName);
|
||||
}
|
||||
|
||||
heap_delete(rdesc, &(tup->t_ctid));
|
||||
|
||||
heap_endscan(scanDesc);
|
||||
heap_close(rdesc);
|
||||
}
|
@ -26,11 +26,11 @@
|
||||
#include "utils/mcxt.h"
|
||||
#include "utils/inval.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
#ifndef NO_SECURITY
|
||||
#include "miscadmin.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/syscache.h"
|
||||
#endif
|
||||
|
||||
TriggerData *CurrentTriggerData = NULL;
|
||||
@ -87,8 +87,8 @@ CreateTrigger(CreateTrigStmt * stmt)
|
||||
if (stmt->row)
|
||||
TRIGGER_SETT_ROW(tgtype);
|
||||
else
|
||||
elog (WARN, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
|
||||
|
||||
elog(WARN, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
|
||||
|
||||
for (i = 0; i < 3 && stmt->actions[i]; i++)
|
||||
{
|
||||
switch (stmt->actions[i])
|
||||
@ -142,7 +142,22 @@ CreateTrigger(CreateTrigStmt * stmt)
|
||||
elog(WARN, "CreateTrigger: function %s () does not exist", stmt->funcname);
|
||||
|
||||
if (((Form_pg_proc) GETSTRUCT(tuple))->prolang != ClanguageId)
|
||||
elog(WARN, "CreateTrigger: only C functions are supported");
|
||||
{
|
||||
HeapTuple langTup;
|
||||
|
||||
langTup = SearchSysCacheTuple(LANOID,
|
||||
ObjectIdGetDatum(((Form_pg_proc) GETSTRUCT(tuple))->prolang),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(langTup))
|
||||
{
|
||||
elog(WARN, "CreateTrigger: cache lookup for PL failed");
|
||||
}
|
||||
|
||||
if (((Form_pg_language) GETSTRUCT(langTup))->lanispl == false)
|
||||
{
|
||||
elog(WARN, "CreateTrigger: only C and PL functions are supported");
|
||||
}
|
||||
}
|
||||
|
||||
MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));
|
||||
|
||||
@ -159,10 +174,10 @@ CreateTrigger(CreateTrigStmt * stmt)
|
||||
|
||||
foreach(le, stmt->args)
|
||||
{
|
||||
char *ar = (char *) lfirst(le);
|
||||
char *ar = (char *) lfirst(le);
|
||||
|
||||
len += strlen(ar) + 4;
|
||||
for ( ; *ar; ar++)
|
||||
for (; *ar; ar++)
|
||||
{
|
||||
if (*ar == '\\')
|
||||
len++;
|
||||
@ -172,9 +187,9 @@ CreateTrigger(CreateTrigStmt * stmt)
|
||||
args[0] = 0;
|
||||
foreach(le, stmt->args)
|
||||
{
|
||||
char *s = (char *) lfirst(le);
|
||||
char *d = args + strlen(args);
|
||||
|
||||
char *s = (char *) lfirst(le);
|
||||
char *d = args + strlen(args);
|
||||
|
||||
while (*s)
|
||||
{
|
||||
if (*s == '\\')
|
||||
@ -399,6 +414,7 @@ RelationBuildTriggers(Relation relation)
|
||||
build->tgname = nameout(&(pg_trigger->tgname));
|
||||
build->tgfoid = pg_trigger->tgfoid;
|
||||
build->tgfunc = NULL;
|
||||
build->tgplfunc = NULL;
|
||||
build->tgtype = pg_trigger->tgtype;
|
||||
build->tgnargs = pg_trigger->tgnargs;
|
||||
memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16));
|
||||
@ -578,6 +594,54 @@ DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger)
|
||||
|
||||
}
|
||||
|
||||
static HeapTuple
|
||||
ExecCallTriggerFunc(Trigger * trigger)
|
||||
{
|
||||
|
||||
if (trigger->tgfunc != NULL)
|
||||
{
|
||||
return (HeapTuple) ((*(trigger->tgfunc)) ());
|
||||
}
|
||||
|
||||
if (trigger->tgplfunc == NULL)
|
||||
{
|
||||
HeapTuple procTuple;
|
||||
HeapTuple langTuple;
|
||||
Form_pg_proc procStruct;
|
||||
Form_pg_language langStruct;
|
||||
int nargs;
|
||||
|
||||
procTuple = SearchSysCacheTuple(PROOID,
|
||||
ObjectIdGetDatum(trigger->tgfoid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(procTuple))
|
||||
{
|
||||
elog(WARN, "ExecCallTriggerFunc(): Cache lookup for proc %ld failed",
|
||||
ObjectIdGetDatum(trigger->tgfoid));
|
||||
}
|
||||
procStruct = (Form_pg_proc) GETSTRUCT(procTuple);
|
||||
|
||||
langTuple = SearchSysCacheTuple(LANOID,
|
||||
ObjectIdGetDatum(procStruct->prolang),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(langTuple))
|
||||
{
|
||||
elog(WARN, "ExecCallTriggerFunc(): Cache lookup for language %ld failed",
|
||||
ObjectIdGetDatum(procStruct->prolang));
|
||||
}
|
||||
langStruct = (Form_pg_language) GETSTRUCT(langTuple);
|
||||
|
||||
if (langStruct->lanispl == false)
|
||||
{
|
||||
fmgr_info(trigger->tgfoid, &(trigger->tgfunc), &nargs);
|
||||
return (HeapTuple) ((*(trigger->tgfunc)) ());
|
||||
}
|
||||
fmgr_info(langStruct->lanplcallfoid, &(trigger->tgplfunc), &nargs);
|
||||
}
|
||||
|
||||
return (HeapTuple) ((*(trigger->tgplfunc)) (trigger->tgfoid));
|
||||
}
|
||||
|
||||
HeapTuple
|
||||
ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
|
||||
{
|
||||
@ -586,7 +650,6 @@ ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
|
||||
Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
|
||||
HeapTuple newtuple = trigtuple;
|
||||
HeapTuple oldtuple;
|
||||
int nargs;
|
||||
int i;
|
||||
|
||||
SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
|
||||
@ -599,9 +662,7 @@ ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
|
||||
CurrentTriggerData = SaveTriggerData;
|
||||
CurrentTriggerData->tg_trigtuple = oldtuple = newtuple;
|
||||
CurrentTriggerData->tg_trigger = trigger[i];
|
||||
if (trigger[i]->tgfunc == NULL)
|
||||
fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
|
||||
newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ());
|
||||
newtuple = ExecCallTriggerFunc(trigger[i]);
|
||||
if (newtuple == NULL)
|
||||
break;
|
||||
else if (oldtuple != newtuple && oldtuple != trigtuple)
|
||||
@ -618,7 +679,6 @@ ExecARInsertTriggers(Relation rel, HeapTuple trigtuple)
|
||||
TriggerData *SaveTriggerData;
|
||||
int ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT];
|
||||
Trigger **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT];
|
||||
int nargs;
|
||||
int i;
|
||||
|
||||
SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
|
||||
@ -630,9 +690,7 @@ ExecARInsertTriggers(Relation rel, HeapTuple trigtuple)
|
||||
CurrentTriggerData = SaveTriggerData;
|
||||
CurrentTriggerData->tg_trigtuple = trigtuple;
|
||||
CurrentTriggerData->tg_trigger = trigger[i];
|
||||
if (trigger[i]->tgfunc == NULL)
|
||||
fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
|
||||
(void) ((*(trigger[i]->tgfunc)) ());
|
||||
ExecCallTriggerFunc(trigger[i]);
|
||||
}
|
||||
CurrentTriggerData = NULL;
|
||||
pfree(SaveTriggerData);
|
||||
@ -647,7 +705,6 @@ ExecBRDeleteTriggers(Relation rel, ItemPointer tupleid)
|
||||
Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_DELETE];
|
||||
HeapTuple trigtuple;
|
||||
HeapTuple newtuple = NULL;
|
||||
int nargs;
|
||||
int i;
|
||||
|
||||
trigtuple = GetTupleForTrigger(rel, tupleid, true);
|
||||
@ -664,9 +721,7 @@ ExecBRDeleteTriggers(Relation rel, ItemPointer tupleid)
|
||||
CurrentTriggerData = SaveTriggerData;
|
||||
CurrentTriggerData->tg_trigtuple = trigtuple;
|
||||
CurrentTriggerData->tg_trigger = trigger[i];
|
||||
if (trigger[i]->tgfunc == NULL)
|
||||
fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
|
||||
newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ());
|
||||
newtuple = ExecCallTriggerFunc(trigger[i]);
|
||||
if (newtuple == NULL)
|
||||
break;
|
||||
}
|
||||
@ -684,7 +739,6 @@ ExecARDeleteTriggers(Relation rel, ItemPointer tupleid)
|
||||
int ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE];
|
||||
Trigger **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE];
|
||||
HeapTuple trigtuple;
|
||||
int nargs;
|
||||
int i;
|
||||
|
||||
trigtuple = GetTupleForTrigger(rel, tupleid, false);
|
||||
@ -700,9 +754,7 @@ ExecARDeleteTriggers(Relation rel, ItemPointer tupleid)
|
||||
CurrentTriggerData = SaveTriggerData;
|
||||
CurrentTriggerData->tg_trigtuple = trigtuple;
|
||||
CurrentTriggerData->tg_trigger = trigger[i];
|
||||
if (trigger[i]->tgfunc == NULL)
|
||||
fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
|
||||
(void) ((*(trigger[i]->tgfunc)) ());
|
||||
ExecCallTriggerFunc(trigger[i]);
|
||||
}
|
||||
CurrentTriggerData = NULL;
|
||||
pfree(SaveTriggerData);
|
||||
@ -719,7 +771,6 @@ ExecBRUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
|
||||
HeapTuple trigtuple;
|
||||
HeapTuple oldtuple;
|
||||
HeapTuple intuple = newtuple;
|
||||
int nargs;
|
||||
int i;
|
||||
|
||||
trigtuple = GetTupleForTrigger(rel, tupleid, true);
|
||||
@ -736,9 +787,7 @@ ExecBRUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
|
||||
CurrentTriggerData->tg_trigtuple = trigtuple;
|
||||
CurrentTriggerData->tg_newtuple = oldtuple = newtuple;
|
||||
CurrentTriggerData->tg_trigger = trigger[i];
|
||||
if (trigger[i]->tgfunc == NULL)
|
||||
fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
|
||||
newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ());
|
||||
newtuple = ExecCallTriggerFunc(trigger[i]);
|
||||
if (newtuple == NULL)
|
||||
break;
|
||||
else if (oldtuple != newtuple && oldtuple != intuple)
|
||||
@ -757,7 +806,6 @@ ExecARUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
|
||||
int ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE];
|
||||
Trigger **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE];
|
||||
HeapTuple trigtuple;
|
||||
int nargs;
|
||||
int i;
|
||||
|
||||
trigtuple = GetTupleForTrigger(rel, tupleid, false);
|
||||
@ -773,9 +821,7 @@ ExecARUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
|
||||
CurrentTriggerData->tg_trigtuple = trigtuple;
|
||||
CurrentTriggerData->tg_newtuple = newtuple;
|
||||
CurrentTriggerData->tg_trigger = trigger[i];
|
||||
if (trigger[i]->tgfunc == NULL)
|
||||
fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
|
||||
(void) ((*(trigger[i]->tgfunc)) ());
|
||||
ExecCallTriggerFunc(trigger[i]);
|
||||
}
|
||||
CurrentTriggerData = NULL;
|
||||
pfree(SaveTriggerData);
|
||||
|
Reference in New Issue
Block a user