📄 pltcl.c
字号:
{ case TCL_OK: break; default: elog(ERROR, "unsupported TCL return code: %d", tcl_rc); } /************************************************************ * The return value from the procedure might be one of * the magic strings OK or SKIP or a list from array get ************************************************************/ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish() failed"); if (strcmp(interp->result, "OK") == 0) return rettup; if (strcmp(interp->result, "SKIP") == 0) return (HeapTuple) NULL; /************************************************************ * Convert the result value from the Tcl interpreter * and setup structures for SPI_modifytuple(); ************************************************************/ if (Tcl_SplitList(interp, interp->result, &ret_numvals, &ret_values) != TCL_OK) elog(ERROR, "could not split return value from trigger: %s", interp->result); if (ret_numvals % 2 != 0) { ckfree((char *) ret_values); elog(ERROR, "invalid return list from trigger - must have even # of elements"); } modattrs = (int *) palloc(tupdesc->natts * sizeof(int)); modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum)); for (i = 0; i < tupdesc->natts; i++) { modattrs[i] = i + 1; modvalues[i] = (Datum) NULL; } modnulls = palloc(tupdesc->natts); memset(modnulls, 'n', tupdesc->natts); /************************************************************ * Care for possible elog(ERROR)'s below ************************************************************/ if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); ckfree((char *) ret_values); pltcl_restart_in_progress = 1; if (--pltcl_call_level == 0) pltcl_restart_in_progress = 0; siglongjmp(Warn_restart, 1); } for (i = 0; i < ret_numvals; i += 2) { CONST84 char *ret_name = ret_values[i]; CONST84 char *ret_value = ret_values[i + 1]; int attnum; HeapTuple typeTup; Oid typinput; Oid typelem; FmgrInfo finfo; /************************************************************ * Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values) ************************************************************/ if (strcmp(ret_name, ".tupno") == 0) continue; /************************************************************ * Get the attribute number ************************************************************/ attnum = SPI_fnumber(tupdesc, ret_name); if (attnum == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "invalid attribute \"%s\"", ret_name); if (attnum <= 0) elog(ERROR, "cannot set system attribute \"%s\"", ret_name); /************************************************************ * Ignore dropped columns ************************************************************/ if (tupdesc->attrs[attnum - 1]->attisdropped) continue; /************************************************************ * Lookup the attribute type in the syscache * for the input function ************************************************************/ typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", tupdesc->attrs[attnum - 1]->atttypid); typinput = ((Form_pg_type) GETSTRUCT(typeTup))->typinput; typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; ReleaseSysCache(typeTup); /************************************************************ * Set the attribute to NOT NULL and convert the contents ************************************************************/ modnulls[attnum - 1] = ' '; fmgr_info(typinput, &finfo); UTF_BEGIN; modvalues[attnum - 1] = FunctionCall3(&finfo, CStringGetDatum(UTF_U2E(ret_value)), ObjectIdGetDatum(typelem), Int32GetDatum(tupdesc->attrs[attnum - 1]->atttypmod)); UTF_END; } rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts, modattrs, modvalues, modnulls); pfree(modattrs); pfree(modvalues); pfree(modnulls); if (rettup == NULL) elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result); ckfree((char *) ret_values); memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return rettup;}/********************************************************************** * compile_pltcl_function - compile (or hopefully just look up) function * * tgreloid is the OID of the relation when compiling a trigger, or zero * (InvalidOid) when compiling a plain function. **********************************************************************/static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid){ bool is_trigger = OidIsValid(tgreloid); HeapTuple procTup; Form_pg_proc procStruct; char internal_proname[128]; Tcl_HashEntry *hashent; pltcl_proc_desc *prodesc = NULL; Tcl_Interp *interp; int i; int hashnew; int tcl_rc; /* We'll need the pg_proc tuple in any case... */ procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) elog(ERROR, "cache lookup failed for function %u", fn_oid); procStruct = (Form_pg_proc) GETSTRUCT(procTup); /************************************************************ * Build our internal proc name from the functions Oid ************************************************************/ if (!is_trigger) snprintf(internal_proname, sizeof(internal_proname), "__PLTcl_proc_%u", fn_oid); else snprintf(internal_proname, sizeof(internal_proname), "__PLTcl_proc_%u_trigger_%u", fn_oid, tgreloid); /************************************************************ * Lookup the internal proc name in the hashtable ************************************************************/ hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname); /************************************************************ * If it's present, must check whether it's still up to date. * This is needed because CREATE OR REPLACE FUNCTION can modify the * function's pg_proc entry without changing its OID. ************************************************************/ if (hashent != NULL) { bool uptodate; prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)); if (!uptodate) { Tcl_DeleteHashEntry(hashent); 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 Tcl interpreter. ************************************************************/ if (hashent == NULL) { HeapTuple langTup; HeapTuple typeTup; Form_pg_language langStruct; 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)); if (prodesc == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); MemSet(prodesc, 0, sizeof(pltcl_proc_desc)); prodesc->proname = strdup(internal_proname); prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); /************************************************************ * Lookup the pg_language tuple by Oid ************************************************************/ langTup = SearchSysCache(LANGOID, ObjectIdGetDatum(procStruct->prolang), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for language %u", procStruct->prolang); } langStruct = (Form_pg_language) GETSTRUCT(langTup); prodesc->lanpltrusted = langStruct->lanpltrusted; ReleaseSysCache(langTup); if (prodesc->lanpltrusted) interp = pltcl_safe_interp; else interp = pltcl_norm_interp; /************************************************************ * Get the required information for input conversion of the * return value. ************************************************************/ if (!is_trigger) { typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for type %u", procStruct->prorettype); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype result, except VOID */ if (typeStruct->typtype == 'p') { if (procStruct->prorettype == VOIDOID) /* okay */ ; else if (procStruct->prorettype == TRIGGEROID) { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions may only be called as triggers"))); } else { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("pltcl functions cannot return type %s", format_type_be(procStruct->prorettype)))); } } if (typeStruct->typrelid != InvalidOid) { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("pltcl functions cannot return tuples yet"))); } perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); prodesc->result_in_elem = typeStruct->typelem; ReleaseSysCache(typeTup); } /************************************************************ * Get the required information for output conversion * of all procedure arguments ************************************************************/ if (!is_trigger) { prodesc->nargs = procStruct->pronargs; proc_internal_args[0] = '\0'; for (i = 0; i < prodesc->nargs; i++) { typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->proargtypes[i]), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for type %u", procStruct->proargtypes[i]); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype argument */ if (typeStruct->typtype == 'p') { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("pltcl functions cannot take type %s", format_type_be(procStruct->proargtypes[i])))); } if (typeStruct->typrelid != InvalidOid) { prodesc->arg_is_rel[i] = 1; if (i > 0) strcat(proc_internal_args, " "); snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1); strcat(proc_internal_args, buf); ReleaseSysCache(typeTup); continue; } else prodesc->arg_is_rel[i] = 0; perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); prodesc->arg_out_elem[i] = typeStruct->typelem; if (i > 0) strcat(proc_internal_args, " "); snprintf(buf, sizeof(buf), "%d", i + 1); strcat(proc_internal_args, buf); ReleaseSysCache(typeTup); } } else { /* trigger procedure has fixed args */ strcpy(proc_internal_args, "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args"); } /************************************************************ * 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 setting of 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); if (!is_trigger) { for (i = 0; i < prodesc->nargs; i++) { if (!prodesc->arg_is_rel[i]) continue; snprintf(buf, sizeof(buf), "array set %d $__PLTcl_Tup_%d\n", i + 1, i + 1); Tcl_DStringAppend(&proc_internal_body, buf, -1); } } else { Tcl_DStringAppend(&proc_internal_body, "array set NEW $__PLTcl_Tup_NEW\n", -1); Tcl_DStringAppend(&proc_internal_body, "array set OLD $__PLTcl_Tup_OLD\n", -1); Tcl_DStringAppend(&proc_internal_body, "set i 0\n" "set v 0\n"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -