pltclobj-try-1.patch
application/octet-stream
Filename: pltclobj-try-1.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/pl/tcl/pltcl.c | 0 | 0 |
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 6e8c5c0..da5cf27 100644
*** a/src/pl/tcl/pltcl.c
--- b/src/pl/tcl/pltcl.c
***************
*** 33,49 ****
#include "utils/memutils.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
#define HAVE_TCL_VERSION(maj,min) \
((TCL_MAJOR_VERSION > maj) || \
(TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
- /* In Tcl >= 8.0, really not supposed to touch interp->result directly */
- #if !HAVE_TCL_VERSION(8,0)
- #define Tcl_GetStringResult(interp) ((interp)->result)
- #endif
-
/* define our text domain for translations */
#undef TEXTDOMAIN
#define TEXTDOMAIN PG_TEXTDOMAIN("pltcl")
--- 33,45 ----
#include "utils/memutils.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
+ #include "funcapi.h"
#define HAVE_TCL_VERSION(maj,min) \
((TCL_MAJOR_VERSION > maj) || \
(TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
/* define our text domain for translations */
#undef TEXTDOMAIN
#define TEXTDOMAIN PG_TEXTDOMAIN("pltcl")
*************** PG_MODULE_MAGIC;
*** 95,104 ****
**********************************************************************/
typedef struct pltcl_interp_desc
{
! Oid user_id; /* Hash key (must be first!) */
! Tcl_Interp *interp; /* The interpreter */
! Tcl_HashTable query_hash; /* pltcl_query_desc structs */
! } pltcl_interp_desc;
/**********************************************************************
--- 91,100 ----
**********************************************************************/
typedef struct pltcl_interp_desc
{
! Oid user_id; /* Hash key (must be first!) */
! Tcl_Interp *interp; /* The interpreter */
! Tcl_HashTable query_hash; /* pltcl_query_desc structs */
! } pltcl_interp_desc;
/**********************************************************************
*************** typedef struct pltcl_proc_desc
*** 112,124 ****
ItemPointerData fn_tid;
bool fn_readonly;
bool lanpltrusted;
pltcl_interp_desc *interp_desc;
FmgrInfo result_in_func;
Oid result_typioparam;
int nargs;
FmgrInfo arg_out_func[FUNC_MAX_ARGS];
bool arg_is_rowtype[FUNC_MAX_ARGS];
! } pltcl_proc_desc;
/**********************************************************************
--- 108,130 ----
ItemPointerData fn_tid;
bool fn_readonly;
bool lanpltrusted;
+ bool fn_retistuple; /* true, if function returns tuple */
+ bool fn_retisset; /* true, if function returns a set */
pltcl_interp_desc *interp_desc;
FmgrInfo result_in_func;
Oid result_typioparam;
int nargs;
FmgrInfo arg_out_func[FUNC_MAX_ARGS];
bool arg_is_rowtype[FUNC_MAX_ARGS];
!
! TupleDesc ret_tupdesc;
! Tuplestorestate *tuple_store; /* SRFs accumulate result here */
! AttInMetadata *attinmeta;
! int natts;
! MemoryContext tuple_store_cxt;
! ResourceOwner tuple_store_owner;
! ReturnSetInfo *rsi;
! } pltcl_proc_desc;
/**********************************************************************
*************** typedef struct pltcl_query_desc
*** 132,138 ****
Oid *argtypes;
FmgrInfo *arginfuncs;
Oid *argtypioparams;
! } pltcl_query_desc;
/**********************************************************************
--- 138,144 ----
Oid *argtypes;
FmgrInfo *arginfuncs;
Oid *argtypioparams;
! } pltcl_query_desc;
/**********************************************************************
*************** typedef struct pltcl_query_desc
*** 148,167 ****
**********************************************************************/
typedef struct pltcl_proc_key
{
! Oid proc_id; /* Function OID */
/*
* is_trigger is really a bool, but declare as Oid to ensure this struct
* contains no padding
*/
! Oid is_trigger; /* is it a trigger function? */
! Oid user_id; /* User calling the function, or 0 */
! } pltcl_proc_key;
typedef struct pltcl_proc_ptr
{
! pltcl_proc_key proc_key; /* Hash key (must be first!) */
pltcl_proc_desc *proc_ptr;
! } pltcl_proc_ptr;
/**********************************************************************
--- 154,173 ----
**********************************************************************/
typedef struct pltcl_proc_key
{
! Oid proc_id; /* Function OID */
/*
* is_trigger is really a bool, but declare as Oid to ensure this struct
* contains no padding
*/
! Oid is_trigger; /* is it a trigger function? */
! Oid user_id; /* User calling the function, or 0 */
! } pltcl_proc_key;
typedef struct pltcl_proc_ptr
{
! pltcl_proc_key proc_key; /* Hash key (must be first!) */
pltcl_proc_desc *proc_ptr;
! } pltcl_proc_ptr;
/**********************************************************************
*************** Datum pltcl_call_handler(PG_FUNCTION_AR
*** 183,231 ****
Datum pltclu_call_handler(PG_FUNCTION_ARGS);
void _PG_init(void);
! static void pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted);
static pltcl_interp_desc *pltcl_fetch_interp(bool pltrusted);
! static void pltcl_init_load_unknown(Tcl_Interp *interp);
! static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
! static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
! static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
! static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
! bool pltrusted);
! static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static int pltcl_process_SPI_result(Tcl_Interp *interp,
CONST84 char *arrayname,
! CONST84 char *loop_body,
int spi_rc,
! SPITupleTable *tuptable,
int ntuples);
! static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[]);
! static void pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
int tupno, HeapTuple tuple, TupleDesc tupdesc);
static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
! Tcl_DString *retval);
/*
--- 189,243 ----
Datum pltclu_call_handler(PG_FUNCTION_ARGS);
void _PG_init(void);
! static void pltcl_init_interp(pltcl_interp_desc * interp_desc, bool pltrusted);
static pltcl_interp_desc *pltcl_fetch_interp(bool pltrusted);
! static void pltcl_init_load_unknown(Tcl_Interp * interp);
! static Datum pltcl_handler(FunctionCallInfo fcinfo, bool pltrusted);
! static Datum pltcl_func_handler(FunctionCallInfo fcinfo, bool pltrusted);
! static HeapTuple pltcl_trigger_handler(FunctionCallInfo fcinfo, bool pltrusted);
! static void throw_tcl_error(Tcl_Interp * interp, const char *proname);
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
! bool pltrusted);
! static void
! pltcl_pg_returnnext(Tcl_Interp * interp, int rowObjc, Tcl_Obj ** rowObjv);
! static int pltcl_elog(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static int pltcl_quote(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static int pltcl_argisnull(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static int pltcl_returnnull(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static int pltcl_returnnext(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
!
! static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static int pltcl_process_SPI_result(Tcl_Interp * interp,
CONST84 char *arrayname,
! Tcl_Obj * loop_body,
int spi_rc,
! SPITupleTable * tuptable,
int ntuples);
! static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[]);
! static void pltcl_set_tuple_values(Tcl_Interp * interp, CONST84 char *arrayname,
int tupno, HeapTuple tuple, TupleDesc tupdesc);
static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
! Tcl_Obj * retobj);
! static void pltcl_init_tuple_store(pltcl_proc_desc * prodesc);
/*
*************** pltcl_FinalizeNotifier(ClientData client
*** 256,262 ****
}
static void
! pltcl_SetTimer(Tcl_Time *timePtr)
{
}
--- 268,274 ----
}
static void
! pltcl_SetTimer(Tcl_Time * timePtr)
{
}
*************** pltcl_AlertNotifier(ClientData clientDat
*** 267,273 ****
static void
pltcl_CreateFileHandler(int fd, int mask,
! Tcl_FileProc *proc, ClientData clientData)
{
}
--- 279,285 ----
static void
pltcl_CreateFileHandler(int fd, int mask,
! Tcl_FileProc * proc, ClientData clientData)
{
}
*************** pltcl_ServiceModeHook(int mode)
*** 282,293 ****
}
static int
! pltcl_WaitForEvent(Tcl_Time *timePtr)
{
return 0;
}
#endif /* HAVE_TCL_VERSION(8,4) */
/*
* This routine is a crock, and so is everyplace that calls it. The problem
--- 294,414 ----
}
static int
! pltcl_WaitForEvent(Tcl_Time * timePtr)
{
return 0;
}
#endif /* HAVE_TCL_VERSION(8,4) */
+ #if 0
+ static Tcl_Obj *
+ tclobj_from_bool(Datum d)
+ {
+ return Tcl_NewBooleanObj(DatumGetBool(d));
+ }
+
+ static Tcl_Obj *
+ tclobj_from_float4(Datum d)
+ {
+ return Tcl_NewDoubleObj(DatumGetFloat4(d));
+ }
+
+ static Tcl_Obj *
+ tclobj_from_float8(Datum d)
+ {
+ return Tcl_NewDoubleObj(DatumGetFloat8(d));
+ }
+
+ static Tcl_Obj *
+ tclobj_from_int16(Datum d)
+ {
+ return Tcl_NewIntObj(DatumGetInt16(d));
+ }
+
+
+ static Tcl_Obj *
+ tclobj_from_int32(Datum d)
+ {
+ return Tcl_NewIntObj(DatumGetInt32(d));
+ }
+
+ static Tcl_Obj *
+ tclobj_from_int64(Datum d)
+ {
+ return Tcl_NewWideIntObj(DatumGetInt64(d));
+ }
+
+ static Tcl_Obj *
+ tclobj_from_bytea(Datum d)
+ {
+ text *txt = DatumGetByteaP(d);
+ char *str = VARDATA(txt);
+ size_t size = VARSIZE(txt) - VARHDRSZ;
+
+ return Tcl_NewByteArrayObj(str, size);
+ }
+ #endif
+
+ static HeapTuple
+ pltcl_build_tuple_result(Tcl_Interp * interp, Tcl_Obj ** kvObjv, int kvObjc, pltcl_proc_desc * prodesc)
+ {
+ HeapTuple tup;
+ char **values;
+ int i;
+
+ values = (char **) palloc0(prodesc->natts * sizeof(char *));
+
+ for (i = 0; i < kvObjc; i += 2)
+ {
+ char *fieldName = Tcl_GetString(kvObjv[i]);
+ int attn = SPI_fnumber(prodesc->ret_tupdesc, fieldName);
+
+ if (attn <= 0 || prodesc->ret_tupdesc->attrs[attn - 1]->attisdropped)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("Tcl list contains nonexistent column \"%s\"",
+ fieldName)));
+
+ UTF_BEGIN;
+ values[attn - 1] = UTF_E2U(Tcl_GetString(kvObjv[i + 1]));
+ UTF_END;
+ }
+
+ tup = BuildTupleFromCStrings(prodesc->attinmeta, values);
+ pfree(values);
+ return tup;
+ }
+
+ /**********************************************************************
+ * pltcl_reset_state() - reset function's runtime state
+ *
+ * This is called on function and trigger entry
+ * (pltcl_func_handler and pltcl_trigger_handler) to clear
+ * any previous results.
+ *
+ * rsi is present if it's a function but not if it's a trigger.
+ **********************************************************************/
+ static void
+ pltcl_reset_state(pltcl_proc_desc * prodesc, ReturnSetInfo * rsi)
+ {
+ prodesc->ret_tupdesc = NULL;
+ prodesc->tuple_store = NULL;
+ prodesc->attinmeta = NULL;
+ prodesc->natts = 0;
+
+ if (rsi)
+ {
+ prodesc->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
+ prodesc->tuple_store_owner = CurrentResourceOwner;
+ }
+ else
+ {
+ prodesc->tuple_store_cxt = NULL;
+ prodesc->tuple_store_owner = NULL;
+ }
+
+ prodesc->rsi = rsi;
+ }
/*
* This routine is a crock, and so is everyplace that calls it. The problem
*************** _PG_init(void)
*** 390,396 ****
* pltcl_init_interp() - initialize a new Tcl interpreter
**********************************************************************/
static void
! pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
{
Tcl_Interp *interp;
char interpname[32];
--- 511,517 ----
* pltcl_init_interp() - initialize a new Tcl interpreter
**********************************************************************/
static void
! pltcl_init_interp(pltcl_interp_desc * interp_desc, bool pltrusted)
{
Tcl_Interp *interp;
char interpname[32];
*************** pltcl_init_interp(pltcl_interp_desc *int
*** 414,436 ****
/************************************************************
* Install the commands for SPI support in the interpreter
************************************************************/
! Tcl_CreateCommand(interp, "elog",
! pltcl_elog, NULL, NULL);
! Tcl_CreateCommand(interp, "quote",
! pltcl_quote, NULL, NULL);
! Tcl_CreateCommand(interp, "argisnull",
! pltcl_argisnull, NULL, NULL);
! Tcl_CreateCommand(interp, "return_null",
! pltcl_returnnull, NULL, NULL);
! Tcl_CreateCommand(interp, "spi_exec",
! pltcl_SPI_execute, NULL, NULL);
! Tcl_CreateCommand(interp, "spi_prepare",
! pltcl_SPI_prepare, NULL, NULL);
! Tcl_CreateCommand(interp, "spi_execp",
! pltcl_SPI_execute_plan, NULL, NULL);
! Tcl_CreateCommand(interp, "spi_lastoid",
! pltcl_SPI_lastoid, NULL, NULL);
/************************************************************
* Try to load the unknown procedure from pltcl_modules
--- 535,559 ----
/************************************************************
* Install the commands for SPI support in the interpreter
************************************************************/
! Tcl_CreateObjCommand(interp, "elog",
! pltcl_elog, NULL, NULL);
! Tcl_CreateObjCommand(interp, "quote",
! pltcl_quote, NULL, NULL);
! Tcl_CreateObjCommand(interp, "argisnull",
! pltcl_argisnull, NULL, NULL);
! Tcl_CreateObjCommand(interp, "return_null",
! pltcl_returnnull, NULL, NULL);
! Tcl_CreateObjCommand(interp, "return_next",
! pltcl_returnnext, NULL, NULL);
! Tcl_CreateObjCommand(interp, "spi_exec",
! pltcl_SPI_execute, NULL, NULL);
! Tcl_CreateObjCommand(interp, "spi_prepare",
! pltcl_SPI_prepare, NULL, NULL);
! Tcl_CreateObjCommand(interp, "spi_execp",
! pltcl_SPI_execute_plan, NULL, NULL);
! Tcl_CreateObjCommand(interp, "spi_lastoid",
! pltcl_SPI_lastoid, NULL, NULL);
/************************************************************
* Try to load the unknown procedure from pltcl_modules
*************** pltcl_fetch_interp(bool pltrusted)
*** 471,477 ****
* table pltcl_modules (if it exists)
**********************************************************************/
static void
! pltcl_init_load_unknown(Tcl_Interp *interp)
{
Relation pmrel;
char *pmrelname,
--- 594,600 ----
* table pltcl_modules (if it exists)
**********************************************************************/
static void
! pltcl_init_load_unknown(Tcl_Interp * interp)
{
Relation pmrel;
char *pmrelname,
*************** pltclu_call_handler(PG_FUNCTION_ARGS)
*** 611,618 ****
}
static Datum
! pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
{
Datum retval;
FunctionCallInfo save_fcinfo;
--- 734,745 ----
}
+ /**********************************************************************
+ * pltcl_handler() - Handler for function and trigger calls, for
+ * both trusted and untrusted interpreters.
+ **********************************************************************/
static Datum
! pltcl_handler(FunctionCallInfo fcinfo, bool pltrusted)
{
Datum retval;
FunctionCallInfo save_fcinfo;
*************** pltcl_handler(PG_FUNCTION_ARGS, bool plt
*** 632,642 ****
--- 759,771 ----
*/
if (CALLED_AS_TRIGGER(fcinfo))
{
+ /* invoke the trigger handler */
pltcl_current_fcinfo = NULL;
retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
}
else
{
+ /* invoke the function handler */
pltcl_current_fcinfo = fcinfo;
retval = pltcl_func_handler(fcinfo, pltrusted);
}
*************** pltcl_handler(PG_FUNCTION_ARGS, bool plt
*** 660,671 ****
* pltcl_func_handler() - Handler for regular function calls
**********************************************************************/
static Datum
! pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
{
pltcl_proc_desc *prodesc;
Tcl_Interp *volatile interp;
! Tcl_DString tcl_cmd;
! Tcl_DString list_tmp;
int i;
int tcl_rc;
Datum retval;
--- 789,799 ----
* pltcl_func_handler() - Handler for regular function calls
**********************************************************************/
static Datum
! pltcl_func_handler(FunctionCallInfo fcinfo, bool pltrusted)
{
pltcl_proc_desc *prodesc;
Tcl_Interp *volatile interp;
! Tcl_Obj *tcl_cmd = Tcl_NewObj();
int i;
int tcl_rc;
Datum retval;
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 678,694 ****
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
pltrusted);
pltcl_current_prodesc = prodesc;
-
interp = prodesc->interp_desc->interp;
/************************************************************
* Create the tcl command to call the internal
* proc in the Tcl interpreter
************************************************************/
! Tcl_DStringInit(&tcl_cmd);
! Tcl_DStringInit(&list_tmp);
! Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
/************************************************************
* Add all call arguments to the command
--- 806,828 ----
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
pltrusted);
+ /*
+ * globally store current proc description, this can be redone using
+ * clientdata-type structures and eventually allow threading or something
+ */
pltcl_current_prodesc = prodesc;
interp = prodesc->interp_desc->interp;
+ /* reset essential function runtime to a known state */
+ pltcl_reset_state(prodesc, (ReturnSetInfo *) fcinfo->resultinfo);
+
/************************************************************
* Create the tcl command to call the internal
* proc in the Tcl interpreter
************************************************************/
! tcl_cmd = Tcl_NewObj();
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(prodesc->internal_proname, -1));
/************************************************************
* Add all call arguments to the command
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 703,709 ****
* For tuple values, add a list for 'array set ...'
**************************************************/
if (fcinfo->argnull[i])
! Tcl_DStringAppendElement(&tcl_cmd, "");
else
{
HeapTupleHeader td;
--- 837,843 ----
* For tuple values, add a list for 'array set ...'
**************************************************/
if (fcinfo->argnull[i])
! Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
else
{
HeapTupleHeader td;
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 711,716 ****
--- 845,851 ----
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tmptup;
+ Tcl_Obj *list_tmp;
td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
/* Extract rowtype info and find a tupdesc */
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 721,730 ****
tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
tmptup.t_data = td;
! Tcl_DStringSetLength(&list_tmp, 0);
! pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
! Tcl_DStringAppendElement(&tcl_cmd,
! Tcl_DStringValue(&list_tmp));
ReleaseTupleDesc(tupdesc);
}
}
--- 856,865 ----
tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
tmptup.t_data = td;
! list_tmp = Tcl_NewObj();
! pltcl_build_tuple_argument(&tmptup, tupdesc, list_tmp);
! Tcl_ListObjAppendElement(NULL, tcl_cmd, list_tmp);
! Tcl_DecrRefCount(list_tmp);
ReleaseTupleDesc(tupdesc);
}
}
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 735,741 ****
* of their external representation
**************************************************/
if (fcinfo->argnull[i])
! Tcl_DStringAppendElement(&tcl_cmd, "");
else
{
char *tmp;
--- 870,876 ----
* of their external representation
**************************************************/
if (fcinfo->argnull[i])
! Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
else
{
char *tmp;
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 743,749 ****
tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
fcinfo->arg[i]);
UTF_BEGIN;
! Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
UTF_END;
pfree(tmp);
}
--- 878,885 ----
tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
fcinfo->arg[i]);
UTF_BEGIN;
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(UTF_E2U(tmp), -1));
UTF_END;
pfree(tmp);
}
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 752,771 ****
}
PG_CATCH();
{
! Tcl_DStringFree(&tcl_cmd);
! Tcl_DStringFree(&list_tmp);
PG_RE_THROW();
}
PG_END_TRY();
- Tcl_DStringFree(&list_tmp);
/************************************************************
* Call the Tcl function
*
* We assume no PG error can be thrown directly from this call.
************************************************************/
! tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
! Tcl_DStringFree(&tcl_cmd);
/************************************************************
* Check for errors reported by Tcl.
--- 888,905 ----
}
PG_CATCH();
{
! Tcl_DecrRefCount(tcl_cmd);
PG_RE_THROW();
}
PG_END_TRY();
/************************************************************
* Call the Tcl function
*
* We assume no PG error can be thrown directly from this call.
************************************************************/
! Tcl_IncrRefCount(tcl_cmd);
! tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
/************************************************************
* Check for errors reported by Tcl.
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 773,778 ****
--- 907,918 ----
if (tcl_rc != TCL_OK)
throw_tcl_error(interp, prodesc->user_proname);
+ /*
+ * Don't get rid of tcl_cmd until after throwing the error because with
+ * tcl objects it can be referenced from the error handler
+ */
+ Tcl_DecrRefCount(tcl_cmd);
+
/************************************************************
* Disconnect from SPI manager and then create the return
* value datum (if the input function does a palloc for it
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 790,795 ****
--- 930,1001 ----
NULL,
prodesc->result_typioparam,
-1);
+ else if (prodesc->fn_retisset)
+ {
+ ReturnSetInfo *rsi = prodesc->rsi;
+
+ if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ (rsi->allowedModes & SFRM_Materialize) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+
+ rsi->returnMode = SFRM_Materialize;
+
+ /* If we produced any tuples, send back the result */
+ if (prodesc->tuple_store)
+ {
+ rsi->setResult = prodesc->tuple_store;
+ if (prodesc->ret_tupdesc)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(prodesc->tuple_store_cxt);
+ rsi->setDesc = CreateTupleDescCopy(prodesc->ret_tupdesc);
+ MemoryContextSwitchTo(oldcxt);
+ }
+ }
+ retval = (Datum) 0;
+ fcinfo->isnull = true;
+ }
+ else if (prodesc->fn_retistuple)
+ {
+ TupleDesc td;
+ HeapTuple tup;
+ Tcl_Obj *resultObj;
+ Tcl_Obj **resultObjv;
+ int resultObjc;
+
+ if (get_call_result_type(fcinfo, NULL, &td) != TYPEFUNC_COMPOSITE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("function returning record called in context "
+ "that cannot accept type record")));
+ }
+
+ resultObj = Tcl_GetObjResult(interp);
+ if (Tcl_ListObjGetElements (interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
+ {
+ return TCL_ERROR;
+ }
+
+ if (resultObjc & 1)
+ {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("list must have even number of elements", -1));
+ return TCL_ERROR;
+ }
+
+ Assert(!prodesc->ret_tupdesc);
+ Assert(!prodesc->attinmeta);
+ prodesc->ret_tupdesc = td;
+ prodesc->natts = td->natts;
+ prodesc->attinmeta = TupleDescGetAttInMetadata(prodesc->ret_tupdesc);
+
+ tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc, prodesc);
+ retval = HeapTupleGetDatum(tup);
+ }
else
{
UTF_BEGIN;
*************** pltcl_func_handler(PG_FUNCTION_ARGS, boo
*** 808,814 ****
* pltcl_trigger_handler() - Handler for trigger calls
**********************************************************************/
static HeapTuple
! pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
{
pltcl_proc_desc *prodesc;
Tcl_Interp *volatile interp;
--- 1014,1020 ----
* pltcl_trigger_handler() - Handler for trigger calls
**********************************************************************/
static HeapTuple
! pltcl_trigger_handler(FunctionCallInfo fcinfo, bool pltrusted)
{
pltcl_proc_desc *prodesc;
Tcl_Interp *volatile interp;
*************** pltcl_trigger_handler(PG_FUNCTION_ARGS,
*** 816,824 ****
char *stroid;
TupleDesc tupdesc;
volatile HeapTuple rettup;
! Tcl_DString tcl_cmd;
! Tcl_DString tcl_trigtup;
! Tcl_DString tcl_newtup;
int tcl_rc;
int i;
int *modattrs;
--- 1022,1030 ----
char *stroid;
TupleDesc tupdesc;
volatile HeapTuple rettup;
! Tcl_Obj *tcl_cmd;
! Tcl_Obj *tcl_trigtup;
! Tcl_Obj *tcl_newtup;
int tcl_rc;
int i;
int *modattrs;
*************** pltcl_trigger_handler(PG_FUNCTION_ARGS,
*** 838,911 ****
pltrusted);
pltcl_current_prodesc = prodesc;
-
interp = prodesc->interp_desc->interp;
-
tupdesc = trigdata->tg_relation->rd_att;
/************************************************************
* Create the tcl command to call the internal
* proc in the interpreter
************************************************************/
! Tcl_DStringInit(&tcl_cmd);
! Tcl_DStringInit(&tcl_trigtup);
! Tcl_DStringInit(&tcl_newtup);
PG_TRY();
{
/* The procedure name */
! Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
/* The trigger name for argument TG_name */
! Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname);
/* The oid of the trigger relation for argument TG_relid */
stroid = DatumGetCString(DirectFunctionCall1(oidout,
ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
! Tcl_DStringAppendElement(&tcl_cmd, stroid);
pfree(stroid);
/* The name of the table the trigger is acting on: TG_table_name */
stroid = SPI_getrelname(trigdata->tg_relation);
! Tcl_DStringAppendElement(&tcl_cmd, stroid);
pfree(stroid);
/* The schema of the table the trigger is acting on: TG_table_schema */
stroid = SPI_getnspname(trigdata->tg_relation);
! Tcl_DStringAppendElement(&tcl_cmd, stroid);
pfree(stroid);
/* A list of attribute names for argument TG_relatts */
! Tcl_DStringAppendElement(&tcl_trigtup, "");
for (i = 0; i < tupdesc->natts; i++)
{
if (tupdesc->attrs[i]->attisdropped)
! Tcl_DStringAppendElement(&tcl_trigtup, "");
else
! Tcl_DStringAppendElement(&tcl_trigtup,
! NameStr(tupdesc->attrs[i]->attname));
}
! Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
! Tcl_DStringFree(&tcl_trigtup);
! Tcl_DStringInit(&tcl_trigtup);
/* The when part of the event for TG_when */
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
! Tcl_DStringAppendElement(&tcl_cmd, "BEFORE");
else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
! Tcl_DStringAppendElement(&tcl_cmd, "AFTER");
else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
! Tcl_DStringAppendElement(&tcl_cmd, "INSTEAD OF");
else
elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);
/* The level part of the event for TG_level */
if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
{
! Tcl_DStringAppendElement(&tcl_cmd, "ROW");
/* Build the data list for the trigtuple */
pltcl_build_tuple_argument(trigdata->tg_trigtuple,
! tupdesc, &tcl_trigtup);
/*
* Now the command part of the event for TG_op and data for NEW
--- 1044,1127 ----
pltrusted);
pltcl_current_prodesc = prodesc;
interp = prodesc->interp_desc->interp;
tupdesc = trigdata->tg_relation->rd_att;
+ pltcl_reset_state(prodesc, NULL);
+
/************************************************************
* Create the tcl command to call the internal
* proc in the interpreter
************************************************************/
! tcl_cmd = Tcl_NewObj();
! tcl_trigtup = Tcl_NewObj();
! tcl_newtup = Tcl_NewObj();
PG_TRY();
{
/* The procedure name */
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(prodesc->internal_proname, -1));
/* The trigger name for argument TG_name */
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(trigdata->tg_trigger->tgname, -1));
/* The oid of the trigger relation for argument TG_relid */
+ /* NB don't convert to a string for more performance */
stroid = DatumGetCString(DirectFunctionCall1(oidout,
ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(stroid, -1));
pfree(stroid);
/* The name of the table the trigger is acting on: TG_table_name */
stroid = SPI_getrelname(trigdata->tg_relation);
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(stroid, -1));
pfree(stroid);
/* The schema of the table the trigger is acting on: TG_table_schema */
stroid = SPI_getnspname(trigdata->tg_relation);
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(stroid, -1));
pfree(stroid);
/* A list of attribute names for argument TG_relatts */
! Tcl_ListObjAppendElement(NULL, tcl_trigtup, Tcl_NewObj());
for (i = 0; i < tupdesc->natts; i++)
{
if (tupdesc->attrs[i]->attisdropped)
! Tcl_ListObjAppendElement(NULL, tcl_trigtup, Tcl_NewObj());
else
! Tcl_ListObjAppendElement(NULL, tcl_trigtup,
! Tcl_NewStringObj(NameStr(tupdesc->attrs[i]->attname), -1));
}
! Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
! /* Tcl_DecrRefCount(tcl_trigtup); */
! tcl_trigtup = Tcl_NewObj();
/* The when part of the event for TG_when */
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("BEFORE", -1));
else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("AFTER", -1));
else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("INSTEAD OF", -1));
else
elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);
/* The level part of the event for TG_level */
if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
{
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("ROW", -1));
/* Build the data list for the trigtuple */
pltcl_build_tuple_argument(trigdata->tg_trigtuple,
! tupdesc, tcl_trigtup);
/*
* Now the command part of the event for TG_op and data for NEW
*************** pltcl_trigger_handler(PG_FUNCTION_ARGS,
*** 913,943 ****
*/
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
{
! Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
! Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
! Tcl_DStringAppendElement(&tcl_cmd, "");
rettup = trigdata->tg_trigtuple;
}
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
{
! Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
! Tcl_DStringAppendElement(&tcl_cmd, "");
! Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
rettup = trigdata->tg_trigtuple;
}
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{
! Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
pltcl_build_tuple_argument(trigdata->tg_newtuple,
! tupdesc, &tcl_newtup);
! Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup));
! Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
rettup = trigdata->tg_newtuple;
}
--- 1129,1162 ----
*/
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
{
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("INSERT", -1));
! Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
! Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
rettup = trigdata->tg_trigtuple;
}
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
{
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("DELETE", -1));
! Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
! Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
rettup = trigdata->tg_trigtuple;
}
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("UPDATE", -1));
pltcl_build_tuple_argument(trigdata->tg_newtuple,
! tupdesc, tcl_newtup);
! Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_newtup);
! Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
rettup = trigdata->tg_newtuple;
}
*************** pltcl_trigger_handler(PG_FUNCTION_ARGS,
*** 946,966 ****
}
else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
{
! Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT");
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
! Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
! Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
! Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
! Tcl_DStringAppendElement(&tcl_cmd, "TRUNCATE");
else
elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
! Tcl_DStringAppendElement(&tcl_cmd, "");
! Tcl_DStringAppendElement(&tcl_cmd, "");
rettup = (HeapTuple) NULL;
}
--- 1165,1190 ----
}
else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
{
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("STATEMENT", -1));
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("INSERT", -1));
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("DELETE", -1));
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("UPDATE", -1));
else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj("TRUNCATE", -1));
else
elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
! Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
! Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
rettup = (HeapTuple) NULL;
}
*************** pltcl_trigger_handler(PG_FUNCTION_ARGS,
*** 969,995 ****
/* Finally append the arguments from CREATE TRIGGER */
for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
! Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);
}
PG_CATCH();
{
! Tcl_DStringFree(&tcl_cmd);
! Tcl_DStringFree(&tcl_trigtup);
! Tcl_DStringFree(&tcl_newtup);
PG_RE_THROW();
}
PG_END_TRY();
- Tcl_DStringFree(&tcl_trigtup);
- Tcl_DStringFree(&tcl_newtup);
/************************************************************
* Call the Tcl function
*
* We assume no PG error can be thrown directly from this call.
************************************************************/
! tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
! Tcl_DStringFree(&tcl_cmd);
/************************************************************
* Check for errors reported by Tcl.
--- 1193,1221 ----
/* Finally append the arguments from CREATE TRIGGER */
for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
! Tcl_ListObjAppendElement(NULL, tcl_cmd,
! Tcl_NewStringObj(trigdata->tg_trigger->tgargs[i], -1));
}
PG_CATCH();
{
! Tcl_DecrRefCount(tcl_cmd);
! Tcl_DecrRefCount(tcl_trigtup);
! Tcl_DecrRefCount(tcl_newtup);
PG_RE_THROW();
}
PG_END_TRY();
/************************************************************
* Call the Tcl function
*
* We assume no PG error can be thrown directly from this call.
************************************************************/
! Tcl_IncrRefCount(tcl_cmd);
! tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
! /* Tcl_DecrRefCount(tcl_trigtup); */
! /* Tcl_DecrRefCount(tcl_newtup); */
! Tcl_DecrRefCount(tcl_cmd);
/************************************************************
* Check for errors reported by Tcl.
*************** pltcl_trigger_handler(PG_FUNCTION_ARGS,
*** 1126,1132 ****
* throw_tcl_error - ereport an error returned from the Tcl interpreter
**********************************************************************/
static void
! throw_tcl_error(Tcl_Interp *interp, const char *proname)
{
/*
* Caution is needed here because Tcl_GetVar could overwrite the
--- 1352,1358 ----
* throw_tcl_error - ereport an error returned from the Tcl interpreter
**********************************************************************/
static void
! throw_tcl_error(Tcl_Interp * interp, const char *proname)
{
/*
* Caution is needed here because Tcl_GetVar could overwrite the
*************** throw_tcl_error(Tcl_Interp *interp, cons
*** 1151,1156 ****
--- 1377,1427 ----
UTF_END;
}
+ static void
+ pltcl_init_tuple_store(pltcl_proc_desc * prodesc)
+ {
+ ReturnSetInfo *rsi = prodesc->rsi;
+ MemoryContext oldcxt;
+ ResourceOwner oldowner;
+
+ /*
+ * Check caller can handle a set result in the way we want
+ */
+ if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ (rsi->allowedModes & SFRM_Materialize) == 0 ||
+ rsi->expectedDesc == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+
+ Assert(!prodesc->tuple_store);
+ Assert(!prodesc->attinmeta);
+
+ /*
+ * Switch to the right memory context and resource owner for storing the
+ * tuplestore for return set. If we're within a subtransaction opened for
+ * an exception-block, for example, we must still create the tuplestore in
+ * the resource owner that was active when this function was entered, and
+ * not in the subtransaction resource owner.
+ */
+ prodesc->ret_tupdesc = rsi->expectedDesc;
+ prodesc->natts = prodesc->ret_tupdesc->natts;
+
+ oldcxt = MemoryContextSwitchTo(prodesc->tuple_store_cxt);
+ oldowner = CurrentResourceOwner;
+ CurrentResourceOwner = prodesc->tuple_store_owner;
+
+ prodesc->tuple_store =
+ tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ false, work_mem);
+
+ prodesc->attinmeta = TupleDescGetAttInMetadata(prodesc->ret_tupdesc);
+
+ CurrentResourceOwner = oldowner;
+ MemoryContextSwitchTo(oldcxt);
+
+ }
+
/**********************************************************************
* compile_pltcl_function - compile (or hopefully just look up) function
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1230,1235 ****
--- 1501,1507 ----
Tcl_Interp *interp;
int i;
int tcl_rc;
+ FunctionCallInfo fcinfo = pltcl_current_fcinfo;
/************************************************************
* Build our internal proc name from the function's Oid. Append
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1267,1272 ****
--- 1539,1555 ----
/* And whether it is trusted */
prodesc->lanpltrusted = pltrusted;
+ /* not necessary since MemSet 0 above */
+ prodesc->fn_retistuple = false;
+ prodesc->fn_retisset = false;
+ prodesc->tuple_store_cxt = NULL;
+ prodesc->tuple_store_owner = NULL;
+ prodesc->tuple_store = NULL;
+ prodesc->ret_tupdesc = NULL;
+ prodesc->attinmeta = NULL;
+ prodesc->natts = 0;
+
+
/************************************************************
* Identify the interpreter to use for the function
************************************************************/
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1279,1284 ****
--- 1562,1574 ----
************************************************************/
if (!is_trigger)
{
+ prodesc->rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+ if (prodesc->rsi)
+ {
+ prodesc->tuple_store_cxt = prodesc->rsi->econtext->ecxt_per_query_memory;
+ prodesc->tuple_store_owner = CurrentResourceOwner;
+ }
+
typeTup =
SearchSysCache1(TYPEOID,
ObjectIdGetDatum(procStruct->prorettype));
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1306,1311 ****
--- 1596,1603 ----
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions can only be called as triggers")));
}
+ else if (procStruct->prorettype == RECORDOID)
+ ;
else
{
free(prodesc->user_proname);
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1318,1332 ****
}
}
! if (typeStruct->typtype == TYPTYPE_COMPOSITE)
! {
! free(prodesc->user_proname);
! free(prodesc->internal_proname);
! free(prodesc);
! ereport(ERROR,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("PL/Tcl functions cannot return composite types")));
! }
perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
prodesc->result_typioparam = getTypeIOParam(typeTup);
--- 1610,1618 ----
}
}
! prodesc->fn_retisset = procStruct->proretset;
! prodesc->fn_retistuple = (procStruct->prorettype == RECORDOID ||
! typeStruct->typtype == TYPTYPE_COMPOSITE);
perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
prodesc->result_typioparam = getTypeIOParam(typeTup);
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1398,1403 ****
--- 1684,1693 ----
/************************************************************
* Create the tcl command to define the internal
* procedure
+ *
+ * leave this as DString - it's a text processing function
+ * that only gets invoked when the tcl function is invoked
+ * for the first time
************************************************************/
Tcl_DStringInit(&proc_internal_def);
Tcl_DStringInit(&proc_internal_body);
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1495,1533 ****
* pltcl_elog() - elog() support for PLTcl
**********************************************************************/
static int
! pltcl_elog(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
volatile int level;
MemoryContext oldcontext;
! if (argc != 3)
{
! Tcl_SetResult(interp, "syntax error - 'elog level msg'", TCL_STATIC);
return TCL_ERROR;
}
! if (strcmp(argv[1], "DEBUG") == 0)
! level = DEBUG2;
! else if (strcmp(argv[1], "LOG") == 0)
! level = LOG;
! else if (strcmp(argv[1], "INFO") == 0)
! level = INFO;
! else if (strcmp(argv[1], "NOTICE") == 0)
! level = NOTICE;
! else if (strcmp(argv[1], "WARNING") == 0)
! level = WARNING;
! else if (strcmp(argv[1], "ERROR") == 0)
! level = ERROR;
! else if (strcmp(argv[1], "FATAL") == 0)
! level = FATAL;
! else
{
- Tcl_AppendResult(interp, "Unknown elog level '", argv[1],
- "'", NULL);
return TCL_ERROR;
}
if (level == ERROR)
{
/*
--- 1785,1851 ----
* pltcl_elog() - elog() support for PLTcl
**********************************************************************/
static int
! pltcl_elog(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
volatile int level;
MemoryContext oldcontext;
+ int priIndex;
! enum logpriority
{
! LOG_DEBUG, LOG_LOG, LOG_INFO, LOG_NOTICE,
! LOG_WARNING, LOG_ERROR, LOG_FATAL
! };
!
! static CONST84 char *logpriorities[] = {
! "DEBUG", "LOG", "INFO", "NOTICE",
! "WARNING", "ERROR", "FATAL", (char *) NULL
! };
!
! if (objc != 3)
! {
! Tcl_WrongNumArgs(interp, 1, objv, "level msg");
return TCL_ERROR;
}
! if (Tcl_GetIndexFromObj(interp, objv[1], logpriorities, "priority",
! TCL_EXACT, &priIndex) != TCL_OK)
{
return TCL_ERROR;
}
+ switch ((enum logpriority) priIndex)
+ {
+ case LOG_DEBUG:
+ level = DEBUG2;
+ break;
+
+ case LOG_LOG:
+ level = LOG;
+ break;
+
+ case LOG_INFO:
+ level = INFO;
+ break;
+
+ case LOG_NOTICE:
+ level = NOTICE;
+ break;
+
+ case LOG_WARNING:
+ level = WARNING;
+ break;
+
+ case LOG_ERROR:
+ level = ERROR;
+ break;
+
+ case LOG_FATAL:
+ level = FATAL;
+ break;
+ }
+
if (level == ERROR)
{
/*
*************** pltcl_elog(ClientData cdata, Tcl_Interp
*** 1535,1541 ****
* eventually get converted to a PG error when we reach the call
* handler.
*/
! Tcl_SetResult(interp, (char *) argv[2], TCL_VOLATILE);
return TCL_ERROR;
}
--- 1853,1859 ----
* eventually get converted to a PG error when we reach the call
* handler.
*/
! Tcl_SetObjResult(interp, objv[2]);
return TCL_ERROR;
}
*************** pltcl_elog(ClientData cdata, Tcl_Interp
*** 1552,1558 ****
PG_TRY();
{
UTF_BEGIN;
! elog(level, "%s", UTF_U2E(argv[2]));
UTF_END;
}
PG_CATCH();
--- 1870,1876 ----
PG_TRY();
{
UTF_BEGIN;
! elog(level, "%s", UTF_U2E(Tcl_GetString(objv[2])));
UTF_END;
}
PG_CATCH();
*************** pltcl_elog(ClientData cdata, Tcl_Interp
*** 1566,1572 ****
/* Pass the error message to Tcl */
UTF_BEGIN;
! Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
UTF_END;
FreeErrorData(edata);
--- 1884,1890 ----
/* Pass the error message to Tcl */
UTF_BEGIN;
! Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
UTF_END;
FreeErrorData(edata);
*************** pltcl_elog(ClientData cdata, Tcl_Interp
*** 1583,1590 ****
* be used in SPI_execute query strings
**********************************************************************/
static int
! pltcl_quote(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
char *tmp;
const char *cp1;
--- 1901,1908 ----
* be used in SPI_execute query strings
**********************************************************************/
static int
! pltcl_quote(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
char *tmp;
const char *cp1;
*************** pltcl_quote(ClientData cdata, Tcl_Interp
*** 1593,1601 ****
/************************************************************
* Check call syntax
************************************************************/
! if (argc != 2)
{
! Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_STATIC);
return TCL_ERROR;
}
--- 1911,1919 ----
/************************************************************
* Check call syntax
************************************************************/
! if (objc != 2)
{
! Tcl_WrongNumArgs(interp, 1, objv, "string");
return TCL_ERROR;
}
*************** pltcl_quote(ClientData cdata, Tcl_Interp
*** 1603,1610 ****
* Allocate space for the maximum the string can
* grow to and initialize pointers
************************************************************/
! tmp = palloc(strlen(argv[1]) * 2 + 1);
! cp1 = argv[1];
cp2 = tmp;
/************************************************************
--- 1921,1928 ----
* Allocate space for the maximum the string can
* grow to and initialize pointers
************************************************************/
! tmp = palloc(strlen(Tcl_GetString(objv[1])) * 2 + 1);
! cp1 = Tcl_GetString(objv[1]);
cp2 = tmp;
/************************************************************
*************** pltcl_quote(ClientData cdata, Tcl_Interp
*** 1626,1632 ****
* Terminate the string and set it as result
************************************************************/
*cp2 = '\0';
! Tcl_SetResult(interp, tmp, TCL_VOLATILE);
pfree(tmp);
return TCL_OK;
}
--- 1944,1950 ----
* Terminate the string and set it as result
************************************************************/
*cp2 = '\0';
! Tcl_SetObjResult(interp, Tcl_NewStringObj(tmp, -1));
pfree(tmp);
return TCL_OK;
}
*************** pltcl_quote(ClientData cdata, Tcl_Interp
*** 1636,1643 ****
* pltcl_argisnull() - determine if a specific argument is NULL
**********************************************************************/
static int
! pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
int argno;
FunctionCallInfo fcinfo = pltcl_current_fcinfo;
--- 1954,1961 ----
* pltcl_argisnull() - determine if a specific argument is NULL
**********************************************************************/
static int
! pltcl_argisnull(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
int argno;
FunctionCallInfo fcinfo = pltcl_current_fcinfo;
*************** pltcl_argisnull(ClientData cdata, Tcl_In
*** 1645,1654 ****
/************************************************************
* Check call syntax
************************************************************/
! if (argc != 2)
{
! Tcl_SetResult(interp, "syntax error - 'argisnull argno'",
! TCL_STATIC);
return TCL_ERROR;
}
--- 1963,1971 ----
/************************************************************
* Check call syntax
************************************************************/
! if (objc != 2)
{
! Tcl_WrongNumArgs(interp, 1, objv, "argno");
return TCL_ERROR;
}
*************** pltcl_argisnull(ClientData cdata, Tcl_In
*** 1657,1671 ****
************************************************************/
if (fcinfo == NULL)
{
! Tcl_SetResult(interp, "argisnull cannot be used in triggers",
! TCL_STATIC);
return TCL_ERROR;
}
/************************************************************
* Get the argument number
************************************************************/
! if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
return TCL_ERROR;
/************************************************************
--- 1974,1988 ----
************************************************************/
if (fcinfo == NULL)
{
! Tcl_SetObjResult(interp,
! Tcl_NewStringObj("argisnull cannot be used in triggers", -1));
return TCL_ERROR;
}
/************************************************************
* Get the argument number
************************************************************/
! if (Tcl_GetIntFromObj(interp, objv[1], &argno) != TCL_OK)
return TCL_ERROR;
/************************************************************
*************** pltcl_argisnull(ClientData cdata, Tcl_In
*** 1674,1691 ****
argno--;
if (argno < 0 || argno >= fcinfo->nargs)
{
! Tcl_SetResult(interp, "argno out of range", TCL_STATIC);
return TCL_ERROR;
}
/************************************************************
* Get the requested NULL state
************************************************************/
! if (PG_ARGISNULL(argno))
! Tcl_SetResult(interp, "1", TCL_STATIC);
! else
! Tcl_SetResult(interp, "0", TCL_STATIC);
!
return TCL_OK;
}
--- 1991,2005 ----
argno--;
if (argno < 0 || argno >= fcinfo->nargs)
{
! Tcl_SetObjResult(interp,
! Tcl_NewStringObj("argno out of range", -1));
return TCL_ERROR;
}
/************************************************************
* Get the requested NULL state
************************************************************/
! Tcl_SetObjResult(interp, Tcl_NewBooleanObj(PG_ARGISNULL(argno)));
return TCL_OK;
}
*************** pltcl_argisnull(ClientData cdata, Tcl_In
*** 1694,1710 ****
* pltcl_returnnull() - Cause a NULL return from a function
**********************************************************************/
static int
! pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
FunctionCallInfo fcinfo = pltcl_current_fcinfo;
/************************************************************
* Check call syntax
************************************************************/
! if (argc != 1)
{
! Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_STATIC);
return TCL_ERROR;
}
--- 2008,2024 ----
* pltcl_returnnull() - Cause a NULL return from a function
**********************************************************************/
static int
! pltcl_returnnull(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
FunctionCallInfo fcinfo = pltcl_current_fcinfo;
/************************************************************
* Check call syntax
************************************************************/
! if (objc != 1)
{
! Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
*************** pltcl_returnnull(ClientData cdata, Tcl_I
*** 1713,1720 ****
************************************************************/
if (fcinfo == NULL)
{
! Tcl_SetResult(interp, "return_null cannot be used in triggers",
! TCL_STATIC);
return TCL_ERROR;
}
--- 2027,2034 ----
************************************************************/
if (fcinfo == NULL)
{
! Tcl_SetObjResult(interp,
! Tcl_NewStringObj("return_null cannot be used in triggers", -1));
return TCL_ERROR;
}
*************** pltcl_returnnull(ClientData cdata, Tcl_I
*** 1727,1732 ****
--- 2041,2134 ----
return TCL_RETURN;
}
+ /**********************************************************************
+ * pltcl_pg_returnnext() - Queue a row of Tcl key-value pairs into the
+ * function's tuple_store
+ **********************************************************************/
+ static void
+ pltcl_pg_returnnext(Tcl_Interp * interp, int rowObjc, Tcl_Obj ** rowObjv)
+ {
+ pltcl_proc_desc *prodesc = pltcl_current_prodesc;
+
+ if (!prodesc->fn_retisset)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("cannot use return_next in a non-SETOF function")));
+
+ if (prodesc->tuple_store == NULL)
+ pltcl_init_tuple_store(prodesc);
+
+ if (prodesc->fn_retistuple)
+ {
+ HeapTuple tuple;
+
+ tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc, prodesc);
+ tuplestore_puttuple(prodesc->tuple_store, tuple);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unprepared for non-retistuple state at this point")));
+ }
+ }
+
+ /**********************************************************************
+ * pltcl_returnnext() - Tcl-callable command take a list of key-value
+ * pairs and store in the tuple_store
+ * for sending as a result when the
+ * function is complete.
+ **********************************************************************/
+ static int
+ pltcl_returnnext(ClientData cdata, Tcl_Interp * interp,
+ int objc, Tcl_Obj * const objv[])
+ {
+ FunctionCallInfo fcinfo = pltcl_current_fcinfo;
+ Tcl_Obj **rowObjv;
+ int rowObjc;
+ pltcl_proc_desc *prodesc = pltcl_current_prodesc;
+
+ /************************************************************
+ * Check call syntax
+ ************************************************************/
+ if (objc != 2)
+ {
+ Tcl_WrongNumArgs(interp, 1, objv, "list");
+ return TCL_ERROR;
+ }
+
+ /************************************************************
+ * Check that we're called as a normal function
+ ************************************************************/
+ if (fcinfo == NULL)
+ {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("return_next cannot be used in triggers", -1));
+ return TCL_ERROR;
+ }
+
+ if (!prodesc->fn_retisset)
+ {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("cannot use return_next in a non-SETOF function", -1));
+ return TCL_ERROR;
+ }
+
+ if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
+ {
+ return TCL_ERROR;
+ }
+
+ if (rowObjc & 1)
+ {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("list must have even number of elements", -1));
+ return TCL_ERROR;
+ }
+
+ pltcl_pg_returnnext(interp, rowObjc, rowObjv);
+ return TCL_OK;
+ }
/*----------
* Support for running SPI operations inside subtransactions
*************** pltcl_subtrans_commit(MemoryContext oldc
*** 1777,1783 ****
}
static void
! pltcl_subtrans_abort(Tcl_Interp *interp,
MemoryContext oldcontext, ResourceOwner oldowner)
{
ErrorData *edata;
--- 2179,2185 ----
}
static void
! pltcl_subtrans_abort(Tcl_Interp * interp,
MemoryContext oldcontext, ResourceOwner oldowner)
{
ErrorData *edata;
*************** pltcl_subtrans_abort(Tcl_Interp *interp,
*** 1812,1830 ****
* for the Tcl interpreter
**********************************************************************/
static int
! pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
int my_rc;
int spi_rc;
int query_idx;
int i;
int count = 0;
CONST84 char *volatile arrayname = NULL;
! CONST84 char *volatile loop_body = NULL;
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
char *usage = "syntax error - 'SPI_exec "
"?-count n? "
"?-array name? query ?loop body?";
--- 2214,2242 ----
* for the Tcl interpreter
**********************************************************************/
static int
! pltcl_SPI_execute(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
int my_rc;
int spi_rc;
int query_idx;
int i;
+ int optIndex;
int count = 0;
CONST84 char *volatile arrayname = NULL;
! Tcl_Obj *loop_body = NULL;
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
+ enum options
+ {
+ OPT_ARRAY, OPT_COUNT
+ };
+
+ static CONST84 char *options[] = {
+ "-array", "-count", (char *) NULL
+ };
+
char *usage = "syntax error - 'SPI_exec "
"?-count n? "
"?-array name? query ?loop body?";
*************** pltcl_SPI_execute(ClientData cdata, Tcl_
*** 1832,1880 ****
/************************************************************
* Check the call syntax and get the options
************************************************************/
! if (argc < 2)
{
Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
i = 1;
! while (i < argc)
{
! if (strcmp(argv[i], "-array") == 0)
{
! if (++i >= argc)
! {
! Tcl_SetResult(interp, usage, TCL_STATIC);
! return TCL_ERROR;
! }
! arrayname = argv[i++];
! continue;
}
! if (strcmp(argv[i], "-count") == 0)
{
! if (++i >= argc)
! {
! Tcl_SetResult(interp, usage, TCL_STATIC);
! return TCL_ERROR;
! }
! if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
! return TCL_ERROR;
! continue;
}
! break;
}
query_idx = i;
! if (query_idx >= argc || query_idx + 2 < argc)
{
Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
! if (query_idx + 1 < argc)
! loop_body = argv[query_idx + 1];
/************************************************************
* Execute the query inside a sub-transaction, so we can cope with
--- 2244,2296 ----
/************************************************************
* Check the call syntax and get the options
************************************************************/
! if (objc < 2)
{
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "?-count n? ?-array name? query ?loop body?");
Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
i = 1;
! while (i < objc)
{
! if (Tcl_GetIndexFromObj(interp, objv[i], options, "option",
! TCL_EXACT, &optIndex) != TCL_OK)
{
! return TCL_ERROR;
}
! if (++i >= objc)
{
! Tcl_SetObjResult(interp,
! Tcl_NewStringObj("missing argument to -count or -array", -1));
! return TCL_ERROR;
}
! switch ((enum options) optIndex)
! {
! case OPT_ARRAY:
! arrayname = Tcl_GetString(objv[i++]);
! break;
!
! case OPT_COUNT:
! if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
! return TCL_ERROR;
! break;
! }
}
query_idx = i;
! if (query_idx >= objc || query_idx + 2 < objc)
{
Tcl_SetResult(interp, usage, TCL_STATIC);
+ Tcl_WrongNumArgs(interp, query_idx - 1, objv, "query ?loop body?");
return TCL_ERROR;
}
!
! if (query_idx + 1 < objc)
! loop_body = objv[query_idx + 1];
/************************************************************
* Execute the query inside a sub-transaction, so we can cope with
*************** pltcl_SPI_execute(ClientData cdata, Tcl_
*** 1886,1892 ****
PG_TRY();
{
UTF_BEGIN;
! spi_rc = SPI_execute(UTF_U2E(argv[query_idx]),
pltcl_current_prodesc->fn_readonly, count);
UTF_END;
--- 2302,2308 ----
PG_TRY();
{
UTF_BEGIN;
! spi_rc = SPI_execute(UTF_U2E(Tcl_GetString(objv[query_idx])),
pltcl_current_prodesc->fn_readonly, count);
UTF_END;
*************** pltcl_SPI_execute(ClientData cdata, Tcl_
*** 1915,1929 ****
* Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan
*/
static int
! pltcl_process_SPI_result(Tcl_Interp *interp,
CONST84 char *arrayname,
! CONST84 char *loop_body,
int spi_rc,
! SPITupleTable *tuptable,
int ntuples)
{
int my_rc = TCL_OK;
- char buf[64];
int i;
int loop_rc;
HeapTuple *tuples;
--- 2331,2344 ----
* Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan
*/
static int
! pltcl_process_SPI_result(Tcl_Interp * interp,
CONST84 char *arrayname,
! Tcl_Obj * loop_body,
int spi_rc,
! SPITupleTable * tuptable,
int ntuples)
{
int my_rc = TCL_OK;
int i;
int loop_rc;
HeapTuple *tuples;
*************** pltcl_process_SPI_result(Tcl_Interp *int
*** 1935,1949 ****
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
! snprintf(buf, sizeof(buf), "%d", ntuples);
! Tcl_SetResult(interp, buf, TCL_VOLATILE);
break;
case SPI_OK_UTILITY:
case SPI_OK_REWRITTEN:
if (tuptable == NULL)
{
! Tcl_SetResult(interp, "0", TCL_STATIC);
break;
}
/* FALL THRU for utility returning tuples */
--- 2350,2363 ----
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
! Tcl_SetObjResult(interp, Tcl_NewIntObj(ntuples));
break;
case SPI_OK_UTILITY:
case SPI_OK_REWRITTEN:
if (tuptable == NULL)
{
! Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
break;
}
/* FALL THRU for utility returning tuples */
*************** pltcl_process_SPI_result(Tcl_Interp *int
*** 1980,1986 ****
pltcl_set_tuple_values(interp, arrayname, i,
tuples[i], tupdesc);
! loop_rc = Tcl_Eval(interp, loop_body);
if (loop_rc == TCL_OK)
continue;
--- 2394,2400 ----
pltcl_set_tuple_values(interp, arrayname, i,
tuples[i], tupdesc);
! loop_rc = Tcl_EvalObjEx(interp, loop_body, 0);
if (loop_rc == TCL_OK)
continue;
*************** pltcl_process_SPI_result(Tcl_Interp *int
*** 2000,2007 ****
if (my_rc == TCL_OK)
{
! snprintf(buf, sizeof(buf), "%d", ntuples);
! Tcl_SetResult(interp, buf, TCL_VOLATILE);
}
break;
--- 2414,2420 ----
if (my_rc == TCL_OK)
{
! Tcl_SetObjResult(interp, Tcl_NewIntObj(ntuples));
}
break;
*************** pltcl_process_SPI_result(Tcl_Interp *int
*** 2027,2037 ****
* and not save the plan currently.
**********************************************************************/
static int
! pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
int nargs;
! CONST84 char **args;
pltcl_query_desc *qdesc;
void *plan;
int i;
--- 2440,2450 ----
* and not save the plan currently.
**********************************************************************/
static int
! pltcl_SPI_prepare(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
int nargs;
! Tcl_Obj **argsObj;
pltcl_query_desc *qdesc;
void *plan;
int i;
*************** pltcl_SPI_prepare(ClientData cdata, Tcl_
*** 2044,2060 ****
/************************************************************
* Check the call syntax
************************************************************/
! if (argc != 3)
{
! Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
! TCL_STATIC);
return TCL_ERROR;
}
/************************************************************
* Split the argument type list
************************************************************/
! if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK)
return TCL_ERROR;
/************************************************************
--- 2457,2472 ----
/************************************************************
* Check the call syntax
************************************************************/
! if (objc != 3)
{
! Tcl_WrongNumArgs(interp, 1, objv, "query argtypes");
return TCL_ERROR;
}
/************************************************************
* Split the argument type list
************************************************************/
! if (Tcl_ListObjGetElements(interp, objv[2], &nargs, &argsObj) != TCL_OK)
return TCL_ERROR;
/************************************************************
*************** pltcl_SPI_prepare(ClientData cdata, Tcl_
*** 2088,2094 ****
typIOParam;
int32 typmod;
! parseTypeString(args[i], &typId, &typmod);
getTypeInputInfo(typId, &typInput, &typIOParam);
--- 2500,2506 ----
typIOParam;
int32 typmod;
! parseTypeString(Tcl_GetString(argsObj[i]), &typId, &typmod);
getTypeInputInfo(typId, &typInput, &typIOParam);
*************** pltcl_SPI_prepare(ClientData cdata, Tcl_
*** 2101,2107 ****
* Prepare the plan and check for errors
************************************************************/
UTF_BEGIN;
! plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes);
UTF_END;
if (plan == NULL)
--- 2513,2519 ----
* Prepare the plan and check for errors
************************************************************/
UTF_BEGIN;
! plan = SPI_prepare(UTF_U2E(Tcl_GetString(argsObj[1])), nargs, qdesc->argtypes);
UTF_END;
if (plan == NULL)
*************** pltcl_SPI_prepare(ClientData cdata, Tcl_
*** 2128,2134 ****
free(qdesc->arginfuncs);
free(qdesc->argtypioparams);
free(qdesc);
- ckfree((char *) args);
return TCL_ERROR;
}
--- 2540,2545 ----
*************** pltcl_SPI_prepare(ClientData cdata, Tcl_
*** 2143,2152 ****
hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
Tcl_SetHashValue(hashent, (ClientData) qdesc);
- ckfree((char *) args);
-
/* qname is ASCII, so no need for encoding conversion */
! Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
return TCL_OK;
}
--- 2554,2561 ----
hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
Tcl_SetHashValue(hashent, (ClientData) qdesc);
/* qname is ASCII, so no need for encoding conversion */
! Tcl_SetObjResult(interp, Tcl_NewStringObj(qdesc->qname, -1));
return TCL_OK;
}
*************** pltcl_SPI_prepare(ClientData cdata, Tcl_
*** 2155,2172 ****
* pltcl_SPI_execute_plan() - Execute a prepared plan
**********************************************************************/
static int
! pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
int my_rc;
int spi_rc;
int i;
int j;
Tcl_HashEntry *hashent;
pltcl_query_desc *qdesc;
const char *volatile nulls = NULL;
CONST84 char *volatile arrayname = NULL;
! CONST84 char *volatile loop_body = NULL;
int count = 0;
int callnargs;
CONST84 char **callargs = NULL;
--- 2564,2582 ----
* pltcl_SPI_execute_plan() - Execute a prepared plan
**********************************************************************/
static int
! pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
int my_rc;
int spi_rc;
int i;
int j;
+ int optIndex;
Tcl_HashEntry *hashent;
pltcl_query_desc *qdesc;
const char *volatile nulls = NULL;
CONST84 char *volatile arrayname = NULL;
! Tcl_Obj *loop_body = NULL;
int count = 0;
int callnargs;
CONST84 char **callargs = NULL;
*************** pltcl_SPI_execute_plan(ClientData cdata,
*** 2175,2180 ****
--- 2585,2599 ----
ResourceOwner oldowner = CurrentResourceOwner;
Tcl_HashTable *query_hash;
+ enum options
+ {
+ OPT_ARRAY, OPT_COUNT, OPT_NULLS
+ };
+
+ static CONST84 char *options[] = {
+ "-array", "-count", "-nulls", (char *) NULL
+ };
+
char *usage = "syntax error - 'SPI_execp "
"?-nulls string? ?-count n? "
"?-array name? query ?args? ?loop body?";
*************** pltcl_SPI_execute_plan(ClientData cdata,
*** 2183,2240 ****
* Get the options and check syntax
************************************************************/
i = 1;
! while (i < argc)
{
! if (strcmp(argv[i], "-array") == 0)
{
! if (++i >= argc)
! {
! Tcl_SetResult(interp, usage, TCL_STATIC);
! return TCL_ERROR;
! }
! arrayname = argv[i++];
! continue;
}
! if (strcmp(argv[i], "-nulls") == 0)
{
! if (++i >= argc)
! {
! Tcl_SetResult(interp, usage, TCL_STATIC);
! return TCL_ERROR;
! }
! nulls = argv[i++];
! continue;
}
! if (strcmp(argv[i], "-count") == 0)
{
! if (++i >= argc)
! {
! Tcl_SetResult(interp, usage, TCL_STATIC);
! return TCL_ERROR;
! }
! if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
! return TCL_ERROR;
! continue;
! }
! break;
}
/************************************************************
* Get the prepared plan descriptor by its key
************************************************************/
! if (i >= argc)
{
! Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
! hashent = Tcl_FindHashEntry(query_hash, argv[i]);
if (hashent == NULL)
{
! Tcl_AppendResult(interp, "invalid queryid '", argv[i], "'", NULL);
return TCL_ERROR;
}
qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
--- 2602,2655 ----
* Get the options and check syntax
************************************************************/
i = 1;
! while (i < objc)
{
! if (Tcl_GetIndexFromObj(interp, objv[i], options, "option",
! TCL_EXACT, &optIndex) != TCL_OK)
{
! return TCL_ERROR;
}
!
! if (++i >= objc)
{
! Tcl_SetObjResult(interp,
! Tcl_NewStringObj("missing argument to -count or -array", -1));
! return TCL_ERROR;
}
!
! switch ((enum options) optIndex)
{
! case OPT_ARRAY:
! arrayname = Tcl_GetString(objv[i++]);
! break;
! case OPT_COUNT:
! if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
! return TCL_ERROR;
! break;
!
! case OPT_NULLS:
! nulls = Tcl_GetString(objv[i++]);
! break;
! }
}
/************************************************************
* Get the prepared plan descriptor by its key
************************************************************/
! if (i >= objc)
{
! Tcl_SetObjResult(interp,
! Tcl_NewStringObj("missing argument to -count or -array", -1));
return TCL_ERROR;
}
query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
! hashent = Tcl_FindHashEntry(query_hash, Tcl_GetString(objv[i]));
if (hashent == NULL)
{
! Tcl_AppendResult(interp, "invalid queryid '", Tcl_GetString(objv[i]), "'", NULL);
return TCL_ERROR;
}
qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
*************** pltcl_SPI_execute_plan(ClientData cdata,
*** 2260,2266 ****
************************************************************/
if (qdesc->nargs > 0)
{
! if (i >= argc)
{
Tcl_SetResult(interp, "missing argument list", TCL_STATIC);
return TCL_ERROR;
--- 2675,2681 ----
************************************************************/
if (qdesc->nargs > 0)
{
! if (i >= objc)
{
Tcl_SetResult(interp, "missing argument list", TCL_STATIC);
return TCL_ERROR;
*************** pltcl_SPI_execute_plan(ClientData cdata,
*** 2269,2275 ****
/************************************************************
* Split the argument values
************************************************************/
! if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK)
return TCL_ERROR;
/************************************************************
--- 2684,2690 ----
/************************************************************
* Split the argument values
************************************************************/
! if (Tcl_SplitList(interp, Tcl_GetString(objv[i++]), &callnargs, &callargs) != TCL_OK)
return TCL_ERROR;
/************************************************************
*************** pltcl_SPI_execute_plan(ClientData cdata,
*** 2290,2299 ****
/************************************************************
* Get loop body if present
************************************************************/
! if (i < argc)
! loop_body = argv[i++];
! if (i != argc)
{
Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
--- 2705,2714 ----
/************************************************************
* Get loop body if present
************************************************************/
! if (i < objc)
! loop_body = objv[i++];
! if (i != objc)
{
Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
*************** pltcl_SPI_execute_plan(ClientData cdata,
*** 2373,2385 ****
* be used after insert queries
**********************************************************************/
static int
! pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
! int argc, CONST84 char *argv[])
{
! char buf[64];
!
! snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
! Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
--- 2788,2797 ----
* be used after insert queries
**********************************************************************/
static int
! pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp * interp,
! int objc, Tcl_Obj * const objv[])
{
! Tcl_SetObjResult(interp, Tcl_NewWideIntObj(SPI_lastoid));
return TCL_OK;
}
*************** pltcl_SPI_lastoid(ClientData cdata, Tcl_
*** 2389,2400 ****
* of a given tuple
**********************************************************************/
static void
! pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
int tupno, HeapTuple tuple, TupleDesc tupdesc)
{
int i;
char *outputstr;
- char buf[64];
Datum attr;
bool isnull;
--- 2801,2811 ----
* of a given tuple
**********************************************************************/
static void
! pltcl_set_tuple_values(Tcl_Interp * interp, CONST84 char *arrayname,
int tupno, HeapTuple tuple, TupleDesc tupdesc)
{
int i;
char *outputstr;
Datum attr;
bool isnull;
*************** pltcl_set_tuple_values(Tcl_Interp *inter
*** 2419,2426 ****
{
arrptr = &arrayname;
nameptr = &attname;
! snprintf(buf, sizeof(buf), "%d", tupno);
! Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
}
for (i = 0; i < tupdesc->natts; i++)
--- 2830,2836 ----
{
arrptr = &arrayname;
nameptr = &attname;
! Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewIntObj(tupno), 0);
}
for (i = 0; i < tupdesc->natts; i++)
*************** pltcl_set_tuple_values(Tcl_Interp *inter
*** 2464,2470 ****
{
outputstr = OidOutputFunctionCall(typoutput, attr);
UTF_BEGIN;
! Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
UTF_END;
pfree(outputstr);
}
--- 2874,2881 ----
{
outputstr = OidOutputFunctionCall(typoutput, attr);
UTF_BEGIN;
! Tcl_SetVar2Ex(interp, *arrptr, *nameptr,
! Tcl_NewStringObj(UTF_E2U(outputstr), -1), 0);
UTF_END;
pfree(outputstr);
}
*************** pltcl_set_tuple_values(Tcl_Interp *inter
*** 2480,2486 ****
**********************************************************************/
static void
pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
! Tcl_DString *retval)
{
int i;
char *outputstr;
--- 2891,2897 ----
**********************************************************************/
static void
pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
! Tcl_Obj * retobj)
{
int i;
char *outputstr;
*************** pltcl_build_tuple_argument(HeapTuple tup
*** 2531,2539 ****
if (!isnull && OidIsValid(typoutput))
{
outputstr = OidOutputFunctionCall(typoutput, attr);
! Tcl_DStringAppendElement(retval, attname);
UTF_BEGIN;
! Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));
UTF_END;
pfree(outputstr);
}
--- 2942,2951 ----
if (!isnull && OidIsValid(typoutput))
{
outputstr = OidOutputFunctionCall(typoutput, attr);
! Tcl_ListObjAppendElement(NULL, retobj,
! Tcl_NewStringObj(attname, -1));
UTF_BEGIN;
! Tcl_ListObjAppendElement(NULL, retobj, Tcl_NewStringObj(UTF_E2U(outputstr), -1));
UTF_END;
pfree(outputstr);
}