📄 pltcl.c
字号:
} } PG_CATCH(); { pltcl_current_fcinfo = save_fcinfo; pltcl_current_prodesc = save_prodesc; PG_RE_THROW(); } PG_END_TRY(); pltcl_current_fcinfo = save_fcinfo; pltcl_current_prodesc = save_prodesc; return retval;}/* * Alternate handler for unsafe functions */PG_FUNCTION_INFO_V1(pltclu_call_handler);/* keep non-static */Datumpltclu_call_handler(PG_FUNCTION_ARGS){ return pltcl_call_handler(fcinfo);}/********************************************************************** * pltcl_func_handler() - Handler for regular function calls **********************************************************************/static Datumpltcl_func_handler(PG_FUNCTION_ARGS){ pltcl_proc_desc *prodesc; Tcl_Interp *volatile interp; Tcl_DString tcl_cmd; Tcl_DString list_tmp; int i; int tcl_rc; Datum retval; /* Connect to SPI manager */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "could not connect to SPI manager"); /* Find or compile the function */ prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid); pltcl_current_prodesc = prodesc; if (prodesc->lanpltrusted) interp = pltcl_safe_interp; else interp = pltcl_norm_interp; /************************************************************ * Create the tcl command to call the internal * proc in the Tcl interpreter ************************************************************/ Tcl_DStringInit(&tcl_cmd); Tcl_DStringInit(&list_tmp); Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname); /************************************************************ * Add all call arguments to the command ************************************************************/ PG_TRY(); { for (i = 0; i < prodesc->nargs; i++) { if (prodesc->arg_is_rowtype[i]) { /************************************************** * For tuple values, add a list for 'array set ...' **************************************************/ if (fcinfo->argnull[i]) Tcl_DStringAppendElement(&tcl_cmd, ""); else { HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup; td = DatumGetHeapTupleHeader(fcinfo->arg[i]); /* Extract rowtype info and find a tupdesc */ tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); tupdesc = CreateTupleDescCopy(tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; Tcl_DStringSetLength(&list_tmp, 0); pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp); Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp)); FreeTupleDesc(tupdesc); } } else { /************************************************** * Single values are added as string element * of their external representation **************************************************/ if (fcinfo->argnull[i]) Tcl_DStringAppendElement(&tcl_cmd, ""); else { char *tmp; tmp = DatumGetCString(FunctionCall1(&prodesc->arg_out_func[i], fcinfo->arg[i])); UTF_BEGIN; Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp)); UTF_END; pfree(tmp); } } } } PG_CATCH(); { Tcl_DStringFree(&tcl_cmd); Tcl_DStringFree(&list_tmp); PG_RE_THROW(); } PG_END_TRY(); Tcl_DStringFree(&list_tmp); /************************************************************ * Call the Tcl function * * We assume no PG error can be thrown directly from this call. ************************************************************/ tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd)); Tcl_DStringFree(&tcl_cmd); /************************************************************ * Check for errors reported by Tcl. ************************************************************/ if (tcl_rc != TCL_OK) { UTF_BEGIN; ereport(ERROR, (errmsg("%s", interp->result), errcontext("%s", UTF_U2E(Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY))))); UTF_END; } /************************************************************ * Disconnect from SPI manager and then create the return * value 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). But don't try to call * the result_in_func if we've been told to return a NULL; * the contents of interp->result may not be a valid value of * the result type in that case. ************************************************************/ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish() failed"); if (fcinfo->isnull) retval = (Datum) 0; else { UTF_BEGIN; retval = FunctionCall3(&prodesc->result_in_func, PointerGetDatum(UTF_U2E(interp->result)), ObjectIdGetDatum(prodesc->result_typioparam), Int32GetDatum(-1)); UTF_END; } return retval;}/********************************************************************** * pltcl_trigger_handler() - Handler for trigger calls **********************************************************************/static HeapTuplepltcl_trigger_handler(PG_FUNCTION_ARGS){ pltcl_proc_desc *prodesc; Tcl_Interp *volatile interp; TriggerData *trigdata = (TriggerData *) fcinfo->context; char *stroid; TupleDesc tupdesc; volatile 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; CONST84 char **ret_values; /* Connect to SPI manager */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "could not connect to SPI manager"); /* Find or compile the function */ prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, RelationGetRelid(trigdata->tg_relation)); pltcl_current_prodesc = prodesc; if (prodesc->lanpltrusted) interp = pltcl_safe_interp; else interp = pltcl_norm_interp; tupdesc = trigdata->tg_relation->rd_att; /************************************************************ * Create the tcl command to call the internal * proc in the interpreter ************************************************************/ Tcl_DStringInit(&tcl_cmd); Tcl_DStringInit(&tcl_trigtup); Tcl_DStringInit(&tcl_newtup); PG_TRY(); { /* The procedure name */ Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname); /* The trigger name for argument TG_name */ Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname); /* The oid of the trigger relation for argument TG_relid */ stroid = DatumGetCString(DirectFunctionCall1(oidout, ObjectIdGetDatum(trigdata->tg_relation->rd_id))); Tcl_DStringAppendElement(&tcl_cmd, stroid); pfree(stroid); /* A list of attribute names for argument TG_relatts */ Tcl_DStringAppendElement(&tcl_trigtup, ""); for (i = 0; i < tupdesc->natts; i++) { if (tupdesc->attrs[i]->attisdropped) Tcl_DStringAppendElement(&tcl_trigtup, ""); else Tcl_DStringAppendElement(&tcl_trigtup, NameStr(tupdesc->attrs[i]->attname)); } Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); Tcl_DStringFree(&tcl_trigtup); Tcl_DStringInit(&tcl_trigtup); /* The when part of the event for TG_when */ if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) Tcl_DStringAppendElement(&tcl_cmd, "BEFORE"); else if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) Tcl_DStringAppendElement(&tcl_cmd, "AFTER"); else elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event); /* The level part of the event for TG_level */ if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) { Tcl_DStringAppendElement(&tcl_cmd, "ROW"); /* Build the data list for the trigtuple */ pltcl_build_tuple_argument(trigdata->tg_trigtuple, tupdesc, &tcl_trigtup); /* * Now the command part of the event for TG_op and data for NEW * and OLD */ if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) { Tcl_DStringAppendElement(&tcl_cmd, "INSERT"); Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); Tcl_DStringAppendElement(&tcl_cmd, ""); rettup = trigdata->tg_trigtuple; } else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) { Tcl_DStringAppendElement(&tcl_cmd, "DELETE"); Tcl_DStringAppendElement(&tcl_cmd, ""); Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); rettup = trigdata->tg_trigtuple; } else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) { Tcl_DStringAppendElement(&tcl_cmd, "UPDATE"); pltcl_build_tuple_argument(trigdata->tg_newtuple, tupdesc, &tcl_newtup); Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup)); Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup)); rettup = trigdata->tg_newtuple; } else elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event); } else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) { Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT"); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) Tcl_DStringAppendElement(&tcl_cmd, "INSERT"); else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) Tcl_DStringAppendElement(&tcl_cmd, "DELETE"); else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) Tcl_DStringAppendElement(&tcl_cmd, "UPDATE"); else elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event); Tcl_DStringAppendElement(&tcl_cmd, ""); Tcl_DStringAppendElement(&tcl_cmd, ""); rettup = (HeapTuple) NULL; } else elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event); /* Finally append the arguments from CREATE TRIGGER */ for (i = 0; i < trigdata->tg_trigger->tgnargs; i++) Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]); } PG_CATCH(); { Tcl_DStringFree(&tcl_cmd); Tcl_DStringFree(&tcl_trigtup); Tcl_DStringFree(&tcl_newtup); PG_RE_THROW(); } PG_END_TRY(); Tcl_DStringFree(&tcl_trigtup); Tcl_DStringFree(&tcl_newtup); /************************************************************ * Call the Tcl function * * We assume no PG error can be thrown directly from this call. ************************************************************/ tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd)); Tcl_DStringFree(&tcl_cmd); /************************************************************ * Check for errors reported by Tcl. ************************************************************/ if (tcl_rc != TCL_OK) { UTF_BEGIN; ereport(ERROR, (errmsg("%s", interp->result), errcontext("%s", UTF_U2E(Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY))))); UTF_END; } /************************************************************ * 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); /* Use a TRY to ensure ret_values will get freed */ PG_TRY(); { if (ret_numvals % 2 != 0) 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); 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 typioparam; 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -