📄 pltcl.c
字号:
/********************************************************************** * pltcl.c - PostgreSQL support for Tcl as * procedural language (PL) * * This software is copyrighted by Jan Wieck - Hamburg. * * The author hereby grants permission to use, copy, modify, * distribute, and license this software and its documentation * for any purpose, provided that existing copyright notices are * retained in all copies and that this notice is included * verbatim in any distributions. No written agreement, license, * or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their * author and need not follow the licensing terms described * here, provided that the new terms are clearly indicated on * the first page of each file where they apply. * * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.98.2.2 2006/01/17 17:33:23 tgl Exp $ * **********************************************************************/#include "postgres.h"#include <tcl.h>#include <unistd.h>#include <fcntl.h>/* Hack to deal with Tcl 8.4 const-ification without losing compatibility */#ifndef CONST84#define CONST84#endif#include "access/heapam.h"#include "catalog/pg_language.h"#include "catalog/pg_proc.h"#include "commands/trigger.h"#include "executor/spi.h"#include "fmgr.h"#include "nodes/makefuncs.h"#include "parser/parse_type.h"#include "tcop/tcopprot.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/syscache.h"#include "utils/typcache.h"#if defined(UNICODE_CONVERSION) && TCL_MAJOR_VERSION == 8 \ && TCL_MINOR_VERSION > 0#include "mb/pg_wchar.h"static unsigned char *utf_u2e(unsigned char *src){ return pg_do_encoding_conversion(src, strlen(src), PG_UTF8, GetDatabaseEncoding());}static unsigned char *utf_e2u(unsigned char *src){ return pg_do_encoding_conversion(src, strlen(src), GetDatabaseEncoding(), PG_UTF8);}#define PLTCL_UTF#define UTF_BEGIN do { \ unsigned char *_pltcl_utf_src; \ unsigned char *_pltcl_utf_dst#define UTF_END if (_pltcl_utf_src!=_pltcl_utf_dst) \ pfree(_pltcl_utf_dst); } while (0)#define UTF_U2E(x) (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x)))#define UTF_E2U(x) (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x)))#else /* !PLTCL_UTF */#define UTF_BEGIN#define UTF_END#define UTF_U2E(x) (x)#define UTF_E2U(x) (x)#endif /* PLTCL_UTF *//********************************************************************** * The information we cache about loaded procedures **********************************************************************/typedef struct pltcl_proc_desc{ char *proname; TransactionId fn_xmin; CommandId fn_cmin; bool fn_readonly; bool lanpltrusted; 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;/********************************************************************** * The information we cache about prepared and saved plans **********************************************************************/typedef struct pltcl_query_desc{ char qname[20]; void *plan; int nargs; Oid *argtypes; FmgrInfo *arginfuncs; Oid *argtypioparams;} pltcl_query_desc;/********************************************************************** * Global data **********************************************************************/static bool pltcl_pm_init_done = false;static bool pltcl_be_init_done = false;static Tcl_Interp *pltcl_hold_interp = NULL;static Tcl_Interp *pltcl_norm_interp = NULL;static Tcl_Interp *pltcl_safe_interp = NULL;static Tcl_HashTable *pltcl_proc_hash = NULL;static Tcl_HashTable *pltcl_norm_query_hash = NULL;static Tcl_HashTable *pltcl_safe_query_hash = NULL;/* these are saved and restored by pltcl_call_handler */static FunctionCallInfo pltcl_current_fcinfo = NULL;static pltcl_proc_desc *pltcl_current_prodesc = NULL;/********************************************************************** * Forward declarations **********************************************************************/static void pltcl_init_all(void);static void pltcl_init_interp(Tcl_Interp *interp);static void pltcl_init_load_unknown(Tcl_Interp *interp);Datum pltcl_call_handler(PG_FUNCTION_ARGS);Datum pltclu_call_handler(PG_FUNCTION_ARGS);void pltcl_init(void);static Datum pltcl_func_handler(PG_FUNCTION_ARGS);static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS);static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid);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);/* * This routine is a crock, and so is everyplace that calls it. The problem * is that the cached form of pltcl functions/queries is allocated permanently * (mostly via malloc()) and never released until backend exit. Subsidiary * data structures such as fmgr info records therefore must live forever * as well. A better implementation would store all this stuff in a per- * function memory context that could be reclaimed at need. In the meantime, * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever * it might allocate, and whatever the eventual function might allocate using * fn_mcxt, will live forever too. */static voidperm_fmgr_info(Oid functionId, FmgrInfo *finfo){ fmgr_info_cxt(functionId, finfo, TopMemoryContext);}/********************************************************************** * pltcl_init() - Initialize all that's safe to do in the postmaster * * DO NOT make this static --- it has to be callable by preload **********************************************************************/voidpltcl_init(void){ /************************************************************ * Do initialization only once ************************************************************/ if (pltcl_pm_init_done) return;#ifdef WIN32 /* Required on win32 to prevent error loading init.tcl */ Tcl_FindExecutable("");#endif /************************************************************ * Create the dummy hold interpreter to prevent close of * stdout and stderr on DeleteInterp ************************************************************/ if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL) elog(ERROR, "could not create \"hold\" interpreter"); /************************************************************ * Create the two interpreters ************************************************************/ if ((pltcl_norm_interp = Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL) elog(ERROR, "could not create \"normal\" interpreter"); pltcl_init_interp(pltcl_norm_interp); if ((pltcl_safe_interp = Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL) elog(ERROR, "could not create \"safe\" interpreter"); pltcl_init_interp(pltcl_safe_interp); /************************************************************ * Initialize the proc and query hash tables ************************************************************/ pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); pltcl_norm_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); pltcl_safe_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS); Tcl_InitHashTable(pltcl_norm_query_hash, TCL_STRING_KEYS); Tcl_InitHashTable(pltcl_safe_query_hash, TCL_STRING_KEYS); pltcl_pm_init_done = true;}/********************************************************************** * pltcl_init_all() - Initialize all **********************************************************************/static voidpltcl_init_all(void){ /************************************************************ * Execute postmaster-startup safe initialization ************************************************************/ if (!pltcl_pm_init_done) pltcl_init(); /************************************************************ * Any other initialization that must be done each time a new * backend starts: * - Try to load the unknown procedure from pltcl_modules ************************************************************/ if (!pltcl_be_init_done) { if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); pltcl_init_load_unknown(pltcl_norm_interp); pltcl_init_load_unknown(pltcl_safe_interp); if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); pltcl_be_init_done = true; }}/********************************************************************** * pltcl_init_interp() - initialize a Tcl interpreter **********************************************************************/static voidpltcl_init_interp(Tcl_Interp *interp){ /************************************************************ * 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);}/********************************************************************** * pltcl_init_load_unknown() - Load the unknown procedure from * table pltcl_modules (if it exists) **********************************************************************/static voidpltcl_init_load_unknown(Tcl_Interp *interp){ int spi_rc; int tcl_rc; Tcl_DString unknown_src; char *part; int i; int fno; /************************************************************ * Check if table pltcl_modules exists ************************************************************/ spi_rc = SPI_execute("select 1 from pg_catalog.pg_class " "where relname = 'pltcl_modules'", false, 1); SPI_freetuptable(SPI_tuptable); if (spi_rc != SPI_OK_SELECT) elog(ERROR, "select from pg_class failed"); if (SPI_processed == 0) return; /************************************************************ * Read all the row's from it where modname = 'unknown' in * the order of modseq ************************************************************/ Tcl_DStringInit(&unknown_src); spi_rc = SPI_execute("select modseq, modsrc from pltcl_modules " "where modname = 'unknown' " "order by modseq", false, 0); if (spi_rc != SPI_OK_SELECT) elog(ERROR, "select from pltcl_modules failed"); /************************************************************ * If there's nothing, module unknown doesn't exist ************************************************************/ if (SPI_processed == 0) { Tcl_DStringFree(&unknown_src); SPI_freetuptable(SPI_tuptable); elog(WARNING, "module \"unknown\" not found in pltcl_modules"); return; } /************************************************************ * There is a module named unknown. Resemble the * source from the modsrc attributes and evaluate * it in the Tcl interpreter ************************************************************/ fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc"); for (i = 0; i < SPI_processed; i++) { part = SPI_getvalue(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, fno); if (part != NULL) { UTF_BEGIN; Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1); UTF_END; pfree(part); } } tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src)); Tcl_DStringFree(&unknown_src); SPI_freetuptable(SPI_tuptable);}/********************************************************************** * pltcl_call_handler - This is the only visible function * of the PL interpreter. The PostgreSQL * function manager and trigger manager * call this function for execution of * PL/Tcl procedures. **********************************************************************/PG_FUNCTION_INFO_V1(pltcl_call_handler);/* keep non-static */Datumpltcl_call_handler(PG_FUNCTION_ARGS){ Datum retval; FunctionCallInfo save_fcinfo; pltcl_proc_desc *save_prodesc; /* * Initialize interpreters if first time through */ pltcl_init_all(); /* * Ensure that static pointers are saved/restored properly */ save_fcinfo = pltcl_current_fcinfo; save_prodesc = pltcl_current_prodesc; PG_TRY(); { /* * Determine if called as function or trigger and call appropriate * subhandler */ if (CALLED_AS_TRIGGER(fcinfo)) { pltcl_current_fcinfo = NULL; retval = PointerGetDatum(pltcl_trigger_handler(fcinfo)); } else { pltcl_current_fcinfo = fcinfo; retval = pltcl_func_handler(fcinfo);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -