📄 pltcl.c
字号:
pltcl_func_handler(FmgrInfo *proinfo, FmgrValues *proargs, bool *isNull){ int i; char internal_proname[512]; char *stroid; Tcl_HashEntry *hashent; int hashnew; pltcl_proc_desc *prodesc; Tcl_DString tcl_cmd; Tcl_DString list_tmp; int tcl_rc; Datum retval; sigjmp_buf save_restart; /************************************************************ * Build our internal proc name from the functions Oid ************************************************************/ stroid = oidout(proinfo->fn_oid); strcpy(internal_proname, "__PLTcl_proc_"); strcat(internal_proname, stroid); pfree(stroid); /************************************************************ * Lookup the internal proc name in the hashtable ************************************************************/ hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname); if (hashent == NULL) { /************************************************************ * If we haven't found it in the hashtable, we analyze * the functions arguments and returntype and store * the in-/out-functions in the prodesc block and create * a new hashtable entry for it. * * Then we load the procedure into the safe interpreter. ************************************************************/ HeapTuple procTup; HeapTuple typeTup; Form_pg_proc procStruct; Form_pg_type typeStruct; Tcl_DString proc_internal_def; Tcl_DString proc_internal_body; char proc_internal_args[4096]; char *proc_source; char buf[512]; /************************************************************ * Allocate a new procedure description block ************************************************************/ prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc)); prodesc->proname = malloc(strlen(internal_proname) + 1); strcpy(prodesc->proname, internal_proname); /************************************************************ * Lookup the pg_proc tuple by Oid ************************************************************/ procTup = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(proinfo->fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: cache lookup from pg_proc failed"); } procStruct = (Form_pg_proc) GETSTRUCT(procTup); /************************************************************ * Get the required information for input conversion of the * return value. ************************************************************/ typeTup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: cache lookup for return type failed"); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); if (typeStruct->typrelid != InvalidOid) { free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: return types of tuples not supported yet"); } fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); prodesc->result_in_elem = (Oid) (typeStruct->typelem); prodesc->result_in_len = typeStruct->typlen; /************************************************************ * Get the required information for output conversion * of all procedure arguments ************************************************************/ prodesc->nargs = proinfo->fn_nargs; proc_internal_args[0] = '\0'; for (i = 0; i < proinfo->fn_nargs; i++) { typeTup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(procStruct->proargtypes[i]), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: cache lookup for argument type failed"); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); if (typeStruct->typrelid != InvalidOid) { prodesc->arg_is_rel[i] = 1; if (i > 0) strcat(proc_internal_args, " "); sprintf(buf, "__PLTcl_Tup_%d", i + 1); strcat(proc_internal_args, buf); continue; } else prodesc->arg_is_rel[i] = 0; fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); prodesc->arg_out_elem[i] = (Oid) (typeStruct->typelem); prodesc->arg_out_len[i] = typeStruct->typlen; if (i > 0) strcat(proc_internal_args, " "); sprintf(buf, "%d", i + 1); strcat(proc_internal_args, buf); } /************************************************************ * Create the tcl command to define the internal * procedure ************************************************************/ Tcl_DStringInit(&proc_internal_def); Tcl_DStringInit(&proc_internal_body); Tcl_DStringAppendElement(&proc_internal_def, "proc"); Tcl_DStringAppendElement(&proc_internal_def, internal_proname); Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args); /************************************************************ * prefix procedure body with * upvar #0 <internal_procname> GD * and with appropriate upvars for tuple arguments ************************************************************/ Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1); Tcl_DStringAppend(&proc_internal_body, internal_proname, -1); Tcl_DStringAppend(&proc_internal_body, " GD\n", -1); for (i = 0; i < proinfo->fn_nargs; i++) { if (!prodesc->arg_is_rel[i]) continue; sprintf(buf, "array set %d $__PLTcl_Tup_%d\n", i + 1, i + 1); Tcl_DStringAppend(&proc_internal_body, buf, -1); } proc_source = textout(&(procStruct->prosrc)); Tcl_DStringAppend(&proc_internal_body, proc_source, -1); pfree(proc_source); Tcl_DStringAppendElement(&proc_internal_def, Tcl_DStringValue(&proc_internal_body)); Tcl_DStringFree(&proc_internal_body); /************************************************************ * Create the procedure in the safe interpreter ************************************************************/ tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&proc_internal_def)); Tcl_DStringFree(&proc_internal_def); if (tcl_rc != TCL_OK) { free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: cannot create internal procedure %s - %s", internal_proname, pltcl_safe_interp->result); } /************************************************************ * Add the proc description block to the hashtable ************************************************************/ hashent = Tcl_CreateHashEntry(pltcl_proc_hash, prodesc->proname, &hashnew); Tcl_SetHashValue(hashent, (ClientData) prodesc); } else { /************************************************************ * Found the proc description block in the hashtable ************************************************************/ prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); } /************************************************************ * Create the tcl command to call the internal * proc in the safe interpreter ************************************************************/ Tcl_DStringInit(&tcl_cmd); Tcl_DStringInit(&list_tmp); Tcl_DStringAppendElement(&tcl_cmd, internal_proname); /************************************************************ * Catch elog(ERROR) during build of the Tcl command ************************************************************/ memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); Tcl_DStringFree(&tcl_cmd); Tcl_DStringFree(&list_tmp); pltcl_restart_in_progress = 1; if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; siglongjmp(Warn_restart, 1); } /************************************************************ * Add all call arguments to the command ************************************************************/ for (i = 0; i < prodesc->nargs; i++) { if (prodesc->arg_is_rel[i]) { /************************************************** * For tuple values, add a list for 'array set ...' **************************************************/ Tcl_DStringInit(&list_tmp); pltcl_build_tuple_argument( ((TupleTableSlot *) (proargs->data[i]))->val, ((TupleTableSlot *) (proargs->data[i]))->ttc_tupleDescriptor, &list_tmp); Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp)); Tcl_DStringFree(&list_tmp); Tcl_DStringInit(&list_tmp); } else { /************************************************** * Single values are added as string element * of their external representation **************************************************/ char *tmp; tmp = (*fmgr_faddr(&(prodesc->arg_out_func[i]))) (proargs->data[i], prodesc->arg_out_elem[i], prodesc->arg_out_len[i]); Tcl_DStringAppendElement(&tcl_cmd, tmp); pfree(tmp); } } Tcl_DStringFree(&list_tmp); memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); /************************************************************ * Call the Tcl function ************************************************************/ tcl_rc = Tcl_GlobalEval(pltcl_safe_interp, Tcl_DStringValue(&tcl_cmd)); Tcl_DStringFree(&tcl_cmd); /************************************************************ * Check the return code from Tcl and handle * our special restart mechanism to get rid * of all nested call levels on transaction * abort. ************************************************************/ if (tcl_rc != TCL_OK || pltcl_restart_in_progress) { if (!pltcl_restart_in_progress) { pltcl_restart_in_progress = 1; if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; elog(ERROR, "pltcl: %s", pltcl_safe_interp->result); } if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; siglongjmp(Warn_restart, 1); } /************************************************************ * Convert the result value from the safe interpreter * into it's PostgreSQL data format and return it. * Again, the call to fmgr() could fire an elog and we * have to count for the current interpreter level we are * on. The save_restart from above is still good. ************************************************************/ if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); pltcl_restart_in_progress = 1; if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; siglongjmp(Warn_restart, 1); } /************************************************************ * Disconnect from SPI manager and then create the return * values datum (if the input function does a palloc for it * this must not be allocated in the SPI memory context * because SPI_finish would free it). ************************************************************/ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "pltcl: SPI_finish() failed"); retval = (Datum) (*fmgr_faddr(&prodesc->result_in_func)) (pltcl_safe_interp->result, prodesc->result_in_elem, prodesc->result_in_len); memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return retval;}/********************************************************************** * pltcl_trigger_handler() - Handler for trigger calls **********************************************************************/static HeapTuplepltcl_trigger_handler(FmgrInfo *proinfo){ TriggerData *trigdata; char internal_proname[512]; char *stroid; Tcl_HashEntry *hashent; int hashnew; pltcl_proc_desc *prodesc; TupleDesc tupdesc; HeapTuple rettup; Tcl_DString tcl_cmd; Tcl_DString tcl_trigtup; Tcl_DString tcl_newtup; int tcl_rc; int i; int *modattrs; Datum *modvalues; char *modnulls; int ret_numvals; char **ret_values; sigjmp_buf save_restart; /************************************************************ * Save the current trigger data local ************************************************************/ trigdata = CurrentTriggerData; CurrentTriggerData = NULL; /************************************************************ * Build our internal proc name from the functions Oid ************************************************************/ stroid = oidout(proinfo->fn_oid); strcpy(internal_proname, "__PLTcl_proc_"); strcat(internal_proname, stroid); pfree(stroid); /************************************************************ * Lookup the internal proc name in the hashtable ************************************************************/ hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname); if (hashent == NULL) { /************************************************************ * If we haven't found it in the hashtable, * we load the procedure into the safe interpreter. ************************************************************/ Tcl_DString proc_internal_def; Tcl_DString proc_internal_body; HeapTuple procTup; Form_pg_proc procStruct; char *proc_source; /************************************************************ * Allocate a new procedure description block ************************************************************/ prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc)); memset(prodesc, 0, sizeof(pltcl_proc_desc)); prodesc->proname = malloc(strlen(internal_proname) + 1); strcpy(prodesc->proname, internal_proname); /************************************************************ * Lookup the pg_proc tuple by Oid ************************************************************/ procTup = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(proinfo->fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "pltcl: cache lookup from pg_proc failed"); } procStruct = (Form_pg_proc) GETSTRUCT(procTup); /************************************************************ * Create the tcl command to define the internal * procedure ************************************************************/ Tcl_DStringInit(&proc_internal_def); Tcl_DStringInit(&proc_internal_body); Tcl_DStringAppendElement(&proc_internal_def, "proc"); Tcl_DStringAppendElement(&proc_internal_def, internal_proname); Tcl_DStringAppendElement(&proc_internal_def, "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args"); /************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -