|
|
|
@ -6,15 +6,42 @@ |
|
|
|
|
*------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
#include "executor/spi.h" |
|
|
|
|
#include "access/printtup.h" |
|
|
|
|
#include "fmgr.h" |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
QueryTreeList *qtlist; |
|
|
|
|
List *ptlist; |
|
|
|
|
int nargs; |
|
|
|
|
Oid *argtypes; |
|
|
|
|
} _SPI_plan; |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
int len; |
|
|
|
|
void *data; |
|
|
|
|
} _SPI_data; |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
char *ident; |
|
|
|
|
int plcnt; |
|
|
|
|
_SPI_plan **plan; |
|
|
|
|
int dtcnt; |
|
|
|
|
_SPI_data *data; |
|
|
|
|
} _SPI_entry; |
|
|
|
|
|
|
|
|
|
static _SPI_entry *_SPI_xtab = NULL; |
|
|
|
|
static int _SPI_xtabsz = 0; |
|
|
|
|
static _SPI_entry *_SPI_stab = NULL; |
|
|
|
|
static int _SPI_stabsz = 0; |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
QueryTreeList *qtlist; /* malloced */ |
|
|
|
|
uint32 processed; /* by Executor */ |
|
|
|
|
SPITupleTable *tuptable; |
|
|
|
|
Portal portal; /* portal per procedure */ |
|
|
|
|
MemoryContext savedcntx; |
|
|
|
|
MemoryContext savedcxt; |
|
|
|
|
CommandId savedId; |
|
|
|
|
_SPI_entry ltab; |
|
|
|
|
} _SPI_connection; |
|
|
|
|
|
|
|
|
|
static Portal _SPI_portal = (Portal) NULL; |
|
|
|
@ -25,25 +52,41 @@ static int _SPI_curid = -1; |
|
|
|
|
|
|
|
|
|
uint32 SPI_processed = 0; |
|
|
|
|
SPITupleTable *SPI_tuptable; |
|
|
|
|
int SPI_error; |
|
|
|
|
|
|
|
|
|
void spi_printtup (HeapTuple tuple, TupleDesc tupdesc); |
|
|
|
|
static int _SPI_pquery (QueryDesc *queryDesc); |
|
|
|
|
|
|
|
|
|
static int _SPI_execute (char *src, int tcount, _SPI_plan *plan); |
|
|
|
|
static int _SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount); |
|
|
|
|
#if 0 |
|
|
|
|
static void _SPI_fetch (FetchStmt *stmt); |
|
|
|
|
#endif |
|
|
|
|
static int _SPI_execute_plan (_SPI_plan *plan,
|
|
|
|
|
char **Values, char *Nulls, int tcount); |
|
|
|
|
|
|
|
|
|
static int _SPI_copy_plan (int dspace, _SPI_plan *plan); |
|
|
|
|
|
|
|
|
|
static int _SPI_begin_call (bool execmem); |
|
|
|
|
static int _SPI_end_call (bool exfree, bool procmem); |
|
|
|
|
static int _SPI_end_call (bool procmem); |
|
|
|
|
static MemoryContext _SPI_execmem (void); |
|
|
|
|
static MemoryContext _SPI_procmem (void); |
|
|
|
|
static bool _SPI_checktuples (bool isRetrieveIntoRelation); |
|
|
|
|
|
|
|
|
|
#ifdef SPI_EXECUTOR_STATS |
|
|
|
|
extern int ShowExecutorStats; |
|
|
|
|
extern void ResetUsage (void); |
|
|
|
|
extern void ShowUsage (void); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
SPI_connect () |
|
|
|
|
SPI_connect (char *ident) |
|
|
|
|
{ |
|
|
|
|
char pname[64]; |
|
|
|
|
PortalVariableMemory pvmem; |
|
|
|
|
|
|
|
|
|
if ( !ident || *ident == 0 ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* It's possible on startup and after commit/abort. |
|
|
|
|
* In future we'll catch commit/abort in some way... |
|
|
|
@ -56,6 +99,8 @@ SPI_connect () |
|
|
|
|
free (_SPI_stack); |
|
|
|
|
_SPI_current = _SPI_stack = NULL; |
|
|
|
|
_SPI_connected = _SPI_curid = -1; |
|
|
|
|
_SPI_xtab = NULL; |
|
|
|
|
_SPI_xtabsz = 0; |
|
|
|
|
SPI_processed = 0; |
|
|
|
|
SPI_tuptable = NULL; |
|
|
|
|
_SPI_portal = CreatePortal (pname); |
|
|
|
@ -101,11 +146,15 @@ SPI_connect () |
|
|
|
|
|
|
|
|
|
/* ... and switch to Portal' Variable memory - procedure' context */ |
|
|
|
|
pvmem = PortalGetVariableMemory (_SPI_current->portal); |
|
|
|
|
_SPI_current->savedcntx = MemoryContextSwitchTo ((MemoryContext)pvmem); |
|
|
|
|
_SPI_current->savedcxt = MemoryContextSwitchTo ((MemoryContext)pvmem); |
|
|
|
|
|
|
|
|
|
_SPI_current->savedId = GetScanCommandId (); |
|
|
|
|
SetScanCommandId (GetCurrentCommandId ()); |
|
|
|
|
|
|
|
|
|
_SPI_current->ltab.ident = pstrdup (ident); |
|
|
|
|
_SPI_current->ltab.plcnt = 0; |
|
|
|
|
_SPI_current->ltab.dtcnt = 0; |
|
|
|
|
|
|
|
|
|
return (SPI_OK_CONNECT); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
@ -120,7 +169,7 @@ SPI_finish () |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
/* Restore memory context as it was before procedure call */ |
|
|
|
|
MemoryContextSwitchTo (_SPI_current->savedcntx); |
|
|
|
|
MemoryContextSwitchTo (_SPI_current->savedcxt); |
|
|
|
|
PortalDestroy (&(_SPI_current->portal)); |
|
|
|
|
|
|
|
|
|
SetScanCommandId (_SPI_current->savedId); |
|
|
|
@ -150,33 +199,100 @@ SPI_finish () |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
SPI_exec (char *src) |
|
|
|
|
{ |
|
|
|
|
int res; |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (true); |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
res = _SPI_execute (src, 0, NULL); |
|
|
|
|
|
|
|
|
|
_SPI_end_call (true); |
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
SPI_execn (char *src, int tcount) |
|
|
|
|
{ |
|
|
|
|
int res; |
|
|
|
|
|
|
|
|
|
if ( tcount < 0 ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (true); |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
res = _SPI_execute (src, tcount, NULL); |
|
|
|
|
|
|
|
|
|
_SPI_end_call (true); |
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
SPI_prepare (char *src, int nargs, Oid *argtypes) |
|
|
|
|
{ |
|
|
|
|
_SPI_plan *plan; |
|
|
|
|
int res; |
|
|
|
|
|
|
|
|
|
if ( nargs < 0 || ( nargs > 0 && argtypes == NULL ) ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (true); |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
plan = (_SPI_plan *) palloc (sizeof (_SPI_plan)); |
|
|
|
|
plan->argtypes = argtypes; |
|
|
|
|
plan->nargs = nargs; |
|
|
|
|
|
|
|
|
|
res = _SPI_execute (src, 0, plan); |
|
|
|
|
|
|
|
|
|
if ( res >= 0 ) /* copy plan to local data space */ |
|
|
|
|
res = _SPI_copy_plan (SPI_DSPACE_LOCAL, plan); |
|
|
|
|
|
|
|
|
|
_SPI_end_call (true); |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
_SPI_execute (char *src, int tcount, _SPI_plan *plan) |
|
|
|
|
{ |
|
|
|
|
QueryTreeList *queryTree_list; |
|
|
|
|
List *planTree_list; |
|
|
|
|
List *ptlist; |
|
|
|
|
QueryDesc *qdesc; |
|
|
|
|
Query *queryTree; |
|
|
|
|
Plan *planTree; |
|
|
|
|
EState *state; |
|
|
|
|
int qlen; |
|
|
|
|
int nargs = 0; |
|
|
|
|
Oid *argtypes = NULL; |
|
|
|
|
int res; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (true); |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
/* Increment CommandCounter to see changes made by now */ |
|
|
|
|
CommandCounterIncrement (); |
|
|
|
|
StartPortalAllocMode (DefaultAllocMode, 0); |
|
|
|
|
|
|
|
|
|
SPI_processed = 0; |
|
|
|
|
SPI_tuptable = NULL; |
|
|
|
|
_SPI_current->tuptable = NULL; |
|
|
|
|
_SPI_current->qtlist = NULL; |
|
|
|
|
|
|
|
|
|
planTree_list = (List *) |
|
|
|
|
pg_plan (src, NULL, 0, &queryTree_list, None); |
|
|
|
|
if ( plan ) |
|
|
|
|
{ |
|
|
|
|
nargs = plan->nargs; |
|
|
|
|
argtypes = plan->argtypes; |
|
|
|
|
} |
|
|
|
|
ptlist = planTree_list = (List *) |
|
|
|
|
pg_plan (src, argtypes, nargs, &queryTree_list, None); |
|
|
|
|
|
|
|
|
|
_SPI_current->qtlist = queryTree_list; |
|
|
|
|
|
|
|
|
|
for (i=0; i < queryTree_list->len - 1; i++) |
|
|
|
|
qlen = queryTree_list->len; |
|
|
|
|
for (i=0; ;i++) |
|
|
|
|
{ |
|
|
|
|
queryTree = (Query*) (queryTree_list->qtrees[i]); |
|
|
|
|
planTree = lfirst(planTree_list); |
|
|
|
@ -190,90 +306,60 @@ SPI_exec (char *src) |
|
|
|
|
CopyStmt *stmt = (CopyStmt *)(queryTree->utilityStmt); |
|
|
|
|
|
|
|
|
|
if ( stmt->filename == NULL ) |
|
|
|
|
{ |
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_ERROR_COPY); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if ( nodeTag (queryTree->utilityStmt ) == T_ClosePortalStmt ||
|
|
|
|
|
nodeTag (queryTree->utilityStmt ) == T_FetchStmt ) |
|
|
|
|
{ |
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_ERROR_CURSOR); |
|
|
|
|
} |
|
|
|
|
else if ( nodeTag (queryTree->utilityStmt ) == T_TransactionStmt ) |
|
|
|
|
{ |
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_ERROR_TRANSACTION); |
|
|
|
|
} |
|
|
|
|
ProcessUtility (queryTree->utilityStmt, None); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
ProcessQuery (queryTree, planTree, NULL, NULL, 0, None); |
|
|
|
|
CommandCounterIncrement (); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Last query in list. Note that we don't call CommandCounterIncrement |
|
|
|
|
* after last query - it will be done by up-level or by next call |
|
|
|
|
* to SPI_exec. |
|
|
|
|
*/ |
|
|
|
|
queryTree = (Query*) (queryTree_list->qtrees[i]); |
|
|
|
|
planTree = lfirst(planTree_list); |
|
|
|
|
|
|
|
|
|
if ( queryTree->commandType == CMD_UTILITY ) |
|
|
|
|
{ |
|
|
|
|
if ( nodeTag (queryTree->utilityStmt ) == T_CopyStmt ) |
|
|
|
|
{ |
|
|
|
|
CopyStmt *stmt = (CopyStmt *)(queryTree->utilityStmt); |
|
|
|
|
|
|
|
|
|
if ( stmt->filename == NULL ) |
|
|
|
|
res = SPI_OK_UTILITY; |
|
|
|
|
if ( plan == NULL ) |
|
|
|
|
{ |
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_ERROR_COPY); |
|
|
|
|
ProcessUtility (queryTree->utilityStmt, None); |
|
|
|
|
if ( i < qlen - 1 ) |
|
|
|
|
CommandCounterIncrement (); |
|
|
|
|
else |
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
#if 0 |
|
|
|
|
else if ( nodeTag (queryTree->utilityStmt ) == T_FetchStmt ) |
|
|
|
|
{ |
|
|
|
|
_SPI_fetch ((FetchStmt *) (queryTree->utilityStmt)); |
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_OK_FETCH); |
|
|
|
|
else if ( i >= qlen - 1 ) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
else if ( nodeTag (queryTree->utilityStmt ) == T_ClosePortalStmt ||
|
|
|
|
|
nodeTag (queryTree->utilityStmt ) == T_FetchStmt ) |
|
|
|
|
else if ( plan == NULL ) |
|
|
|
|
{ |
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_ERROR_CURSOR); |
|
|
|
|
qdesc = CreateQueryDesc (queryTree, planTree,
|
|
|
|
|
( i < qlen - 1 ) ? None : SPI); |
|
|
|
|
state = CreateExecutorState(); |
|
|
|
|
res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount); |
|
|
|
|
if ( res < 0 || i >= qlen - 1 ) |
|
|
|
|
return (res); |
|
|
|
|
CommandCounterIncrement (); |
|
|
|
|
} |
|
|
|
|
else if ( nodeTag (queryTree->utilityStmt ) == T_TransactionStmt ) |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_ERROR_TRANSACTION); |
|
|
|
|
qdesc = CreateQueryDesc (queryTree, planTree,
|
|
|
|
|
( i < qlen - 1 ) ? None : SPI); |
|
|
|
|
res = _SPI_pquery (qdesc, NULL, ( i < qlen - 1 ) ? 0 : tcount); |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
if ( i >= qlen - 1 ) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
ProcessUtility (queryTree->utilityStmt, None); |
|
|
|
|
|
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
return (SPI_OK_UTILITY); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
qdesc = CreateQueryDesc (queryTree, planTree, SPI); |
|
|
|
|
|
|
|
|
|
res = _SPI_pquery (qdesc); |
|
|
|
|
|
|
|
|
|
_SPI_end_call (true, true); |
|
|
|
|
plan->qtlist = queryTree_list; |
|
|
|
|
plan->ptlist = ptlist; |
|
|
|
|
|
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
_SPI_pquery (QueryDesc *queryDesc) |
|
|
|
|
_SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount) |
|
|
|
|
{ |
|
|
|
|
Query *parseTree; |
|
|
|
|
Plan *plan; |
|
|
|
|
int operation; |
|
|
|
|
EState *state; |
|
|
|
|
TupleDesc tupdesc; |
|
|
|
|
bool isRetrieveIntoPortal = false; |
|
|
|
|
bool isRetrieveIntoRelation = false; |
|
|
|
@ -316,9 +402,13 @@ _SPI_pquery (QueryDesc *queryDesc) |
|
|
|
|
return (SPI_ERROR_OPUNKNOWN); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
state = CreateExecutorState(); |
|
|
|
|
|
|
|
|
|
tupdesc = ExecutorStart(queryDesc, state); |
|
|
|
|
if ( state == NULL ) /* plan preparation */ |
|
|
|
|
return (res); |
|
|
|
|
#ifdef SPI_EXECUTOR_STATS |
|
|
|
|
if ( ShowExecutorStats ) |
|
|
|
|
ResetUsage (); |
|
|
|
|
#endif |
|
|
|
|
tupdesc = ExecutorStart (queryDesc, state); |
|
|
|
|
|
|
|
|
|
/* Don't work currently */ |
|
|
|
|
if (isRetrieveIntoPortal) |
|
|
|
@ -332,10 +422,10 @@ _SPI_pquery (QueryDesc *queryDesc) |
|
|
|
|
return (SPI_OK_CURSOR); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ExecutorRun (queryDesc, state, EXEC_RUN, 0); |
|
|
|
|
ExecutorRun (queryDesc, state, EXEC_RUN, tcount); |
|
|
|
|
|
|
|
|
|
_SPI_current->processed = state->es_processed; |
|
|
|
|
if ( operation == CMD_SELECT ) |
|
|
|
|
if ( operation == CMD_SELECT && queryDesc->dest == SPI ) |
|
|
|
|
{ |
|
|
|
|
if ( _SPI_checktuples (isRetrieveIntoRelation) ) |
|
|
|
|
elog (FATAL, "SPI_select: # of processed tuples check failed"); |
|
|
|
@ -343,8 +433,19 @@ _SPI_pquery (QueryDesc *queryDesc) |
|
|
|
|
|
|
|
|
|
ExecutorEnd (queryDesc, state); |
|
|
|
|
|
|
|
|
|
SPI_processed = _SPI_current->processed; |
|
|
|
|
SPI_tuptable = _SPI_current->tuptable; |
|
|
|
|
#ifdef SPI_EXECUTOR_STATS |
|
|
|
|
if ( ShowExecutorStats ) |
|
|
|
|
{ |
|
|
|
|
fprintf (stderr, "! Executor Stats:\n"); |
|
|
|
|
ShowUsage (); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
if ( queryDesc->dest == SPI ) |
|
|
|
|
{ |
|
|
|
|
SPI_processed = _SPI_current->processed; |
|
|
|
|
SPI_tuptable = _SPI_current->tuptable; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
@ -398,7 +499,7 @@ void |
|
|
|
|
spi_printtup (HeapTuple tuple, TupleDesc tupdesc) |
|
|
|
|
{ |
|
|
|
|
SPITupleTable *tuptable; |
|
|
|
|
MemoryContext oldcntx; |
|
|
|
|
MemoryContext oldcxt; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* When called by Executor _SPI_curid expected to be |
|
|
|
@ -409,7 +510,7 @@ spi_printtup (HeapTuple tuple, TupleDesc tupdesc) |
|
|
|
|
if ( _SPI_current != &(_SPI_stack[_SPI_curid]) ) |
|
|
|
|
elog (FATAL, "SPI: stack corrupted in spi_printtup"); |
|
|
|
|
|
|
|
|
|
oldcntx = _SPI_procmem (); /* switch to procedure memory context */ |
|
|
|
|
oldcxt = _SPI_procmem (); /* switch to procedure memory context */ |
|
|
|
|
|
|
|
|
|
tuptable = _SPI_current->tuptable; |
|
|
|
|
if ( tuptable == NULL ) |
|
|
|
@ -431,33 +532,33 @@ spi_printtup (HeapTuple tuple, TupleDesc tupdesc) |
|
|
|
|
tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple (tuple); |
|
|
|
|
(tuptable->free)--; |
|
|
|
|
|
|
|
|
|
MemoryContextSwitchTo (oldcntx); |
|
|
|
|
MemoryContextSwitchTo (oldcxt); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static MemoryContext |
|
|
|
|
_SPI_execmem () |
|
|
|
|
{ |
|
|
|
|
MemoryContext oldcntx; |
|
|
|
|
MemoryContext oldcxt; |
|
|
|
|
PortalHeapMemory phmem; |
|
|
|
|
|
|
|
|
|
phmem = PortalGetHeapMemory (_SPI_current->portal); |
|
|
|
|
oldcntx = MemoryContextSwitchTo ((MemoryContext)phmem); |
|
|
|
|
oldcxt = MemoryContextSwitchTo ((MemoryContext)phmem); |
|
|
|
|
|
|
|
|
|
return (oldcntx); |
|
|
|
|
return (oldcxt); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static MemoryContext |
|
|
|
|
_SPI_procmem () |
|
|
|
|
{ |
|
|
|
|
MemoryContext oldcntx; |
|
|
|
|
MemoryContext oldcxt; |
|
|
|
|
PortalVariableMemory pvmem; |
|
|
|
|
|
|
|
|
|
pvmem = PortalGetVariableMemory (_SPI_current->portal); |
|
|
|
|
oldcntx = MemoryContextSwitchTo ((MemoryContext)pvmem); |
|
|
|
|
oldcxt = MemoryContextSwitchTo ((MemoryContext)pvmem); |
|
|
|
|
|
|
|
|
|
return (oldcntx); |
|
|
|
|
return (oldcxt); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -475,20 +576,23 @@ _SPI_begin_call (bool execmem) |
|
|
|
|
elog (FATAL, "SPI: stack corrupted"); |
|
|
|
|
|
|
|
|
|
if ( execmem ) /* switch to the Executor memory context */ |
|
|
|
|
{ |
|
|
|
|
_SPI_execmem (); |
|
|
|
|
StartPortalAllocMode (DefaultAllocMode, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
_SPI_end_call (bool exfree, bool procmem) |
|
|
|
|
_SPI_end_call (bool procmem) |
|
|
|
|
{ |
|
|
|
|
/*
|
|
|
|
|
* We' returning to procedure where _SPI_curid == _SPI_connected - 1 |
|
|
|
|
*/ |
|
|
|
|
_SPI_curid--; |
|
|
|
|
|
|
|
|
|
if ( exfree ) /* free SPI_exec allocations */ |
|
|
|
|
if ( _SPI_current->qtlist) /* free _SPI_plan allocations */ |
|
|
|
|
{ |
|
|
|
|
free (_SPI_current->qtlist->qtrees); |
|
|
|
|
free (_SPI_current->qtlist); |
|
|
|
@ -531,3 +635,499 @@ _SPI_checktuples (bool isRetrieveIntoRelation) |
|
|
|
|
|
|
|
|
|
return (failed); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
SPI_fnumber (TupleDesc tupdesc, char *fname) |
|
|
|
|
{ |
|
|
|
|
int res; |
|
|
|
|
|
|
|
|
|
if ( _SPI_curid + 1 != _SPI_connected ) |
|
|
|
|
return (SPI_ERROR_UNCONNECTED); |
|
|
|
|
|
|
|
|
|
for (res = 0; res < tupdesc->natts; res++) |
|
|
|
|
{ |
|
|
|
|
if ( strcmp (tupdesc->attrs[res]->attname.data, fname) == 0 ) |
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (SPI_ERROR_NOATTRIBUTE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char * |
|
|
|
|
SPI_getvalue (HeapTuple tuple, TupleDesc tupdesc, int fnumber) |
|
|
|
|
{ |
|
|
|
|
char *val; |
|
|
|
|
bool isnull; |
|
|
|
|
Oid foutoid; |
|
|
|
|
|
|
|
|
|
SPI_error = 0; |
|
|
|
|
if ( _SPI_curid + 1 != _SPI_connected ) |
|
|
|
|
{ |
|
|
|
|
SPI_error = SPI_ERROR_UNCONNECTED; |
|
|
|
|
return (NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( tuple->t_natts <= fnumber || fnumber < 0 ) |
|
|
|
|
return (NULL); |
|
|
|
|
|
|
|
|
|
val = heap_getattr (tuple, InvalidBuffer, fnumber + 1, tupdesc, &isnull); |
|
|
|
|
if ( isnull ) |
|
|
|
|
return (NULL); |
|
|
|
|
foutoid = typtoout ((Oid) tupdesc->attrs[fnumber]->atttypid); |
|
|
|
|
if ( !OidIsValid (foutoid) ) |
|
|
|
|
{ |
|
|
|
|
SPI_error = SPI_ERROR_NOOUTFUNC; |
|
|
|
|
return (NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (fmgr (foutoid, val, gettypelem (tupdesc->attrs[fnumber]->atttypid))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char * |
|
|
|
|
SPI_getbinval (HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull) |
|
|
|
|
{ |
|
|
|
|
char *val; |
|
|
|
|
|
|
|
|
|
*isnull = true; |
|
|
|
|
SPI_error = 0; |
|
|
|
|
if ( _SPI_curid + 1 != _SPI_connected ) |
|
|
|
|
{ |
|
|
|
|
SPI_error = SPI_ERROR_UNCONNECTED; |
|
|
|
|
return (NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( tuple->t_natts <= fnumber || fnumber < 0 ) |
|
|
|
|
return (NULL); |
|
|
|
|
|
|
|
|
|
val = heap_getattr (tuple, InvalidBuffer, fnumber + 1, tupdesc, isnull); |
|
|
|
|
|
|
|
|
|
return (val); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char * |
|
|
|
|
SPI_gettype (TupleDesc tupdesc, int fnumber) |
|
|
|
|
{ |
|
|
|
|
HeapTuple typeTuple; |
|
|
|
|
|
|
|
|
|
SPI_error = 0; |
|
|
|
|
if ( _SPI_curid + 1 != _SPI_connected ) |
|
|
|
|
{ |
|
|
|
|
SPI_error = SPI_ERROR_UNCONNECTED; |
|
|
|
|
return (NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( tupdesc->natts <= fnumber || fnumber < 0 ) |
|
|
|
|
return (NULL); |
|
|
|
|
|
|
|
|
|
typeTuple = SearchSysCacheTuple (TYPOID,
|
|
|
|
|
ObjectIdGetDatum (tupdesc->attrs[fnumber]->atttypid), |
|
|
|
|
0, 0, 0); |
|
|
|
|
|
|
|
|
|
if ( !HeapTupleIsValid (typeTuple) ) |
|
|
|
|
{ |
|
|
|
|
SPI_error = SPI_ERROR_TYPUNKNOWN; |
|
|
|
|
return (NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (pstrdup (((TypeTupleForm) GETSTRUCT (typeTuple))->typname.data)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Oid |
|
|
|
|
SPI_gettypeid (TupleDesc tupdesc, int fnumber) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
SPI_error = 0; |
|
|
|
|
if ( _SPI_curid + 1 != _SPI_connected ) |
|
|
|
|
{ |
|
|
|
|
SPI_error = SPI_ERROR_UNCONNECTED; |
|
|
|
|
return (InvalidOid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( tupdesc->natts <= fnumber || fnumber < 0 ) |
|
|
|
|
return (InvalidOid); |
|
|
|
|
|
|
|
|
|
return (tupdesc->attrs[fnumber]->atttypid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char * |
|
|
|
|
SPI_getrelname (Relation rel) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
SPI_error = 0; |
|
|
|
|
if ( _SPI_curid + 1 != _SPI_connected ) |
|
|
|
|
{ |
|
|
|
|
SPI_error = SPI_ERROR_UNCONNECTED; |
|
|
|
|
return (NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (pstrdup (rel->rd_rel->relname.data)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static _SPI_entry * |
|
|
|
|
_SPI_fnentry (int dspace, int **tabsz, MemoryContext *oldcxt) |
|
|
|
|
{ |
|
|
|
|
char *ident = _SPI_current->ltab.ident; |
|
|
|
|
int *size = NULL; |
|
|
|
|
_SPI_entry **ep = NULL; |
|
|
|
|
_SPI_entry *entry; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
switch (dspace) |
|
|
|
|
{ |
|
|
|
|
case SPI_DSPACE_SESSION : |
|
|
|
|
if ( tabsz != NULL ) |
|
|
|
|
*oldcxt = MemoryContextSwitchTo (TopMemoryContext); |
|
|
|
|
ep = &(_SPI_stab); |
|
|
|
|
size = &(_SPI_stabsz); |
|
|
|
|
break; |
|
|
|
|
case SPI_DSPACE_XACT : |
|
|
|
|
if ( tabsz != NULL ) |
|
|
|
|
*oldcxt = MemoryContextSwitchTo ((MemoryContext)
|
|
|
|
|
PortalGetVariableMemory (_SPI_portal)); |
|
|
|
|
ep = &(_SPI_xtab); |
|
|
|
|
size = &(_SPI_xtabsz); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (i = 0; i < *size; i++, ep++) |
|
|
|
|
{ |
|
|
|
|
if ( strcmp ((*ep)->ident, ident) == 0 ) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if ( i == *size ) |
|
|
|
|
{ |
|
|
|
|
if ( tabsz == NULL ) /* don't expand table */ |
|
|
|
|
return (NULL); |
|
|
|
|
*tabsz = size; |
|
|
|
|
if ( *size == 0 ) |
|
|
|
|
*ep = (_SPI_entry *) palloc (sizeof (_SPI_entry)); |
|
|
|
|
else |
|
|
|
|
*ep = (_SPI_entry *) repalloc (*ep,
|
|
|
|
|
(*size + 1) * sizeof (_SPI_entry)); |
|
|
|
|
entry = (*ep) + *size; |
|
|
|
|
entry->ident = pstrdup (ident); |
|
|
|
|
entry->plcnt = entry->dtcnt = 0; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
entry = *ep; |
|
|
|
|
|
|
|
|
|
return (entry); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
_SPI_copy_plan (int dspace, _SPI_plan *plan) |
|
|
|
|
{ |
|
|
|
|
_SPI_entry *entry; |
|
|
|
|
_SPI_plan *newplan; |
|
|
|
|
int *tabsz = NULL; |
|
|
|
|
MemoryContext oldcxt; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
if ( dspace == SPI_DSPACE_LOCAL ) |
|
|
|
|
{ |
|
|
|
|
oldcxt = MemoryContextSwitchTo ((MemoryContext) |
|
|
|
|
PortalGetVariableMemory (_SPI_current->portal)); |
|
|
|
|
entry = &(_SPI_current->ltab); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
entry = _SPI_fnentry (dspace, &tabsz, &oldcxt); |
|
|
|
|
|
|
|
|
|
if ( entry->plcnt == 0 ) |
|
|
|
|
entry->plan = (_SPI_plan **) palloc (sizeof (_SPI_plan *)); |
|
|
|
|
else |
|
|
|
|
entry->plan = (_SPI_plan **) repalloc (entry->plan, |
|
|
|
|
(entry->plcnt + 1) * sizeof (_SPI_plan *)); |
|
|
|
|
newplan = (_SPI_plan *) palloc (sizeof (_SPI_plan)); |
|
|
|
|
entry->plan[entry->plcnt] = newplan; |
|
|
|
|
newplan->qtlist = (QueryTreeList*) palloc (sizeof (QueryTreeList)); |
|
|
|
|
newplan->qtlist->len = plan->qtlist->len; |
|
|
|
|
newplan->qtlist->qtrees = (Query**) palloc (plan->qtlist->len *
|
|
|
|
|
sizeof (Query*)); |
|
|
|
|
for (i = 0; i < plan->qtlist->len; i++) |
|
|
|
|
newplan->qtlist->qtrees[i] = (Query *)
|
|
|
|
|
copyObject (plan->qtlist->qtrees[i]); |
|
|
|
|
|
|
|
|
|
newplan->ptlist = (List *) copyObject (plan->ptlist); |
|
|
|
|
newplan->nargs = plan->nargs; |
|
|
|
|
if ( plan->nargs > 0 ) |
|
|
|
|
{ |
|
|
|
|
newplan->argtypes = (Oid *) palloc (plan->nargs * sizeof (Oid)); |
|
|
|
|
memcpy (newplan->argtypes, plan->argtypes, plan->nargs * sizeof (Oid)); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
newplan->argtypes = NULL; |
|
|
|
|
(entry->plcnt)++; |
|
|
|
|
|
|
|
|
|
if ( tabsz != NULL ) /* table expanded */ |
|
|
|
|
(*tabsz)++; |
|
|
|
|
|
|
|
|
|
MemoryContextSwitchTo (oldcxt); |
|
|
|
|
|
|
|
|
|
return (entry->plcnt - 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
SPI_expdata (int dspace, int count, void **data, int *len) |
|
|
|
|
{ |
|
|
|
|
_SPI_entry *entry; |
|
|
|
|
_SPI_data *newdata; |
|
|
|
|
int *tabsz = NULL; |
|
|
|
|
MemoryContext oldcxt; |
|
|
|
|
int res; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
if ( ( dspace != SPI_DSPACE_XACT &&
|
|
|
|
|
dspace != SPI_DSPACE_SESSION ) ||
|
|
|
|
|
count <= 0 || data == NULL || len == NULL ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (false); /* don't change context */ |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
entry = _SPI_fnentry (dspace, &tabsz, &oldcxt); |
|
|
|
|
|
|
|
|
|
if ( entry->dtcnt == 0 ) |
|
|
|
|
entry->data = (_SPI_data *) palloc (count * sizeof (_SPI_data)); |
|
|
|
|
else |
|
|
|
|
entry->data = (_SPI_data *) repalloc (entry->data, |
|
|
|
|
(entry->dtcnt + count) * sizeof (_SPI_data)); |
|
|
|
|
for (i = 0, newdata = &(entry->data[entry->dtcnt]); i < count; i++, newdata++) |
|
|
|
|
{ |
|
|
|
|
if ( len[i] <= 0 || data[i] == NULL ) |
|
|
|
|
break; |
|
|
|
|
newdata->data = (void *) palloc (len[i]); |
|
|
|
|
memcpy (newdata->data, data[i], len[i]); |
|
|
|
|
newdata->len = len[i]; |
|
|
|
|
} |
|
|
|
|
entry->dtcnt += i; |
|
|
|
|
res = i; |
|
|
|
|
|
|
|
|
|
if ( tabsz != NULL ) /* table expanded */ |
|
|
|
|
(*tabsz)++; |
|
|
|
|
|
|
|
|
|
MemoryContextSwitchTo (oldcxt); |
|
|
|
|
|
|
|
|
|
_SPI_curid--; |
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
SPI_impdata (int dspace, int start, int count, void **data, int **len) |
|
|
|
|
{ |
|
|
|
|
_SPI_entry *entry; |
|
|
|
|
int *dl; |
|
|
|
|
int res; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
if ( ( dspace != SPI_DSPACE_XACT &&
|
|
|
|
|
dspace != SPI_DSPACE_SESSION ) ||
|
|
|
|
|
start < 0 || count < 0 || |
|
|
|
|
(count > 0 && (data == NULL || len == NULL) ) ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (false); /* don't change context */ |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
entry = _SPI_fnentry (dspace, NULL, NULL); |
|
|
|
|
|
|
|
|
|
_SPI_curid--; |
|
|
|
|
|
|
|
|
|
if ( entry == NULL || entry->dtcnt == 0 ) |
|
|
|
|
return (0); |
|
|
|
|
if ( count == 0 ) |
|
|
|
|
return (entry->dtcnt); |
|
|
|
|
if ( start >= entry->dtcnt ) |
|
|
|
|
return (0); |
|
|
|
|
|
|
|
|
|
i = ( entry->dtcnt - start >= count ) ? count : entry->dtcnt - start; |
|
|
|
|
data = (void **) palloc (i * sizeof (void *)); |
|
|
|
|
dl = *len = (int *) palloc (i * sizeof (int)); |
|
|
|
|
|
|
|
|
|
for (i = start, res = 0; i < entry->dtcnt && res < count; i++, res++) |
|
|
|
|
{ |
|
|
|
|
dl[res] = entry->data[i].len; |
|
|
|
|
data[res] = (void *) palloc (dl[res]); |
|
|
|
|
memcpy (data[res], entry->data[i].data, dl[res]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
SPI_expplan (int dspace, int start, int count) |
|
|
|
|
{ |
|
|
|
|
_SPI_entry *entry = &(_SPI_current->ltab); |
|
|
|
|
int res; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
if ( ( dspace != SPI_DSPACE_XACT &&
|
|
|
|
|
dspace != SPI_DSPACE_SESSION ) ||
|
|
|
|
|
start < 0 || count <= 0 ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (false); /* don't change context */ |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
if ( start >= entry->plcnt ) |
|
|
|
|
{ |
|
|
|
|
_SPI_curid--; |
|
|
|
|
return (0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (i = start, res = 0; i < entry->plcnt && res < count; i++, res++) |
|
|
|
|
{ |
|
|
|
|
_SPI_copy_plan (dspace, entry->plan[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_SPI_curid--; |
|
|
|
|
|
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
SPI_impplan (int dspace, int start, int count) |
|
|
|
|
{ |
|
|
|
|
_SPI_entry *to = &(_SPI_current->ltab); |
|
|
|
|
_SPI_entry *from; |
|
|
|
|
int res; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
if ( ( dspace != SPI_DSPACE_XACT &&
|
|
|
|
|
dspace != SPI_DSPACE_SESSION ) ||
|
|
|
|
|
start < 0 || count < 0 ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (false); /* don't change context */ |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
from = _SPI_fnentry (dspace, NULL, NULL); /* don't expand table */ |
|
|
|
|
|
|
|
|
|
_SPI_curid--; |
|
|
|
|
|
|
|
|
|
if ( from == NULL || from->plcnt == 0 ) |
|
|
|
|
return (0); |
|
|
|
|
if ( count == 0 ) |
|
|
|
|
return (from->plcnt); |
|
|
|
|
if ( start >= from->plcnt ) |
|
|
|
|
return (0); |
|
|
|
|
|
|
|
|
|
i = ( from->plcnt - start >= count ) ? count : from->plcnt - start; |
|
|
|
|
if ( to->plcnt == 0 ) |
|
|
|
|
to->plan = (_SPI_plan **) palloc (i * sizeof (_SPI_plan *)); |
|
|
|
|
else |
|
|
|
|
to->plan = (_SPI_plan **) repalloc (to->plan,
|
|
|
|
|
(to->plcnt + i) * sizeof (_SPI_plan *)); |
|
|
|
|
|
|
|
|
|
for (i = start, res = 0; i < from->plcnt && res < count; i++, res++) |
|
|
|
|
{ |
|
|
|
|
to->plan[res] = from->plan[i]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
SPI_execp (int pid, char **Values, char *Nulls) |
|
|
|
|
{ |
|
|
|
|
_SPI_entry *entry = &(_SPI_current->ltab); |
|
|
|
|
int res; |
|
|
|
|
|
|
|
|
|
if ( pid < 0 ) |
|
|
|
|
return (SPI_ERROR_ARGUMENT); |
|
|
|
|
|
|
|
|
|
res = _SPI_begin_call (false); |
|
|
|
|
if ( res < 0 ) |
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
if ( entry->plcnt < pid ) |
|
|
|
|
{ |
|
|
|
|
_SPI_curid--; |
|
|
|
|
return (SPI_ERROR_NOENTRY); |
|
|
|
|
} |
|
|
|
|
else if ( entry->plan[pid]->nargs > 0 && ( Values == NULL || Nulls == NULL ) ) |
|
|
|
|
{ |
|
|
|
|
_SPI_curid--; |
|
|
|
|
return (SPI_ERROR_PARAM); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_SPI_execmem(); |
|
|
|
|
StartPortalAllocMode (DefaultAllocMode, 0); |
|
|
|
|
|
|
|
|
|
res = _SPI_execute_plan (entry->plan[pid], Values, Nulls, 0); |
|
|
|
|
|
|
|
|
|
_SPI_end_call (true); |
|
|
|
|
return (res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
_SPI_execute_plan (_SPI_plan *plan, char **Values, char *Nulls, int tcount) |
|
|
|
|
{ |
|
|
|
|
QueryTreeList *queryTree_list = plan->qtlist; |
|
|
|
|
List *planTree_list = plan->ptlist; |
|
|
|
|
QueryDesc *qdesc; |
|
|
|
|
Query *queryTree; |
|
|
|
|
Plan *planTree; |
|
|
|
|
EState *state; |
|
|
|
|
int nargs = plan->nargs; |
|
|
|
|
int qlen = queryTree_list->len; |
|
|
|
|
int res; |
|
|
|
|
int i, k; |
|
|
|
|
|
|
|
|
|
/* Increment CommandCounter to see changes made by now */ |
|
|
|
|
CommandCounterIncrement (); |
|
|
|
|
|
|
|
|
|
SPI_processed = 0; |
|
|
|
|
SPI_tuptable = NULL; |
|
|
|
|
_SPI_current->tuptable = NULL; |
|
|
|
|
_SPI_current->qtlist = NULL; |
|
|
|
|
|
|
|
|
|
for (i=0; ;i++) |
|
|
|
|
{ |
|
|
|
|
queryTree = (Query*) (queryTree_list->qtrees[i]); |
|
|
|
|
planTree = lfirst(planTree_list); |
|
|
|
|
|
|
|
|
|
planTree_list = lnext (planTree_list); |
|
|
|
|
|
|
|
|
|
if ( queryTree->commandType == CMD_UTILITY ) |
|
|
|
|
{ |
|
|
|
|
ProcessUtility (queryTree->utilityStmt, None); |
|
|
|
|
if ( i < qlen - 1 ) |
|
|
|
|
CommandCounterIncrement (); |
|
|
|
|
else |
|
|
|
|
return (SPI_OK_UTILITY); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
qdesc = CreateQueryDesc (queryTree, planTree,
|
|
|
|
|
( i < qlen - 1 ) ? None : SPI); |
|
|
|
|
state = CreateExecutorState(); |
|
|
|
|
if ( nargs > 0 ) |
|
|
|
|
{ |
|
|
|
|
ParamListInfo paramLI = (ParamListInfo) palloc ((nargs + 1) *
|
|
|
|
|
sizeof (ParamListInfoData)); |
|
|
|
|
state->es_param_list_info = paramLI; |
|
|
|
|
for (k = 0; k < plan->nargs; paramLI++, k++) |
|
|
|
|
{ |
|
|
|
|
paramLI->kind = PARAM_NUM; |
|
|
|
|
paramLI->id = i+1; |
|
|
|
|
paramLI->isnull = (Nulls[i] != 0); |
|
|
|
|
paramLI->value = (Datum) Values[i]; |
|
|
|
|
} |
|
|
|
|
paramLI->kind = PARAM_INVALID; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
state->es_param_list_info = NULL; |
|
|
|
|
res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount); |
|
|
|
|
if ( res < 0 || i >= qlen - 1 ) |
|
|
|
|
return (res); |
|
|
|
|
CommandCounterIncrement (); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return (res); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|