📄 pltcl.c
字号:
SPI_freetuptable(tuptable); return my_rc;}/********************************************************************** * pltcl_SPI_prepare() - Builtin support for prepared plans * The Tcl command SPI_prepare * always 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, CONST84 char *argv[]){ int nargs; CONST84 char **args; pltcl_query_desc *qdesc; void *plan; int i; HeapTuple typeTup; Tcl_HashEntry *hashent; int hashnew; Tcl_HashTable *query_hash; MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; /************************************************************ * 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)); snprintf(qdesc->qname, sizeof(qdesc->qname), "%lx", (long) qdesc); qdesc->nargs = nargs; qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid)); qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo)); qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid)); /************************************************************ * Execute the prepare inside a sub-transaction, so we can cope with * errors sanely ************************************************************/ pltcl_subtrans_begin(oldcontext, oldowner); PG_TRY(); { /************************************************************ * Lookup the argument types by name in the system cache * and remember the required information for input conversion ************************************************************/ for (i = 0; i < nargs; i++) { char *argcopy; List *names = NIL; ListCell *l; TypeName *typename; /************************************************************ * Use SplitIdentifierString() on a copy of the type name, * turn the resulting pointer list into a TypeName node * and call typenameType() to get the pg_type tuple. ************************************************************/ argcopy = pstrdup(args[i]); SplitIdentifierString(argcopy, '.', &names); typename = makeNode(TypeName); foreach(l, names) typename->names = lappend(typename->names, makeString(lfirst(l))); typeTup = typenameType(typename); qdesc->argtypes[i] = HeapTupleGetOid(typeTup); perm_fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput, &(qdesc->arginfuncs[i])); qdesc->argtypioparams[i] = getTypeIOParam(typeTup); ReleaseSysCache(typeTup); list_free(typename->names); pfree(typename); list_free(names); pfree(argcopy); } /************************************************************ * Prepare the plan and check for errors ************************************************************/ UTF_BEGIN; plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes); UTF_END; if (plan == NULL) elog(ERROR, "SPI_prepare() failed"); /************************************************************ * Save the plan into permanent memory (right now it's in the * SPI procCxt, which will go away at function end). ************************************************************/ qdesc->plan = SPI_saveplan(plan); if (qdesc->plan == NULL) elog(ERROR, "SPI_saveplan() failed"); /* Release the procCxt copy to avoid within-function memory leak */ SPI_freeplan(plan); pltcl_subtrans_commit(oldcontext, oldowner); } PG_CATCH(); { pltcl_subtrans_abort(interp, oldcontext, oldowner); free(qdesc->argtypes); free(qdesc->arginfuncs); free(qdesc->argtypioparams); free(qdesc); ckfree((char *) args); return TCL_ERROR; } PG_END_TRY(); /************************************************************ * Insert a hashtable entry for the plan and return * the key to the caller ************************************************************/ if (interp == pltcl_norm_interp) query_hash = pltcl_norm_query_hash; else query_hash = pltcl_safe_query_hash; hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew); Tcl_SetHashValue(hashent, (ClientData) qdesc); ckfree((char *) args); Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE); return TCL_OK;}/********************************************************************** * pltcl_SPI_execute_plan() - Execute a prepared plan **********************************************************************/static intpltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]){ int my_rc; int spi_rc; int i; int j; Tcl_HashEntry *hashent; pltcl_query_desc *qdesc; const char *volatile nulls = NULL; CONST84 char *volatile arrayname = NULL; CONST84 char *volatile loop_body = NULL; int count = 0; int callnargs; CONST84 char **callargs = NULL; Datum *argvalues; MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; Tcl_HashTable *query_hash; char *usage = "syntax error - 'SPI_execp " "?-nulls string? ?-count n? " "?-array name? query ?args? ?loop body?"; /************************************************************ * Get the options and check syntax ************************************************************/ 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], "-nulls") == 0) { if (++i >= argc) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } nulls = 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; } /************************************************************ * Get the prepared plan descriptor by its key ************************************************************/ if (i >= argc) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } if (interp == pltcl_norm_interp) query_hash = pltcl_norm_query_hash; else query_hash = pltcl_safe_query_hash; hashent = Tcl_FindHashEntry(query_hash, argv[i]); if (hashent == NULL) { Tcl_AppendResult(interp, "invalid queryid '", argv[i], "'", NULL); return TCL_ERROR; } qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent); i++; /************************************************************ * If a nulls string is given, check for correct length ************************************************************/ if (nulls != NULL) { if (strlen(nulls) != qdesc->nargs) { Tcl_SetResult(interp, "length of nulls string doesn't match # of arguments", TCL_VOLATILE); return TCL_ERROR; } } /************************************************************ * If there was a argtype list on preparation, we need * an argument value list now ************************************************************/ if (qdesc->nargs > 0) { if (i >= argc) { Tcl_SetResult(interp, "missing argument list", TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Split the argument values ************************************************************/ if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK) return TCL_ERROR; /************************************************************ * Check that the # of arguments matches ************************************************************/ if (callnargs != qdesc->nargs) { Tcl_SetResult(interp, "argument list length doesn't match # of arguments for query", TCL_VOLATILE); ckfree((char *) callargs); return TCL_ERROR; } } else callnargs = 0; /************************************************************ * Get loop body if present ************************************************************/ if (i < argc) loop_body = argv[i++]; if (i != argc) { Tcl_SetResult(interp, usage, TCL_VOLATILE); return TCL_ERROR; } /************************************************************ * Execute the plan inside a sub-transaction, so we can cope with * errors sanely ************************************************************/ pltcl_subtrans_begin(oldcontext, oldowner); PG_TRY(); { /************************************************************ * Setup the value array for SPI_execute_plan() using * the type specific input functions ************************************************************/ argvalues = (Datum *) palloc(callnargs * sizeof(Datum)); for (j = 0; j < callnargs; j++) { if (nulls && nulls[j] == 'n') { /* don't try to convert the input for a null */ argvalues[j] = (Datum) 0; } else { UTF_BEGIN; argvalues[j] = FunctionCall3(&qdesc->arginfuncs[j], CStringGetDatum(UTF_U2E(callargs[j])), ObjectIdGetDatum(qdesc->argtypioparams[j]), Int32GetDatum(-1)); UTF_END; } } if (callargs) ckfree((char *) callargs); callargs = NULL; /************************************************************ * Execute the plan ************************************************************/ spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls, pltcl_current_prodesc->fn_readonly, count); 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); if (callargs) ckfree((char *) callargs); return TCL_ERROR; } PG_END_TRY(); return my_rc;}/********************************************************************** * pltcl_SPI_lastoid() - return the last oid. To * be used after insert queries **********************************************************************/static intpltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]){ char buf[64]; snprintf(buf, sizeof(buf), "%u", SPI_lastoid); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_OK;}/********************************************************************** * pltcl_set_tuple_values() - Set variables for all attributes * of a given tuple **********************************************************************/static voidpltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname, int tupno, HeapTuple tuple, TupleDesc tupdesc){ int i; char *outputstr; char buf[64]; Datum attr; bool isnull; CONST84 char *attname; HeapTuple typeTup; Oid typoutput; CONST84 char **arrptr; CONST84 char **nameptr; CONST84 char *nullname = NULL; /************************************************************ * Prepare pointers for Tcl_SetVar2() below and in array * mode set the .tupno element ************************************************************/ if (arrayname == NULL) { arrptr = &attname; nameptr = &nullname; } else { arrptr = &arrayname; nameptr = &attname; snprintf(buf, sizeof(buf), "%d", tupno); Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0); } for (i = 0; i < tupdesc->natts; i++) { /* ignore dropped attributes */ if (tupdesc->attrs[i]->attisdropped) continue; /************************************************************ * Get the attribute name ************************************************************/ attname = NameStr(tupdesc
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -