mirror of
https://github.com/postgres/postgres.git
synced 2025-08-05 07:41:25 +03:00
Added untrusted PL/TclU (pltclu) language. Executes all procedures
in a non-safe interpreter, so with full OS access! Language is restricted to be used by DB superusers. Added "argisnull n" and "return_null" commands to gain full control over NULL values from new FMGR capabilities. Jan
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# IDENTIFICATION
|
||||||
# $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createlang.sh,v 1.12 2000/05/28 17:56:08 tgl Exp $
|
# $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createlang.sh,v 1.13 2000/07/19 11:53:02 wieck Exp $
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -180,18 +180,29 @@ fi
|
|||||||
# ----------
|
# ----------
|
||||||
case "$langname" in
|
case "$langname" in
|
||||||
plpgsql)
|
plpgsql)
|
||||||
lancomp="PL/pgSQL"
|
lancomp="PL/pgSQL"
|
||||||
trusted="TRUSTED "
|
trusted="TRUSTED "
|
||||||
handler="plpgsql_call_handler"
|
handler="plpgsql_call_handler"
|
||||||
;;
|
object="plpgsql"
|
||||||
|
;;
|
||||||
pltcl)
|
pltcl)
|
||||||
lancomp="PL/Tcl"
|
lancomp="PL/Tcl"
|
||||||
trusted="TRUSTED "
|
trusted="TRUSTED "
|
||||||
handler="pltcl_call_handler";;
|
handler="pltcl_call_handler"
|
||||||
|
object="pltcl"
|
||||||
|
;;
|
||||||
|
pltclu)
|
||||||
|
lancomp="PL/Tcl (untrusted)"
|
||||||
|
trusted=""
|
||||||
|
handler="pltclu_call_handler"
|
||||||
|
object="pltcl"
|
||||||
|
;;
|
||||||
plperl)
|
plperl)
|
||||||
lancomp="PL/Perl"
|
lancomp="PL/Perl"
|
||||||
trusted="TRUSTED "
|
trusted="TRUSTED "
|
||||||
handler="plperl_call_handler";;
|
handler="plperl_call_handler"
|
||||||
|
object="plperl"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "$CMDNAME: unsupported language '$langname'"
|
echo "$CMDNAME: unsupported language '$langname'"
|
||||||
echo "Supported languages are 'plpgsql', 'pltcl', and 'plperl'."
|
echo "Supported languages are 'plpgsql', 'pltcl', and 'plperl'."
|
||||||
@@ -204,7 +215,7 @@ esac
|
|||||||
# Check that the shared object for the call handler is installed
|
# Check that the shared object for the call handler is installed
|
||||||
# in PGLIB
|
# in PGLIB
|
||||||
# ----------
|
# ----------
|
||||||
if [ ! -f $PGLIB/${langname}__DLSUFFIX__ ]; then
|
if [ ! -f $PGLIB/${object}__DLSUFFIX__ ]; then
|
||||||
echo "$CMDNAME: cannot find the file $PGLIB/${langname}__DLSUFFIX__"
|
echo "$CMDNAME: cannot find the file $PGLIB/${langname}__DLSUFFIX__"
|
||||||
echo ""
|
echo ""
|
||||||
echo "This file contains the call handler for $lancomp. By default,"
|
echo "This file contains the call handler for $lancomp. By default,"
|
||||||
@@ -244,7 +255,7 @@ fi
|
|||||||
# ----------
|
# ----------
|
||||||
# Create the call handler and the language
|
# Create the call handler and the language
|
||||||
# ----------
|
# ----------
|
||||||
$PSQL "CREATE FUNCTION $handler () RETURNS OPAQUE AS '$PGLIB/${langname}__DLSUFFIX__' LANGUAGE 'newC'"
|
$PSQL "CREATE FUNCTION $handler () RETURNS OPAQUE AS '$PGLIB/${object}__DLSUFFIX__' LANGUAGE 'newC'"
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "$CMDNAME: language installation failed"
|
echo "$CMDNAME: language installation failed"
|
||||||
exit 1
|
exit 1
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* pltcl.c - PostgreSQL support for Tcl as
|
* pltcl.c - PostgreSQL support for Tcl as
|
||||||
* procedural language (PL)
|
* procedural language (PL)
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
* ENHANCEMENTS, OR MODIFICATIONS.
|
* ENHANCEMENTS, OR MODIFICATIONS.
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.27 2000/07/05 23:12:03 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.28 2000/07/19 11:53:02 wieck Exp $
|
||||||
*
|
*
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@
|
|||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "catalog/pg_language.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@
|
|||||||
typedef struct pltcl_proc_desc
|
typedef struct pltcl_proc_desc
|
||||||
{
|
{
|
||||||
char *proname;
|
char *proname;
|
||||||
|
bool lanpltrusted;
|
||||||
FmgrInfo result_in_func;
|
FmgrInfo result_in_func;
|
||||||
Oid result_in_elem;
|
Oid result_in_elem;
|
||||||
int nargs;
|
int nargs;
|
||||||
@@ -96,22 +98,26 @@ static int pltcl_firstcall = 1;
|
|||||||
static int pltcl_call_level = 0;
|
static int pltcl_call_level = 0;
|
||||||
static int pltcl_restart_in_progress = 0;
|
static int pltcl_restart_in_progress = 0;
|
||||||
static Tcl_Interp *pltcl_hold_interp = NULL;
|
static Tcl_Interp *pltcl_hold_interp = NULL;
|
||||||
|
static Tcl_Interp *pltcl_norm_interp = NULL;
|
||||||
static Tcl_Interp *pltcl_safe_interp = NULL;
|
static Tcl_Interp *pltcl_safe_interp = NULL;
|
||||||
static Tcl_HashTable *pltcl_proc_hash = NULL;
|
static Tcl_HashTable *pltcl_proc_hash = NULL;
|
||||||
static Tcl_HashTable *pltcl_query_hash = NULL;
|
static Tcl_HashTable *pltcl_norm_query_hash = NULL;
|
||||||
|
static Tcl_HashTable *pltcl_safe_query_hash = NULL;
|
||||||
|
static FunctionCallInfo pltcl_actual_fcinfo = NULL;
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* Forward declarations
|
* Forward declarations
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
static void pltcl_init_all(void);
|
static void pltcl_init_all(void);
|
||||||
static void pltcl_init_safe_interp(void);
|
static void pltcl_init_interp(Tcl_Interp *interp);
|
||||||
|
|
||||||
#ifdef PLTCL_UNKNOWN_SUPPORT
|
#ifdef PLTCL_UNKNOWN_SUPPORT
|
||||||
static void pltcl_init_load_unknown(void);
|
static void pltcl_init_load_unknown(Tcl_Interp *interp);
|
||||||
|
|
||||||
#endif /* PLTCL_UNKNOWN_SUPPORT */
|
#endif /* PLTCL_UNKNOWN_SUPPORT */
|
||||||
|
|
||||||
Datum pltcl_call_handler(PG_FUNCTION_ARGS);
|
Datum pltcl_call_handler(PG_FUNCTION_ARGS);
|
||||||
|
Datum pltclu_call_handler(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
|
static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
@@ -121,6 +127,10 @@ static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
|
|||||||
int argc, char *argv[]);
|
int argc, char *argv[]);
|
||||||
static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
|
static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
|
||||||
int argc, char *argv[]);
|
int argc, char *argv[]);
|
||||||
|
static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
|
||||||
|
int argc, char *argv[]);
|
||||||
|
static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
|
||||||
|
int argc, char *argv[]);
|
||||||
|
|
||||||
static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
|
static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
|
||||||
int argc, char *argv[]);
|
int argc, char *argv[]);
|
||||||
@@ -155,64 +165,40 @@ pltcl_init_all(void)
|
|||||||
* Create the dummy hold interpreter to prevent close of
|
* Create the dummy hold interpreter to prevent close of
|
||||||
* stdout and stderr on DeleteInterp
|
* stdout and stderr on DeleteInterp
|
||||||
************************************************************/
|
************************************************************/
|
||||||
if (pltcl_hold_interp == NULL)
|
if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
|
||||||
{
|
{
|
||||||
if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
|
elog(ERROR, "pltcl: internal error - cannot create 'hold' "
|
||||||
{
|
"interpreter");
|
||||||
elog(ERROR, "pltcl: internal error - cannot create 'hold' "
|
|
||||||
"interpreter");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Destroy the existing safe interpreter
|
* Create the two interpreters
|
||||||
************************************************************/
|
************************************************************/
|
||||||
if (pltcl_safe_interp != NULL)
|
if ((pltcl_norm_interp =
|
||||||
|
Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL)
|
||||||
{
|
{
|
||||||
Tcl_DeleteInterp(pltcl_safe_interp);
|
elog(ERROR,
|
||||||
pltcl_safe_interp = NULL;
|
"pltcl: internal error - cannot create 'normal' interpreter");
|
||||||
}
|
}
|
||||||
|
pltcl_init_interp(pltcl_norm_interp);
|
||||||
|
|
||||||
|
if ((pltcl_safe_interp =
|
||||||
|
Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL)
|
||||||
|
{
|
||||||
|
elog(ERROR,
|
||||||
|
"pltcl: internal error - cannot create 'safe' interpreter");
|
||||||
|
}
|
||||||
|
pltcl_init_interp(pltcl_safe_interp);
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Free the proc hash table
|
* Initialize the proc and query hash tables
|
||||||
************************************************************/
|
************************************************************/
|
||||||
if (pltcl_proc_hash != NULL)
|
pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
|
||||||
{
|
pltcl_norm_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
|
||||||
hashent = Tcl_FirstHashEntry(pltcl_proc_hash, &hashsearch);
|
pltcl_safe_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
|
||||||
while (hashent != NULL)
|
Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS);
|
||||||
{
|
Tcl_InitHashTable(pltcl_norm_query_hash, TCL_STRING_KEYS);
|
||||||
prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
|
Tcl_InitHashTable(pltcl_safe_query_hash, TCL_STRING_KEYS);
|
||||||
free(prodesc->proname);
|
|
||||||
free(prodesc);
|
|
||||||
hashent = Tcl_NextHashEntry(&hashsearch);
|
|
||||||
}
|
|
||||||
Tcl_DeleteHashTable(pltcl_proc_hash);
|
|
||||||
free(pltcl_proc_hash);
|
|
||||||
pltcl_proc_hash = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
* Free the prepared query hash table
|
|
||||||
************************************************************/
|
|
||||||
if (pltcl_query_hash != NULL)
|
|
||||||
{
|
|
||||||
hashent = Tcl_FirstHashEntry(pltcl_query_hash, &hashsearch);
|
|
||||||
while (hashent != NULL)
|
|
||||||
{
|
|
||||||
querydesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
|
|
||||||
free(querydesc->argtypes);
|
|
||||||
free(querydesc);
|
|
||||||
hashent = Tcl_NextHashEntry(&hashsearch);
|
|
||||||
}
|
|
||||||
Tcl_DeleteHashTable(pltcl_query_hash);
|
|
||||||
free(pltcl_query_hash);
|
|
||||||
pltcl_query_hash = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
* Now recreate a new safe interpreter
|
|
||||||
************************************************************/
|
|
||||||
pltcl_init_safe_interp();
|
|
||||||
|
|
||||||
pltcl_firstcall = 0;
|
pltcl_firstcall = 0;
|
||||||
return;
|
return;
|
||||||
@@ -220,52 +206,28 @@ pltcl_init_all(void)
|
|||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* pltcl_init_safe_interp() - Create the safe Tcl interpreter
|
* pltcl_init_interp() - initialize a Tcl interpreter
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
static void
|
static void
|
||||||
pltcl_init_safe_interp(void)
|
pltcl_init_interp(Tcl_Interp *interp)
|
||||||
{
|
{
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Create the interpreter as a safe slave of the hold interp.
|
* Install the commands for SPI support in the interpreter
|
||||||
************************************************************/
|
************************************************************/
|
||||||
if ((pltcl_safe_interp =
|
Tcl_CreateCommand(interp, "elog",
|
||||||
Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL)
|
|
||||||
{
|
|
||||||
elog(ERROR,
|
|
||||||
"pltcl: internal error - cannot create 'safe' interpreter");
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
* Enable debugging output from the Tcl bytecode compiler
|
|
||||||
* To see the trace, the interpreter must be created unsafe
|
|
||||||
* USE ONLY FOR DEBUGGING!!!
|
|
||||||
************************************************************/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tcl_SetVar(pltcl_safe_interp, "tcl_traceCompile", "1", 0);
|
|
||||||
*/
|
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
* Initialize the proc and query hash tables
|
|
||||||
************************************************************/
|
|
||||||
pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
|
|
||||||
pltcl_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
|
|
||||||
Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS);
|
|
||||||
Tcl_InitHashTable(pltcl_query_hash, TCL_STRING_KEYS);
|
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
* Install the commands for SPI support in the safe interpreter
|
|
||||||
************************************************************/
|
|
||||||
Tcl_CreateCommand(pltcl_safe_interp, "elog",
|
|
||||||
pltcl_elog, NULL, NULL);
|
pltcl_elog, NULL, NULL);
|
||||||
Tcl_CreateCommand(pltcl_safe_interp, "quote",
|
Tcl_CreateCommand(interp, "quote",
|
||||||
pltcl_quote, NULL, NULL);
|
pltcl_quote, NULL, NULL);
|
||||||
|
Tcl_CreateCommand(interp, "argisnull",
|
||||||
|
pltcl_argisnull, NULL, NULL);
|
||||||
|
Tcl_CreateCommand(interp, "return_null",
|
||||||
|
pltcl_returnnull, NULL, NULL);
|
||||||
|
|
||||||
Tcl_CreateCommand(pltcl_safe_interp, "spi_exec",
|
Tcl_CreateCommand(interp, "spi_exec",
|
||||||
pltcl_SPI_exec, NULL, NULL);
|
pltcl_SPI_exec, NULL, NULL);
|
||||||
Tcl_CreateCommand(pltcl_safe_interp, "spi_prepare",
|
Tcl_CreateCommand(interp, "spi_prepare",
|
||||||
pltcl_SPI_prepare, NULL, NULL);
|
pltcl_SPI_prepare, NULL, NULL);
|
||||||
Tcl_CreateCommand(pltcl_safe_interp, "spi_execp",
|
Tcl_CreateCommand(interp, "spi_execp",
|
||||||
pltcl_SPI_execp, NULL, NULL);
|
pltcl_SPI_execp, NULL, NULL);
|
||||||
|
|
||||||
#ifdef PLTCL_UNKNOWN_SUPPORT
|
#ifdef PLTCL_UNKNOWN_SUPPORT
|
||||||
@@ -273,10 +235,10 @@ pltcl_init_safe_interp(void)
|
|||||||
* Try to load the unknown procedure from pltcl_modules
|
* Try to load the unknown procedure from pltcl_modules
|
||||||
************************************************************/
|
************************************************************/
|
||||||
if (SPI_connect() != SPI_OK_CONNECT)
|
if (SPI_connect() != SPI_OK_CONNECT)
|
||||||
elog(ERROR, "pltcl_init_safe_interp(): SPI_connect failed");
|
elog(ERROR, "pltcl_init_interp(): SPI_connect failed");
|
||||||
pltcl_init_load_unknown();
|
pltcl_init_load_unknown(interp);
|
||||||
if (SPI_finish() != SPI_OK_FINISH)
|
if (SPI_finish() != SPI_OK_FINISH)
|
||||||
elog(ERROR, "pltcl_init_safe_interp(): SPI_finish failed");
|
elog(ERROR, "pltcl_init_interp(): SPI_finish failed");
|
||||||
#endif /* PLTCL_UNKNOWN_SUPPORT */
|
#endif /* PLTCL_UNKNOWN_SUPPORT */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +311,7 @@ pltcl_init_load_unknown(void)
|
|||||||
pfree(part);
|
pfree(part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&unknown_src));
|
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));
|
||||||
Tcl_DStringFree(&unknown_src);
|
Tcl_DStringFree(&unknown_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +331,7 @@ Datum
|
|||||||
pltcl_call_handler(PG_FUNCTION_ARGS)
|
pltcl_call_handler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Datum retval;
|
Datum retval;
|
||||||
|
FunctionCallInfo save_fcinfo;
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Initialize interpreters on first call
|
* Initialize interpreters on first call
|
||||||
@@ -390,22 +353,38 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
|
|||||||
* Determine if called as function or trigger and
|
* Determine if called as function or trigger and
|
||||||
* call appropriate subhandler
|
* call appropriate subhandler
|
||||||
************************************************************/
|
************************************************************/
|
||||||
|
save_fcinfo = pltcl_actual_fcinfo;
|
||||||
|
|
||||||
if (CALLED_AS_TRIGGER(fcinfo))
|
if (CALLED_AS_TRIGGER(fcinfo))
|
||||||
|
{
|
||||||
|
pltcl_actual_fcinfo = NULL;
|
||||||
retval = PointerGetDatum(pltcl_trigger_handler(fcinfo));
|
retval = PointerGetDatum(pltcl_trigger_handler(fcinfo));
|
||||||
else
|
} else {
|
||||||
|
pltcl_actual_fcinfo = fcinfo;
|
||||||
retval = pltcl_func_handler(fcinfo);
|
retval = pltcl_func_handler(fcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
pltcl_actual_fcinfo = save_fcinfo;
|
||||||
|
|
||||||
pltcl_call_level--;
|
pltcl_call_level--;
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* keep non-static */
|
||||||
|
Datum
|
||||||
|
pltclu_call_handler(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
return pltcl_call_handler(fcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* pltcl_func_handler() - Handler for regular function calls
|
* pltcl_func_handler() - Handler for regular function calls
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
static Datum
|
static Datum
|
||||||
pltcl_func_handler(PG_FUNCTION_ARGS)
|
pltcl_func_handler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
Tcl_Interp *interp;
|
||||||
int i;
|
int i;
|
||||||
char internal_proname[512];
|
char internal_proname[512];
|
||||||
Tcl_HashEntry *hashent;
|
Tcl_HashEntry *hashent;
|
||||||
@@ -436,15 +415,17 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
*
|
*
|
||||||
* Then we load the procedure into the safe interpreter.
|
* Then we load the procedure into the safe interpreter.
|
||||||
************************************************************/
|
************************************************************/
|
||||||
HeapTuple procTup;
|
HeapTuple procTup;
|
||||||
HeapTuple typeTup;
|
HeapTuple langTup;
|
||||||
Form_pg_proc procStruct;
|
HeapTuple typeTup;
|
||||||
Form_pg_type typeStruct;
|
Form_pg_proc procStruct;
|
||||||
Tcl_DString proc_internal_def;
|
Form_pg_language langStruct;
|
||||||
Tcl_DString proc_internal_body;
|
Form_pg_type typeStruct;
|
||||||
char proc_internal_args[4096];
|
Tcl_DString proc_internal_def;
|
||||||
char *proc_source;
|
Tcl_DString proc_internal_body;
|
||||||
char buf[512];
|
char proc_internal_args[4096];
|
||||||
|
char *proc_source;
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Allocate a new procedure description block
|
* Allocate a new procedure description block
|
||||||
@@ -468,6 +449,27 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Lookup the pg_language tuple by Oid
|
||||||
|
************************************************************/
|
||||||
|
langTup = SearchSysCacheTuple(LANGOID,
|
||||||
|
ObjectIdGetDatum(procStruct->prolang),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(langTup))
|
||||||
|
{
|
||||||
|
free(prodesc->proname);
|
||||||
|
free(prodesc);
|
||||||
|
elog(ERROR, "pltcl: cache lookup for language %u failed",
|
||||||
|
procStruct->prolang);
|
||||||
|
}
|
||||||
|
langStruct = (Form_pg_language) GETSTRUCT(langTup);
|
||||||
|
|
||||||
|
prodesc->lanpltrusted = langStruct->lanpltrusted;
|
||||||
|
if (prodesc->lanpltrusted)
|
||||||
|
interp = pltcl_safe_interp;
|
||||||
|
else
|
||||||
|
interp = pltcl_norm_interp;
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Get the required information for input conversion of the
|
* Get the required information for input conversion of the
|
||||||
* return value.
|
* return value.
|
||||||
@@ -570,9 +572,9 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
Tcl_DStringFree(&proc_internal_body);
|
Tcl_DStringFree(&proc_internal_body);
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Create the procedure in the safe interpreter
|
* Create the procedure in the interpreter
|
||||||
************************************************************/
|
************************************************************/
|
||||||
tcl_rc = Tcl_GlobalEval(pltcl_safe_interp,
|
tcl_rc = Tcl_GlobalEval(interp,
|
||||||
Tcl_DStringValue(&proc_internal_def));
|
Tcl_DStringValue(&proc_internal_def));
|
||||||
Tcl_DStringFree(&proc_internal_def);
|
Tcl_DStringFree(&proc_internal_def);
|
||||||
if (tcl_rc != TCL_OK)
|
if (tcl_rc != TCL_OK)
|
||||||
@@ -580,7 +582,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
free(prodesc->proname);
|
free(prodesc->proname);
|
||||||
free(prodesc);
|
free(prodesc);
|
||||||
elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
|
elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
|
||||||
internal_proname, pltcl_safe_interp->result);
|
internal_proname, interp->result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
@@ -596,6 +598,11 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
* Found the proc description block in the hashtable
|
* Found the proc description block in the hashtable
|
||||||
************************************************************/
|
************************************************************/
|
||||||
prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
|
prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
|
||||||
|
|
||||||
|
if (prodesc->lanpltrusted)
|
||||||
|
interp = pltcl_safe_interp;
|
||||||
|
else
|
||||||
|
interp = pltcl_norm_interp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
@@ -671,7 +678,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
/************************************************************
|
/************************************************************
|
||||||
* Call the Tcl function
|
* Call the Tcl function
|
||||||
************************************************************/
|
************************************************************/
|
||||||
tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd));
|
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
|
||||||
Tcl_DStringFree(&tcl_cmd);
|
Tcl_DStringFree(&tcl_cmd);
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
@@ -687,7 +694,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
pltcl_restart_in_progress = 1;
|
pltcl_restart_in_progress = 1;
|
||||||
if (--pltcl_call_level == 0)
|
if (--pltcl_call_level == 0)
|
||||||
pltcl_restart_in_progress = 0;
|
pltcl_restart_in_progress = 0;
|
||||||
elog(ERROR, "pltcl: %s", pltcl_safe_interp->result);
|
elog(ERROR, "pltcl: %s", interp->result);
|
||||||
}
|
}
|
||||||
if (--pltcl_call_level == 0)
|
if (--pltcl_call_level == 0)
|
||||||
pltcl_restart_in_progress = 0;
|
pltcl_restart_in_progress = 0;
|
||||||
@@ -720,7 +727,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
elog(ERROR, "pltcl: SPI_finish() failed");
|
elog(ERROR, "pltcl: SPI_finish() failed");
|
||||||
|
|
||||||
retval = FunctionCall3(&prodesc->result_in_func,
|
retval = FunctionCall3(&prodesc->result_in_func,
|
||||||
PointerGetDatum(pltcl_safe_interp->result),
|
PointerGetDatum(interp->result),
|
||||||
ObjectIdGetDatum(prodesc->result_in_elem),
|
ObjectIdGetDatum(prodesc->result_in_elem),
|
||||||
Int32GetDatum(-1));
|
Int32GetDatum(-1));
|
||||||
|
|
||||||
@@ -735,6 +742,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
|
|||||||
static HeapTuple
|
static HeapTuple
|
||||||
pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
Tcl_Interp *interp;
|
||||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||||
char internal_proname[512];
|
char internal_proname[512];
|
||||||
char *stroid;
|
char *stroid;
|
||||||
@@ -776,7 +784,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
Tcl_DString proc_internal_def;
|
Tcl_DString proc_internal_def;
|
||||||
Tcl_DString proc_internal_body;
|
Tcl_DString proc_internal_body;
|
||||||
HeapTuple procTup;
|
HeapTuple procTup;
|
||||||
|
HeapTuple langTup;
|
||||||
Form_pg_proc procStruct;
|
Form_pg_proc procStruct;
|
||||||
|
Form_pg_language langStruct;
|
||||||
char *proc_source;
|
char *proc_source;
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
@@ -802,6 +812,27 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Lookup the pg_language tuple by Oid
|
||||||
|
************************************************************/
|
||||||
|
langTup = SearchSysCacheTuple(LANGOID,
|
||||||
|
ObjectIdGetDatum(procStruct->prolang),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(langTup))
|
||||||
|
{
|
||||||
|
free(prodesc->proname);
|
||||||
|
free(prodesc);
|
||||||
|
elog(ERROR, "pltcl: cache lookup for language %u failed",
|
||||||
|
procStruct->prolang);
|
||||||
|
}
|
||||||
|
langStruct = (Form_pg_language) GETSTRUCT(langTup);
|
||||||
|
|
||||||
|
prodesc->lanpltrusted = langStruct->lanpltrusted;
|
||||||
|
if (prodesc->lanpltrusted)
|
||||||
|
interp = pltcl_safe_interp;
|
||||||
|
else
|
||||||
|
interp = pltcl_norm_interp;
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Create the tcl command to define the internal
|
* Create the tcl command to define the internal
|
||||||
* procedure
|
* procedure
|
||||||
@@ -846,9 +877,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
Tcl_DStringFree(&proc_internal_body);
|
Tcl_DStringFree(&proc_internal_body);
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Create the procedure in the safe interpreter
|
* Create the procedure in the interpreter
|
||||||
************************************************************/
|
************************************************************/
|
||||||
tcl_rc = Tcl_GlobalEval(pltcl_safe_interp,
|
tcl_rc = Tcl_GlobalEval(interp,
|
||||||
Tcl_DStringValue(&proc_internal_def));
|
Tcl_DStringValue(&proc_internal_def));
|
||||||
Tcl_DStringFree(&proc_internal_def);
|
Tcl_DStringFree(&proc_internal_def);
|
||||||
if (tcl_rc != TCL_OK)
|
if (tcl_rc != TCL_OK)
|
||||||
@@ -856,7 +887,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
free(prodesc->proname);
|
free(prodesc->proname);
|
||||||
free(prodesc);
|
free(prodesc);
|
||||||
elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
|
elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
|
||||||
internal_proname, pltcl_safe_interp->result);
|
internal_proname, interp->result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
@@ -872,13 +903,18 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
* Found the proc description block in the hashtable
|
* Found the proc description block in the hashtable
|
||||||
************************************************************/
|
************************************************************/
|
||||||
prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
|
prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
|
||||||
|
|
||||||
|
if (prodesc->lanpltrusted)
|
||||||
|
interp = pltcl_safe_interp;
|
||||||
|
else
|
||||||
|
interp = pltcl_norm_interp;
|
||||||
}
|
}
|
||||||
|
|
||||||
tupdesc = trigdata->tg_relation->rd_att;
|
tupdesc = trigdata->tg_relation->rd_att;
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Create the tcl command to call the internal
|
* Create the tcl command to call the internal
|
||||||
* proc in the safe interpreter
|
* proc in the interpreter
|
||||||
************************************************************/
|
************************************************************/
|
||||||
Tcl_DStringInit(&tcl_cmd);
|
Tcl_DStringInit(&tcl_cmd);
|
||||||
Tcl_DStringInit(&tcl_trigtup);
|
Tcl_DStringInit(&tcl_trigtup);
|
||||||
@@ -998,7 +1034,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
/************************************************************
|
/************************************************************
|
||||||
* Call the Tcl function
|
* Call the Tcl function
|
||||||
************************************************************/
|
************************************************************/
|
||||||
tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd));
|
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
|
||||||
Tcl_DStringFree(&tcl_cmd);
|
Tcl_DStringFree(&tcl_cmd);
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
@@ -1014,7 +1050,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
pltcl_restart_in_progress = 1;
|
pltcl_restart_in_progress = 1;
|
||||||
if (--pltcl_call_level == 0)
|
if (--pltcl_call_level == 0)
|
||||||
pltcl_restart_in_progress = 0;
|
pltcl_restart_in_progress = 0;
|
||||||
elog(ERROR, "pltcl: %s", pltcl_safe_interp->result);
|
elog(ERROR, "pltcl: %s", interp->result);
|
||||||
}
|
}
|
||||||
if (--pltcl_call_level == 0)
|
if (--pltcl_call_level == 0)
|
||||||
pltcl_restart_in_progress = 0;
|
pltcl_restart_in_progress = 0;
|
||||||
@@ -1037,9 +1073,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
if (SPI_finish() != SPI_OK_FINISH)
|
if (SPI_finish() != SPI_OK_FINISH)
|
||||||
elog(ERROR, "pltcl: SPI_finish() failed");
|
elog(ERROR, "pltcl: SPI_finish() failed");
|
||||||
|
|
||||||
if (strcmp(pltcl_safe_interp->result, "OK") == 0)
|
if (strcmp(interp->result, "OK") == 0)
|
||||||
return rettup;
|
return rettup;
|
||||||
if (strcmp(pltcl_safe_interp->result, "SKIP") == 0)
|
if (strcmp(interp->result, "SKIP") == 0)
|
||||||
{
|
{
|
||||||
return (HeapTuple) NULL;;
|
return (HeapTuple) NULL;;
|
||||||
}
|
}
|
||||||
@@ -1048,11 +1084,11 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
|
|||||||
* Convert the result value from the safe interpreter
|
* Convert the result value from the safe interpreter
|
||||||
* and setup structures for SPI_modifytuple();
|
* and setup structures for SPI_modifytuple();
|
||||||
************************************************************/
|
************************************************************/
|
||||||
if (Tcl_SplitList(pltcl_safe_interp, pltcl_safe_interp->result,
|
if (Tcl_SplitList(interp, interp->result,
|
||||||
&ret_numvals, &ret_values) != TCL_OK)
|
&ret_numvals, &ret_values) != TCL_OK)
|
||||||
{
|
{
|
||||||
elog(NOTICE, "pltcl: cannot split return value from trigger");
|
elog(NOTICE, "pltcl: cannot split return value from trigger");
|
||||||
elog(ERROR, "pltcl: %s", pltcl_safe_interp->result);
|
elog(ERROR, "pltcl: %s", interp->result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret_numvals % 2 != 0)
|
if (ret_numvals % 2 != 0)
|
||||||
@@ -1275,6 +1311,91 @@ pltcl_quote(ClientData cdata, Tcl_Interp *interp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* pltcl_argisnull() - determine if a specific argument is NULL
|
||||||
|
**********************************************************************/
|
||||||
|
static int
|
||||||
|
pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
|
||||||
|
int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int argno;
|
||||||
|
FunctionCallInfo fcinfo = pltcl_actual_fcinfo;
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Check call syntax
|
||||||
|
************************************************************/
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
Tcl_SetResult(interp, "syntax error - 'argisnull argno'", TCL_VOLATILE);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Get the argument number
|
||||||
|
************************************************************/
|
||||||
|
if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
|
||||||
|
return TCL_ERROR;
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Check that we're called as a normal function
|
||||||
|
************************************************************/
|
||||||
|
if (fcinfo == NULL)
|
||||||
|
{
|
||||||
|
Tcl_SetResult(interp, "argisnull cannot be used in triggers",
|
||||||
|
TCL_VOLATILE);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Check that the argno is valid
|
||||||
|
************************************************************/
|
||||||
|
argno--;
|
||||||
|
if (argno < 0 || argno >= fcinfo->nargs)
|
||||||
|
{
|
||||||
|
Tcl_SetResult(interp, "argno out of range", TCL_VOLATILE);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Get the requested NULL state
|
||||||
|
************************************************************/
|
||||||
|
if (PG_ARGISNULL(argno))
|
||||||
|
Tcl_SetResult(interp, "1", TCL_VOLATILE);
|
||||||
|
else
|
||||||
|
Tcl_SetResult(interp, "0", TCL_VOLATILE);
|
||||||
|
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* pltcl_returnnull() - Cause a NULL return from a function
|
||||||
|
**********************************************************************/
|
||||||
|
static int
|
||||||
|
pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
|
||||||
|
int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int argno;
|
||||||
|
FunctionCallInfo fcinfo = pltcl_actual_fcinfo;
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Check call syntax
|
||||||
|
************************************************************/
|
||||||
|
if (argc != 1)
|
||||||
|
{
|
||||||
|
Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_VOLATILE);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Set the NULL return flag and cause Tcl to return from the
|
||||||
|
* procedure.
|
||||||
|
************************************************************/
|
||||||
|
fcinfo->isnull = true;
|
||||||
|
return TCL_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* pltcl_SPI_exec() - The builtin SPI_exec command
|
* pltcl_SPI_exec() - The builtin SPI_exec command
|
||||||
* for the safe interpreter
|
* for the safe interpreter
|
||||||
@@ -1524,6 +1645,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
|
|||||||
Tcl_HashEntry *hashent;
|
Tcl_HashEntry *hashent;
|
||||||
int hashnew;
|
int hashnew;
|
||||||
sigjmp_buf save_restart;
|
sigjmp_buf save_restart;
|
||||||
|
Tcl_HashTable *query_hash;
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Don't do anything if we are already in restart mode
|
* Don't do anything if we are already in restart mode
|
||||||
@@ -1680,8 +1802,13 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
|
|||||||
* Insert a hashtable entry for the plan and return
|
* Insert a hashtable entry for the plan and return
|
||||||
* the key to the caller
|
* the key to the caller
|
||||||
************************************************************/
|
************************************************************/
|
||||||
|
if (interp == pltcl_norm_interp)
|
||||||
|
query_hash = pltcl_norm_query_hash;
|
||||||
|
else
|
||||||
|
query_hash = pltcl_safe_query_hash;
|
||||||
|
|
||||||
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
|
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
|
||||||
hashent = Tcl_CreateHashEntry(pltcl_query_hash, qdesc->qname, &hashnew);
|
hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
|
||||||
Tcl_SetHashValue(hashent, (ClientData) qdesc);
|
Tcl_SetHashValue(hashent, (ClientData) qdesc);
|
||||||
|
|
||||||
Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
|
Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
|
||||||
@@ -1713,6 +1840,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
|
|||||||
HeapTuple *volatile tuples = NULL;
|
HeapTuple *volatile tuples = NULL;
|
||||||
volatile TupleDesc tupdesc = NULL;
|
volatile TupleDesc tupdesc = NULL;
|
||||||
sigjmp_buf save_restart;
|
sigjmp_buf save_restart;
|
||||||
|
Tcl_HashTable *query_hash;
|
||||||
|
|
||||||
char *usage = "syntax error - 'SPI_execp "
|
char *usage = "syntax error - 'SPI_execp "
|
||||||
"?-nulls string? ?-count n? "
|
"?-nulls string? ?-count n? "
|
||||||
@@ -1786,7 +1914,12 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
|
|||||||
/************************************************************
|
/************************************************************
|
||||||
* Get the prepared plan descriptor by its key
|
* Get the prepared plan descriptor by its key
|
||||||
************************************************************/
|
************************************************************/
|
||||||
hashent = Tcl_FindHashEntry(pltcl_query_hash, argv[i++]);
|
if (interp == pltcl_norm_interp)
|
||||||
|
query_hash = pltcl_norm_query_hash;
|
||||||
|
else
|
||||||
|
query_hash = pltcl_safe_query_hash;
|
||||||
|
|
||||||
|
hashent = Tcl_FindHashEntry(query_hash, argv[i++]);
|
||||||
if (hashent == NULL)
|
if (hashent == NULL)
|
||||||
{
|
{
|
||||||
Tcl_AppendResult(interp, "invalid queryid '", argv[--i], "'", NULL);
|
Tcl_AppendResult(interp, "invalid queryid '", argv[--i], "'", NULL);
|
||||||
|
Reference in New Issue
Block a user