📄 pltcl.c
字号:
/********************************************************************** * pltcl.c - PostgreSQL support for Tcl as * procedural language (PL) * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.12 1999/05/26 12:57:23 momjian Exp $ * * 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. * **********************************************************************/#include <tcl.h>#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <setjmp.h>#include "executor/spi.h"#include "commands/trigger.h"#include "utils/elog.h"#include "utils/builtins.h"#include "fmgr.h"#include "access/heapam.h"#include "tcop/tcopprot.h"#include "utils/syscache.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"/********************************************************************** * The information we cache about loaded procedures **********************************************************************/typedef struct pltcl_proc_desc{ char *proname; FmgrInfo result_in_func; Oid result_in_elem; int result_in_len; int nargs; FmgrInfo arg_out_func[MAXFMGRARGS]; Oid arg_out_elem[MAXFMGRARGS]; int arg_out_len[MAXFMGRARGS]; int arg_is_rel[MAXFMGRARGS];} 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 *argtypelems; Datum *argvalues; int *arglen;} pltcl_query_desc;/********************************************************************** * Global data **********************************************************************/static int pltcl_firstcall = 1;static int pltcl_call_level = 0;static int pltcl_restart_in_progress = 0;static Tcl_Interp *pltcl_hold_interp = NULL;static Tcl_Interp *pltcl_safe_interp = NULL;static Tcl_HashTable *pltcl_proc_hash = NULL;static Tcl_HashTable *pltcl_query_hash = NULL;/********************************************************************** * Forward declarations **********************************************************************/static void pltcl_init_all(void);static void pltcl_init_safe_interp(void);#ifdef PLTCL_UNKNOWN_SUPPORTstatic void pltcl_init_load_unknown(void);#endif /* PLTCL_UNKNOWN_SUPPORT */Datum pltcl_call_handler(FmgrInfo *proinfo, FmgrValues *proargs, bool *isNull);static Datum pltcl_func_handler(FmgrInfo *proinfo, FmgrValues *proargs, bool *isNull);static HeapTuple pltcl_trigger_handler(FmgrInfo *proinfo);static int pltcl_elog(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]);static int pltcl_quote(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]);static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]);static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]);static int pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]);static void pltcl_set_tuple_values(Tcl_Interp *interp, char *arrayname, int tupno, HeapTuple tuple, TupleDesc tupdesc);static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, Tcl_DString *retval);/********************************************************************** * pltcl_init_all() - Initialize all **********************************************************************/static voidpltcl_init_all(void){ Tcl_HashEntry *hashent; Tcl_HashSearch hashsearch; pltcl_proc_desc *prodesc; pltcl_query_desc *querydesc; /************************************************************ * Do initialization only once ************************************************************/ if (!pltcl_firstcall) return; /************************************************************ * Create the dummy hold interpreter to prevent close of * stdout and stderr on DeleteInterp ************************************************************/ if (pltcl_hold_interp == NULL) { if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL) { elog(ERROR, "pltcl: internal error - cannot create 'hold' " "interpreter"); } } /************************************************************ * Destroy the existing safe interpreter ************************************************************/ if (pltcl_safe_interp != NULL) { Tcl_DeleteInterp(pltcl_safe_interp); pltcl_safe_interp = NULL; } /************************************************************ * Free the proc hash table ************************************************************/ if (pltcl_proc_hash != NULL) { hashent = Tcl_FirstHashEntry(pltcl_proc_hash, &hashsearch); while (hashent != NULL) { prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); 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; return;}/********************************************************************** * pltcl_init_safe_interp() - Create the safe Tcl interpreter **********************************************************************/static voidpltcl_init_safe_interp(void){ /************************************************************ * Create the interpreter as a safe slave of the hold interp. ************************************************************/ if ((pltcl_safe_interp = 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); Tcl_CreateCommand(pltcl_safe_interp, "quote", pltcl_quote, NULL, NULL); Tcl_CreateCommand(pltcl_safe_interp, "spi_exec", pltcl_SPI_exec, NULL, NULL); Tcl_CreateCommand(pltcl_safe_interp, "spi_prepare", pltcl_SPI_prepare, NULL, NULL); Tcl_CreateCommand(pltcl_safe_interp, "spi_execp", pltcl_SPI_execp, NULL, NULL);#ifdef PLTCL_UNKNOWN_SUPPORT /************************************************************ * Try to load the unknown procedure from pltcl_modules ************************************************************/ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "pltcl_init_safe_interp(): SPI_connect failed"); pltcl_init_load_unknown(); if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "pltcl_init_safe_interp(): SPI_finish failed");#endif /* PLTCL_UNKNOWN_SUPPORT */}#ifdef PLTCL_UNKNOWN_SUPPORT/********************************************************************** * pltcl_init_load_unknown() - Load the unknown procedure from * table pltcl_modules (if it exists) **********************************************************************/static voidpltcl_init_load_unknown(void){ 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_exec("select 1 from pg_class " "where relname = 'pltcl_modules'", 1); if (spi_rc != SPI_OK_SELECT) elog(ERROR, "pltcl_init_load_unknown(): 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_exec("select modseq, modsrc from pltcl_modules " "where modname = 'unknown' " "order by modseq", 0); if (spi_rc != SPI_OK_SELECT) { elog(ERROR, "pltcl_init_load_unknown(): select from pltcl_modules " "failed"); } /************************************************************ * If there's nothing, module unknown doesn't exist ************************************************************/ if (SPI_processed == 0) { Tcl_DStringFree(&unknown_src); elog(NOTICE, "pltcl: 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 safe 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) { Tcl_DStringAppend(&unknown_src, part, -1); pfree(part); } } tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&unknown_src)); Tcl_DStringFree(&unknown_src);}#endif /* PLTCL_UNKNOWN_SUPPORT *//********************************************************************** * 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. **********************************************************************//* keep non-static */Datumpltcl_call_handler(FmgrInfo *proinfo, FmgrValues *proargs, bool *isNull){ Datum retval; /************************************************************ * Initialize interpreters on first call ************************************************************/ if (pltcl_firstcall) pltcl_init_all(); /************************************************************ * Connect to SPI manager ************************************************************/ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "pltcl: cannot connect to SPI manager"); /************************************************************ * Keep track about the nesting of Tcl-SPI-Tcl-... calls ************************************************************/ pltcl_call_level++; /************************************************************ * Determine if called as function or trigger and * call appropriate subhandler ************************************************************/ if (CurrentTriggerData == NULL) retval = pltcl_func_handler(proinfo, proargs, isNull); else retval = (Datum) pltcl_trigger_handler(proinfo); pltcl_call_level--; return retval;}/********************************************************************** * pltcl_func_handler() - Handler for regular function calls **********************************************************************/static Datum
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -