<Chapter id="spi">
<DocInfo>
<AuthorGroup>
<Author>
<FirstName>Vadim</FirstName>
<Surname>Mikheev</Surname>
</Author>
</AuthorGroup>
<Date>Transcribed 1998-01-16</Date>
</DocInfo>

<Title>Server Programming Interface</Title>

<Para>
The <FirstTerm>Server Programming Interface</FirstTerm> 
(<Acronym>SPI</Acronym>) gives users the
ability to run <Acronym>SQL</Acronym> queries inside user-defined 
<Acronym>C</Acronym> functions.
The available Procedural Languages (<Acronym>PL</Acronym>) give an alternate
means to access these capabilities.
</Para>

<Para>
In fact, <Acronym>SPI</Acronym> is just a set of native interface functions
to simplify access to the Parser, Planner, Optimizer and Executor. 
<Acronym>SPI</Acronym> also does some memory management.
</Para>

<Para>
To avoid misunderstanding we'll use <FirstTerm>function</FirstTerm> 
to mean <Acronym>SPI</Acronym> interface functions and 
<FirstTerm>procedure</FirstTerm> for user-defined C-functions 
using <Acronym>SPI</Acronym>.
</Para>

<Para>
<Acronym>SPI</Acronym> procedures are always called by some (upper) 
Executor and the <Acronym>SPI</Acronym>
manager uses the Executor to run your queries. Other procedures may be
called by the Executor running queries from your procedure.
</Para>

<Para>
Note, that if during execution of a query from a procedure the transaction
is aborted then control will not be returned to your procedure. Rather, all work
will be rolled back and the server will wait for the next command from the
client.  This will be changed in future versions.
</Para>

<Para>
Other restrictions are the inability to execute BEGIN, END and ABORT
(transaction control statements) and cursor operations.  This will also be
changed in the future.
</Para>

<Para>
If successful, <Acronym>SPI</Acronym> functions return a non-negative result (either via
a returned integer value or in SPI_result global variable, as described below).
On error, a negative or NULL result will be returned.
</Para>

<Sect1>
<Title>Interface Functions</Title>

<REFENTRY ID="SPI-SPICONNECT">
<REFMETA>
<REFENTRYTITLE>SPI_connect</REFENTRYTITLE>
<REFMISCINFO>SPI - Connection Management</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_connect
</REFNAME>
<REFPURPOSE>
   Connects your procedure to the SPI manager.
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPICONNECT-1"><PRIMARY>SPI</PRIMARY><SECONDARY>connecting</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPICONNECT-2"><PRIMARY>SPI_connect</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
int SPI_connect(void)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPICONNECT-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<PARA>None
</PARA>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPICONNECT-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>int
</TERM>
<LISTITEM>
<PARA>
Return status
<VARIABLELIST>
<VARLISTENTRY>
<TERM><ReturnValue>SPI_OK_CONNECT</ReturnValue>
</TERM>
<LISTITEM>
<PARA>
   if connected
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM><ReturnValue>SPI_ERROR_CONNECT</ReturnValue>
</TERM>
<LISTITEM>
<PARA>
   if not connected
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</para>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPICONNECT-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_connect</FUNCTION> opens a connection to the <ProductName>Postgres</ProductName> backend.
   You should call this function if you will need to execute queries. Some
   utility SPI functions may be called from un-connected procedures.
</PARA>
<PARA>
   You may get <ReturnValue>SPI_ERROR_CONNECT</ReturnValue> error if <Function>SPI_connect</Function> is
   called from an already connected procedure - e.g. if you directly call one
   procedure from another connected one.  Actually, while the child procedure
   will be able to use SPI, your parent procedure will not be able to continue
   to use SPI after the child returns (if <Function>SPI_finish</Function> is called by the child).
   It's bad practice.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPICONNECT-2">
<TITLE>Usage
</TITLE>
<PARA>
<!--
XXX thomas 1997-12-24
-->
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPICONNECT-3">
<TITLE>Algorithm
</TITLE>
<PARA><FUNCTION>SPI_connect</FUNCTION> performs the following:
</PARA>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>&bull;
</TERM>
<LISTITEM>
<PARA>
  Initializes the SPI internal
   structures for query execution and memory management.
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
<PARA>
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPICONNECT-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIFINISH">
<REFMETA>
<REFENTRYTITLE>SPI_finish</REFENTRYTITLE>
<REFMISCINFO>SPI - Connection Management</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_finish
</REFNAME>
<REFPURPOSE>
   Disconnects your procedure from the SPI manager.
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIFINISH-1"><PRIMARY>SPI</PRIMARY><SECONDARY>disconnecting</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIFINISH-2"><PRIMARY>SPI_finish</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_finish(void)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIFINISH-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<PARA>None
</PARA>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIFINISH-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>int
</TERM>
<LISTITEM>
<PARA>
<SimpleList>
<Member>
<ReturnValue>SPI_OK_FINISH</ReturnValue>
   if properly disconnected
</Member>
<Member>
<ReturnValue>SPI_ERROR_UNCONNECTED</ReturnValue>
   if called from an un-connected procedure
</Member>
</SimpleList>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIFINISH-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_finish</FUNCTION> closes an existing connection to the <ProductName>Postgres</ProductName> backend.
   You should call this function after completing operations through the SPI manager.
</para>
<PARA>
   You may get the error return <ReturnValue>SPI_ERROR_UNCONNECTED</ReturnValue> if <Function>SPI_finish</Function> is
   called without having a current valid connection.
 There is no fundamental problem
   with this; it means that nothing was done by the SPI manager.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIFINISH-2">
<TITLE>Usage
</TITLE>
<PARA>
   <Function>SPI_finish</Function> <Emphasis>must</Emphasis> be called as a final step by a connected procedure
 or you may get
   unpredictable results! Note that you can safely skip the call to <Function>SPI_finish</Function>
   if you abort the transaction (via elog(ERROR)).

</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIFINISH-3">
<TITLE>Algorithm
</TITLE>
<PARA><FUNCTION>SPI_finish</FUNCTION> performs the following:
</PARA>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>&bull;
</TERM>
<LISTITEM>
<PARA>
   Disconnects your procedure from the SPI manager and frees all memory
   allocations made by your procedure via <Function>palloc</Function> since
 the <Function>SPI_connect</Function>. 
   These allocations can't be used any more! See Memory management.
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
<PARA>
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIFINISH-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIEXEC">
<REFMETA>
<REFENTRYTITLE>SPI_exec</REFENTRYTITLE>
<REFMISCINFO>SPI - Connection Management</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_exec
</REFNAME>
<REFPURPOSE>
   Creates an execution plan (parser+planner+optimizer) and executes a query.
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIEXEC-1"><PRIMARY>SPI</PRIMARY><SECONDARY>executing</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIEXEC-2"><PRIMARY>SPI_exec</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_exec(<REPLACEABLE CLASS="PARAMETER">query</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIEXEC-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
char *<REPLACEABLE CLASS="PARAMETER">query</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
String containing query plan
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Maximum number of tuples to return
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIEXEC-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>int
</TERM>
<LISTITEM>
<PARA>
<SimpleList>
<Member>
   <ReturnValue>SPI_OK_EXEC</ReturnValue> if properly disconnected
</Member>
<Member>
   <ReturnValue>SPI_ERROR_UNCONNECTED</ReturnValue> if called from an un-connected procedure
</Member>
<Member>
   <ReturnValue>SPI_ERROR_ARGUMENT</ReturnValue> if query is NULL or <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE> < 0.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_UNCONNECTED</ReturnValue> if procedure is unconnected.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_COPY</ReturnValue> if COPY TO/FROM stdin.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_CURSOR</ReturnValue> if DECLARE/CLOSE CURSOR, FETCH.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_TRANSACTION</ReturnValue> if BEGIN/ABORT/END.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_OPUNKNOWN</ReturnValue> if type of query is unknown (this shouldn't occur).
</Member>
</SimpleList>
</para>
<Para>
   If execution of your query was successful then one of the following
   (non-negative) values will be returned:
<SimpleList>
<Member>
   <ReturnValue>SPI_OK_UTILITY</ReturnValue> if some utility (e.g. CREATE TABLE ...) was executed
</Member>
<Member>
   <ReturnValue>SPI_OK_SELECT</ReturnValue> if SELECT (but not SELECT ... INTO!) was executed
</Member>
<Member>
   <ReturnValue>SPI_OK_SELINTO</ReturnValue> if SELECT ... INTO was executed
</Member>
<Member>
   <ReturnValue>SPI_OK_INSERT</ReturnValue> if INSERT (or INSERT ... SELECT) was executed
</Member>
<Member>
   <ReturnValue>SPI_OK_DELETE</ReturnValue> if DELETE was executed
</Member>
<Member>
   <ReturnValue>SPI_OK_UPDATE</ReturnValue> if UPDATE was executed
</Member>
</SimpleList>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIEXEC-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_exec</FUNCTION> creates an execution plan (parser+planner+optimizer)
 and executes the query for <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE> tuples.

</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIEXEC-2">
<TITLE>Usage
</TITLE>
<PARA>
  This should only be called from a connected procedure.
   If <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE> is zero then it executes the query for all tuples returned by the
   query scan. Using <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE> > 0 you may restrict the number of tuples for
   which the query will be executed. For example,

<ProgramListing>
SPI_exec ("insert into table select * from table", 5);
</ProgramListing>

will allow at most 5 tuples to be inserted into table.

   If execution of your query was successful then a non-negative value will be returned.

<Note>
<Para>
You may pass many queries in one string or query string may be
   re-written by RULEs. <Function>SPI_exec</Function> returns the result for the last query
   executed.
</Para>
</Note>
</para>
<Para>
   The actual number of tuples for which the (last) query was executed is
   returned in the global variable SPI_processed (if not <ReturnValue>SPI_OK_UTILITY</ReturnValue>).

   If <ReturnValue>SPI_OK_SELECT</ReturnValue> returned and SPI_processed &gt; 0 then you may use global
   pointer SPITupleTable *SPI_tuptable to access the selected tuples:

   Also NOTE, that <Function>SPI_finish</Function> frees and makes all SPITupleTables
   unusable! (See Memory management).
</Para>

<Para>
   <Function>SPI_exec</Function> may return one of the following (negative) values:
<SimpleList>
<Member>
   <ReturnValue>SPI_ERROR_ARGUMENT</ReturnValue> if query is NULL or <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE> < 0.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_UNCONNECTED</ReturnValue> if procedure is unconnected.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_COPY</ReturnValue> if COPY TO/FROM stdin.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_CURSOR</ReturnValue> if DECLARE/CLOSE CURSOR, FETCH.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_TRANSACTION</ReturnValue> if BEGIN/ABORT/END.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_OPUNKNOWN</ReturnValue> if type of query is unknown (this shouldn't occur).
</Member>
</SimpleList>

</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIEXEC-3">
<TITLE>Algorithm
</TITLE>
<PARA><FUNCTION>SPI_exec</FUNCTION> performs the following:
</PARA>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>&bull;
</TERM>
<LISTITEM>
<PARA>
   Disconnects your procedure from the SPI manager and frees all memory
   allocations made by your procedure via <Function>palloc</Function> since the <Function>SPI_connect</Function>. 
   These allocations can't be used any more! See Memory management.
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
<PARA>
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIEXEC-4">
<TITLE>Structures
</TITLE>
<PARA>
   If <ReturnValue>SPI_OK_SELECT</ReturnValue> returned and SPI_processed > 0 then you may use the global
   pointer SPITupleTable *SPI_tuptable to access the selected tuples.

<Para>
   Structure SPITupleTable is defined in spi.h:
<ProgramListing>
   typedef struct
   {
       uint32      alloced;        /* # of alloced vals */
       uint32      free;           /* # of free vals */
       TupleDesc   tupdesc;        /* tuple descriptor */
       HeapTuple  *vals;           /* tuples */
   } SPITupleTable;
</ProgramListing>

<Para>
   HeapTuple *vals is an array of pointers to tuples. TupleDesc tupdesc is
   a tuple descriptor which you may pass to SPI functions dealing with
   tuples.

<Para>
   NOTE! Functions <Function>SPI_exec</Function>, <Function>SPI_execp</Function> and <Function>SPI_prepare</Function> change both
   SPI_processed and SPI_tuptable (just the pointer, not the contents of the
   structure)!  So, save them in local procedure variables if you need them.

<Para>
   Also NOTE, that <Function>SPI_finish</Function> frees and makes all SPITupleTables
   unusable! (See Memory management).
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIPREPARE">
<REFMETA>
<REFENTRYTITLE>SPI_prepare</REFENTRYTITLE>
<REFMISCINFO>SPI - Plan Preparation</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_prepare
</REFNAME>
<REFPURPOSE>
   Connects your procedure to the SPI manager.
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIPREPARE-1"><PRIMARY>SPI</PRIMARY><SECONDARY>connecting</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIPREPARE-2"><PRIMARY>SPI_prepare</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_prepare(<REPLACEABLE CLASS="PARAMETER">query</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">nargs</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">argtypes</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIPREPARE-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
<REPLACEABLE CLASS="PARAMETER">query</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Query string
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
<REPLACEABLE CLASS="PARAMETER">nargs</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Number of input parameters ($1 ... $nargs - as in SQL-functions)
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
<REPLACEABLE CLASS="PARAMETER">argtypes</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Pointer list of type <Acronym>OID</Acronym>s to input arguments
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIPREPARE-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>void *
</TERM>
<LISTITEM>
<PARA>
Pointer to an execution plan (parser+planner+optimizer)
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIPREPARE-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_prepare</FUNCTION> 
   creates and returns an execution plan (parser+planner+optimizer) but doesn't
   execute the query. Should only be called from a connected procedure.

</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIPREPARE-2">
<TITLE>Usage
</TITLE>
<PARA>
   nargs is number of parameters ($1 ... $nargs - as in SQL-functions),
   and nargs may be 0 only if there is not any $1 in query.
</para>
<Para>
   Execution of prepared execution plans is sometimes much faster so this
   feature may be useful if the same query will be executed many times.
</para>
<Para>
The plan returned by <Function>SPI_prepare</Function> may be used only in current
   invocation of the procedure since <Function>SPI_finish</Function> frees memory allocated for a plan. 
   See <Function>SPI_saveplan</Function>.
</para>
<Para>
   If successful, a non-null pointer will be returned. Otherwise, you'll get
   a NULL plan.  In both cases SPI_result will be set like the value returned
   by SPI_exec, except that it is set to 
   <ReturnValue>SPI_ERROR_ARGUMENT</ReturnValue> if query is NULL or nargs < 0 or nargs > 0 && argtypes
   is NULL.

</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIPREPARE-3">
<TITLE>Algorithm
</TITLE>
<PARA><FUNCTION>SPI_prepare</FUNCTION> performs the following:
</PARA>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>&bull;
</TERM>
<LISTITEM>
<PARA>
TBD
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
<PARA>
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPIPREPARE-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPISAVEPLAN">
<REFMETA>
<REFENTRYTITLE>SPI_saveplan</REFENTRYTITLE>
<REFMISCINFO>SPI - Plan Storage</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_saveplan
</REFNAME>
<REFPURPOSE>
   Saves a passed plan
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPISAVEPLAN-1"><PRIMARY>SPI</PRIMARY><SECONDARY>connecting</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPISAVEPLAN-2"><PRIMARY>SPI_saveplan</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_saveplan(<REPLACEABLE CLASS="PARAMETER">plan</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPISAVEPLAN-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
void *<REPLACEABLE CLASS="PARAMETER">query</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Passed plan
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPISAVEPLAN-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>void *
</TERM>
<LISTITEM>
<PARA>
Execution plan location. NULL if unsuccessful.
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>SPI_result
</TERM>
<LISTITEM>
<PARA>
<SimpleList>
<Member>
   <ReturnValue>SPI_ERROR_ARGUMENT</ReturnValue> if plan is NULL
</Member>
<Member>
   <ReturnValue>SPI_ERROR_UNCONNECTED</ReturnValue> if procedure is un-connected
</Member>
</SimpleList>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPISAVEPLAN-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_saveplan</FUNCTION> 
   stores a plan prepared by <Function>SPI_prepare</Function> in safe memory
   protected from freeing by <Function>SPI_finish</Function> or the transaction manager.
</para>
<Para>
   In the current version of <ProductName>Postgres</ProductName> there is no ability to
 store prepared plans in the system
   catalog and fetch them from there for execution. This will be implemented
   in future versions.

   As an alternative, there is the ability to reuse prepared plans in the
   consequent invocations of your procedure in the current session.
   Use <Function>SPI_execp</Function> to execute this saved plan.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPISAVEPLAN-2">
<TITLE>Usage
</TITLE>
<Para>
   <Function>SPI_saveplan</Function> saves a passed plan (prepared by <Function>SPI_prepare</Function>) in memory
   protected from freeing by <Function>SPI_finish</Function> and by the transaction manager and
   returns a pointer to the saved plan.  You may save the pointer returned in
   a local variable.  Always check if this pointer is NULL or not either when
   preparing a plan or using an already prepared plan in SPI_execp (see below).

<Note>
<Para>
   If one of the objects (a relation, function, etc.) referenced by the prepared
   plan is dropped during your session (by your backend or another process) then the
   results of <Function>SPI_execp</Function> for this plan will be unpredictable.
</Para>
</Note>

</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPISAVEPLAN-3">
<TITLE>Algorithm
</TITLE>
<PARA><FUNCTION>SPI_saveplan</FUNCTION> performs the following:
</PARA>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>&bull;
</TERM>
<LISTITEM>
<PARA>
TBD
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
<PARA>
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPISAVEPLAN-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIEXECP">
<REFMETA>
<REFENTRYTITLE>SPI_execp</REFENTRYTITLE>
<REFMISCINFO>SPI - Plan Execution</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_execp
</REFNAME>
<REFPURPOSE>
Executes a plan from <Function>SPI_saveplan</Function>
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIEXECP-1"><PRIMARY>SPI</PRIMARY><SECONDARY>connecting</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIEXECP-2"><PRIMARY>SPI_execp</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_execp(<REPLACEABLE CLASS="PARAMETER">plan</REPLACEABLE>,
<REPLACEABLE CLASS="PARAMETER">values</REPLACEABLE>,
<REPLACEABLE CLASS="PARAMETER">nulls</REPLACEABLE>,
<REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIEXECP-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
void *<REPLACEABLE CLASS="PARAMETER">plan</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Execution plan
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
Datum *<REPLACEABLE CLASS="PARAMETER">values</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Actual parameter values
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
char *<REPLACEABLE CLASS="PARAMETER">nulls</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Array describing what parameters get NULLs
<SimpleList>
<Member>'n' indicates NULL allowed</Member>
<Member>' ' indicates NULL not allowed</Member>
</SimpleList>
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Number of tuples for which plan is to be executed
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIEXECP-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>int
</TERM>
<LISTITEM>
<PARA>
   Returns the same value as <Function>SPI_exec</Function> as well as
<SimpleList>
<Member>
 <ReturnValue>SPI_ERROR_ARGUMENT</ReturnValue>
 if <REPLACEABLE CLASS="PARAMETER">plan</REPLACEABLE>
 is NULL or <REPLACEABLE CLASS="PARAMETER">tcount</REPLACEABLE> &lt; 0
</Member>
<Member>
   <ReturnValue>SPI_ERROR_PARAM</ReturnValue>
 if <REPLACEABLE CLASS="PARAMETER">values</REPLACEABLE>
 is NULL
 and <REPLACEABLE CLASS="PARAMETER">plan</REPLACEABLE>
 was prepared with some parameters.
</Member>
</SimpleList>
</para>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>SPI_tuptable
</TERM>
<LISTITEM>
<PARA>
initialized as in
   <Function>SPI_exec</Function> if successful
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>SPI_processed
</TERM>
<LISTITEM>
<PARA>
initialized as in
   <Function>SPI_exec</Function> if successful
</para>
</listitem>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIEXECP-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_execp</FUNCTION> 
   stores a plan prepared by <Function>SPI_prepare</Function> in safe memory
   protected from freeing by <Function>SPI_finish</Function> or the transaction manager.
</para>
<Para>
   In the current version of <ProductName>Postgres</ProductName> there is no ability to
 store prepared plans in the system
   catalog and fetch them from there for execution. This will be implemented
   in future versions.

   As a work arround, there is the ability to reuse prepared plans in the
   consequent invocations of your procedure in the current session.
   Use <Function>SPI_execp</Function> to execute this saved plan.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIEXECP-2">
<TITLE>Usage
</TITLE>
<Para>
   If <REPLACEABLE CLASS="PARAMETER">nulls</REPLACEABLE>
is NULL then 
   <Function>SPI_execp</Function> 
assumes that all values (if any) are NOT NULL.

<Note>
<Para>
   If one of the objects (a relation, function, etc.) referenced by the prepared
   plan is dropped during your session (by your backend or another process) then the
   results of <Function>SPI_execp</Function> for this plan will be unpredictable.
</Para>
</Note>

</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIEXECP-3">
<TITLE>Algorithm
</TITLE>
<PARA><FUNCTION>SPI_execp</FUNCTION> performs the following:
</PARA>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>&bull;
</TERM>
<LISTITEM>
<PARA>
TBD
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
<PARA>
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPIEXECP-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

</Sect1>

<Sect1>
<Title>Interface Support Functions</Title>

<Para>
All functions described below may be used by connected and unconnected
procedures.
</Para>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPICOPYTUPLE">
<REFMETA>
<REFENTRYTITLE>SPI_copytuple</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Copy</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_copytuple
</REFNAME>
<REFPURPOSE>
Makes copy of tuple in upper Executor context
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPICOPYTUPLE-1"><PRIMARY>SPI</PRIMARY><SECONDARY>copying tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPICOPYTUPLE-2"><PRIMARY>SPI_copytuple</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_copytuple(<REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPICOPYTUPLE-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
HeapTuple <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple to be copied
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPICOPYTUPLE-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
HeapTuple
</TERM>
<LISTITEM>
<PARA>
Copied tuple
<SimpleList>
<Member>
 <ReturnValue>non-NULL</ReturnValue>
 if <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
 is not NULL and the copy was successful
</Member>
<Member>
   <ReturnValue>NULL</ReturnValue>
 only if <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
 is NULL
</Member>
</SimpleList>
</para>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPICOPYTUPLE-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_copytuple</FUNCTION> 
   makes a copy of tuple in upper Executor context. See the section on Memory Management.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPICOPYTUPLE-2">
<TITLE>Usage
</TITLE>
<Para>
TBD
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPICOPYTUPLE-3">
<TITLE>Algorithm
</TITLE>
<PARA>
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPICOPYTUPLE-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIMODIFYTUPLE">
<REFMETA>
<REFENTRYTITLE>SPI_modifytuple</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Modify</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_modifytuple
</REFNAME>
<REFPURPOSE>
Modifies tuple of relation
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIMODIFYTUPLE-1"><PRIMARY>SPI</PRIMARY><SECONDARY>modifying tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIMODIFYTUPLE-2"><PRIMARY>SPI_modifytuple</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_modifytuple(<REPLACEABLE CLASS="PARAMETER">rel</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE> , <REPLACEABLE CLASS="PARAMETER">nattrs</REPLACEABLE>
, <REPLACEABLE CLASS="PARAMETER">attnum</REPLACEABLE> , <REPLACEABLE CLASS="PARAMETER">Values</REPLACEABLE> , <REPLACEABLE CLASS="PARAMETER">Nulls</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIMODIFYTUPLE-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
Relation <REPLACEABLE CLASS="PARAMETER">rel</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
HeapTuple <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple to be modified
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int <REPLACEABLE CLASS="PARAMETER">nattrs</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Number of attribute numbers in attnum
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int * <REPLACEABLE CLASS="PARAMETER">attnum</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Array of numbers of the attributes which are to be changed
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
Datum * <REPLACEABLE CLASS="PARAMETER">Values</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
New values for the attributes specified
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
char * <REPLACEABLE CLASS="PARAMETER">Nulls</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Which attributes are NULL, if any
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIMODIFYTUPLE-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
HeapTuple
</TERM>
<LISTITEM>
<PARA>
New tuple with modifications
<SimpleList>
<Member>
 <ReturnValue>non-NULL</ReturnValue>
 if <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
 is not NULL and the modify was successful
</Member>
<Member>
   <ReturnValue>NULL</ReturnValue>
 only if <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
 is NULL
</Member>
</SimpleList>
</para>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
SPI_result
</TERM>
<LISTITEM>
<PARA>
<SimpleList>
<Member>
   <ReturnValue>SPI_ERROR_ARGUMENT</ReturnValue> if rel is NULL or tuple is NULL or natts &le; 0 or
   attnum is NULL or Values is NULL.
</Member>
<Member>
   <ReturnValue>SPI_ERROR_NOATTRIBUTE</ReturnValue> if there is an invalid 
   attribute number in attnum (attnum &le; 0 or &gt; number of
   attributes in tuple)
</Member>
</SimpleList>
</para>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIMODIFYTUPLE-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_modifytuple</FUNCTION> 
Modifies a tuple in upper Executor context. See the section on Memory Management.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIMODIFYTUPLE-2">
<TITLE>Usage
</TITLE>
<Para>
If successful, a pointer to the new tuple is returned. The new tuple is
allocated in upper Executor context (see Memory management). Passed tuple
is not changed.
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIMODIFYTUPLE-3">
<TITLE>Algorithm
</TITLE>
<PARA>
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPIMODIFYTUPLE-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIFNUMBER">
<REFMETA>
<REFENTRYTITLE>SPI_fnumber</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Information</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_fnumber
</REFNAME>
<REFPURPOSE>
Finds the attribute number for specified attribute
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIFNUMBER-1"><PRIMARY>SPI</PRIMARY><SECONDARY>decoding tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIFNUMBER-2"><PRIMARY>SPI_fnumber</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_fnumber(<REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">fname</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIFNUMBER-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
TupleDesc <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple description
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
char * <REPLACEABLE CLASS="PARAMETER">fname</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Field name
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIFNUMBER-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
int
</TERM>
<LISTITEM>
<PARA>
Attribute number
<SimpleList>
<Member>
Valid one-based index number of attribute
</Member>
<Member>
<ReturnValue>SPI_ERROR_NOATTRIBUTE</ReturnValue> if the named attribute is not found
</Member>
</SimpleList>
</para>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIFNUMBER-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_fnumber</FUNCTION> 
   returns the attribute number for the attribute with name in fname.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIFNUMBER-2">
<TITLE>Usage
</TITLE>
<Para>
Attribute numbers are 1 based.
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIFNUMBER-3">
<TITLE>Algorithm
</TITLE>
<PARA>
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPIFNUMBER-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIFNAME">
<REFMETA>
<REFENTRYTITLE>SPI_fname</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Information</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_fname
</REFNAME>
<REFPURPOSE>
Finds the attribute name for the specified attribute
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIFNAME-1"><PRIMARY>SPI</PRIMARY><SECONDARY>decoding tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIFNAME-2"><PRIMARY>SPI_fname</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_fname(<REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">fname</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIFNAME-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
TupleDesc <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple description
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
char * <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Attribute number
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIFNAME-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
char *
</TERM>
<LISTITEM>
<PARA>
Attribute name
<SimpleList>
<Member>
NULL if fnumber is out of range
</Member>
<Member>
SPI_result set to
<ReturnValue>SPI_ERROR_NOATTRIBUTE</ReturnValue> on error
</Member>
</SimpleList>
</para>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIFNAME-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_fname</FUNCTION> 
   returns the attribute name for the specified attribute.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIFNAME-2">
<TITLE>Usage
</TITLE>
<Para>
Attribute numbers are 1 based.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIFNAME-3">
<TITLE>Algorithm
</TITLE>
<PARA>
Returns a newly-allocated copy of the attribute name.
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIFNAME-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIGETVALUE">
<REFMETA>
<REFENTRYTITLE>SPI_getvalue</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Information</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_getvalue
</REFNAME>
<REFPURPOSE>
Returns the string value of the specified attribute
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIGETVALUE-1"><PRIMARY>SPI</PRIMARY><SECONDARY>decoding tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIGETVALUE-2"><PRIMARY>SPI_getvalue</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_getvalue(<REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIGETVALUE-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
HeapTuple <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple to be examined
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
TupleDesc <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple description
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Attribute number
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIGETVALUE-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
char *
</TERM>
<LISTITEM>
<PARA>
Attribute value or NULL if
<SimpleList>
<Member>
attribute is NULL
</Member>
<Member>
fnumber is out of range
(SPI_result set to
<ReturnValue>SPI_ERROR_NOATTRIBUTE</ReturnValue>)
</Member>
<Member>
no output function available
(SPI_result set to
<ReturnValue>SPI_ERROR_NOOUTFUNC</ReturnValue>)
</Member>
</SimpleList>
</para>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIGETVALUE-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_getvalue</FUNCTION> 
   returns an external (string) representation of the value of the specified attribute.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETVALUE-2">
<TITLE>Usage
</TITLE>
<Para>
Attribute numbers are 1 based.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETVALUE-3">
<TITLE>Algorithm
</TITLE>
<PARA>
Allocates memory as required by the value.
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIGETVALUE-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIGETBINVAL">
<REFMETA>
<REFENTRYTITLE>SPI_getbinval</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Information</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_getbinval
</REFNAME>
<REFPURPOSE>
Returns the binary value of the specified attribute
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIGETBINVAL-1"><PRIMARY>SPI</PRIMARY><SECONDARY>decoding tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIGETBINVAL-2"><PRIMARY>SPI_getbinval</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_getbinval(<REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">isnull</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIGETBINVAL-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
HeapTuple <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple to be examined
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
TupleDesc <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple description
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Attribute number
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIGETBINVAL-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
Datum
</TERM>
<LISTITEM>
<PARA>
Attribute binary value
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
bool * <REPLACEABLE CLASS="PARAMETER">isnull</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
flag for null value in attribute
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
SPI_result
</TERM>
<LISTITEM>
<PARA>
<SimpleList>
<Member>
<ReturnValue>SPI_ERROR_NOATTRIBUTE</ReturnValue>
</Member>
</SimpleList>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIGETBINVAL-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_getbinval</FUNCTION> 
   returns the binary value of the specified attribute.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETBINVAL-2">
<TITLE>Usage
</TITLE>
<Para>
Attribute numbers are 1 based.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETBINVAL-3">
<TITLE>Algorithm
</TITLE>
<PARA>
Does not allocate new space for the binary value.
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIGETBINVAL-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIGETTYPE">
<REFMETA>
<REFENTRYTITLE>SPI_gettype</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Information</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_gettype
</REFNAME>
<REFPURPOSE>
Returns the type name of the specified attribute
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIGETTYPE-1"><PRIMARY>SPI</PRIMARY><SECONDARY>decoding tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIGETTYPE-2"><PRIMARY>SPI_gettype</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_gettype(<REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIGETTYPE-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
TupleDesc <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple description
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Attribute number
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIGETTYPE-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
char *
</TERM>
<LISTITEM>
<PARA>
The type name for the specified attribute number
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
SPI_result
</TERM>
<LISTITEM>
<PARA>
<SimpleList>
<Member>
<ReturnValue>SPI_ERROR_NOATTRIBUTE</ReturnValue>
</Member>
</SimpleList>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIGETTYPE-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_gettype</FUNCTION> 
   returns a copy of the type name for the specified attribute.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETTYPE-2">
<TITLE>Usage
</TITLE>
<Para>
Attribute numbers are 1 based.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETTYPE-3">
<TITLE>Algorithm
</TITLE>
<PARA>
Does not allocate new space for the binary value.
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIGETTYPE-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIGETTYPEID">
<REFMETA>
<REFENTRYTITLE>SPI_gettypeid</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Information</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_gettypeid
</REFNAME>
<REFPURPOSE>
Returns the type <Acronym>OID</Acronym> of the specified attribute
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIGETTYPEID-1"><PRIMARY>SPI</PRIMARY><SECONDARY>decoding tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIGETTYPEID-2"><PRIMARY>SPI_gettypeid</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_gettypeid(<REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIGETTYPEID-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
TupleDesc <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input tuple description
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
int <REPLACEABLE CLASS="PARAMETER">fnumber</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Attribute number
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIGETTYPEID-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
<Acronym>OID</Acronym>
</TERM>
<LISTITEM>
<PARA>
The type <Acronym>OID</Acronym> for the specified attribute number
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
SPI_result
</TERM>
<LISTITEM>
<PARA>
<SimpleList>
<Member>
<ReturnValue>SPI_ERROR_NOATTRIBUTE</ReturnValue>
</Member>
</SimpleList>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIGETTYPEID-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_gettypeid</FUNCTION> 
   returns the type <Acronym>OID</Acronym> for the specified attribute.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETTYPEID-2">
<TITLE>Usage
</TITLE>
<Para>
Attribute numbers are 1 based.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETTYPEID-3">
<TITLE>Algorithm
</TITLE>
<PARA>
TBD
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIGETTYPEID-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIGETRELNAME">
<REFMETA>
<REFENTRYTITLE>SPI_getrelname</REFENTRYTITLE>
<REFMISCINFO>SPI - Tuple Information</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_getrelname
</REFNAME>
<REFPURPOSE>
Returns the name of the specified relation
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIGETRELNAME-1"><PRIMARY>SPI</PRIMARY><SECONDARY>decoding tuples</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIGETRELNAME-2"><PRIMARY>SPI_getrelname</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_getrelname(<REPLACEABLE CLASS="PARAMETER">rel</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIGETRELNAME-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
Relation <REPLACEABLE CLASS="PARAMETER">rel</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Input relation
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIGETRELNAME-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
char *
</TERM>
<LISTITEM>
<PARA>
The name of the specified relation
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIGETRELNAME-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_getrelname</FUNCTION> 
   returns the name of the specified relation.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETRELNAME-2">
<TITLE>Usage
</TITLE>
<Para>
TBD
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIGETRELNAME-3">
<TITLE>Algorithm
</TITLE>
<PARA>
Copies the relation name into new storage.
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIGETRELNAME-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIPALLOC">
<REFMETA>
<REFENTRYTITLE>SPI_palloc</REFENTRYTITLE>
<REFMISCINFO>SPI - Memory Management</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_palloc
</REFNAME>
<REFPURPOSE>
Allocates memory in upper Executor context
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIPALLOC-1"><PRIMARY>SPI</PRIMARY><SECONDARY>allocating space</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIPALLOC-2"><PRIMARY>SPI_palloc</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_palloc(<REPLACEABLE CLASS="PARAMETER">size</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIPALLOC-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
Size <REPLACEABLE CLASS="PARAMETER">size</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Octet size of storage to allocate
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIPALLOC-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
void *
</TERM>
<LISTITEM>
<PARA>
New storage space of specified size
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIPALLOC-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_palloc</FUNCTION> 
   allocates memory in upper Executor context. See section on memory management.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIPALLOC-2">
<TITLE>Usage
</TITLE>
<Para>
TBD
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIPALLOC-3">
<TITLE>Algorithm
</TITLE>
<PARA>
TBD
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPIPALLOC-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIREPALLOC">
<REFMETA>
<REFENTRYTITLE>SPI_repalloc</REFENTRYTITLE>
<REFMISCINFO>SPI - Memory Management</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_repalloc
</REFNAME>
<REFPURPOSE>
Re-allocates memory in upper Executor context
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIREPALLOC-1"><PRIMARY>SPI</PRIMARY><SECONDARY>allocating space</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIREPALLOC-2"><PRIMARY>SPI_repalloc</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_repalloc(<REPLACEABLE CLASS="PARAMETER">pointer</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">size</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIREPALLOC-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
void * <REPLACEABLE CLASS="PARAMETER">pointer</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Pointer to existing storage
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
Size <REPLACEABLE CLASS="PARAMETER">size</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Octet size of storage to allocate
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIREPALLOC-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
void *
</TERM>
<LISTITEM>
<PARA>
New storage space of specified size with contents copied from existing area
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIREPALLOC-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_repalloc</FUNCTION> 
   re-allocates memory in upper Executor context. See section on memory management.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIREPALLOC-2">
<TITLE>Usage
</TITLE>
<Para>
TBD
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIREPALLOC-3">
<TITLE>Algorithm
</TITLE>
<PARA>
TBD
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPIREPALLOC-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

<!-- *********************************************** -->
<!-- *********************************************** -->
<!-- *********************************************** -->

<REFENTRY ID="SPI-SPIPFREE">
<REFMETA>
<REFENTRYTITLE>SPI_pfree</REFENTRYTITLE>
<REFMISCINFO>SPI - Memory Management</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>SPI_pfree
</REFNAME>
<REFPURPOSE>
Frees memory from upper Executor context
</REFPURPOSE>
<INDEXTERM ID="IX-SPI-SPIPFREE-1"><PRIMARY>SPI</PRIMARY><SECONDARY>allocating space</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-SPI-SPIPFREE-2"><PRIMARY>SPI_pfree</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1997-12-24</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
SPI_pfree(<REPLACEABLE CLASS="PARAMETER">pointer</REPLACEABLE>)
</SYNOPSIS>

<REFSECT2 ID="R2-SPI-SPIPFREE-1">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
void * <REPLACEABLE CLASS="PARAMETER">pointer</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>
Pointer to existing storage
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>

<REFSECT2 ID="R2-SPI-SPIPFREE-2">
<REFSECT2INFO>
<DATE>1997-12-24</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
None
</TERM>
<LISTITEM>
<PARA>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>

<REFSECT1 ID="R1-SPI-SPIPFREE-1">
<REFSECT1INFO>
<DATE>1997-12-24</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA>
<FUNCTION>SPI_pfree</FUNCTION> 
   frees memory in upper Executor context. See section on memory management.
</PARA>
</REFSECT1>
<REFSECT1 ID="R1-SPI-SPIPFREE-2">
<TITLE>Usage
</TITLE>
<Para>
TBD
</PARA>
</REFSECT1>
<!--
<REFSECT1 ID="R1-SPI-SPIPFREE-3">
<TITLE>Algorithm
</TITLE>
<PARA>
TBD
</PARA>
</REFSECT1>
-->
<!--
<REFSECT1 ID="R1-SPI-SPIPFREE-4">
<TITLE>Structures
</TITLE>
<PARA>None
</PARA>
</REFSECT1>
-->
</REFENTRY>

</Sect1>

<Sect1>
<Title>Memory Management</Title>

<Para>
   Server allocates memory in memory contexts in such way that allocations
made in one context may be freed by context destruction without affecting
allocations made in other contexts. All allocations (via <Function>palloc</Function>, etc) are
made in the context which are chosen as current one. You'll get
unpredictable results if you'll try to free (or reallocate) memory allocated
not in current context.
</Para>

<Para>
   Creation and switching between memory contexts are subject of SPI manager
memory management.
</Para>

<Para>

   SPI procedures deal with two memory contexts: upper Executor memory
context and procedure memory context (if connected). 
</Para>

<Para>

   Before a procedure is connected to the SPI manager, current memory context
is upper Executor context so all allocation made by the procedure itself via
<Function>palloc</Function>/<Function>repalloc</Function> or by SPI utility functions before connecting to SPI are
made in this context.
</Para>

<Para>

   After <Function>SPI_connect</Function> is called current context is the procedure's one.  All
allocations made via <Function>palloc</Function>/<Function>repalloc</Function> or by SPI utility functions (except
for <Function>SPI_copytuple</Function>, <Function>SPI_modifytuple</Function>,
 <Function>SPI_palloc</Function> and <Function>SPI_repalloc</Function>) are
made in this context.
</Para>

<Para>

   When a procedure disconnects from the SPI manager (via <Function>SPI_finish</Function>) the
current context is restored to the upper Executor context and all allocations
made in the procedure memory context are freed and can't be used any more!
</Para>

<Para>

   If you want to return something to the upper Executor then you have to
allocate memory for this in the upper context!
</Para>

<Para>

   SPI has no ability to automatically free allocations in the upper Executor
context!
</Para>

<Para>

   SPI automatically frees memory allocated during execution of a query when
this query is done!
</Para>

</Sect1>

<Sect1>
<Title>Visibility of Data Changes</Title>

<Para>
<ProductName>Postgres</ProductName> data changes visibility rule: during a query execution, data
changes made by the query itself (via SQL-function, SPI-function, triggers)
are invisible to the query scan.  For example, in query

   INSERT INTO a SELECT * FROM a

   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.
</Para>

<Para>
   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.
</Para>
</Sect1>

<Sect1>
<Title>Examples</Title>

<Para>
   This example of SPI usage demonstrates the visibility rule.
   There are more complex examples in in src/test/regress/regress.c and
in contrib/spi.
</Para>

<Para>
   This is a very simple example of SPI usage. The procedure execq accepts
an SQL-query in its first argument and tcount in its second, executes the
query using SPI_exec and returns the number of tuples for which the query
executed:

<ProgramListing>
#include "executor/spi.h"	/* this is what you need to work with SPI */

int execq(text *sql, int cnt);

int
execq(text *sql, int cnt)
{
	int ret;
	int proc = 0;
	
	SPI_connect();
	
	ret = SPI_exec(textout(sql), cnt);
	
	proc = SPI_processed;
	/*
	 * If this is SELECT and some tuple(s) fetched -
	 * returns tuples to the caller via elog (NOTICE).
	 */
	if ( ret == SPI_OK_SELECT && SPI_processed > 0 )
	{
		TupleDesc tupdesc = SPI_tuptable->tupdesc;
		SPITupleTable *tuptable = SPI_tuptable;
		char buf[8192];
		int i;
		
		for (ret = 0; ret < proc; ret++)
		{
			HeapTuple tuple = tuptable->vals[ret];
			
			for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++)
				sprintf(buf + strlen (buf), " %s%s",
					SPI_getvalue(tuple, tupdesc, i),
					(i == tupdesc->natts) ? " " : " |");
			elog (NOTICE, "EXECQ: %s", buf);
		}
	}

	SPI_finish();

	return (proc);
}
</ProgramListing>
</Para>

<Para>
   Now, compile and create the function:

<ProgramListing>
create function execq (text, int4) returns int4 as '...path_to_so' language 'c';
</ProgramListing>

<ProgramListing>
vac=> select execq('create table a (x int4)', 0);
execq
-----
    0
(1 row)

vac=> insert into a values (execq('insert into a values (0)',0));
INSERT 167631 1
vac=> select execq('select * from a',0);
NOTICE:EXECQ:  0 <<< inserted by execq

NOTICE:EXECQ:  1 <<< value returned by execq and inserted by upper INSERT

execq
-----
    2
(1 row)

vac=> select execq('insert into a select x + 2 from a',1);
execq
-----
    1
(1 row)

vac=> select execq('select * from a', 10);
NOTICE:EXECQ:  0 

NOTICE:EXECQ:  1 

NOTICE:EXECQ:  2 <<< 0 + 2, only one tuple inserted - as specified

execq
-----
    3            <<< 10 is max value only, 3 is real # of tuples
(1 row)

vac=> delete from a;
DELETE 3
vac=> insert into a values (execq('select * from a', 0) + 1);
INSERT 167712 1
vac=> select * from a;
x
-
1                <<< no tuples in a (0) + 1
(1 row)

vac=> insert into a values (execq('select * from a', 0) + 1);
NOTICE:EXECQ:  0 
INSERT 167713 1
vac=> select * from a;
x
-
1
2                <<< there was single tuple in a + 1
(2 rows)

--   This demonstrates data changes visibility rule:

vac=> insert into a select execq('select * from a', 0) * x from a;
NOTICE:EXECQ:  1 
NOTICE:EXECQ:  2 
NOTICE:EXECQ:  1 
NOTICE:EXECQ:  2 
NOTICE:EXECQ:  2 
INSERT 0 2
vac=> select * from a;
x
-
1
2
2                <<< 2 tuples * 1 (x in first tuple)
6                <<< 3 tuples (2 + 1 just inserted) * 2 (x in second tuple)
(4 rows)             ^^^^^^^^ 
                     tuples visible to execq() in different invocations
</ProgramListing>
</Para>
</Sect1>
</Chapter>
