New edition by D'Arcy and me.
This commit is contained in:
parent
d75206fdf5
commit
91eb34e9eb
278
doc/spi.txt
278
doc/spi.txt
@ -1,46 +1,46 @@
|
|||||||
|
|
||||||
PostgreSQL Server Programming Interface
|
PostgreSQL Server Programming Interface
|
||||||
|
|
||||||
Server Programming Interface (SPI) is attempt to give users ability run
|
The Server Programming Interface (SPI) is an attempt to give users the
|
||||||
SQL-queries inside user-defined C-function. For lack of Procedural Language
|
ability to run SQL-queries inside user-defined C-functions. Given the lack
|
||||||
(PL) in current version of PostgreSQL, SPI is only way to write server
|
of a proper Procedural Language (PL) in the current version of PostgreSQL,
|
||||||
stored procedures and triggers. In the future, SPI will be used as
|
SPI is only way to write server stored procedures and triggers. In the future
|
||||||
"workhorse" for PL.
|
SPI will be used as the "workhorse" for PL.
|
||||||
|
|
||||||
Actually, SPI is just set of builtin interface functions to simplify
|
In fact, SPI is just set of builtin interface functions to simplify
|
||||||
access to Parser/Planner/Optimizer and Executor. Also, SPI does some memory
|
access to the Parser, Planner, Optimizer and Executor. SPI also does some
|
||||||
management.
|
memory management.
|
||||||
|
|
||||||
To avoid misunderstanding we'll use word "function" for SPI interface
|
To avoid misunderstanding we'll use the word "function" for SPI interface
|
||||||
functions and word "procedure" for user-defined C-functions using SPI.
|
functions and the word "procedure" for user-defined C-functions using SPI.
|
||||||
|
|
||||||
SPI procedures are always called by some (upper) Executor and SPI manager
|
SPI procedures are always called by some (upper) Executor and the SPI
|
||||||
uses Executor to run your queries. Other procedures may be called by
|
manager uses the Executor to run your queries. Other procedures may be
|
||||||
Executor running queries from your procedure.
|
called by the Executor running queries from your procedure.
|
||||||
|
|
||||||
Note, that if during execution of query from a procedure transaction will
|
Note, that if during execution of a query from a procedure the transaction
|
||||||
be aborted then control will not be returned to your procedure - all work
|
is be aborted then control will not be returned to your procedure - all work
|
||||||
will be rollbacked and server will wait for the next command from client.
|
will be rolled back and the server will wait for the next command from the
|
||||||
It will be changed in the next versions.
|
client. This will be changed in the future versions.
|
||||||
|
|
||||||
Other restrictions are unability to execute BEGIN, END and ABORT
|
Other restrictions are the inability to execute BEGIN, END and ABORT
|
||||||
(transaction control statements) and cursor operations.
|
(transaction control statements) and cursor operations. This will also be
|
||||||
These are also to be changed in future.
|
changed in future.
|
||||||
|
|
||||||
|
|
||||||
Interface functions
|
Interface functions
|
||||||
|
|
||||||
If successful, SPI functions returns non-negative result (either via
|
If successful, SPI functions return a non-negative result (either via
|
||||||
returned (int) value or in SPI_result global variable, as described below).
|
returned (int) value or in SPI_result global variable, as described below).
|
||||||
Otherwise, negative result will be returned.
|
On error, a negative result will be returned.
|
||||||
|
|
||||||
|
|
||||||
int SPI_connect (void)
|
int SPI_connect (void)
|
||||||
|
|
||||||
Connects your procedure to SPI manager. Initializes SPI internal
|
Connects your procedure to the SPI manager. Initializes the SPI internal
|
||||||
structures for query execution and memory management.
|
structures for query execution and memory management.
|
||||||
|
|
||||||
You are to call this function if you need in execution of queries. Some
|
You should call this function if you will need to execute queries. Some
|
||||||
utility SPI functions may be called from un-connected procedures.
|
utility SPI functions may be called from un-connected procedures.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -48,45 +48,47 @@ int SPI_connect (void)
|
|||||||
SPI_OK_CONNECT if connected.
|
SPI_OK_CONNECT if connected.
|
||||||
|
|
||||||
SPI_ERROR_CONNECT if not. You may get this error if SPI_connect() is
|
SPI_ERROR_CONNECT if not. You may get this error if SPI_connect() is
|
||||||
called from already connected procedure - e.g. if you directly call one
|
called from an already connected procedure - e.g. if you directly call one
|
||||||
procedure from another connected one. Actually, while child procedure
|
procedure from another connected one. Actually, while the child procedure
|
||||||
will be able to use SPI, your parent procedure will not be able continue
|
will be able to use SPI, your parent procedure will not be able to continue
|
||||||
use SPI after child returned (if SPI_finish() called by child). It's bad
|
to use SPI after the child returns (if SPI_finish() is called by the child).
|
||||||
practice.
|
It's bad practice.
|
||||||
|
|
||||||
|
|
||||||
int SPI_finish(void)
|
int SPI_finish(void)
|
||||||
|
|
||||||
Dis-connects your procedure from SPI manager. Frees all memory
|
Disconnects your procedure from the SPI manager and frees all memory
|
||||||
allocations made by your procedure via palloc() after SPI_connect().
|
allocations made by your procedure via palloc() since the SPI_connect().
|
||||||
These allocations can't be used any more! See Memory management.
|
These allocations can't be used any more! See Memory management.
|
||||||
|
|
||||||
After SPI_finish() is called your procedure loses ability to run queries.
|
After SPI_finish() is called your procedure loses the ability to run
|
||||||
Server is in the same state as just before call to SPI_connect().
|
queries. The server is in the same state as just before the call to
|
||||||
|
SPI_connect().
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
SPI_OK_FINISH if properly disconnected.
|
SPI_OK_FINISH if properly disconnected.
|
||||||
SPI_ERROR_UNCONNECTED if called from un-connected procedure. No problems
|
SPI_ERROR_UNCONNECTED if called from an un-connected procedure. No problem
|
||||||
with this - it means that nothing was made by SPI manager.
|
with this - it means that nothing was made by the SPI manager.
|
||||||
|
|
||||||
NOTE! SPI_finish() MUST be called by connected procedure or you may get
|
NOTE! SPI_finish() MUST be called by connected procedure or you may get
|
||||||
unpredictable results! But you are able to don't call SPI_finish() if you
|
unpredictable results! But you are able to skip the call to SPI_finish()
|
||||||
abort transaction (via elog(WARN)).
|
if you abort the transaction (via elog(WARN)).
|
||||||
|
|
||||||
|
|
||||||
int SPI_exec(char *query, int tcount)
|
int SPI_exec(char *query, int tcount)
|
||||||
|
|
||||||
Creates execution plan (parser+planner+optimizer) and executes query for
|
Creates an execution plan (parser+planner+optimizer) and executes query
|
||||||
tcount tuples. Should be called from connected procedure. If tcount eq 0
|
for tcount tuples. This should only be called from a connected procedure.
|
||||||
then executes query for all tuples returned by query scan. Using tcount >
|
If tcount eq 0 then it executes the query for all tuples returned by the
|
||||||
0 you may restrict number of tuples for which query will be executed:
|
query scan. Using tcount > 0 you may restrict the number of tuples for
|
||||||
|
which the query will be executed:
|
||||||
|
|
||||||
SPI_exec ("insert into _table_ select * from _table_", 5);
|
SPI_exec ("insert into _table_ select * from _table_", 5);
|
||||||
|
|
||||||
- at max 5 tuples will be inserted into _table_.
|
- at max 5 tuples will be inserted into _table_.
|
||||||
|
|
||||||
If execution of your query was successful then one of the next
|
If execution of your query was successful then one of the following
|
||||||
(non-negative) values will be returned:
|
(non-negative) values will be returned:
|
||||||
|
|
||||||
SPI_OK_UTILITY if some utility (e.g. CREATE TABLE ...) was executed.
|
SPI_OK_UTILITY if some utility (e.g. CREATE TABLE ...) was executed.
|
||||||
@ -97,13 +99,14 @@ int SPI_exec(char *query, int tcount)
|
|||||||
SPI_OK_UPDATE if UPDATE was executed.
|
SPI_OK_UPDATE if UPDATE was executed.
|
||||||
|
|
||||||
NOTE! You may pass many queries in one string or query string may be
|
NOTE! You may pass many queries in one string or query string may be
|
||||||
re-written by RULEs. SPI_exec() returns result for last query executed.
|
re-written by RULEs. SPI_exec() returns the result for the last query
|
||||||
|
executed.
|
||||||
|
|
||||||
Actual number of tuples for which (last) query was executed is returned
|
The actual number of tuples for which the (last) query was executed is
|
||||||
in global variable SPI_processed (if not SPI_OK_UTILITY).
|
returned in the global variable SPI_processed (if not SPI_OK_UTILITY).
|
||||||
|
|
||||||
If SPI_OK_SELECT returned and SPI_processed > 0 then you may use global
|
If SPI_OK_SELECT returned and SPI_processed > 0 then you may use global
|
||||||
pointer SPITupleTable *SPI_tuptable to access selected tuples:
|
pointer SPITupleTable *SPI_tuptable to access the selected tuples:
|
||||||
|
|
||||||
Structure SPITupleTable is defined in spi.h:
|
Structure SPITupleTable is defined in spi.h:
|
||||||
|
|
||||||
@ -115,47 +118,47 @@ int SPI_exec(char *query, int tcount)
|
|||||||
HeapTuple *vals; /* tuples */
|
HeapTuple *vals; /* tuples */
|
||||||
} SPITupleTable;
|
} SPITupleTable;
|
||||||
|
|
||||||
HeapTuple *vals is array of pointers to tuples. TupleDesc tupdesc is
|
HeapTuple *vals is an array of pointers to tuples. TupleDesc tupdesc is
|
||||||
tuple descriptor which you are to pass to SPI functions dealing with
|
a tuple descriptor which you may pass to SPI functions dealing with
|
||||||
tuples.
|
tuples.
|
||||||
|
|
||||||
NOTE! Functions SPI_exec(), SPI_execp() and SPI_prepare() change both
|
NOTE! Functions SPI_exec(), SPI_execp() and SPI_prepare() change both
|
||||||
SPI_processed and SPI_tuptable (just pointer, not context of structure)!
|
SPI_processed and SPI_tuptable (just the pointer, not the contents of the
|
||||||
So, save theme in local procedure variables if you need.
|
structure)! So, save them in local procedure variables if you need them.
|
||||||
|
|
||||||
Also NOTE, that SPI_finish() frees and makes all SPITupleTable-s
|
Also NOTE, that SPI_finish() frees and makes all SPITupleTables
|
||||||
unusable! (See Memory management).
|
unusable! (See Memory management).
|
||||||
|
|
||||||
SPI_exec() may return one of the next (negative) values:
|
SPI_exec() may return one of the following (negative) values:
|
||||||
|
|
||||||
SPI_ERROR_ARGUMENT if query is NULL or tcount < 0.
|
SPI_ERROR_ARGUMENT if query is NULL or tcount < 0.
|
||||||
SPI_ERROR_UNCONNECTED if procedure is un-connected.
|
SPI_ERROR_UNCONNECTED if procedure is unconnected.
|
||||||
SPI_ERROR_COPY if COPY TO/FROM stdin.
|
SPI_ERROR_COPY if COPY TO/FROM stdin.
|
||||||
SPI_ERROR_CURSOR if DECLARE/CLOSE CURSOR, FETCH.
|
SPI_ERROR_CURSOR if DECLARE/CLOSE CURSOR, FETCH.
|
||||||
SPI_ERROR_TRANSACTION if BEGIN/ABORT/END.
|
SPI_ERROR_TRANSACTION if BEGIN/ABORT/END.
|
||||||
SPI_ERROR_OPUNKNOWN if type of query is unknown (this shouldn't occure).
|
SPI_ERROR_OPUNKNOWN if type of query is unknown (this shouldn't occur).
|
||||||
|
|
||||||
|
|
||||||
void *SPI_prepare(char *query, int nargs, Oid * argtypes)
|
void *SPI_prepare(char *query, int nargs, Oid * argtypes)
|
||||||
|
|
||||||
Creates and returns execution plan (parser+planner+optimizer) but doesn't
|
Creates and returns an execution plan (parser+planner+optimizer) but doesn't
|
||||||
execute query. Should be called from connected procedure.
|
execute the query. Should only be called from a connected procedure.
|
||||||
|
|
||||||
nargs is number of parameters ($1 ... $<nargs> - like in SQL-functions),
|
nargs is number of parameters ($1 ... $<nargs> - as in SQL-functions),
|
||||||
*argtypes is array of parameter type OIDs.
|
*argtypes is an array of parameter type OIDs.
|
||||||
|
|
||||||
nargs may be 0 only if there is no any $1 in query.
|
nargs may be 0 only if there is not any $1 in query.
|
||||||
|
|
||||||
Execution of prepared execution plans is much faster sometimes... So this
|
Execution of prepared execution plans is sometimes much faster so this
|
||||||
feature may be useful if the same query will be executed may times.
|
feature may be useful if the same query will be executed many times.
|
||||||
|
|
||||||
NOTE! Plan returned by SPI_prepare() may be used only in current
|
NOTE! The plan returned by SPI_prepare() may be used only in current
|
||||||
invocation of procedure: SPI_finish() frees memory allocated for a plan.
|
invocation of procedure: SPI_finish() frees memory allocated for a plan.
|
||||||
See SPI_saveplan().
|
See SPI_saveplan().
|
||||||
|
|
||||||
If successful, NOT NULL pointer will be returned. Otherwise, you'll get
|
If successful, NOT NULL pointer will be returned. Otherwise, you'll get
|
||||||
NULL plan. In both cases SPI_result will be setted like value returned by
|
a NULL plan. In both cases SPI_result will be set like the value returned
|
||||||
SPI_exec, but
|
by SPI_exec, except
|
||||||
|
|
||||||
SPI_ERROR_ARGUMENT if query is NULL or nargs < 0 or nargs > 0 && argtypes
|
SPI_ERROR_ARGUMENT if query is NULL or nargs < 0 or nargs > 0 && argtypes
|
||||||
is NULL.
|
is NULL.
|
||||||
@ -163,24 +166,24 @@ void *SPI_prepare(char *query, int nargs, Oid * argtypes)
|
|||||||
|
|
||||||
void *SPI_saveplan(void *plan)
|
void *SPI_saveplan(void *plan)
|
||||||
|
|
||||||
Currently, there is no ability to store prepared plans in system catalog
|
Currently, there is no ability to store prepared plans in the system
|
||||||
and fetch them from there for execution. This will be implemented in
|
catalog and fetch them from there for execution. This will be implemented
|
||||||
future versions.
|
in future versions.
|
||||||
|
|
||||||
As work arround, there is ability to re-use prepared plans in the
|
As a work arround, there is the ability to reuse prepared plans in the
|
||||||
consequent invocations of your procedure in current session.
|
consequent invocations of your procedure in the current session.
|
||||||
|
|
||||||
SPI_saveplan() saves passed plan (prepared by SPI_prepare()) in memory
|
SPI_saveplan() saves a passed plan (prepared by SPI_prepare()) in memory
|
||||||
protected from free-ing by SPI_finish() and by transaction manager and
|
protected from freeing by SPI_finish() and by the transaction manager and
|
||||||
returns pointer to saved plan. You may preserve pointer returned in local
|
returns a pointer to the saved plan. You may save the pointer returned in
|
||||||
variable and always check is this pointer NULL or not to either prepare
|
a local variable. Always check if this pointer is NULL or not either when
|
||||||
plan or use already prepared plan in SPI_execp (see below).
|
preparing a plan or using an already prepared plan in SPI_execp (see below).
|
||||||
|
|
||||||
NOTE! If one of objects (relation, function, ...) referenced by prepared
|
NOTE! If one of objects (relation, function, ...) referenced by prepared
|
||||||
plan will be dropped during your session (by your or another backend)
|
plan is dropped during your session (by your backend or another) then the
|
||||||
then results of SPI_execp (for this plan) will be unpredictable.
|
results of SPI_execp (for this plan) will be unpredictable.
|
||||||
|
|
||||||
If successful, NOT NULL returned. Otherwise, SPI_result setted to
|
If successful, NOT NULL is returned otherwise, SPI_result is set to
|
||||||
|
|
||||||
SPI_ERROR_ARGUMENT if plan is NULL.
|
SPI_ERROR_ARGUMENT if plan is NULL.
|
||||||
SPI_ERROR_UNCONNECTED if procedure is un-connected.
|
SPI_ERROR_UNCONNECTED if procedure is un-connected.
|
||||||
@ -188,26 +191,26 @@ void *SPI_saveplan(void *plan)
|
|||||||
|
|
||||||
int SPI_execp(void *plan, Datum * values, char *Nulls, int tcount)
|
int SPI_execp(void *plan, Datum * values, char *Nulls, int tcount)
|
||||||
|
|
||||||
Executes plan prepared by SPI_prepare() (or returned by SPI_saveplan()).
|
Executes a plan prepared by SPI_prepare() (or returned by SPI_saveplan()).
|
||||||
Should be called from connected procedure.
|
Should only be called from a connected procedure.
|
||||||
|
|
||||||
plan is pointer to execution plan, values points to actual parameter
|
plan is pointer to an execution plan, values points to actual parameter
|
||||||
values, Nulls - to array describing what parameters get NULLs ('n' -
|
values, Nulls - to array describing what parameters get NULLs ('n' -
|
||||||
NULL, ' ' - NOT NULL), tcount - number of tuples for which plan is to be
|
NULL, ' ' - NOT NULL), tcount - number of tuples for which plan is to be
|
||||||
executed.
|
executed.
|
||||||
|
|
||||||
If Nulls is NULL then SPI assumes that all values (if any) are NOT NULL.
|
If Nulls is NULL then SPI assumes that all values (if any) are NOT NULL.
|
||||||
|
|
||||||
Returns value like SPI_exec, but
|
Returns the same value as SPI_exec, except
|
||||||
|
|
||||||
SPI_ERROR_ARGUMENT if plan is NULL or tcount < 0.
|
SPI_ERROR_ARGUMENT if plan is NULL or tcount < 0.
|
||||||
SPI_ERROR_PARAM if Values is NULL and plan prepared with some parameters.
|
SPI_ERROR_PARAM if Values is NULL and plan prepared with some parameters.
|
||||||
|
|
||||||
If successful, SPI_tuptable and SPI_processed are initialized like by
|
If successful, SPI_tuptable and SPI_processed are initialized as in
|
||||||
SPI_exec().
|
SPI_exec().
|
||||||
|
|
||||||
|
|
||||||
All functions described below may be used by connected and un-connected
|
All functions described below may be used by connected and unconnected
|
||||||
procedures.
|
procedures.
|
||||||
|
|
||||||
|
|
||||||
@ -216,25 +219,25 @@ HeapTuple SPI_copytuple(HeapTuple tuple)
|
|||||||
Makes copy of tuple in upper Executor context (see Memory management).
|
Makes copy of tuple in upper Executor context (see Memory management).
|
||||||
|
|
||||||
If successful, NOT NULL returned. NULL (i.e. - error) will be returned
|
If successful, NOT NULL returned. NULL (i.e. - error) will be returned
|
||||||
only if NULL passed in.
|
only if NULL is passed in.
|
||||||
|
|
||||||
|
|
||||||
HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
|
HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
|
||||||
int *attnum, Datum * Values, char *Nulls)
|
int *attnum, Datum * Values, char *Nulls)
|
||||||
|
|
||||||
Modifies tuple of relation rel as described by the rest of arguments.
|
Modifies tuple of relation rel as described by the rest of the arguments.
|
||||||
|
|
||||||
natts is number of attribute numbers in attnum.
|
natts is the number of attribute numbers in attnum.
|
||||||
attnum is array of numbers of attributes which are to be changed.
|
attnum is an array of numbers of the attributes which are to be changed.
|
||||||
Values are new values for attributes specified.
|
Values are new values for the attributes specified.
|
||||||
Nulls describes what of attributes specified are NULL (if Nulls is
|
Nulls describes which of the attributes specified are NULL (if Nulls is
|
||||||
NULL then no NULLs).
|
NULL then no NULLs).
|
||||||
|
|
||||||
If successful, NOT NULL pointer to new tuple returned. New tuple is
|
If successful, NOT NULL pointer to new tuple returned. New tuple is
|
||||||
allocated in upper Executor context (see Memory management). Passed tuple
|
allocated in upper Executor context (see Memory management). Passed tuple
|
||||||
is not changed.
|
is not changed.
|
||||||
|
|
||||||
Returns NULL if failed and cause in SPI_result:
|
Returns NULL if failed with cause in SPI_result:
|
||||||
|
|
||||||
SPI_ERROR_ARGUMENT if rel is NULL or tuple is NULL or natts le 0 or
|
SPI_ERROR_ARGUMENT if rel is NULL or tuple is NULL or natts le 0 or
|
||||||
attnum is NULL or Values is NULL.
|
attnum is NULL or Values is NULL.
|
||||||
@ -244,26 +247,27 @@ HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
|
|||||||
|
|
||||||
int SPI_fnumber(TupleDesc tupdesc, char *fname)
|
int SPI_fnumber(TupleDesc tupdesc, char *fname)
|
||||||
|
|
||||||
Returns attribute number for attribute with name as in fname.
|
Returns the attribute number for the attribute with name in fname.
|
||||||
tupdesc is tuple description.
|
tupdesc is tuple description.
|
||||||
|
|
||||||
Attribute numbers are 1-based.
|
Attribute numbers are 1 based.
|
||||||
|
|
||||||
Returns SPI_ERROR_NOATTRIBUTE if attribute not found.
|
Returns SPI_ERROR_NOATTRIBUTE if the named attribute is not found.
|
||||||
|
|
||||||
|
|
||||||
char *SPI_fname(TupleDesc tupdesc, int fnumber)
|
char *SPI_fname(TupleDesc tupdesc, int fnumber)
|
||||||
|
|
||||||
Returns (copy of) name of attribute with number fnumber.
|
Returns (a copy of) the name of the attribute with number fnumber.
|
||||||
|
|
||||||
Returns NULL and (SPI_ERROR_NOATTRIBUTE in SPI_result) if fnumber is
|
Returns NULL and (SPI_ERROR_NOATTRIBUTE in SPI_result) if fnumber is
|
||||||
greater number of attributes in tupdesc or fnumber le 0.
|
greater than the number of attributes in tupdesc or fnumber le 0.
|
||||||
|
|
||||||
|
|
||||||
char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
|
char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
|
||||||
|
|
||||||
Returns external (string) representation of value of attribute fnumber in
|
Returns an external (string) representation of the value of attribute
|
||||||
tuple with descriptor tupdesc. Allocates memory as required by value.
|
fnumber in tuple with descriptor tupdesc. Allocates memory as required
|
||||||
|
by the value.
|
||||||
|
|
||||||
Returns NULL if
|
Returns NULL if
|
||||||
|
|
||||||
@ -275,8 +279,8 @@ char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
|
|||||||
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber,
|
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber,
|
||||||
bool *isnull)
|
bool *isnull)
|
||||||
|
|
||||||
Returns value of attribute fnumber in tuple with descriptor tupdesc. This
|
Returns the value of attribute fnumber in the tuple with descriptor
|
||||||
is binary value in internal form. This is not copy!
|
tupdesc. This is a binary value in internal form. This is not a copy!
|
||||||
|
|
||||||
Returns NULL indicator in *isnull.
|
Returns NULL indicator in *isnull.
|
||||||
|
|
||||||
@ -285,7 +289,7 @@ Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber,
|
|||||||
|
|
||||||
char *SPI_gettype(TupleDesc tupdesc, int fnumber)
|
char *SPI_gettype(TupleDesc tupdesc, int fnumber)
|
||||||
|
|
||||||
Returns (copy of) type name for attribute fnumber.
|
Returns (a copy of) the type name for attribute fnumber.
|
||||||
|
|
||||||
Returns NULL (and SPI_ERROR_NOATTRIBUTE in SPI_result) if fnumber
|
Returns NULL (and SPI_ERROR_NOATTRIBUTE in SPI_result) if fnumber
|
||||||
is invalid.
|
is invalid.
|
||||||
@ -300,7 +304,7 @@ Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber)
|
|||||||
|
|
||||||
char *SPI_getrelname(Relation rel)
|
char *SPI_getrelname(Relation rel)
|
||||||
|
|
||||||
Returns (copy of) relation name of relation rel.
|
Returns (a copy of) the name of relation rel.
|
||||||
|
|
||||||
|
|
||||||
void *SPI_palloc (Size size)
|
void *SPI_palloc (Size size)
|
||||||
@ -323,67 +327,71 @@ void SPI_pfree(void *pointer)
|
|||||||
|
|
||||||
Server allocates memory in memory contexts in such way that allocations
|
Server allocates memory in memory contexts in such way that allocations
|
||||||
made in one context may be freed by context destruction without affecting
|
made in one context may be freed by context destruction without affecting
|
||||||
allocations made in other contexts. There is way to choose some context as
|
allocations made in other contexts. All allocations (via palloc(), etc) are
|
||||||
current one. All allocations (via palloc(), etc) are made in current
|
made in the context which are chosen as current one. You'll get
|
||||||
context. You'll get unpredictable results if you'll try to free (or
|
unpredictable results if you'll try to free (or reallocate) memory allocated
|
||||||
reallocate) memory allocated not in current context.
|
not in current context.
|
||||||
|
|
||||||
|
Creation and switching between memory contexts are subject of SPI manager
|
||||||
|
memory management.
|
||||||
|
|
||||||
SPI procedures deal with two memory contexts: upper Executor memory
|
SPI procedures deal with two memory contexts: upper Executor memory
|
||||||
context and procedure memory context (if connected).
|
context and procedure memory context (if connected).
|
||||||
|
|
||||||
Before a procedure is connected to SPI manager current memory context is
|
Before a procedure is connected to the SPI manager, current memory context
|
||||||
upper Executor context. And so, all allocation made by procedure itself via
|
is upper Executor context so all allocation made by the procedure itself via
|
||||||
palloc()/repalloc() or by SPI utility functions before connection to SPI are
|
palloc()/repalloc() or by SPI utility functions before connecting to SPI are
|
||||||
made in this context.
|
made in this context.
|
||||||
|
|
||||||
After SPI_connect() is called current context is procedure one. All
|
After SPI_connect() is called current context is the procedure's one. All
|
||||||
allocations made via palloc()/repalloc() or by SPI utility functions (except
|
allocations made via palloc()/repalloc() or by SPI utility functions (except
|
||||||
for SPI_copytuple(), SPI_modifytuple, SPI_palloc() and SPI_repalloc()) are
|
for SPI_copytuple(), SPI_modifytuple, SPI_palloc() and SPI_repalloc()) are
|
||||||
made in this context.
|
made in this context.
|
||||||
|
|
||||||
When a procedure dis-connects from SPI manager (via SPI_finish()) current
|
When a procedure disconnects from the SPI manager (via SPI_finish()) the
|
||||||
context is restored to upper Executor context and all allocations made in
|
current context is restored to the upper Executor context and all allocations
|
||||||
procedure memory context are freed and can't be used any more!
|
made in the procedure memory context are freed and can't be used any more!
|
||||||
|
|
||||||
If you want to return something to upper Executor then you have to
|
If you want to return something to the upper Executor then you have to
|
||||||
allocate memory for this in upper context!
|
allocate memory for this in the upper context!
|
||||||
|
|
||||||
SPI has no ability to automatically free allocations in upper Executor
|
SPI has no ability to automatically free allocations in the upper Executor
|
||||||
context!
|
context!
|
||||||
|
|
||||||
SPI automatically frees memory allocated during execution of a query when
|
SPI automatically frees memory allocated during execution of a query when
|
||||||
this query is done!
|
this query is done!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Data changes visibility
|
Data changes visibility
|
||||||
|
|
||||||
PostgreSQL data changes visibility rule: during query execution data
|
PostgreSQL data changes visibility rule: during a query execution, data
|
||||||
changes made by query itself (via SQL-function, SPI-function, triggers)
|
changes made by the query itself (via SQL-function, SPI-function, triggers)
|
||||||
are invisible to the query scan.
|
are invisible to the query scan. For example, in query
|
||||||
|
|
||||||
For example, in query
|
|
||||||
|
|
||||||
INSERT INTO a SELECT * FROM a
|
INSERT INTO a SELECT * FROM a
|
||||||
|
|
||||||
tuples inserted are invisible for SELECT' scan.
|
tuples inserted are invisible for SELECT' scan. In effect, this
|
||||||
|
duplicates the database table within itself (subject to unique index
|
||||||
|
rules, of course) without recursing.
|
||||||
|
|
||||||
But also note that
|
Changes made by query Q are visible by queries which are started after
|
||||||
|
query Q, no matter whether they are started inside Q (during the execution
|
||||||
|
of Q) or after Q is done.
|
||||||
|
|
||||||
changes made by query Q are visible by queries which are started after
|
The last example of the usage of SPI procedure below demonstrates the
|
||||||
query Q, no matter - are they started inside Q (during execution of Q) or
|
visibility rule.
|
||||||
after Q is done.
|
|
||||||
|
|
||||||
Last example of usage SPI function below demonstrates visibility rule.
|
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
|
||||||
There are complex examples in contrib/spi and in
|
There are more complex examples in in src/test/regress/regress.c and
|
||||||
src/test/regress/regress.c.
|
in contrib/spi.
|
||||||
|
|
||||||
This is very simple example of SPI using. Function execq accepts
|
This is a very simple example of SPI usage. The procedure execq accepts
|
||||||
SQL-query in first arguments and tcount in second, executes query
|
an SQL-query in its first argument and tcount in its second, executes the
|
||||||
using SPI_exec and returns number of tuples for which query executed:
|
query using SPI_exec and returns the number of tuples for which the query
|
||||||
|
executed:
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
#include "executor/spi.h" /* this is what you need to work with SPI */
|
#include "executor/spi.h" /* this is what you need to work with SPI */
|
||||||
@ -430,7 +438,7 @@ execq(text *sql, int cnt)
|
|||||||
}
|
}
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
Now, compile and create function:
|
Now, compile and create the function:
|
||||||
create function execq (text, int4) returns int4 as '...path_to_so' language 'c';
|
create function execq (text, int4) returns int4 as '...path_to_so' language 'c';
|
||||||
|
|
||||||
vac=> select execq('create table a (x int4)', 0);
|
vac=> select execq('create table a (x int4)', 0);
|
||||||
|
194
doc/trigger.txt
194
doc/trigger.txt
@ -1,62 +1,89 @@
|
|||||||
|
|
||||||
PostgreSQL Trigger Programming Guide
|
PostgreSQL Trigger Programming Guide
|
||||||
|
|
||||||
For the lack of Procedural Language (PL) in current version of
|
While the current version of PostgreSQL has various client interfaces
|
||||||
PostgreSQL, there is only ability to specify call to a C-function as trigger
|
such as Perl, Tcl, Python and C, it lacks an actual Procedural Language
|
||||||
action.
|
(PL). We hope to have a proper PL one day. In the meantime it is possible
|
||||||
Also, STATEMENT-level trigger events are not supported in current
|
to call C functions as trigger actions. Note that STATEMENT-level trigger
|
||||||
version, and so you are only able to specify BEFORE | AFTER
|
events are not supported in the current version. You can currently specify
|
||||||
INSERT|DELETE|UPDATE of a tuple as trigger event.
|
BEFORE or AFTER on INSERT, DELETE or UPDATE of a tuple as a trigger event.
|
||||||
|
|
||||||
If trigger event occures, trigger manager (called by Executor)
|
If a trigger event occurs, the trigger manager (called by the Executor)
|
||||||
initializes global structure TriggerData *CurrentTriggerData (described
|
initializes the global structure TriggerData *CurrentTriggerData (described
|
||||||
below) and calls trigger function to handle event.
|
below) and calls the trigger function to handle the event.
|
||||||
|
|
||||||
Trigger function must be created before trigger creation as function
|
The trigger function must be created before the trigger is created as a
|
||||||
not accepting any arguments and returns opaque.
|
function taking no arguments and returns opaque.
|
||||||
Actually, there are two specific features in triggers handling.
|
|
||||||
|
The syntax for creating triggers is as follows.
|
||||||
|
|
||||||
|
CREATE TRIGGER <trigger name> <BEFORE|AFTER> <INSERT|DELETE|UPDATE>
|
||||||
|
ON <relation name> FOR EACH <ROW|STATEMENT>
|
||||||
|
EXECUTE PROCEDURE <procedure name> (<function args>);
|
||||||
|
|
||||||
|
The name of the trigger is used if you ever have to delete the trigger.
|
||||||
|
It is used as an argument to the DROP TRIGGER command.
|
||||||
|
|
||||||
|
The next word determines whether the function is called before or after
|
||||||
|
the event.
|
||||||
|
|
||||||
|
The next element of the command determines on what event(s) will trigger
|
||||||
|
the function. Multiple events can be specified separated by OR.
|
||||||
|
|
||||||
|
The relation name determines which table the event applies to.
|
||||||
|
|
||||||
|
The FOR EACH statement determines whether the trigger is fired for each
|
||||||
|
affected row or before (or after) the entire statement has completed.
|
||||||
|
|
||||||
|
The procedure name is the C function called.
|
||||||
|
|
||||||
|
The args are passed to the function in the CurrentTriggerData structure.
|
||||||
|
The purpose of passing arguments to the function is to allow different
|
||||||
|
triggers with similar requirements to call the same function.
|
||||||
|
|
||||||
First, in CREATE TRIGGER one may specify arguments for trigger
|
|
||||||
function (EXECUTE PROCEDURE tfunc (aa,'bb', 1)), and these arguments
|
|
||||||
will be passed to trigger function in CurrentTriggerData.
|
|
||||||
It allows to use single function for many triggers and process events in
|
|
||||||
different ways.
|
|
||||||
Also, function may be used for triggering different relations (these
|
Also, function may be used for triggering different relations (these
|
||||||
functions are named as "general trigger functions").
|
functions are named as "general trigger functions").
|
||||||
|
|
||||||
Second, trigger function has to return HeapTuple to upper Executor.
|
As example of using both features above, there could be a general
|
||||||
No matter for triggers fired AFTER operation (INSERT, DELETE, UPDATE),
|
function that takes as its arguments two field names and puts the current
|
||||||
but it allows to BEFORE triggers:
|
user in one and the current timestamp in the other. This allows triggers to
|
||||||
- return NULL to skip operation for current tuple (and so tuple
|
be written on INSERT events to automatically track creation of records in a
|
||||||
will not be inserted/updated/deleted);
|
transaction table for example. It could also be used as a "last updated"
|
||||||
- return pointer to another tuple (INSERT and UPDATE only) which will be
|
function if used in an UPDATE event.
|
||||||
inserted (as new version of updated tuple if UPDATE) instead of
|
|
||||||
original tuple.
|
|
||||||
|
|
||||||
Note, that there is no initialization performed by CREATE TRIGGER
|
Trigger functions return HeapTuple to the calling Executor. This
|
||||||
handler. It will be changed in the future.
|
is ignored for triggers fired after an INSERT, DELETE or UPDATE operation
|
||||||
|
but it allows BEFORE triggers to:
|
||||||
|
|
||||||
Also, if more than one trigger defined for the same event on the same
|
- return NULL to skip the operation for the current tuple (and so the
|
||||||
relation then order of trigger firing is unpredictable. It may be changed in
|
tuple will not be inserted/updated/deleted);
|
||||||
the future.
|
- return a pointer to another tuple (INSERT and UPDATE only) which will
|
||||||
|
be inserted (as the new version of the updated tuple if UPDATE) instead
|
||||||
|
of original tuple.
|
||||||
|
|
||||||
Also, if a trigger function executes SQL-queries (using SPI) then these
|
Note, that there is no initialization performed by the CREATE TRIGGER
|
||||||
queries may fire triggers again. This is known as cascading of triggers.
|
handler. This will be changed in the future. Also, if more than one trigger
|
||||||
There is no explicit limitation for number of cascade levels.
|
is defined for the same event on the same relation, the order of trigger
|
||||||
If a trigger is fired by INSERT and inserts new tuple in the same
|
firing is unpredictable. This may be changed in the future.
|
||||||
|
|
||||||
|
If a trigger function executes SQL-queries (using SPI) then these queries
|
||||||
|
may fire triggers again. This is known as cascading triggers. There is no
|
||||||
|
explicit limitation on the number of cascade levels.
|
||||||
|
|
||||||
|
If a trigger is fired by INSERT and inserts a new tuple in the same
|
||||||
relation then this trigger will be fired again. Currently, there is nothing
|
relation then this trigger will be fired again. Currently, there is nothing
|
||||||
provided for synchronization (etc) of these cases. It may be changed. At
|
provided for synchronization (etc) of these cases but this may change. At
|
||||||
the moment, there is function funny_dup17() in the regress tests which uses
|
the moment, there is function funny_dup17() in the regress tests which uses
|
||||||
some technics to stop recursion (cascading) of itself...
|
some techniques to stop recursion (cascading) on itself...
|
||||||
|
|
||||||
|
|
||||||
Interaction with trigger manager
|
Interaction with the trigger manager
|
||||||
|
|
||||||
As it's mentioned above when function is called by trigger manager
|
As mentioned above, when function is called by the trigger manager,
|
||||||
structure TriggerData *CurrentTriggerData is NOT NULL and initialized. And
|
structure TriggerData *CurrentTriggerData is NOT NULL and initialized. So
|
||||||
so, it's better to check CurrentTriggerData against being NULL in the
|
it is better to check CurrentTriggerData against being NULL at the start
|
||||||
begining and set it to NULL just after fetching information - to prevent
|
and set it to NULL just after fetching the information to prevent calls to
|
||||||
calls to trigger function not from trigger manager.
|
a trigger function not from the trigger manager.
|
||||||
|
|
||||||
struct TriggerData is defined in src/include/commands/trigger.h:
|
struct TriggerData is defined in src/include/commands/trigger.h:
|
||||||
|
|
||||||
@ -70,8 +97,8 @@ typedef struct TriggerData
|
|||||||
} TriggerData;
|
} TriggerData;
|
||||||
|
|
||||||
tg_event
|
tg_event
|
||||||
describes event for what function is called. You may use macros
|
describes event for which the function is called. You may use the
|
||||||
to deal with tg_event:
|
following macros to examine tg_event:
|
||||||
|
|
||||||
TRIGGER_FIRED_BEFORE(event) returns TRUE if trigger fired BEFORE;
|
TRIGGER_FIRED_BEFORE(event) returns TRUE if trigger fired BEFORE;
|
||||||
TRIGGER_FIRED_AFTER(event) returns TRUE if trigger fired AFTER;
|
TRIGGER_FIRED_AFTER(event) returns TRUE if trigger fired AFTER;
|
||||||
@ -84,23 +111,25 @@ tg_event
|
|||||||
TRIGGER_FIRED_BY_UPDATE(event) returns TRUE if trigger fired by UPDATE.
|
TRIGGER_FIRED_BY_UPDATE(event) returns TRUE if trigger fired by UPDATE.
|
||||||
|
|
||||||
tg_relation
|
tg_relation
|
||||||
is pointer to structure describing triggered relation. Look @
|
is pointer to structure describing the triggered relation. Look at
|
||||||
src/include/utils/rel.h about this structure. The most interest things
|
src/include/utils/rel.h for details about this structure. The most
|
||||||
are tg_relation->rd_att (descriptor of relation tuples) and
|
interest things are tg_relation->rd_att (descriptor of the relation
|
||||||
tg_relation->rd_rel->relname (relation' name. This is not char*, but
|
tuples) and tg_relation->rd_rel->relname (relation's name. This is not
|
||||||
NameData - use SPI_getrelname(tg_relation) to get char* to copy of name).
|
char*, but NameData. Use SPI_getrelname(tg_relation) to get char* if
|
||||||
|
you need a copy of name).
|
||||||
|
|
||||||
tg_trigtuple
|
tg_trigtuple
|
||||||
is tuple (pointer) for which trigger is fired. This is tuple to being
|
is a pointer to the tuple for which the trigger is fired. This is the tuple
|
||||||
inserted (if INSERT), deleted (if DELETE) or updated (if UPDATE).
|
being inserted (if INSERT), deleted (if DELETE) or updated (if UPDATE).
|
||||||
If INSERT/DELETE then this is what you are to return to Executor if
|
If INSERT/DELETE then this is what you are to return to Executor if
|
||||||
you don't want to replace tuple with another one (INSERT) or skip
|
you don't want to replace tuple with another one (INSERT) or skip the
|
||||||
operation.
|
operation.
|
||||||
|
|
||||||
tg_newtuple
|
tg_newtuple
|
||||||
is pointer to new version of tuple if UPDATE and NULL if INSERT/DELETE.
|
is a pointer to the new version of tuple if UPDATE and NULL if this is
|
||||||
This is what you are to return to Executor if UPDATE and you don't want
|
for an INSERT or a DELETE. This is what you are to return to Executor if
|
||||||
to replace tuple with another one or skip operation.
|
UPDATE and you don't want to replace this tuple with another one or skip
|
||||||
|
the operation.
|
||||||
|
|
||||||
tg_trigger
|
tg_trigger
|
||||||
is pointer to structure Trigger defined in src/include/utils/rel.h:
|
is pointer to structure Trigger defined in src/include/utils/rel.h:
|
||||||
@ -116,45 +145,44 @@ typedef struct Trigger
|
|||||||
char **tgargs;
|
char **tgargs;
|
||||||
} Trigger;
|
} Trigger;
|
||||||
|
|
||||||
tgname is trigger' name, tgnargs is number of arguments in tgargs, tgargs
|
tgname is the trigger's name, tgnargs is number of arguments in tgargs,
|
||||||
is array of pointers to arguments specified in CREATE TRIGGER. Other
|
tgargs is an array of pointers to the arguments specified in the CREATE
|
||||||
members are for internal use.
|
TRIGGER statement. Other members are for internal use only.
|
||||||
|
|
||||||
|
|
||||||
Data changes visibility
|
Visibility of Data Changes
|
||||||
|
|
||||||
PostgreSQL data changes visibility rule: during query execution data
|
PostgreSQL data changes visibility rule: during a query execution, data
|
||||||
changes made by query itself (via SQL-function, SPI-function, triggers)
|
changes made by the query itself (via SQL-function, SPI-function, triggers)
|
||||||
are invisible to the query scan.
|
are invisible to the query scan. For example, in query
|
||||||
|
|
||||||
For example, in query
|
|
||||||
|
|
||||||
INSERT INTO a SELECT * FROM a
|
INSERT INTO a SELECT * FROM a
|
||||||
|
|
||||||
tuples inserted are invisible for SELECT' scan.
|
tuples inserted are invisible for SELECT' scan. In effect, this
|
||||||
|
duplicates the database table within itself (subject to unique index
|
||||||
|
rules, of course) without recursing.
|
||||||
|
|
||||||
But keep in mind notices about visibility in SPI documentation:
|
But keep in mind this notice about visibility in the SPI documentation:
|
||||||
|
|
||||||
changes made by query Q are visible by queries which are started after
|
Changes made by query Q are visible by queries which are started after
|
||||||
query Q, no matter - are they started inside Q (during execution of Q) or
|
query Q, no matter whether they are started inside Q (during the
|
||||||
after Q is done.
|
execution of Q) or after Q is done.
|
||||||
|
|
||||||
This is true for triggers as well. And so, though tuple being inserted
|
This is true for triggers as well so, though a tuple being inserted
|
||||||
(tg_trigtuple) is not visible to queries in BEFORE trigger, this tuple (just
|
(tg_trigtuple) is not visible to queries in a BEFORE trigger, this tuple
|
||||||
inserted) is visible to queries in AFTER trigger, and to queries in
|
(just inserted) is visible to queries in an AFTER trigger, and to queries
|
||||||
BEFORE/AFTER triggers fired after this!
|
in BEFORE/AFTER triggers fired after this!
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
|
||||||
There are complex examples in contrib/spi and in
|
There are more complex examples in in src/test/regress/regress.c and
|
||||||
src/test/regress/regress.c.
|
in contrib/spi.
|
||||||
|
|
||||||
This is very simple example of trigger usage. Function trigf reports
|
Here is a very simple example of trigger usage. Function trigf reports
|
||||||
about number of tuples in triggered relation ttest and in trigger fired
|
the number of tuples in the triggered relation ttest and skips the
|
||||||
BEFORE INSERT/UPDATE checks against is attribute x NULL and skips operations
|
operation if the query attempts to insert NULL into x (i.e - it acts as a
|
||||||
for NULLs (ala NOT NULL implementation using triggers without aborting
|
NOT NULL constraint but doesn't abort the transaction).
|
||||||
transaction if NULL).
|
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
#include "executor/spi.h" /* this is what you need to work with SPI */
|
#include "executor/spi.h" /* this is what you need to work with SPI */
|
||||||
@ -247,7 +275,7 @@ vac=> insert into ttest values (1);
|
|||||||
NOTICE:trigf (fired before): there are 0 tuples in ttest
|
NOTICE:trigf (fired before): there are 0 tuples in ttest
|
||||||
NOTICE:trigf (fired after ): there are 1 tuples in ttest
|
NOTICE:trigf (fired after ): there are 1 tuples in ttest
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
remember about visibility
|
remember what we said about visibility.
|
||||||
INSERT 167793 1
|
INSERT 167793 1
|
||||||
vac=> select * from ttest;
|
vac=> select * from ttest;
|
||||||
x
|
x
|
||||||
@ -259,7 +287,7 @@ vac=> insert into ttest select x * 2 from ttest;
|
|||||||
NOTICE:trigf (fired before): there are 1 tuples in ttest
|
NOTICE:trigf (fired before): there are 1 tuples in ttest
|
||||||
NOTICE:trigf (fired after ): there are 2 tuples in ttest
|
NOTICE:trigf (fired after ): there are 2 tuples in ttest
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
remember about visibility
|
remember what we said about visibility.
|
||||||
INSERT 167794 1
|
INSERT 167794 1
|
||||||
vac=> select * from ttest;
|
vac=> select * from ttest;
|
||||||
x
|
x
|
||||||
@ -288,7 +316,7 @@ NOTICE:trigf (fired after ): there are 1 tuples in ttest
|
|||||||
NOTICE:trigf (fired before): there are 1 tuples in ttest
|
NOTICE:trigf (fired before): there are 1 tuples in ttest
|
||||||
NOTICE:trigf (fired after ): there are 0 tuples in ttest
|
NOTICE:trigf (fired after ): there are 0 tuples in ttest
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
remember about visibility
|
remember what we said about visibility.
|
||||||
DELETE 2
|
DELETE 2
|
||||||
vac=> select * from ttest;
|
vac=> select * from ttest;
|
||||||
x
|
x
|
||||||
|
Loading…
x
Reference in New Issue
Block a user