mirror of https://github.com/postgres/postgres
Simplify code by getting rid of SPI_push, SPI_pop, SPI_restore_connection.
The idea behind SPI_push was to allow transitioning back into an "unconnected" state when a SPI-using procedure calls unrelated code that might or might not invoke SPI. That sounds good, but in practice the only thing it does for us is to catch cases where a called SPI-using function forgets to call SPI_connect --- which is a highly improbable failure mode, since it would be exposed immediately by direct testing of said function. As against that, we've had multiple bugs induced by forgetting to call SPI_push/SPI_pop around code that might invoke SPI-using functions; these are much harder to catch and indeed have gone undetected for years in some cases. And we've had to band-aid around some problems of this ilk by introducing conditional push/pop pairs in some places, which really kind of defeats the purpose altogether; if we can't draw bright lines between connected and unconnected code, what's the point? Hence, get rid of SPI_push[_conditional], SPI_pop[_conditional], and the underlying state variable _SPI_curid. It turns out SPI_restore_connection can go away too, which is a nice side benefit since it was never more than a kluge. Provide no-op macros for the deleted functions so as to avoid an API break for external modules. A side effect of this removal is that SPI_palloc and allied functions no longer permit being called when unconnected; they'll throw an error instead. The apparent usefulness of the previous behavior was a mirage as well, because it was depended on by only a few places (which I fixed in preceding commits), and it posed a risk of allocations being unexpectedly long-lived if someone forgot a SPI_push call. Discussion: <20808.1478481403@sss.pgh.pa.us>
This commit is contained in:
parent
577f0bdd2b
commit
1833f1a1c3
|
@ -90,21 +90,6 @@ int SPI_connect(void)
|
||||||
function if you want to execute commands through SPI. Some utility
|
function if you want to execute commands through SPI. Some utility
|
||||||
SPI functions can be called from unconnected procedures.
|
SPI functions can be called from unconnected procedures.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
If your procedure is already connected,
|
|
||||||
<function>SPI_connect</function> will return the error code
|
|
||||||
<returnvalue>SPI_ERROR_CONNECT</returnvalue>. This could happen if
|
|
||||||
a procedure that has called <function>SPI_connect</function>
|
|
||||||
directly calls another procedure that calls
|
|
||||||
<function>SPI_connect</function>. While recursive calls to the
|
|
||||||
<acronym>SPI</acronym> manager are permitted when an SQL command
|
|
||||||
called through SPI invokes another function that uses
|
|
||||||
<acronym>SPI</acronym>, directly nested calls to
|
|
||||||
<function>SPI_connect</function> and
|
|
||||||
<function>SPI_finish</function> are forbidden.
|
|
||||||
(But see <function>SPI_push</function> and <function>SPI_pop</function>.)
|
|
||||||
</para>
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -164,13 +149,6 @@ int SPI_finish(void)
|
||||||
abort the transaction via <literal>elog(ERROR)</literal>. In that
|
abort the transaction via <literal>elog(ERROR)</literal>. In that
|
||||||
case SPI will clean itself up automatically.
|
case SPI will clean itself up automatically.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
If <function>SPI_finish</function> is called without having a valid
|
|
||||||
connection, it will return <symbol>SPI_ERROR_UNCONNECTED</symbol>.
|
|
||||||
There is no fundamental problem with this; it means that the SPI
|
|
||||||
manager has nothing to do.
|
|
||||||
</para>
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -200,86 +178,6 @@ int SPI_finish(void)
|
||||||
|
|
||||||
<!-- *********************************************** -->
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
<refentry id="spi-spi-push">
|
|
||||||
<indexterm><primary>SPI_push</primary></indexterm>
|
|
||||||
|
|
||||||
<refmeta>
|
|
||||||
<refentrytitle>SPI_push</refentrytitle>
|
|
||||||
<manvolnum>3</manvolnum>
|
|
||||||
</refmeta>
|
|
||||||
|
|
||||||
<refnamediv>
|
|
||||||
<refname>SPI_push</refname>
|
|
||||||
<refpurpose>push SPI stack to allow recursive SPI usage</refpurpose>
|
|
||||||
</refnamediv>
|
|
||||||
|
|
||||||
<refsynopsisdiv>
|
|
||||||
<synopsis>
|
|
||||||
void SPI_push(void)
|
|
||||||
</synopsis>
|
|
||||||
</refsynopsisdiv>
|
|
||||||
|
|
||||||
<refsect1>
|
|
||||||
<title>Description</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function>SPI_push</function> should be called before executing another
|
|
||||||
procedure that might itself wish to use SPI.
|
|
||||||
After <function>SPI_push</function>, SPI is no longer in a
|
|
||||||
<quote>connected</> state, and SPI function calls will be rejected unless
|
|
||||||
a fresh <function>SPI_connect</function> is done. This ensures a clean
|
|
||||||
separation between your procedure's SPI state and that of another procedure
|
|
||||||
you call. After the other procedure returns, call
|
|
||||||
<function>SPI_pop</function> to restore access to your own SPI state.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Note that <function>SPI_execute</function> and related functions
|
|
||||||
automatically do the equivalent of <function>SPI_push</function> before
|
|
||||||
passing control back to the SQL execution engine, so it is not necessary
|
|
||||||
for you to worry about this when using those functions.
|
|
||||||
Only when you are directly calling arbitrary code that might contain
|
|
||||||
<function>SPI_connect</function> calls do you need to issue
|
|
||||||
<function>SPI_push</function> and <function>SPI_pop</function>.
|
|
||||||
</para>
|
|
||||||
</refsect1>
|
|
||||||
|
|
||||||
</refentry>
|
|
||||||
|
|
||||||
<!-- *********************************************** -->
|
|
||||||
|
|
||||||
<refentry id="spi-spi-pop">
|
|
||||||
<indexterm><primary>SPI_pop</primary></indexterm>
|
|
||||||
|
|
||||||
<refmeta>
|
|
||||||
<refentrytitle>SPI_pop</refentrytitle>
|
|
||||||
<manvolnum>3</manvolnum>
|
|
||||||
</refmeta>
|
|
||||||
|
|
||||||
<refnamediv>
|
|
||||||
<refname>SPI_pop</refname>
|
|
||||||
<refpurpose>pop SPI stack to return from recursive SPI usage</refpurpose>
|
|
||||||
</refnamediv>
|
|
||||||
|
|
||||||
<refsynopsisdiv>
|
|
||||||
<synopsis>
|
|
||||||
void SPI_pop(void)
|
|
||||||
</synopsis>
|
|
||||||
</refsynopsisdiv>
|
|
||||||
|
|
||||||
<refsect1>
|
|
||||||
<title>Description</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function>SPI_pop</function> pops the previous environment from the
|
|
||||||
SPI call stack. See <function>SPI_push</function>.
|
|
||||||
</para>
|
|
||||||
</refsect1>
|
|
||||||
|
|
||||||
</refentry>
|
|
||||||
|
|
||||||
<!-- *********************************************** -->
|
|
||||||
|
|
||||||
<refentry id="spi-spi-execute">
|
<refentry id="spi-spi-execute">
|
||||||
<indexterm><primary>SPI_execute</primary></indexterm>
|
<indexterm><primary>SPI_execute</primary></indexterm>
|
||||||
|
|
||||||
|
@ -3361,17 +3259,8 @@ char * SPI_getnspname(Relation <parameter>rel</parameter>)
|
||||||
<quote>upper executor context</quote>, that is, the memory context
|
<quote>upper executor context</quote>, that is, the memory context
|
||||||
that was current when <function>SPI_connect</function> was called,
|
that was current when <function>SPI_connect</function> was called,
|
||||||
which is precisely the right context for a value returned from your
|
which is precisely the right context for a value returned from your
|
||||||
procedure.
|
procedure. Several of the other utility procedures described in
|
||||||
</para>
|
this section also return objects created in the upper executor context.
|
||||||
|
|
||||||
<para>
|
|
||||||
If <function>SPI_palloc</function> is called while the procedure is
|
|
||||||
not connected to SPI, then it acts the same as a normal
|
|
||||||
<function>palloc</function>. Before a procedure connects to the
|
|
||||||
SPI manager, the current memory context is the upper executor
|
|
||||||
context, so all allocations made by the procedure via
|
|
||||||
<function>palloc</function> or by SPI utility functions are made in
|
|
||||||
this context.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -3379,25 +3268,14 @@ char * SPI_getnspname(Relation <parameter>rel</parameter>)
|
||||||
context of the procedure, which is created by
|
context of the procedure, which is created by
|
||||||
<function>SPI_connect</function>, is made the current context. All
|
<function>SPI_connect</function>, is made the current context. All
|
||||||
allocations made by <function>palloc</function>,
|
allocations made by <function>palloc</function>,
|
||||||
<function>repalloc</function>, or SPI utility functions (except for
|
<function>repalloc</function>, or SPI utility functions (except as
|
||||||
<function>SPI_copytuple</function>,
|
described in this section) are made in this context. When a
|
||||||
<function>SPI_returntuple</function>,
|
|
||||||
<function>SPI_modifytuple</function>,
|
|
||||||
<function>SPI_palloc</function>, and
|
|
||||||
<function>SPI_datumTransfer</function>) are made in this context. When a
|
|
||||||
procedure disconnects from the SPI manager (via
|
procedure disconnects from the SPI manager (via
|
||||||
<function>SPI_finish</function>) the current context is restored to
|
<function>SPI_finish</function>) the current context is restored to
|
||||||
the upper executor context, and all allocations made in the
|
the upper executor context, and all allocations made in the
|
||||||
procedure memory context are freed and cannot be used any more.
|
procedure memory context are freed and cannot be used any more.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
All functions described in this section can be used by both
|
|
||||||
connected and unconnected procedures. In an unconnected procedure,
|
|
||||||
they act the same as the underlying ordinary server functions
|
|
||||||
(<function>palloc</>, etc.).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<!-- *********************************************** -->
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
<refentry id="spi-spi-palloc">
|
<refentry id="spi-spi-palloc">
|
||||||
|
@ -3426,6 +3304,11 @@ void * SPI_palloc(Size <parameter>size</parameter>)
|
||||||
<function>SPI_palloc</function> allocates memory in the upper
|
<function>SPI_palloc</function> allocates memory in the upper
|
||||||
executor context.
|
executor context.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This function can only be used while connected to SPI.
|
||||||
|
Otherwise, it throws an error.
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -3605,6 +3488,12 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
|
||||||
row from a trigger. In a function declared to return a composite
|
row from a trigger. In a function declared to return a composite
|
||||||
type, use <function>SPI_returntuple</function> instead.
|
type, use <function>SPI_returntuple</function> instead.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This function can only be used while connected to SPI.
|
||||||
|
Otherwise, it returns NULL and sets <varname>SPI_result</varname> to
|
||||||
|
<symbol>SPI_ERROR_UNCONNECTED</symbol>.
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -3626,8 +3515,8 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
|
||||||
<title>Return Value</title>
|
<title>Return Value</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
the copied row; <symbol>NULL</symbol> only if
|
the copied row, or <symbol>NULL</symbol> on error
|
||||||
<parameter>tuple</parameter> is <symbol>NULL</symbol>
|
(see <varname>SPI_result</varname> for an error indication)
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
@ -3663,6 +3552,12 @@ HeapTupleHeader SPI_returntuple(HeapTuple <parameter>row</parameter>, TupleDesc
|
||||||
before returning.
|
before returning.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This function can only be used while connected to SPI.
|
||||||
|
Otherwise, it returns NULL and sets <varname>SPI_result</varname> to
|
||||||
|
<symbol>SPI_ERROR_UNCONNECTED</symbol>.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Note that this should be used for functions that are declared to return
|
Note that this should be used for functions that are declared to return
|
||||||
composite types. It is not used for triggers; use
|
composite types. It is not used for triggers; use
|
||||||
|
@ -3699,10 +3594,9 @@ HeapTupleHeader SPI_returntuple(HeapTuple <parameter>row</parameter>, TupleDesc
|
||||||
<title>Return Value</title>
|
<title>Return Value</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<type>HeapTupleHeader</type> pointing to copied row;
|
<type>HeapTupleHeader</type> pointing to copied row,
|
||||||
<symbol>NULL</symbol> only if
|
or <symbol>NULL</symbol> on error
|
||||||
<parameter>row</parameter> or <parameter>rowdesc</parameter> is
|
(see <varname>SPI_result</varname> for an error indication)
|
||||||
<symbol>NULL</symbol>
|
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
@ -3736,6 +3630,13 @@ HeapTuple SPI_modifytuple(Relation <parameter>rel</parameter>, HeapTuple <parame
|
||||||
<function>SPI_modifytuple</function> creates a new row by
|
<function>SPI_modifytuple</function> creates a new row by
|
||||||
substituting new values for selected columns, copying the original
|
substituting new values for selected columns, copying the original
|
||||||
row's columns at other positions. The input row is not modified.
|
row's columns at other positions. The input row is not modified.
|
||||||
|
The new row is returned in the upper executor context.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This function can only be used while connected to SPI.
|
||||||
|
Otherwise, it returns NULL and sets <varname>SPI_result</varname> to
|
||||||
|
<symbol>SPI_ERROR_UNCONNECTED</symbol>.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -3821,8 +3722,8 @@ HeapTuple SPI_modifytuple(Relation <parameter>rel</parameter>, HeapTuple <parame
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
new row with modifications, allocated in the upper executor
|
new row with modifications, allocated in the upper executor
|
||||||
context; <symbol>NULL</symbol> only if <parameter>row</parameter>
|
context, or <symbol>NULL</symbol> on error
|
||||||
is <symbol>NULL</symbol>
|
(see <varname>SPI_result</varname> for an error indication)
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -3845,11 +3746,20 @@ HeapTuple SPI_modifytuple(Relation <parameter>rel</parameter>, HeapTuple <parame
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
if <parameter>colnum</> contains an invalid column number (less
|
if <parameter>colnum</> contains an invalid column number (less
|
||||||
than or equal to 0 or greater than the number of column in
|
than or equal to 0 or greater than the number of columns in
|
||||||
<parameter>row</>)
|
<parameter>row</>)
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><symbol>SPI_ERROR_UNCONNECTED</symbol></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
if SPI is not active
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
|
@ -44,8 +44,7 @@ int SPI_result;
|
||||||
static _SPI_connection *_SPI_stack = NULL;
|
static _SPI_connection *_SPI_stack = NULL;
|
||||||
static _SPI_connection *_SPI_current = NULL;
|
static _SPI_connection *_SPI_current = NULL;
|
||||||
static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
|
static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
|
||||||
static int _SPI_connected = -1;
|
static int _SPI_connected = -1; /* current stack index */
|
||||||
static int _SPI_curid = -1;
|
|
||||||
|
|
||||||
static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
||||||
ParamListInfo paramLI, bool read_only);
|
ParamListInfo paramLI, bool read_only);
|
||||||
|
@ -86,13 +85,7 @@ SPI_connect(void)
|
||||||
{
|
{
|
||||||
int newdepth;
|
int newdepth;
|
||||||
|
|
||||||
/*
|
/* Enlarge stack if necessary */
|
||||||
* When procedure called by Executor _SPI_curid expected to be equal to
|
|
||||||
* _SPI_connected
|
|
||||||
*/
|
|
||||||
if (_SPI_curid != _SPI_connected)
|
|
||||||
return SPI_ERROR_CONNECT;
|
|
||||||
|
|
||||||
if (_SPI_stack == NULL)
|
if (_SPI_stack == NULL)
|
||||||
{
|
{
|
||||||
if (_SPI_connected != -1 || _SPI_stack_depth != 0)
|
if (_SPI_connected != -1 || _SPI_stack_depth != 0)
|
||||||
|
@ -117,9 +110,7 @@ SPI_connect(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Enter new stack level */
|
||||||
* We're entering procedure where _SPI_curid == _SPI_connected - 1
|
|
||||||
*/
|
|
||||||
_SPI_connected++;
|
_SPI_connected++;
|
||||||
Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
|
Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
|
||||||
|
|
||||||
|
@ -178,14 +169,9 @@ SPI_finish(void)
|
||||||
SPI_lastoid = InvalidOid;
|
SPI_lastoid = InvalidOid;
|
||||||
SPI_tuptable = NULL;
|
SPI_tuptable = NULL;
|
||||||
|
|
||||||
/*
|
/* Exit stack level */
|
||||||
* After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are closing
|
|
||||||
* connection to SPI and returning to upper Executor and so _SPI_connected
|
|
||||||
* must be equal to _SPI_curid.
|
|
||||||
*/
|
|
||||||
_SPI_connected--;
|
_SPI_connected--;
|
||||||
_SPI_curid--;
|
if (_SPI_connected < 0)
|
||||||
if (_SPI_connected == -1)
|
|
||||||
_SPI_current = NULL;
|
_SPI_current = NULL;
|
||||||
else
|
else
|
||||||
_SPI_current = &(_SPI_stack[_SPI_connected]);
|
_SPI_current = &(_SPI_stack[_SPI_connected]);
|
||||||
|
@ -212,7 +198,7 @@ AtEOXact_SPI(bool isCommit)
|
||||||
|
|
||||||
_SPI_current = _SPI_stack = NULL;
|
_SPI_current = _SPI_stack = NULL;
|
||||||
_SPI_stack_depth = 0;
|
_SPI_stack_depth = 0;
|
||||||
_SPI_connected = _SPI_curid = -1;
|
_SPI_connected = -1;
|
||||||
SPI_processed = 0;
|
SPI_processed = 0;
|
||||||
SPI_lastoid = InvalidOid;
|
SPI_lastoid = InvalidOid;
|
||||||
SPI_tuptable = NULL;
|
SPI_tuptable = NULL;
|
||||||
|
@ -258,8 +244,7 @@ AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
|
||||||
* be already gone.
|
* be already gone.
|
||||||
*/
|
*/
|
||||||
_SPI_connected--;
|
_SPI_connected--;
|
||||||
_SPI_curid = _SPI_connected;
|
if (_SPI_connected < 0)
|
||||||
if (_SPI_connected == -1)
|
|
||||||
_SPI_current = NULL;
|
_SPI_current = NULL;
|
||||||
else
|
else
|
||||||
_SPI_current = &(_SPI_stack[_SPI_connected]);
|
_SPI_current = &(_SPI_stack[_SPI_connected]);
|
||||||
|
@ -313,53 +298,6 @@ AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Pushes SPI stack to allow recursive SPI calls */
|
|
||||||
void
|
|
||||||
SPI_push(void)
|
|
||||||
{
|
|
||||||
_SPI_curid++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pops SPI stack to allow recursive SPI calls */
|
|
||||||
void
|
|
||||||
SPI_pop(void)
|
|
||||||
{
|
|
||||||
_SPI_curid--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Conditional push: push only if we're inside a SPI procedure */
|
|
||||||
bool
|
|
||||||
SPI_push_conditional(void)
|
|
||||||
{
|
|
||||||
bool pushed = (_SPI_curid != _SPI_connected);
|
|
||||||
|
|
||||||
if (pushed)
|
|
||||||
{
|
|
||||||
_SPI_curid++;
|
|
||||||
/* We should now be in a state where SPI_connect would succeed */
|
|
||||||
Assert(_SPI_curid == _SPI_connected);
|
|
||||||
}
|
|
||||||
return pushed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Conditional pop: pop only if SPI_push_conditional pushed */
|
|
||||||
void
|
|
||||||
SPI_pop_conditional(bool pushed)
|
|
||||||
{
|
|
||||||
/* We should be in a state where SPI_connect would succeed */
|
|
||||||
Assert(_SPI_curid == _SPI_connected);
|
|
||||||
if (pushed)
|
|
||||||
_SPI_curid--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore state of SPI stack after aborting a subtransaction */
|
|
||||||
void
|
|
||||||
SPI_restore_connection(void)
|
|
||||||
{
|
|
||||||
Assert(_SPI_connected >= 0);
|
|
||||||
_SPI_curid = _SPI_connected - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse, plan, and execute a query string */
|
/* Parse, plan, and execute a query string */
|
||||||
int
|
int
|
||||||
SPI_execute(const char *src, bool read_only, long tcount)
|
SPI_execute(const char *src, bool read_only, long tcount)
|
||||||
|
@ -691,7 +629,7 @@ SPI_freeplan(SPIPlanPtr plan)
|
||||||
HeapTuple
|
HeapTuple
|
||||||
SPI_copytuple(HeapTuple tuple)
|
SPI_copytuple(HeapTuple tuple)
|
||||||
{
|
{
|
||||||
MemoryContext oldcxt = NULL;
|
MemoryContext oldcxt;
|
||||||
HeapTuple ctuple;
|
HeapTuple ctuple;
|
||||||
|
|
||||||
if (tuple == NULL)
|
if (tuple == NULL)
|
||||||
|
@ -700,17 +638,17 @@ SPI_copytuple(HeapTuple tuple)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
if (_SPI_current == NULL)
|
||||||
{
|
{
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
SPI_result = SPI_ERROR_UNCONNECTED;
|
||||||
elog(ERROR, "SPI stack corrupted");
|
return NULL;
|
||||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||||
|
|
||||||
ctuple = heap_copytuple(tuple);
|
ctuple = heap_copytuple(tuple);
|
||||||
|
|
||||||
if (oldcxt)
|
MemoryContextSwitchTo(oldcxt);
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
|
|
||||||
return ctuple;
|
return ctuple;
|
||||||
}
|
}
|
||||||
|
@ -718,7 +656,7 @@ SPI_copytuple(HeapTuple tuple)
|
||||||
HeapTupleHeader
|
HeapTupleHeader
|
||||||
SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
|
SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
|
||||||
{
|
{
|
||||||
MemoryContext oldcxt = NULL;
|
MemoryContext oldcxt;
|
||||||
HeapTupleHeader dtup;
|
HeapTupleHeader dtup;
|
||||||
|
|
||||||
if (tuple == NULL || tupdesc == NULL)
|
if (tuple == NULL || tupdesc == NULL)
|
||||||
|
@ -727,22 +665,22 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_SPI_current == NULL)
|
||||||
|
{
|
||||||
|
SPI_result = SPI_ERROR_UNCONNECTED;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* For RECORD results, make sure a typmod has been assigned */
|
/* For RECORD results, make sure a typmod has been assigned */
|
||||||
if (tupdesc->tdtypeid == RECORDOID &&
|
if (tupdesc->tdtypeid == RECORDOID &&
|
||||||
tupdesc->tdtypmod < 0)
|
tupdesc->tdtypmod < 0)
|
||||||
assign_record_type_typmod(tupdesc);
|
assign_record_type_typmod(tupdesc);
|
||||||
|
|
||||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||||
{
|
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
|
||||||
elog(ERROR, "SPI stack corrupted");
|
|
||||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
dtup = DatumGetHeapTupleHeader(heap_copy_tuple_as_datum(tuple, tupdesc));
|
dtup = DatumGetHeapTupleHeader(heap_copy_tuple_as_datum(tuple, tupdesc));
|
||||||
|
|
||||||
if (oldcxt)
|
MemoryContextSwitchTo(oldcxt);
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
|
|
||||||
return dtup;
|
return dtup;
|
||||||
}
|
}
|
||||||
|
@ -751,7 +689,7 @@ HeapTuple
|
||||||
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
|
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
|
||||||
Datum *Values, const char *Nulls)
|
Datum *Values, const char *Nulls)
|
||||||
{
|
{
|
||||||
MemoryContext oldcxt = NULL;
|
MemoryContext oldcxt;
|
||||||
HeapTuple mtuple;
|
HeapTuple mtuple;
|
||||||
int numberOfAttributes;
|
int numberOfAttributes;
|
||||||
Datum *v;
|
Datum *v;
|
||||||
|
@ -764,13 +702,16 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
if (_SPI_current == NULL)
|
||||||
{
|
{
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
SPI_result = SPI_ERROR_UNCONNECTED;
|
||||||
elog(ERROR, "SPI stack corrupted");
|
return NULL;
|
||||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||||
|
|
||||||
SPI_result = 0;
|
SPI_result = 0;
|
||||||
|
|
||||||
numberOfAttributes = rel->rd_att->natts;
|
numberOfAttributes = rel->rd_att->natts;
|
||||||
v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
|
v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
|
||||||
n = (bool *) palloc(numberOfAttributes * sizeof(bool));
|
n = (bool *) palloc(numberOfAttributes * sizeof(bool));
|
||||||
|
@ -810,8 +751,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
|
||||||
pfree(v);
|
pfree(v);
|
||||||
pfree(n);
|
pfree(n);
|
||||||
|
|
||||||
if (oldcxt)
|
MemoryContextSwitchTo(oldcxt);
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
|
|
||||||
return mtuple;
|
return mtuple;
|
||||||
}
|
}
|
||||||
|
@ -980,22 +920,10 @@ SPI_getnspname(Relation rel)
|
||||||
void *
|
void *
|
||||||
SPI_palloc(Size size)
|
SPI_palloc(Size size)
|
||||||
{
|
{
|
||||||
MemoryContext oldcxt = NULL;
|
if (_SPI_current == NULL)
|
||||||
void *pointer;
|
elog(ERROR, "SPI_palloc called while not connected to SPI");
|
||||||
|
|
||||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
return MemoryContextAlloc(_SPI_current->savedcxt, size);
|
||||||
{
|
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
|
||||||
elog(ERROR, "SPI stack corrupted");
|
|
||||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer = palloc(size);
|
|
||||||
|
|
||||||
if (oldcxt)
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
|
|
||||||
return pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
@ -1015,20 +943,17 @@ SPI_pfree(void *pointer)
|
||||||
Datum
|
Datum
|
||||||
SPI_datumTransfer(Datum value, bool typByVal, int typLen)
|
SPI_datumTransfer(Datum value, bool typByVal, int typLen)
|
||||||
{
|
{
|
||||||
MemoryContext oldcxt = NULL;
|
MemoryContext oldcxt;
|
||||||
Datum result;
|
Datum result;
|
||||||
|
|
||||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
if (_SPI_current == NULL)
|
||||||
{
|
elog(ERROR, "SPI_datumTransfer called while not connected to SPI");
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
|
||||||
elog(ERROR, "SPI stack corrupted");
|
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = datumTransfer(value, typByVal, typLen);
|
result = datumTransfer(value, typByVal, typLen);
|
||||||
|
|
||||||
if (oldcxt)
|
MemoryContextSwitchTo(oldcxt);
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1050,17 +975,12 @@ SPI_freetuptable(SPITupleTable *tuptable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since this function might be called during error recovery, it seems
|
* Search only the topmost SPI context for a matching tuple table.
|
||||||
* best not to insist that the caller be actively connected. We just
|
|
||||||
* search the topmost SPI context, connected or not.
|
|
||||||
*/
|
*/
|
||||||
if (_SPI_connected >= 0)
|
if (_SPI_current != NULL)
|
||||||
{
|
{
|
||||||
slist_mutable_iter siter;
|
slist_mutable_iter siter;
|
||||||
|
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_connected]))
|
|
||||||
elog(ERROR, "SPI stack corrupted");
|
|
||||||
|
|
||||||
/* find tuptable in active list, then remove it */
|
/* find tuptable in active list, then remove it */
|
||||||
slist_foreach_modify(siter, &_SPI_current->tuptables)
|
slist_foreach_modify(siter, &_SPI_current->tuptables)
|
||||||
{
|
{
|
||||||
|
@ -1168,13 +1088,9 @@ SPI_cursor_open_with_args(const char *name,
|
||||||
|
|
||||||
/* We needn't copy the plan; SPI_cursor_open_internal will do so */
|
/* We needn't copy the plan; SPI_cursor_open_internal will do so */
|
||||||
|
|
||||||
/* Adjust stack so that SPI_cursor_open_internal doesn't complain */
|
|
||||||
_SPI_curid--;
|
|
||||||
|
|
||||||
result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
|
result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
|
||||||
|
|
||||||
/* And clean up */
|
/* And clean up */
|
||||||
_SPI_curid++;
|
|
||||||
_SPI_end_call(true);
|
_SPI_end_call(true);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1723,14 +1639,8 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
MemoryContext tuptabcxt;
|
MemoryContext tuptabcxt;
|
||||||
|
|
||||||
/*
|
if (_SPI_current == NULL)
|
||||||
* When called by Executor _SPI_curid expected to be equal to
|
elog(ERROR, "spi_dest_startup called while not connected to SPI");
|
||||||
* _SPI_connected
|
|
||||||
*/
|
|
||||||
if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
|
|
||||||
elog(ERROR, "improper call to spi_dest_startup");
|
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid]))
|
|
||||||
elog(ERROR, "SPI stack corrupted");
|
|
||||||
|
|
||||||
if (_SPI_current->tuptable != NULL)
|
if (_SPI_current->tuptable != NULL)
|
||||||
elog(ERROR, "improper call to spi_dest_startup");
|
elog(ERROR, "improper call to spi_dest_startup");
|
||||||
|
@ -1775,14 +1685,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
|
||||||
SPITupleTable *tuptable;
|
SPITupleTable *tuptable;
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
/*
|
if (_SPI_current == NULL)
|
||||||
* When called by Executor _SPI_curid expected to be equal to
|
elog(ERROR, "spi_printtup called while not connected to SPI");
|
||||||
* _SPI_connected
|
|
||||||
*/
|
|
||||||
if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
|
|
||||||
elog(ERROR, "improper call to spi_printtup");
|
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid]))
|
|
||||||
elog(ERROR, "SPI stack corrupted");
|
|
||||||
|
|
||||||
tuptable = _SPI_current->tuptable;
|
tuptable = _SPI_current->tuptable;
|
||||||
if (tuptable == NULL)
|
if (tuptable == NULL)
|
||||||
|
@ -2534,11 +2438,8 @@ _SPI_procmem(void)
|
||||||
static int
|
static int
|
||||||
_SPI_begin_call(bool execmem)
|
_SPI_begin_call(bool execmem)
|
||||||
{
|
{
|
||||||
if (_SPI_curid + 1 != _SPI_connected)
|
if (_SPI_current == NULL)
|
||||||
return SPI_ERROR_UNCONNECTED;
|
return SPI_ERROR_UNCONNECTED;
|
||||||
_SPI_curid++;
|
|
||||||
if (_SPI_current != &(_SPI_stack[_SPI_curid]))
|
|
||||||
elog(ERROR, "SPI stack corrupted");
|
|
||||||
|
|
||||||
if (execmem) /* switch to the Executor memory context */
|
if (execmem) /* switch to the Executor memory context */
|
||||||
_SPI_execmem();
|
_SPI_execmem();
|
||||||
|
@ -2554,11 +2455,6 @@ _SPI_begin_call(bool execmem)
|
||||||
static int
|
static int
|
||||||
_SPI_end_call(bool procmem)
|
_SPI_end_call(bool procmem)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* We're returning to procedure where _SPI_curid == _SPI_connected - 1
|
|
||||||
*/
|
|
||||||
_SPI_curid--;
|
|
||||||
|
|
||||||
if (procmem) /* switch to the procedure memory context */
|
if (procmem) /* switch to the procedure memory context */
|
||||||
{
|
{
|
||||||
_SPI_procmem();
|
_SPI_procmem();
|
||||||
|
|
|
@ -2644,8 +2644,6 @@ schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls,
|
||||||
|
|
||||||
relid_list = schema_get_xml_visible_tables(nspid);
|
relid_list = schema_get_xml_visible_tables(nspid);
|
||||||
|
|
||||||
SPI_push();
|
|
||||||
|
|
||||||
foreach(cell, relid_list)
|
foreach(cell, relid_list)
|
||||||
{
|
{
|
||||||
Oid relid = lfirst_oid(cell);
|
Oid relid = lfirst_oid(cell);
|
||||||
|
@ -2658,7 +2656,6 @@ schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls,
|
||||||
appendStringInfoChar(result, '\n');
|
appendStringInfoChar(result, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_pop();
|
|
||||||
SPI_finish();
|
SPI_finish();
|
||||||
|
|
||||||
xmldata_root_element_end(result, xmlsn);
|
xmldata_root_element_end(result, xmlsn);
|
||||||
|
@ -2822,8 +2819,6 @@ database_to_xml_internal(const char *xmlschema, bool nulls,
|
||||||
|
|
||||||
nspid_list = database_get_xml_visible_schemas();
|
nspid_list = database_get_xml_visible_schemas();
|
||||||
|
|
||||||
SPI_push();
|
|
||||||
|
|
||||||
foreach(cell, nspid_list)
|
foreach(cell, nspid_list)
|
||||||
{
|
{
|
||||||
Oid nspid = lfirst_oid(cell);
|
Oid nspid = lfirst_oid(cell);
|
||||||
|
@ -2836,7 +2831,6 @@ database_to_xml_internal(const char *xmlschema, bool nulls,
|
||||||
appendStringInfoChar(result, '\n');
|
appendStringInfoChar(result, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_pop();
|
|
||||||
SPI_finish();
|
SPI_finish();
|
||||||
|
|
||||||
xmldata_root_element_end(result, xmlcn);
|
xmldata_root_element_end(result, xmlcn);
|
||||||
|
|
|
@ -53,7 +53,6 @@
|
||||||
#include "access/transam.h"
|
#include "access/transam.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/spi.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "optimizer/cost.h"
|
#include "optimizer/cost.h"
|
||||||
|
@ -878,7 +877,6 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
|
||||||
CachedPlan *plan;
|
CachedPlan *plan;
|
||||||
List *plist;
|
List *plist;
|
||||||
bool snapshot_set;
|
bool snapshot_set;
|
||||||
bool spi_pushed;
|
|
||||||
bool is_transient;
|
bool is_transient;
|
||||||
MemoryContext plan_context;
|
MemoryContext plan_context;
|
||||||
MemoryContext oldcxt = CurrentMemoryContext;
|
MemoryContext oldcxt = CurrentMemoryContext;
|
||||||
|
@ -926,22 +924,11 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
|
||||||
snapshot_set = true;
|
snapshot_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The planner may try to call SPI-using functions, which causes a problem
|
|
||||||
* if we're already inside one. Rather than expect all SPI-using code to
|
|
||||||
* do SPI_push whenever a replan could happen, it seems best to take care
|
|
||||||
* of the case here.
|
|
||||||
*/
|
|
||||||
spi_pushed = SPI_push_conditional();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate the plan.
|
* Generate the plan.
|
||||||
*/
|
*/
|
||||||
plist = pg_plan_queries(qlist, plansource->cursor_options, boundParams);
|
plist = pg_plan_queries(qlist, plansource->cursor_options, boundParams);
|
||||||
|
|
||||||
/* Clean up SPI state */
|
|
||||||
SPI_pop_conditional(spi_pushed);
|
|
||||||
|
|
||||||
/* Release snapshot if we got one */
|
/* Release snapshot if we got one */
|
||||||
if (snapshot_set)
|
if (snapshot_set)
|
||||||
PopActiveSnapshot();
|
PopActiveSnapshot();
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include "catalog/pg_language.h"
|
#include "catalog/pg_language.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "executor/functions.h"
|
#include "executor/functions.h"
|
||||||
#include "executor/spi.h"
|
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
|
@ -1878,25 +1877,16 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
|
||||||
* the caller should assume the result is NULL, but we'll call the input
|
* the caller should assume the result is NULL, but we'll call the input
|
||||||
* function anyway if it's not strict. So this is almost but not quite
|
* function anyway if it's not strict. So this is almost but not quite
|
||||||
* the same as FunctionCall3.
|
* the same as FunctionCall3.
|
||||||
*
|
|
||||||
* One important difference from the bare function call is that we will
|
|
||||||
* push any active SPI context, allowing SPI-using I/O functions to be
|
|
||||||
* called from other SPI functions without extra notation. This is a hack,
|
|
||||||
* but the alternative of expecting all SPI functions to do SPI_push/SPI_pop
|
|
||||||
* around I/O calls seems worse.
|
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
|
InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
|
||||||
{
|
{
|
||||||
FunctionCallInfoData fcinfo;
|
FunctionCallInfoData fcinfo;
|
||||||
Datum result;
|
Datum result;
|
||||||
bool pushed;
|
|
||||||
|
|
||||||
if (str == NULL && flinfo->fn_strict)
|
if (str == NULL && flinfo->fn_strict)
|
||||||
return (Datum) 0; /* just return null result */
|
return (Datum) 0; /* just return null result */
|
||||||
|
|
||||||
pushed = SPI_push_conditional();
|
|
||||||
|
|
||||||
InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
|
InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
|
||||||
|
|
||||||
fcinfo.arg[0] = CStringGetDatum(str);
|
fcinfo.arg[0] = CStringGetDatum(str);
|
||||||
|
@ -1922,8 +1912,6 @@ InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
|
||||||
fcinfo.flinfo->fn_oid);
|
fcinfo.flinfo->fn_oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_pop_conditional(pushed);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1932,22 +1920,12 @@ InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
|
||||||
*
|
*
|
||||||
* Do not call this on NULL datums.
|
* Do not call this on NULL datums.
|
||||||
*
|
*
|
||||||
* This is almost just window dressing for FunctionCall1, but it includes
|
* This is currently little more than window dressing for FunctionCall1.
|
||||||
* SPI context pushing for the same reasons as InputFunctionCall.
|
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
OutputFunctionCall(FmgrInfo *flinfo, Datum val)
|
OutputFunctionCall(FmgrInfo *flinfo, Datum val)
|
||||||
{
|
{
|
||||||
char *result;
|
return DatumGetCString(FunctionCall1(flinfo, val));
|
||||||
bool pushed;
|
|
||||||
|
|
||||||
pushed = SPI_push_conditional();
|
|
||||||
|
|
||||||
result = DatumGetCString(FunctionCall1(flinfo, val));
|
|
||||||
|
|
||||||
SPI_pop_conditional(pushed);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1956,8 +1934,7 @@ OutputFunctionCall(FmgrInfo *flinfo, Datum val)
|
||||||
* "buf" may be NULL to indicate we are reading a NULL. In this case
|
* "buf" may be NULL to indicate we are reading a NULL. In this case
|
||||||
* the caller should assume the result is NULL, but we'll call the receive
|
* the caller should assume the result is NULL, but we'll call the receive
|
||||||
* function anyway if it's not strict. So this is almost but not quite
|
* function anyway if it's not strict. So this is almost but not quite
|
||||||
* the same as FunctionCall3. Also, this includes SPI context pushing for
|
* the same as FunctionCall3.
|
||||||
* the same reasons as InputFunctionCall.
|
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
|
ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
|
||||||
|
@ -1965,13 +1942,10 @@ ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
|
||||||
{
|
{
|
||||||
FunctionCallInfoData fcinfo;
|
FunctionCallInfoData fcinfo;
|
||||||
Datum result;
|
Datum result;
|
||||||
bool pushed;
|
|
||||||
|
|
||||||
if (buf == NULL && flinfo->fn_strict)
|
if (buf == NULL && flinfo->fn_strict)
|
||||||
return (Datum) 0; /* just return null result */
|
return (Datum) 0; /* just return null result */
|
||||||
|
|
||||||
pushed = SPI_push_conditional();
|
|
||||||
|
|
||||||
InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
|
InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
|
||||||
|
|
||||||
fcinfo.arg[0] = PointerGetDatum(buf);
|
fcinfo.arg[0] = PointerGetDatum(buf);
|
||||||
|
@ -1997,8 +1971,6 @@ ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
|
||||||
fcinfo.flinfo->fn_oid);
|
fcinfo.flinfo->fn_oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_pop_conditional(pushed);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2009,22 +1981,12 @@ ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
|
||||||
*
|
*
|
||||||
* This is little more than window dressing for FunctionCall1, but it does
|
* This is little more than window dressing for FunctionCall1, but it does
|
||||||
* guarantee a non-toasted result, which strictly speaking the underlying
|
* guarantee a non-toasted result, which strictly speaking the underlying
|
||||||
* function doesn't. Also, this includes SPI context pushing for the same
|
* function doesn't.
|
||||||
* reasons as InputFunctionCall.
|
|
||||||
*/
|
*/
|
||||||
bytea *
|
bytea *
|
||||||
SendFunctionCall(FmgrInfo *flinfo, Datum val)
|
SendFunctionCall(FmgrInfo *flinfo, Datum val)
|
||||||
{
|
{
|
||||||
bytea *result;
|
return DatumGetByteaP(FunctionCall1(flinfo, val));
|
||||||
bool pushed;
|
|
||||||
|
|
||||||
pushed = SPI_push_conditional();
|
|
||||||
|
|
||||||
result = DatumGetByteaP(FunctionCall1(flinfo, val));
|
|
||||||
|
|
||||||
SPI_pop_conditional(pushed);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -59,6 +59,13 @@ typedef struct _SPI_plan *SPIPlanPtr;
|
||||||
#define SPI_OK_UPDATE_RETURNING 13
|
#define SPI_OK_UPDATE_RETURNING 13
|
||||||
#define SPI_OK_REWRITTEN 14
|
#define SPI_OK_REWRITTEN 14
|
||||||
|
|
||||||
|
/* These used to be functions, now just no-ops for backwards compatibility */
|
||||||
|
#define SPI_push() ((void) 0)
|
||||||
|
#define SPI_pop() ((void) 0)
|
||||||
|
#define SPI_push_conditional() false
|
||||||
|
#define SPI_pop_conditional(pushed) ((void) 0)
|
||||||
|
#define SPI_restore_connection() ((void) 0)
|
||||||
|
|
||||||
extern PGDLLIMPORT uint64 SPI_processed;
|
extern PGDLLIMPORT uint64 SPI_processed;
|
||||||
extern PGDLLIMPORT Oid SPI_lastoid;
|
extern PGDLLIMPORT Oid SPI_lastoid;
|
||||||
extern PGDLLIMPORT SPITupleTable *SPI_tuptable;
|
extern PGDLLIMPORT SPITupleTable *SPI_tuptable;
|
||||||
|
@ -66,11 +73,6 @@ extern PGDLLIMPORT int SPI_result;
|
||||||
|
|
||||||
extern int SPI_connect(void);
|
extern int SPI_connect(void);
|
||||||
extern int SPI_finish(void);
|
extern int SPI_finish(void);
|
||||||
extern void SPI_push(void);
|
|
||||||
extern void SPI_pop(void);
|
|
||||||
extern bool SPI_push_conditional(void);
|
|
||||||
extern void SPI_pop_conditional(bool pushed);
|
|
||||||
extern void SPI_restore_connection(void);
|
|
||||||
extern int SPI_execute(const char *src, bool read_only, long tcount);
|
extern int SPI_execute(const char *src, bool read_only, long tcount);
|
||||||
extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
||||||
bool read_only, long tcount);
|
bool read_only, long tcount);
|
||||||
|
|
|
@ -3057,12 +3057,6 @@ plperl_spi_exec(char *query, int limit)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just
|
|
||||||
* in case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
|
@ -3078,13 +3072,6 @@ plperl_spi_exec(char *query, int limit)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return
|
|
||||||
* to connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Punt the error to Perl */
|
/* Punt the error to Perl */
|
||||||
croak_cstr(edata->message);
|
croak_cstr(edata->message);
|
||||||
|
|
||||||
|
@ -3296,12 +3283,6 @@ plperl_spi_query(char *query)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just
|
|
||||||
* in case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
|
@ -3317,13 +3298,6 @@ plperl_spi_query(char *query)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return
|
|
||||||
* to connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Punt the error to Perl */
|
/* Punt the error to Perl */
|
||||||
croak_cstr(edata->message);
|
croak_cstr(edata->message);
|
||||||
|
|
||||||
|
@ -3382,12 +3356,6 @@ plperl_spi_fetchrow(char *cursor)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just
|
|
||||||
* in case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
|
@ -3403,13 +3371,6 @@ plperl_spi_fetchrow(char *cursor)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return
|
|
||||||
* to connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Punt the error to Perl */
|
/* Punt the error to Perl */
|
||||||
croak_cstr(edata->message);
|
croak_cstr(edata->message);
|
||||||
|
|
||||||
|
@ -3543,12 +3504,6 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just
|
|
||||||
* in case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
|
@ -3574,13 +3529,6 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return
|
|
||||||
* to connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Punt the error to Perl */
|
/* Punt the error to Perl */
|
||||||
croak_cstr(edata->message);
|
croak_cstr(edata->message);
|
||||||
|
|
||||||
|
@ -3694,12 +3642,6 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just
|
|
||||||
* in case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
|
@ -3715,13 +3657,6 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return
|
|
||||||
* to connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Punt the error to Perl */
|
/* Punt the error to Perl */
|
||||||
croak_cstr(edata->message);
|
croak_cstr(edata->message);
|
||||||
|
|
||||||
|
@ -3823,12 +3758,6 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just
|
|
||||||
* in case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
|
@ -3844,13 +3773,6 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return
|
|
||||||
* to connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Punt the error to Perl */
|
/* Punt the error to Perl */
|
||||||
croak_cstr(edata->message);
|
croak_cstr(edata->message);
|
||||||
|
|
||||||
|
|
|
@ -1337,12 +1337,6 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
||||||
* automatically cleaned up during subxact exit.)
|
* automatically cleaned up during subxact exit.)
|
||||||
*/
|
*/
|
||||||
estate->eval_econtext = old_eval_econtext;
|
estate->eval_econtext = old_eval_econtext;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but
|
|
||||||
* just in case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
|
@ -1384,13 +1378,6 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
||||||
/* Revert to outer eval_econtext */
|
/* Revert to outer eval_econtext */
|
||||||
estate->eval_econtext = old_eval_econtext;
|
estate->eval_econtext = old_eval_econtext;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it
|
|
||||||
* will have left us in a disconnected state. We need this hack
|
|
||||||
* to return to connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Must clean up the econtext too. However, any tuple table made
|
* Must clean up the econtext too. However, any tuple table made
|
||||||
* in the subxact will have been thrown away by SPI during subxact
|
* in the subxact will have been thrown away by SPI during subxact
|
||||||
|
@ -5587,8 +5574,6 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
||||||
* Without this, stable functions within the expression would fail to see
|
* Without this, stable functions within the expression would fail to see
|
||||||
* updates made so far by our own function.
|
* updates made so far by our own function.
|
||||||
*/
|
*/
|
||||||
SPI_push();
|
|
||||||
|
|
||||||
oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
|
oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
|
||||||
if (!estate->readonly_func)
|
if (!estate->readonly_func)
|
||||||
{
|
{
|
||||||
|
@ -5636,8 +5621,6 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
SPI_pop();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we can release our refcount on the cached plan.
|
* Now we can release our refcount on the cached plan.
|
||||||
*/
|
*/
|
||||||
|
@ -6281,8 +6264,6 @@ exec_cast_value(PLpgSQL_execstate *estate,
|
||||||
ExprContext *econtext = estate->eval_econtext;
|
ExprContext *econtext = estate->eval_econtext;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
SPI_push();
|
|
||||||
|
|
||||||
oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
|
oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
|
||||||
|
|
||||||
econtext->caseValue_datum = value;
|
econtext->caseValue_datum = value;
|
||||||
|
@ -6296,8 +6277,6 @@ exec_cast_value(PLpgSQL_execstate *estate,
|
||||||
cast_entry->cast_in_use = false;
|
cast_entry->cast_in_use = false;
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
SPI_pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1103,8 +1103,6 @@ PLy_abort_open_subtransactions(int save_subxact_level)
|
||||||
|
|
||||||
RollbackAndReleaseCurrentSubTransaction();
|
RollbackAndReleaseCurrentSubTransaction();
|
||||||
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
subtransactiondata = (PLySubtransactionData *) linitial(explicit_subtransactions);
|
subtransactiondata = (PLySubtransactionData *) linitial(explicit_subtransactions);
|
||||||
explicit_subtransactions = list_delete_first(explicit_subtransactions);
|
explicit_subtransactions = list_delete_first(explicit_subtransactions);
|
||||||
|
|
||||||
|
|
|
@ -516,12 +516,6 @@ PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just in
|
|
||||||
* case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -541,13 +535,6 @@ PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return to
|
|
||||||
* connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Look up the correct exception */
|
/* Look up the correct exception */
|
||||||
entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
|
entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
|
||||||
HASH_FIND, NULL);
|
HASH_FIND, NULL);
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "executor/spi.h"
|
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
#include "plpython.h"
|
#include "plpython.h"
|
||||||
|
@ -213,12 +212,6 @@ PLy_subtransaction_exit(PyObject *self, PyObject *args)
|
||||||
CurrentResourceOwner = subxactdata->oldowner;
|
CurrentResourceOwner = subxactdata->oldowner;
|
||||||
pfree(subxactdata);
|
pfree(subxactdata);
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just in
|
|
||||||
* case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2182,11 +2182,9 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
|
|
||||||
SPI_push();
|
|
||||||
tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
|
tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
|
||||||
call_state);
|
call_state);
|
||||||
tuplestore_puttuple(call_state->tuple_store, tuple);
|
tuplestore_puttuple(call_state->tuple_store, tuple);
|
||||||
SPI_pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2249,12 +2247,6 @@ pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* AtEOSubXact_SPI() should not have popped any SPI context, but just in
|
|
||||||
* case it did, make sure we remain connected.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -2273,13 +2265,6 @@ pltcl_subtrans_abort(Tcl_Interp *interp,
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
CurrentResourceOwner = oldowner;
|
||||||
|
|
||||||
/*
|
|
||||||
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
|
|
||||||
* have left us in a disconnected state. We need this hack to return to
|
|
||||||
* connected state.
|
|
||||||
*/
|
|
||||||
SPI_restore_connection();
|
|
||||||
|
|
||||||
/* Pass the error data to Tcl */
|
/* Pass the error data to Tcl */
|
||||||
pltcl_construct_errorCode(interp, edata);
|
pltcl_construct_errorCode(interp, edata);
|
||||||
UTF_BEGIN;
|
UTF_BEGIN;
|
||||||
|
@ -3029,9 +3014,6 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc)
|
||||||
* mess, there's no way to prevent the datatype input functions it calls
|
* mess, there's no way to prevent the datatype input functions it calls
|
||||||
* from leaking. Run it in a short-lived context, unless we're about to
|
* from leaking. Run it in a short-lived context, unless we're about to
|
||||||
* exit the procedure anyway.
|
* exit the procedure anyway.
|
||||||
*
|
|
||||||
* Also, caller is responsible for doing SPI_push/SPI_pop if calling from
|
|
||||||
* inside SPI environment.
|
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
static HeapTuple
|
static HeapTuple
|
||||||
pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
|
pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
|
||||||
|
|
Loading…
Reference in New Issue