📄 pltcl.c
字号:
/* Must reset elog.c's state */ MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); /* Pass the error message to Tcl */ Tcl_SetResult(interp, edata->message, TCL_VOLATILE); FreeErrorData(edata); return TCL_ERROR; } PG_END_TRY(); return TCL_OK;}/********************************************************************** * pltcl_quote() - quote literal strings that are to * be used in SPI_execute query strings **********************************************************************/static intpltcl_quote(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]){ char *tmp; const char *cp1; char *cp2; /************************************************************ * Check call syntax ************************************************************/ if (argc != 2) { Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Allocate space for the maximum the string can * grow to and initialize pointers ************************************************************/ tmp = palloc(strlen(argv[1]) * 2 + 1); cp1 = argv[1]; cp2 = tmp; /************************************************************ * Walk through string and double every quote and backslash ************************************************************/ while (*cp1) { if (*cp1 == '\'') *cp2++ = '\''; else { if (*cp1 == '\\') *cp2++ = '\\'; } *cp2++ = *cp1++; } /************************************************************ * Terminate the string and set it as result ************************************************************/ *cp2 = '\0'; Tcl_SetResult(interp, tmp, TCL_VOLATILE); pfree(tmp); return TCL_OK;}/********************************************************************** * pltcl_argisnull() - determine if a specific argument is NULL **********************************************************************/static intpltcl_argisnull(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]){ int argno; FunctionCallInfo fcinfo = pltcl_current_fcinfo; /************************************************************ * Check call syntax ************************************************************/ if (argc != 2) { Tcl_SetResult(interp, "syntax error - 'argisnull argno'", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Check that we're called as a normal function ************************************************************/ if (fcinfo == NULL) { Tcl_SetResult(interp, "argisnull cannot be used in triggers", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Get the argument number ************************************************************/ if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK) return TCL_ERROR; /************************************************************ * Check that the argno is valid ************************************************************/ argno--; if (argno < 0 || argno >= fcinfo->nargs) { Tcl_SetResult(interp, "argno out of range", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Get the requested NULL state ************************************************************/ if (PG_ARGISNULL(argno)) Tcl_SetResult(interp, "1", TCL_VOLATILE); else Tcl_SetResult(interp, "0", TCL_VOLATILE); return TCL_OK;}/********************************************************************** * pltcl_returnnull() - Cause a NULL return from a function **********************************************************************/static intpltcl_returnnull(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]){ FunctionCallInfo fcinfo = pltcl_current_fcinfo; /************************************************************ * Check call syntax ************************************************************/ if (argc != 1) { Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Check that we're called as a normal function ************************************************************/ if (fcinfo == NULL) { Tcl_SetResult(interp, "return_null cannot be used in triggers", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Set the NULL return flag and cause Tcl to return from the * procedure. ************************************************************/ fcinfo->isnull = true; return TCL_RETURN;}/*---------- * Support for running SPI operations inside subtransactions * * Intended usage pattern is: * * MemoryContext oldcontext = CurrentMemoryContext; * ResourceOwner oldowner = CurrentResourceOwner; * * ... * pltcl_subtrans_begin(oldcontext, oldowner); * PG_TRY(); * { * do something risky; * pltcl_subtrans_commit(oldcontext, oldowner); * } * PG_CATCH(); * { * pltcl_subtrans_abort(interp, oldcontext, oldowner); * return TCL_ERROR; * } * PG_END_TRY(); * return TCL_OK; *---------- */static voidpltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner){ BeginInternalSubTransaction(NULL); /* Want to run inside function's memory context */ MemoryContextSwitchTo(oldcontext);}static voidpltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner){ /* Commit the inner transaction, return to outer xact context */ ReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* * AtEOSubXact_SPI() should not have popped any SPI context, but just in * case it did, make sure we remain connected. */ SPI_restore_connection();}static voidpltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner){ ErrorData *edata; /* Save error info */ MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); /* Abort the inner transaction */ RollbackAndReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will * have left us in a disconnected state. We need this hack to return to * connected state. */ SPI_restore_connection(); /* Pass the error message to Tcl */ Tcl_SetResult(interp, edata->message, TCL_VOLATILE); FreeErrorData(edata);}/********************************************************************** * pltcl_SPI_execute() - The builtin SPI_execute command * for the Tcl interpreter **********************************************************************/static intpltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]){ int my_rc; int spi_rc; int query_idx; int i; int count = 0; CONST84 char *volatile arrayname = NULL; CONST84 char *volatile loop_body = NULL; MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; char *usage = "syntax error - 'SPI_exec " "?-count n? " "?-array name? query ?loop body?"; /************************************************************ * Check the call syntax and get the options ************************************************************/ if (argc < 2) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } i = 1; while (i < argc) { if (strcmp(argv[i], "-array") == 0) { if (++i >= argc) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } arrayname = argv[i++]; continue; } if (strcmp(argv[i], "-count") == 0) { if (++i >= argc) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK) return TCL_ERROR; continue; } break; } query_idx = i; if (query_idx >= argc || query_idx + 2 < argc) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } if (query_idx + 1 < argc) loop_body = argv[query_idx + 1]; /************************************************************ * Execute the query inside a sub-transaction, so we can cope with * errors sanely ************************************************************/ pltcl_subtrans_begin(oldcontext, oldowner); PG_TRY(); { UTF_BEGIN; spi_rc = SPI_execute(UTF_U2E(argv[query_idx]), pltcl_current_prodesc->fn_readonly, count); UTF_END; my_rc = pltcl_process_SPI_result(interp, arrayname, loop_body, spi_rc, SPI_tuptable, SPI_processed); pltcl_subtrans_commit(oldcontext, oldowner); } PG_CATCH(); { pltcl_subtrans_abort(interp, oldcontext, oldowner); return TCL_ERROR; } PG_END_TRY(); return my_rc;}/* * Process the result from SPI_execute or SPI_execute_plan * * Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan */static intpltcl_process_SPI_result(Tcl_Interp *interp, CONST84 char *arrayname, CONST84 char *loop_body, int spi_rc, SPITupleTable *tuptable, int ntuples){ int my_rc = TCL_OK; char buf[64]; int i; int loop_rc; HeapTuple *tuples; TupleDesc tupdesc; switch (spi_rc) { case SPI_OK_UTILITY: Tcl_SetResult(interp, "0", TCL_VOLATILE); break; case SPI_OK_SELINTO: case SPI_OK_INSERT: case SPI_OK_DELETE: case SPI_OK_UPDATE: snprintf(buf, sizeof(buf), "%d", ntuples); Tcl_SetResult(interp, buf, TCL_VOLATILE); break; case SPI_OK_SELECT: /* * Process the tuples we got */ tuples = tuptable->vals; tupdesc = tuptable->tupdesc; if (loop_body == NULL) { /* * If there is no loop body given, just set the variables from * the first tuple (if any) */ if (ntuples > 0) pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc); } else { /* * There is a loop body - process all tuples and evaluate the * body on each */ for (i = 0; i < ntuples; i++) { pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc); loop_rc = Tcl_Eval(interp, loop_body); if (loop_rc == TCL_OK) continue; if (loop_rc == TCL_CONTINUE) continue; if (loop_rc == TCL_RETURN) { my_rc = TCL_RETURN; break; } if (loop_rc == TCL_BREAK) break; my_rc = TCL_ERROR; break; } } if (my_rc == TCL_OK) { snprintf(buf, sizeof(buf), "%d", ntuples); Tcl_SetResult(interp, buf, TCL_VOLATILE); } break; default: Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ", SPI_result_code_string(spi_rc), NULL); my_rc = TCL_ERROR; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -