📄 pltcl.c
字号:
pltcl_quote(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]){ char *tmp; 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_SPI_exec() - The builtin SPI_exec command * for the safe interpreter **********************************************************************/static intpltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]){ int spi_rc; char buf[64]; int count = 0; char *arrayname = NULL; int query_idx; int i; int loop_rc; int ntuples; HeapTuple *tuples; TupleDesc tupdesc = NULL; sigjmp_buf save_restart; char *usage = "syntax error - 'SPI_exec " "?-count n? " "?-array name? query ?loop body?"; /************************************************************ * Don't do anything if we are already in restart mode ************************************************************/ if (pltcl_restart_in_progress) return TCL_ERROR; /************************************************************ * Check the call syntax and get the count option ************************************************************/ 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) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Prepare to start a controlled return through all * interpreter levels on transaction abort ************************************************************/ memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); pltcl_restart_in_progress = 1; Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Execute the query and handle return codes ************************************************************/ spi_rc = SPI_exec(argv[query_idx], count); memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); switch (spi_rc) { case SPI_OK_UTILITY: Tcl_SetResult(interp, "0", TCL_VOLATILE); return TCL_OK; case SPI_OK_SELINTO: case SPI_OK_INSERT: case SPI_OK_DELETE: case SPI_OK_UPDATE: sprintf(buf, "%d", SPI_processed); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_OK; case SPI_OK_SELECT: break; case SPI_ERROR_ARGUMENT: Tcl_SetResult(interp, "pltcl: SPI_exec() failed - SPI_ERROR_ARGUMENT", TCL_VOLATILE); return TCL_ERROR; case SPI_ERROR_UNCONNECTED: Tcl_SetResult(interp, "pltcl: SPI_exec() failed - SPI_ERROR_UNCONNECTED", TCL_VOLATILE); return TCL_ERROR; case SPI_ERROR_COPY: Tcl_SetResult(interp, "pltcl: SPI_exec() failed - SPI_ERROR_COPY", TCL_VOLATILE); return TCL_ERROR; case SPI_ERROR_CURSOR: Tcl_SetResult(interp, "pltcl: SPI_exec() failed - SPI_ERROR_CURSOR", TCL_VOLATILE); return TCL_ERROR; case SPI_ERROR_TRANSACTION: Tcl_SetResult(interp, "pltcl: SPI_exec() failed - SPI_ERROR_TRANSACTION", TCL_VOLATILE); return TCL_ERROR; case SPI_ERROR_OPUNKNOWN: Tcl_SetResult(interp, "pltcl: SPI_exec() failed - SPI_ERROR_OPUNKNOWN", TCL_VOLATILE); return TCL_ERROR; default: sprintf(buf, "%d", spi_rc); Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ", "unknown RC ", buf, NULL); return TCL_ERROR; } /************************************************************ * Only SELECT queries fall through to here - remember the * tuples we got ************************************************************/ ntuples = SPI_processed; if (ntuples > 0) { tuples = SPI_tuptable->vals; tupdesc = SPI_tuptable->tupdesc; } /************************************************************ * Again prepare for elog(ERROR) ************************************************************/ if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); pltcl_restart_in_progress = 1; Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * If there is no loop body given, just set the variables * from the first tuple (if any) and return the number of * tuples selected ************************************************************/ if (argc == query_idx + 1) { if (ntuples > 0) pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc); sprintf(buf, "%d", ntuples); Tcl_SetResult(interp, buf, TCL_VOLATILE); memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return TCL_OK; } /************************************************************ * There is a loop body - process all tuples and evaluate * the body on each ************************************************************/ query_idx++; for (i = 0; i < ntuples; i++) { pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc); loop_rc = Tcl_Eval(interp, argv[query_idx]); if (loop_rc == TCL_OK) continue; if (loop_rc == TCL_CONTINUE) continue; if (loop_rc == TCL_RETURN) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return TCL_RETURN; } if (loop_rc == TCL_BREAK) break; memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); return TCL_ERROR; } /************************************************************ * Finally return the number of tuples ************************************************************/ memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); sprintf(buf, "%d", ntuples); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_OK;}/********************************************************************** * pltcl_SPI_prepare() - Builtin support for prepared plans * The Tcl command SPI_prepare * allways saves the plan using * SPI_saveplan and returns a key for * access. There is no chance to prepare * and not save the plan currently. **********************************************************************/static intpltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, int argc, char *argv[]){ int nargs; char **args; pltcl_query_desc *qdesc; void *plan; int i; HeapTuple typeTup; Tcl_HashEntry *hashent; int hashnew; sigjmp_buf save_restart; /************************************************************ * Don't do anything if we are already in restart mode ************************************************************/ if (pltcl_restart_in_progress) return TCL_ERROR; /************************************************************ * Check the call syntax ************************************************************/ if (argc != 3) { Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Split the argument type list ************************************************************/ if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK) return TCL_ERROR; /************************************************************ * Allocate the new querydesc structure ************************************************************/ qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc)); sprintf(qdesc->qname, "%lx", (long) qdesc); qdesc->nargs = nargs; qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid)); qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo)); qdesc->argtypelems = (Oid *) malloc(nargs * sizeof(Oid)); qdesc->argvalues = (Datum *) malloc(nargs * sizeof(Datum)); qdesc->arglen = (int *) malloc(nargs * sizeof(int)); /************************************************************ * Prepare to start a controlled return through all * interpreter levels on transaction abort ************************************************************/ memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); pltcl_restart_in_progress = 1; free(qdesc->argtypes); free(qdesc->arginfuncs); free(qdesc->argtypelems); free(qdesc->argvalues); free(qdesc->arglen); free(qdesc); ckfree(args); return TCL_ERROR; } /************************************************************ * Lookup the argument types by name in the system cache * and remember the required information for input conversion ************************************************************/ for (i = 0; i < nargs; i++) { typeTup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(args[i]), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "pltcl: Cache lookup of type %s failed", args[i]); qdesc->argtypes[i] = typeTup->t_data->t_oid; fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput, &(qdesc->arginfuncs[i])); qdesc->argtypelems[i] = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; qdesc->argvalues[i] = (Datum) NULL; qdesc->arglen[i] = (int) (((Form_pg_type) GETSTRUCT(typeTup))->typlen); } /************************************************************ * Prepare the plan and check for errors ************************************************************/ plan = SPI_prepare(argv[1], nargs, qdesc->argtypes); if (plan == NULL) { char buf[128]; char *reason; memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); switch (SPI_result) { case SPI_ERROR_ARGUMENT: reason = "SPI_ERROR_ARGUMENT"; break; case SPI_ERROR_UNCONNECTED: reason = "SPI_ERROR_UNCONNECTED"; break; case SPI_ERROR_COPY: reason = "SPI_ERROR_COPY"; break; case SPI_ERROR_CURSOR: reason = "SPI_ERROR_CURSOR"; break; case SPI_ERROR_TRANSACTION: reason = "SPI_ERROR_TRANSACTION"; break; case SPI_ERROR_OPUNKNOWN: reason = "SPI_ERROR_OPUNKNOWN"; break; default: sprintf(buf, "unknown RC %d", SPI_result); reason = buf;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -