int get_pgfunc(lua_State *L) { Lua_pgfunc *lf; Pgfunc_options opt; MemoryContext m; const char* reg_name = NULL; HeapTuple proctup; Form_pg_proc proc; int luasrc = 0; Oid funcid = 0; BEGINLUA; opt.only_internal = true; opt.throwable = true; if (lua_gettop(L) == 2){ luaL_checktype(L, 2, LUA_TTABLE); parse_options(L, &opt); }else if (lua_gettop(L) != 1){ return luaL_error(L, "pgfunc(text): wrong arguments"); } if(lua_type(L, 1) == LUA_TSTRING){ reg_name = luaL_checkstring(L, 1); m = MemoryContextSwitchTo(tmpcontext); PG_TRY(); { funcid = DatumGetObjectId(DirectFunctionCall1(regprocedurein, CStringGetDatum(reg_name))); } PG_CATCH();{} PG_END_TRY(); MemoryContextSwitchTo(m); MemoryContextReset(tmpcontext); }else if (lua_type(L, 1) == LUA_TNUMBER){ funcid = luaL_checkinteger(L, 1); } if (!OidIsValid(funcid)){ if (reg_name) return luaL_error(L,"failed to register %s", reg_name); return luaL_error(L,"failed to register function with oid %d", funcid); } proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)){ return luaL_error(L,"cache lookup failed for function %d", funcid); } proc = (Form_pg_proc) GETSTRUCT(proctup); luasrc = ((proc->prolang == get_pllua_oid()) || (proc->prolang == get_plluau_oid())); if ( opt.only_internal &&(proc->prolang != INTERNALlanguageId) &&(!luasrc) ){ ReleaseSysCache(proctup); return luaL_error(L, "supported only SQL/internal functions"); } lf = (Lua_pgfunc *)lua_newuserdata(L, sizeof(Lua_pgfunc)); /*make it g/collected*/ luaP_getfield(L, pg_func_type_name); lua_setmetatable(L, -2); lf->prorettype = proc->prorettype; lf->funcid = funcid; lf->options = opt; { Oid *argtypes; char **argnames; char *argmodes; int argc; MemoryContext cur = CurrentMemoryContext; MemoryContextSwitchTo(tmpcontext); argc = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes); MemoryContextSwitchTo(get_common_ctx()); lf->numargs = argc; lf->argtypes = (Oid*)palloc(argc * sizeof(Oid)); memcpy(lf->argtypes, argtypes, argc * sizeof(Oid)); MemoryContextSwitchTo(cur); MemoryContextReset(tmpcontext); } if (luasrc){ bool isnull; text *t; const char *s; luaL_Buffer b; int pcall_result; Datum prosrc; if((lf->numargs != 1) || (lf->argtypes[0] != INTERNALOID) || (lf->prorettype != INTERNALOID)){ luaL_error(L, "pgfunc accepts only 'internal' pllua/u functions with internal argument"); } prosrc = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "[pgfunc]: null lua prosrc"); luaL_buffinit(L, &b); luaL_addstring(&b,"do "); t = DatumGetTextP(prosrc); luaL_addlstring(&b, VARDATA(t), VARSIZE(t) - VARHDRSZ); luaL_addstring(&b, " end"); luaL_pushresult(&b); s = lua_tostring(L, -1); ReleaseSysCache(proctup); clean_pgfuncinfo(lf); if (luaL_loadbuffer(L, s, strlen(s), "pgfunc chunk")) luaL_error(L, "compile"); lua_remove(L, -2); /*delete source element*/ pcall_result = lua_pcall(L, 0, 1, 0); lua_remove(L, -2); /*delete chunk*/ if(pcall_result == 0){ ENDLUAV(1); return 1; } if( pcall_result == LUA_ERRRUN) luaL_error(L,"%s %s","Runtime error:",lua_tostring(L, -1)); else if(pcall_result == LUA_ERRMEM) luaL_error(L,"%s %s","Memory error:",lua_tostring(L, -1)); else if(pcall_result == LUA_ERRERR) luaL_error(L,"%s %s","Error:",lua_tostring(L, -1)); return luaL_error(L, "pgfunc unknown error"); } if(proc->proretset) { lua_pushcclosure(L, pgfunc_rows, 1); } else { fmgr_info(funcid, &lf->fi); lua_pushcclosure(L, pg_callable_func, 1); } ReleaseSysCache(proctup); ENDLUAV(1); return 1; }
/* * initSuffixTree - create suffix tree from file. Function converts * UTF8-encoded file into current encoding. */ static SuffixChar * initSuffixTree(char *filename) { SuffixChar *volatile rootSuffixTree = NULL; MemoryContext ccxt = CurrentMemoryContext; tsearch_readline_state trst; volatile bool skip; filename = get_tsearch_config_filename(filename, "rules"); if (!tsearch_readline_begin(&trst, filename)) ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not open unaccent file \"%s\": %m", filename))); do { /* * pg_do_encoding_conversion() (called by tsearch_readline()) will * emit exception if it finds untranslatable characters in current * locale. We just skip such lines, continuing with the next. */ skip = true; PG_TRY(); { char *line; while ((line = tsearch_readline(&trst)) != NULL) { /* * The format of each line must be "src trg" where src and trg * are sequences of one or more non-whitespace characters, * separated by whitespace. Whitespace at start or end of * line is ignored. */ int state; char *ptr; char *src = NULL; char *trg = NULL; int ptrlen; int srclen = 0; int trglen = 0; state = 0; for (ptr = line; *ptr; ptr += ptrlen) { ptrlen = pg_mblen(ptr); /* ignore whitespace, but end src or trg */ if (t_isspace(ptr)) { if (state == 1) state = 2; else if (state == 3) state = 4; continue; } switch (state) { case 0: /* start of src */ src = ptr; srclen = ptrlen; state = 1; break; case 1: /* continue src */ srclen += ptrlen; break; case 2: /* start of trg */ trg = ptr; trglen = ptrlen; state = 3; break; case 3: /* continue trg */ trglen += ptrlen; break; default: /* bogus line format */ state = -1; break; } } if (state >= 3) rootSuffixTree = placeChar(rootSuffixTree, (unsigned char *) src, srclen, trg, trglen); pfree(line); } skip = false; } PG_CATCH(); { ErrorData *errdata; MemoryContext ecxt; ecxt = MemoryContextSwitchTo(ccxt); errdata = CopyErrorData(); if (errdata->sqlerrcode == ERRCODE_UNTRANSLATABLE_CHARACTER) { FlushErrorState(); } else { MemoryContextSwitchTo(ecxt); PG_RE_THROW(); } } PG_END_TRY(); } while (skip); tsearch_readline_end(&trst); return rootSuffixTree; }
/* function subhandler */ Datum PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc) { Datum rv; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; ErrorContextCallback plerrcontext; PG_TRY(); { if (!proc->is_setof || proc->setof == NULL) { /* * Simple type returning function or first time for SETOF * function: actually execute the function. */ plargs = PLy_function_build_args(fcinfo, proc); plrv = PLy_procedure_call(proc, "args", plargs); if (!proc->is_setof) { /* * SETOF function parameters will be deleted when last row is * returned */ PLy_function_delete_args(proc); } Assert(plrv != NULL); } /* * If it returns a set, call the iterator to get the next return item. * We stay in the SPI context while doing this, because PyIter_Next() * calls back into Python code which might contain SPI calls. */ if (proc->is_setof) { bool has_error = false; ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (proc->setof == NULL) { /* first time -- do checks and setup */ if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_ValuePerCall) == 0) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported set function return mode"), errdetail("PL/Python set-returning functions only support returning only value per call."))); } rsi->returnMode = SFRM_ValuePerCall; /* Make iterator out of returned object */ proc->setof = PyObject_GetIter(plrv); Py_DECREF(plrv); plrv = NULL; if (proc->setof == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("returned object cannot be iterated"), errdetail("PL/Python set-returning functions must return an iterable object."))); } /* Fetch next from iterator */ plrv = PyIter_Next(proc->setof); if (plrv) rsi->isDone = ExprMultipleResult; else { rsi->isDone = ExprEndResult; has_error = PyErr_Occurred() != NULL; } if (rsi->isDone == ExprEndResult) { /* Iterator is exhausted or error happened */ Py_DECREF(proc->setof); proc->setof = NULL; Py_XDECREF(plargs); Py_XDECREF(plrv); PLy_function_delete_args(proc); if (has_error) PLy_elog(ERROR, "error fetching next item from iterator"); /* Disconnect from the SPI manager before returning */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); fcinfo->isnull = true; return (Datum) NULL; } } /* * Disconnect from SPI manager and then create the return values 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). */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); plerrcontext.callback = plpython_return_error_callback; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; /* * If the function is declared to return void, the Python return value * must be None. For void-returning functions, we also treat a None * return value as a special "void datum" rather than NULL (as is the * case for non-void-returning functions). */ if (proc->result.out.d.typoid == VOIDOID) { if (plrv != Py_None) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("PL/Python function with return type \"void\" did not return None"))); fcinfo->isnull = false; rv = (Datum) 0; } else if (plrv == Py_None) { fcinfo->isnull = true; if (proc->result.is_rowtype < 1) rv = InputFunctionCall(&proc->result.out.d.typfunc, NULL, proc->result.out.d.typioparam, -1); else /* Tuple as None */ rv = (Datum) NULL; } else if (proc->result.is_rowtype >= 1) { TupleDesc desc; /* make sure it's not an unnamed record */ Assert((proc->result.out.d.typoid == RECORDOID && proc->result.out.d.typmod != -1) || (proc->result.out.d.typoid != RECORDOID && proc->result.out.d.typmod == -1)); desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid, proc->result.out.d.typmod); rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv); fcinfo->isnull = (rv == (Datum) NULL); } else { fcinfo->isnull = false; rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv); } } PG_CATCH(); { Py_XDECREF(plargs); Py_XDECREF(plrv); /* * If there was an error the iterator might have not been exhausted * yet. Set it to NULL so the next invocation of the function will * start the iteration again. */ Py_XDECREF(proc->setof); proc->setof = NULL; PG_RE_THROW(); } PG_END_TRY(); error_context_stack = plerrcontext.previous; Py_XDECREF(plargs); Py_DECREF(plrv); return rv; }
/* * Assign hook routine for "gp_role" option. This variablle has context * PGC_SUSET so that is can only be set by a superuser via the SET command. * (It can also be set using an option on postmaster start, but this isn't * interesting beccause the derived global CdbRole is always set (along with * CdbSessionRole) on backend startup for a new connection. * * See src/backend/util/misc/guc.c for option definition. */ const char * assign_gp_role(const char *newval, bool doit, GucSource source) { #if FALSE elog(DEBUG1, "assign_gp_role: gp_role=%s, newval=%s, doit=%s", show_gp_role(), newval, (doit ? "true" : "false")); #endif GpRoleValue newrole = string_to_role(newval); GpRoleValue oldrole = Gp_role; if (newrole == GP_ROLE_UNDEFINED) { return NULL; } if (doit) { /* * When changing between roles, we must * call cdb_cleanup and then cdb_setup to get * setup and connections appropriate to the new role. */ bool do_disconnect = false; bool do_connect = false; if (Gp_role != newrole && IsUnderPostmaster) { if (Gp_role != GP_ROLE_UTILITY) do_disconnect = true; if (newrole != GP_ROLE_UTILITY) do_connect = true; } if (do_disconnect) cdb_cleanup(0,0); Gp_role = newrole; if (source != PGC_S_DEFAULT) { if (do_connect) { /* * In case there are problems with the Greenplum Database tables or data, * we catch any error coming out of cdblink_setup so we can set the * gp_role back to what it was. Otherwise we may be left with * inappropriate connections for the new role. */ PG_TRY(); { cdb_setup(); } PG_CATCH(); { cdb_cleanup(0,0); Gp_role = oldrole; if (Gp_role != GP_ROLE_UTILITY) cdb_setup(); PG_RE_THROW(); } PG_END_TRY(); } } } return newval; }
static bool ServiceClientPollRead(ServiceClient *serviceClient, void* response, int responseLen, bool *pollResponseReceived) { ServiceConfig *serviceConfig; int n; int saved_err; char *message; bool result = false; DECLARE_SAVE_SUPPRESS_PANIC(); Assert(serviceClient != NULL); serviceConfig = serviceClient->serviceConfig; Assert(serviceConfig != NULL); Assert(response != NULL); PG_TRY(); { SUPPRESS_PANIC(); /* * Attempt to read the response */ while (true) { n = read(serviceClient->sockfd, ((char *)response), responseLen); saved_err = errno; if (n == 0) { ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Connection to '%s' is closed", serviceConfig->title))); } else if (n < 0) { if (saved_err == EWOULDBLOCK) { *pollResponseReceived = false; break; } if (saved_err == EINTR) continue; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Read error from '%s': %s", serviceConfig->title, strerror(saved_err)))); } if (n != responseLen) { ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Expecting message length %d and actual read length was %d from '%s'", responseLen, n, serviceConfig->title))); return false; } *pollResponseReceived = true; break; } result = true; RESTORE_PANIC(); } PG_CATCH(); { RESTORE_PANIC(); /* Report the error to the server log */ if (!elog_demote(WARNING)) { elog(LOG,"unable to demote error"); PG_RE_THROW(); } message = elog_message(); if (message != NULL && strlen(message) + 1 < sizeof(ClientErrorString)) strcpy(ClientErrorString, message); else strcpy(ClientErrorString, ""); EmitErrorReport(); FlushErrorState(); result = false; } PG_END_TRY(); return result; }
/* trigger subhandler * * the python function is expected to return Py_None if the tuple is * acceptable and unmodified. Otherwise it should return a PyString * object who's value is SKIP, or MODIFY. SKIP means don't perform * this action. MODIFY means the tuple has been modified, so update * tuple and perform action. SKIP and MODIFY assume the trigger fires * BEFORE the event and is ROW level. postgres expects the function * to take no arguments and return an argument of type trigger. */ HeapTuple PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc) { HeapTuple rv = NULL; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; TriggerData *tdata; Assert(CALLED_AS_TRIGGER(fcinfo)); /* * Input/output conversion for trigger tuples. Use the result TypeInfo * variable to store the tuple conversion info. We do this over again on * each call to cover the possibility that the relation's tupdesc changed * since the trigger was last called. PLy_input_tuple_funcs and * PLy_output_tuple_funcs are responsible for not doing repetitive work. */ tdata = (TriggerData *) fcinfo->context; PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); PG_TRY(); { plargs = PLy_trigger_build_args(fcinfo, proc, &rv); plrv = PLy_procedure_call(proc, "TD", plargs); Assert(plrv != NULL); /* * Disconnect from SPI manager */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); /* * return of None means we're happy with the tuple */ if (plrv != Py_None) { char *srv; if (PyString_Check(plrv)) srv = PyString_AsString(plrv); else if (PyUnicode_Check(plrv)) srv = PLyUnicode_AsString(plrv); else { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("unexpected return value from trigger procedure"), errdetail("Expected None or a string."))); srv = NULL; /* keep compiler quiet */ } if (pg_strcasecmp(srv, "SKIP") == 0) rv = NULL; else if (pg_strcasecmp(srv, "MODIFY") == 0) { TriggerData *tdata = (TriggerData *) fcinfo->context; if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event) || TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) rv = PLy_modify_tuple(proc, plargs, tdata, rv); else ereport(WARNING, (errmsg("PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored"))); } else if (pg_strcasecmp(srv, "OK") != 0) { /* * accept "OK" as an alternative to None; otherwise, raise an * error */ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("unexpected return value from trigger procedure"), errdetail("Expected None, \"OK\", \"SKIP\", or \"MODIFY\"."))); } } } PG_CATCH(); { Py_XDECREF(plargs); Py_XDECREF(plrv); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(plargs); Py_DECREF(plrv); return rv; }
inline void * Allocator::internalAllocate(void *inPtr, const size_t inSize) const { // Avoid warning that inPtr is not used if R == NewAllocation (void) inPtr; void *ptr; bool errorOccurred = false; MemoryContext oldContext = NULL; MemoryContext aggContext = NULL; if (F == dbal::ReturnNULL) { /* * HOLD_INTERRUPTS() and RESUME_INTERRUPTS() only change the value of a * global variable but have no other side effects. In particular, they * do not call CHECK_INTERRUPTS(). Hence, we are save to use these * macros outside of a PG_TRY() block. */ HOLD_INTERRUPTS(); } PG_TRY(); { if (MC == dbal::AggregateContext) { if (!AggCheckCallContext(fcinfo, &aggContext)) errorOccurred = true; else { oldContext = MemoryContextSwitchTo(aggContext); ptr = (R == Reallocation) ? internalRePalloc<ZM>(inPtr, inSize) : internalPalloc<ZM>(inSize); MemoryContextSwitchTo(oldContext); } } else { ptr = R ? internalRePalloc<ZM>(inPtr, inSize) : internalPalloc<ZM>(inSize); } } PG_CATCH(); { if (F == dbal::ReturnNULL) { /* * This cannot be due to an interrupt, so it's reasonably safe * to assume that the PG exception was a pure memory-allocation * issue. We ignore the error and flush the error state. * Flushing is necessary for leaving the error state (e.g., the memory * context is restored). */ FlushErrorState(); ptr = NULL; } else { /* * PostgreSQL error messages can be stacked. So, it doesn't hurt to add * our own message. After unwinding the C++ stack, the PostgreSQL * exception will be re-thrown into the PostgreSQL C code. * * Throwing C++ exceptions inside a PG_CATCH block is not problematic * per se, but it is good practise to keep the exception mechanisms clearly * separated. */ errorOccurred = true; } } PG_END_TRY(); if (errorOccurred) { PG_TRY(); { // Clean up after ourselves if (oldContext != NULL) MemoryContextSwitchTo(oldContext); } PG_CATCH(); { if (F == dbal::ReturnNULL) { // We tried to clean up after ourselves. If this fails, we can // only ignore the issue. FlushErrorState(); } // Else do nothing. We will add a bad-allocation exception on top of // the existing PostgreSQL exception stack. } PG_END_TRY(); } if (F == dbal::ReturnNULL) { RESUME_INTERRUPTS(); } if (errorOccurred || !ptr) // We do not want to interleave PG exceptions and C++ exceptions. throw std::bad_alloc(); return ptr; }
static bool ServiceDoConnect(ServiceConfig *serviceConfig, int listenerPort, ServiceClient *serviceClient, bool complain) { int n; struct sockaddr_in addr; int saved_err; char *message; bool result = false; DECLARE_SAVE_SUPPRESS_PANIC(); PG_TRY(); { SUPPRESS_PANIC(); for (;;) { /* * Open a connection to the service. */ serviceClient->sockfd = socket(AF_INET, SOCK_STREAM, 0); addr.sin_family = AF_INET; addr.sin_port = htons(listenerPort); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if ((n = connect(serviceClient->sockfd, (struct sockaddr *)&addr, sizeof(addr))) < 0) { saved_err = errno; close(serviceClient->sockfd); serviceClient->sockfd = -1; if (errno == EINTR) continue; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Could not connect to '%s': %s", serviceConfig->title, strerror(saved_err)))); } else { //success. we're done here! break; } } /* make socket non-blocking BEFORE we connect. */ if (!pg_set_noblock(serviceClient->sockfd)) { saved_err = errno; close(serviceClient->sockfd); serviceClient->sockfd = -1; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Could not set '%s' socket to non-blocking mode: %s", serviceConfig->title, strerror(saved_err)))); } result = true; RESTORE_PANIC(); } PG_CATCH(); { RESTORE_PANIC(); /* Report the error to the server log */ if (!elog_demote(WARNING)) { elog(LOG,"unable to demote error"); PG_RE_THROW(); } message = elog_message(); if (message != NULL && strlen(message) + 1 < sizeof(ClientErrorString)) strcpy(ClientErrorString, message); else strcpy(ClientErrorString, ""); if (complain) EmitErrorReport(); FlushErrorState(); result = false; } PG_END_TRY(); return result; }
/* * Create a new PLyProcedure structure */ static PLyProcedure * PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) { char procName[NAMEDATALEN + 256]; Form_pg_proc procStruct; PLyProcedure *volatile proc; MemoryContext cxt; MemoryContext oldcxt; int rv; char *ptr; procStruct = (Form_pg_proc) GETSTRUCT(procTup); rv = snprintf(procName, sizeof(procName), "__plpython_procedure_%s_%u", NameStr(procStruct->proname), fn_oid); if (rv >= sizeof(procName) || rv < 0) elog(ERROR, "procedure name would overrun buffer"); /* Replace any not-legal-in-Python-names characters with '_' */ for (ptr = procName; *ptr; ptr++) { if (!((*ptr >= 'A' && *ptr <= 'Z') || (*ptr >= 'a' && *ptr <= 'z') || (*ptr >= '0' && *ptr <= '9'))) *ptr = '_'; } cxt = AllocSetContextCreate(TopMemoryContext, procName, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(cxt); proc = (PLyProcedure *) palloc0(sizeof(PLyProcedure)); proc->mcxt = cxt; PG_TRY(); { Datum protrftypes_datum; Datum prosrcdatum; bool isnull; char *procSource; int i; proc->proname = pstrdup(NameStr(procStruct->proname)); proc->pyname = pstrdup(procName); proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data); proc->fn_tid = procTup->t_self; /* Remember if function is STABLE/IMMUTABLE */ proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); PLy_typeinfo_init(&proc->result, proc->mcxt); for (i = 0; i < FUNC_MAX_ARGS; i++) PLy_typeinfo_init(&proc->args[i], proc->mcxt); proc->nargs = 0; proc->langid = procStruct->prolang; protrftypes_datum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_protrftypes, &isnull); proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum); proc->code = proc->statics = NULL; proc->globals = NULL; proc->is_setof = procStruct->proretset; proc->setof = NULL; proc->src = NULL; proc->argnames = NULL; /* * get information required for output conversion of the return value, * but only if this isn't a trigger. */ if (!is_trigger) { HeapTuple rvTypeTup; Form_pg_type rvTypeStruct; rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(procStruct->prorettype)); if (!HeapTupleIsValid(rvTypeTup)) elog(ERROR, "cache lookup failed for type %u", procStruct->prorettype); rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup); /* Disallow pseudotype result, except for void or record */ if (rvTypeStruct->typtype == TYPTYPE_PSEUDO) { if (procStruct->prorettype == TRIGGEROID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions can only be called as triggers"))); else if (procStruct->prorettype != VOIDOID && procStruct->prorettype != RECORDOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PL/Python functions cannot return type %s", format_type_be(procStruct->prorettype)))); } if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE || procStruct->prorettype == RECORDOID) { /* * Tuple: set up later, during first call to * PLy_function_handler */ proc->result.out.d.typoid = procStruct->prorettype; proc->result.out.d.typmod = -1; proc->result.is_rowtype = 2; } else { /* do the real work */ PLy_output_datum_func(&proc->result, rvTypeTup, proc->langid, proc->trftypes); } ReleaseSysCache(rvTypeTup); } /* * Now get information required for input conversion of the * procedure's arguments. Note that we ignore output arguments here. * If the function returns record, those I/O functions will be set up * when the function is first called. */ if (procStruct->pronargs) { Oid *types; char **names, *modes; int pos, total; /* extract argument type info from the pg_proc tuple */ total = get_func_arg_info(procTup, &types, &names, &modes); /* count number of in+inout args into proc->nargs */ if (modes == NULL) proc->nargs = total; else { /* proc->nargs was initialized to 0 above */ for (i = 0; i < total; i++) { if (modes[i] != PROARGMODE_OUT && modes[i] != PROARGMODE_TABLE) (proc->nargs)++; } } proc->argnames = (char **) palloc0(sizeof(char *) * proc->nargs); for (i = pos = 0; i < total; i++) { HeapTuple argTypeTup; Form_pg_type argTypeStruct; if (modes && (modes[i] == PROARGMODE_OUT || modes[i] == PROARGMODE_TABLE)) continue; /* skip OUT arguments */ Assert(types[i] == procStruct->proargtypes.values[pos]); argTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(types[i])); if (!HeapTupleIsValid(argTypeTup)) elog(ERROR, "cache lookup failed for type %u", types[i]); argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup); /* check argument type is OK, set up I/O function info */ switch (argTypeStruct->typtype) { case TYPTYPE_PSEUDO: /* Disallow pseudotype argument */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PL/Python functions cannot accept type %s", format_type_be(types[i])))); break; case TYPTYPE_COMPOSITE: /* we'll set IO funcs at first call */ proc->args[pos].is_rowtype = 2; break; default: PLy_input_datum_func(&(proc->args[pos]), types[i], argTypeTup, proc->langid, proc->trftypes); break; } /* get argument name */ proc->argnames[pos] = names ? pstrdup(names[i]) : NULL; ReleaseSysCache(argTypeTup); pos++; } } /* * get the text of the function. */ prosrcdatum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); procSource = TextDatumGetCString(prosrcdatum); PLy_procedure_compile(proc, procSource); pfree(procSource); } PG_CATCH(); { MemoryContextSwitchTo(oldcxt); PLy_procedure_delete(proc); PG_RE_THROW(); } PG_END_TRY(); MemoryContextSwitchTo(oldcxt); return proc; }
/* * Primary entry point for VACUUM and ANALYZE commands. * * options is a bitmask of VacuumOption flags, indicating what to do. * * relid, if not InvalidOid, indicate the relation to process; otherwise, * the RangeVar is used. (The latter must always be passed, because it's * used for error messages.) * * params contains a set of parameters that can be used to customize the * behavior. * * va_cols is a list of columns to analyze, or NIL to process them all. * * bstrategy is normally given as NULL, but in autovacuum it can be passed * in to use the same buffer strategy object across multiple vacuum() calls. * * isTopLevel should be passed down from ProcessUtility. * * It is the caller's responsibility that all parameters are allocated in a * memory context that will not disappear at transaction commit. */ void vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params, List *va_cols, BufferAccessStrategy bstrategy, bool isTopLevel) { // TODO :: Peloton Changes // Disabled vacuumming in Postgres if(false){ const char *stmttype; volatile bool in_outer_xact, use_own_xacts; List *relations; static bool in_vacuum = false; Assert(params != NULL); stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE"; /* * We cannot run VACUUM inside a user transaction block; if we were inside * a transaction, then our commit- and start-transaction-command calls * would not have the intended effect! There are numerous other subtle * dependencies on this, too. * * ANALYZE (without VACUUM) can run either way. */ if (options & VACOPT_VACUUM) { PreventTransactionChain(isTopLevel, stmttype); in_outer_xact = false; } else in_outer_xact = IsInTransactionChain(isTopLevel); /* * Due to static variables vac_context, anl_context and vac_strategy, * vacuum() is not reentrant. This matters when VACUUM FULL or ANALYZE * calls a hostile index expression that itself calls ANALYZE. */ if (in_vacuum) elog(ERROR, "%s cannot be executed from VACUUM or ANALYZE", stmttype); /* * Send info about dead objects to the statistics collector, unless we are * in autovacuum --- autovacuum.c does this for itself. */ if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) pgstat_vacuum_stat(); /* * Create special memory context for cross-transaction storage. * * Since it is a child of PortalContext, it will go away eventually even * if we suffer an error; there's no need for special abort cleanup logic. */ vac_context = AllocSetContextCreate(PortalContext, "Vacuum", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* * If caller didn't give us a buffer strategy object, make one in the * cross-transaction memory context. */ if (bstrategy == NULL) { MemoryContext old_context = MemoryContextSwitchTo(vac_context); bstrategy = GetAccessStrategy(BAS_VACUUM); MemoryContextSwitchTo(old_context); } vac_strategy = bstrategy; /* * Build list of relations to process, unless caller gave us one. (If we * build one, we put it in vac_context for safekeeping.) */ relations = get_rel_oids(relid, relation); /* * Decide whether we need to start/commit our own transactions. * * For VACUUM (with or without ANALYZE): always do so, so that we can * release locks as soon as possible. (We could possibly use the outer * transaction for a one-table VACUUM, but handling TOAST tables would be * problematic.) * * For ANALYZE (no VACUUM): if inside a transaction block, we cannot * start/commit our own transactions. Also, there's no need to do so if * only processing one relation. For multiple relations when not within a * transaction block, and also in an autovacuum worker, use own * transactions so we can release locks sooner. */ if (options & VACOPT_VACUUM) use_own_xacts = true; else { Assert(options & VACOPT_ANALYZE); if (IsAutoVacuumWorkerProcess()) use_own_xacts = true; else if (in_outer_xact) use_own_xacts = false; else if (list_length(relations) > 1) use_own_xacts = true; else use_own_xacts = false; } /* * vacuum_rel expects to be entered with no transaction active; it will * start and commit its own transaction. But we are called by an SQL * command, and so we are executing inside a transaction already. We * commit the transaction started in PostgresMain() here, and start * another one before exiting to match the commit waiting for us back in * PostgresMain(). */ if (use_own_xacts) { Assert(!in_outer_xact); /* ActiveSnapshot is not set by autovacuum */ if (ActiveSnapshotSet()) PopActiveSnapshot(); /* matches the StartTransaction in PostgresMain() */ CommitTransactionCommand(); } /* Turn vacuum cost accounting on or off */ PG_TRY(); { ListCell *cur; in_vacuum = true; VacuumCostActive = (VacuumCostDelay > 0); VacuumCostBalance = 0; VacuumPageHit = 0; VacuumPageMiss = 0; VacuumPageDirty = 0; /* * Loop to process each selected relation. */ foreach(cur, relations) { Oid relid = lfirst_oid(cur); if (options & VACOPT_VACUUM) { if (!vacuum_rel(relid, relation, options, params)) continue; } if (options & VACOPT_ANALYZE) { /* * If using separate xacts, start one for analyze. Otherwise, * we can use the outer transaction. */ if (use_own_xacts) { StartTransactionCommand(); /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); } analyze_rel(relid, relation, options, params, va_cols, in_outer_xact, vac_strategy); if (use_own_xacts) { PopActiveSnapshot(); CommitTransactionCommand(); } } } } PG_CATCH(); { in_vacuum = false; VacuumCostActive = false; PG_RE_THROW(); } PG_END_TRY(); in_vacuum = false; VacuumCostActive = false; /* * Finish up processing. */ if (use_own_xacts) { /* here, we are not in a transaction */ /* * This matches the CommitTransaction waiting for us in * PostgresMain(). */ StartTransactionCommand(); } if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) { /* * Update pg_database.datfrozenxid, and truncate pg_clog if possible. * (autovacuum.c does this for itself.) */ vac_update_datfrozenxid(); } /* * Clean up working storage --- note we must do this after * StartTransactionCommand, else we might be trying to delete the active * context! */ MemoryContextDelete(vac_context); vac_context = NULL; }
/* prepare(query="select * from foo") * prepare(query="select * from foo where bar = $1", params=["text"]) * prepare(query="select * from foo where bar = $1", params=["text"], limit=5) */ PyObject * PLy_spi_prepare(PyObject *self, PyObject *args) { PLyPlanObject *plan; PyObject *list = NULL; PyObject *volatile optr = NULL; char *query; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; volatile int nargs; if (!PyArg_ParseTuple(args, "s|O", &query, &list)) return NULL; if (list && (!PySequence_Check(list))) { PLy_exception_set(PyExc_TypeError, "second argument of plpy.prepare must be a sequence"); return NULL; } if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) return NULL; plan->mcxt = AllocSetContextCreate(TopMemoryContext, "PL/Python plan context", ALLOCSET_DEFAULT_SIZES); oldcontext = MemoryContextSwitchTo(plan->mcxt); nargs = list ? PySequence_Length(list) : 0; plan->nargs = nargs; plan->types = nargs ? palloc(sizeof(Oid) * nargs) : NULL; plan->values = nargs ? palloc(sizeof(Datum) * nargs) : NULL; plan->args = nargs ? palloc(sizeof(PLyTypeInfo) * nargs) : NULL; MemoryContextSwitchTo(oldcontext); oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { int i; PLyExecutionContext *exec_ctx = PLy_current_execution_context(); /* * the other loop might throw an exception, if PLyTypeInfo member * isn't properly initialized the Py_DECREF(plan) will go boom */ for (i = 0; i < nargs; i++) { PLy_typeinfo_init(&plan->args[i], plan->mcxt); plan->values[i] = PointerGetDatum(NULL); } for (i = 0; i < nargs; i++) { char *sptr; HeapTuple typeTup; Oid typeId; int32 typmod; optr = PySequence_GetItem(list, i); if (PyString_Check(optr)) sptr = PyString_AsString(optr); else if (PyUnicode_Check(optr)) sptr = PLyUnicode_AsString(optr); else { ereport(ERROR, (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i))); sptr = NULL; /* keep compiler quiet */ } /******************************************************** * Resolve argument type names and then look them up by * oid in the system cache, and remember the required *information for input conversion. ********************************************************/ parseTypeString(sptr, &typeId, &typmod, false); typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeId)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", typeId); Py_DECREF(optr); /* * set optr to NULL, so we won't try to unref it again in case of * an error */ optr = NULL; plan->types[i] = typeId; PLy_output_datum_func(&plan->args[i], typeTup, exec_ctx->curr_proc->langid, exec_ctx->curr_proc->trftypes); ReleaseSysCache(typeTup); } pg_verifymbstr(query, strlen(query), false); plan->plan = SPI_prepare(query, plan->nargs, plan->types); if (plan->plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); /* transfer plan from procCxt to topCxt */ if (SPI_keepplan(plan->plan)) elog(ERROR, "SPI_keepplan failed"); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { Py_DECREF(plan); Py_XDECREF(optr); PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(plan->plan != NULL); return (PyObject *) plan; }
static PyObject * PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status) { PLyResultObject *result; volatile MemoryContext oldcontext; result = (PLyResultObject *) PLy_result_new(); Py_DECREF(result->status); result->status = PyInt_FromLong(status); if (status > 0 && tuptable == NULL) { Py_DECREF(result->nrows); result->nrows = (rows > (uint64) LONG_MAX) ? PyFloat_FromDouble((double) rows) : PyInt_FromLong((long) rows); } else if (status > 0 && tuptable != NULL) { PLyTypeInfo args; MemoryContext cxt; Py_DECREF(result->nrows); result->nrows = (rows > (uint64) LONG_MAX) ? PyFloat_FromDouble((double) rows) : PyInt_FromLong((long) rows); cxt = AllocSetContextCreate(CurrentMemoryContext, "PL/Python temp context", ALLOCSET_DEFAULT_SIZES); PLy_typeinfo_init(&args, cxt); oldcontext = CurrentMemoryContext; PG_TRY(); { MemoryContext oldcontext2; if (rows) { uint64 i; /* * PyList_New() and PyList_SetItem() use Py_ssize_t for list * size and list indices; so we cannot support a result larger * than PY_SSIZE_T_MAX. */ if (rows > (uint64) PY_SSIZE_T_MAX) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("query result has too many rows to fit in a Python list"))); Py_DECREF(result->rows); result->rows = PyList_New(rows); PLy_input_tuple_funcs(&args, tuptable->tupdesc); for (i = 0; i < rows; i++) { PyObject *row = PLyDict_FromTuple(&args, tuptable->vals[i], tuptable->tupdesc); PyList_SetItem(result->rows, i, row); } } /* * Save tuple descriptor for later use by result set metadata * functions. Save it in TopMemoryContext so that it survives * outside of an SPI context. We trust that PLy_result_dealloc() * will clean it up when the time is right. (Do this as late as * possible, to minimize the number of ways the tupdesc could get * leaked due to errors.) */ oldcontext2 = MemoryContextSwitchTo(TopMemoryContext); result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc); MemoryContextSwitchTo(oldcontext2); } PG_CATCH(); { MemoryContextSwitchTo(oldcontext); MemoryContextDelete(cxt); Py_DECREF(result); PG_RE_THROW(); } PG_END_TRY(); MemoryContextDelete(cxt); SPI_freetuptable(tuptable); } return (PyObject *) result; }
static PyObject * PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit) { volatile int nargs; int i, rv; PLyPlanObject *plan; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; PyObject *ret; if (list != NULL) { if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list)) { PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument"); return NULL; } nargs = PySequence_Length(list); } else nargs = 0; plan = (PLyPlanObject *) ob; if (nargs != plan->nargs) { char *sv; PyObject *so = PyObject_Str(list); if (!so) PLy_elog(ERROR, "could not execute plan"); sv = PyString_AsString(so); PLy_exception_set_plural(PyExc_TypeError, "Expected sequence of %d argument, got %d: %s", "Expected sequence of %d arguments, got %d: %s", plan->nargs, plan->nargs, nargs, sv); Py_DECREF(so); return NULL; } oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); char *volatile nulls; volatile int j; if (nargs > 0) nulls = palloc(nargs * sizeof(char)); else nulls = NULL; for (j = 0; j < nargs; j++) { PyObject *elem; elem = PySequence_GetItem(list, j); if (elem != Py_None) { PG_TRY(); { plan->values[j] = plan->args[j].out.d.func(&(plan->args[j].out.d), -1, elem); } PG_CATCH(); { Py_DECREF(elem); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(elem); nulls[j] = ' '; } else { Py_DECREF(elem); plan->values[j] = InputFunctionCall(&(plan->args[j].out.d.typfunc), NULL, plan->args[j].out.d.typioparam, -1); nulls[j] = 'n'; } } rv = SPI_execute_plan(plan->plan, plan->values, nulls, exec_ctx->curr_proc->fn_readonly, limit); ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); if (nargs > 0) pfree(nulls); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { int k; /* * cleanup plan->values array */ for (k = 0; k < nargs; k++) { if (!plan->args[k].out.d.typbyval && (plan->values[k] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[k])); plan->values[k] = PointerGetDatum(NULL); } } PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); for (i = 0; i < nargs; i++) { if (!plan->args[i].out.d.typbyval && (plan->values[i] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[i])); plan->values[i] = PointerGetDatum(NULL); } } if (rv < 0) { PLy_exception_set(PLy_exc_spi_error, "SPI_execute_plan failed: %s", SPI_result_code_string(rv)); return NULL; } return ret; }
/* * PersistHoldablePortal * * Prepare the specified Portal for access outside of the current * transaction. When this function returns, all future accesses to the * portal must be done via the Tuplestore (not by invoking the * executor). */ void PersistHoldablePortal(Portal portal) { QueryDesc *queryDesc = PortalGetQueryDesc(portal); Portal saveActivePortal; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext oldcxt; /* * If we're preserving a holdable portal, we had better be inside the * transaction that originally created it. */ Assert(portal->createSubid != InvalidSubTransactionId); Assert(queryDesc != NULL); /* * Caller must have created the tuplestore already ... but not a snapshot. */ Assert(portal->holdContext != NULL); Assert(portal->holdStore != NULL); Assert(portal->holdSnapshot == NULL); /* * Before closing down the executor, we must copy the tupdesc into * long-term memory, since it was created in executor memory. */ oldcxt = MemoryContextSwitchTo(portal->holdContext); portal->tupDesc = CreateTupleDescCopy(portal->tupDesc); MemoryContextSwitchTo(oldcxt); /* * Check for improper portal use, and mark portal active. */ MarkPortalActive(portal); /* * Set up global portal context pointers. */ saveActivePortal = ActivePortal; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; if (portal->resowner) CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); MemoryContextSwitchTo(PortalContext); PushActiveSnapshot(queryDesc->snapshot); /* * Rewind the executor: we need to store the entire result set in the * tuplestore, so that subsequent backward FETCHs can be processed. */ ExecutorRewind(queryDesc); /* * Change the destination to output to the tuplestore. Note we tell * the tuplestore receiver to detoast all data passed through it; this * makes it safe to not keep a snapshot associated with the data. */ queryDesc->dest = CreateDestReceiver(DestTuplestore); SetTuplestoreDestReceiverParams(queryDesc->dest, portal->holdStore, portal->holdContext, true); /* Fetch the result set into the tuplestore */ ExecutorRun(queryDesc, ForwardScanDirection, 0L, false); (*queryDesc->dest->rDestroy) (queryDesc->dest); queryDesc->dest = NULL; /* * Now shut down the inner executor. */ portal->queryDesc = NULL; /* prevent double shutdown */ ExecutorFinish(queryDesc); ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); /* * Set the position in the result set. */ MemoryContextSwitchTo(portal->holdContext); if (portal->atEnd) { /* * Just force the tuplestore forward to its end. The size of the * skip request here is arbitrary. */ while (tuplestore_skiptuples(portal->holdStore, 1000000, true)) /* continue */ ; } else { tuplestore_rescan(portal->holdStore); if (!tuplestore_skiptuples(portal->holdStore, portal->portalPos, true)) elog(ERROR, "unexpected end of tuple stream"); } } PG_CATCH(); { /* Uncaught error while executing portal: mark it dead */ MarkPortalFailed(portal); /* Restore global vars and propagate error */ ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; PG_RE_THROW(); } PG_END_TRY(); MemoryContextSwitchTo(oldcxt); /* Mark portal not active */ portal->status = PORTAL_READY; ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; PopActiveSnapshot(); /* * We can now release any subsidiary memory of the portal's heap context; * we'll never use it again. The executor already dropped its context, * but this will clean up anything that glommed onto the portal's heap via * PortalContext. */ MemoryContextDeleteChildren(PortalGetHeapMemory(portal)); }
static int64 get_size_from_segDBs(const char * cmd) { int spiresult; bool succeeded = false; int64 result = 0; volatile bool connected = false; Assert(Gp_role == GP_ROLE_DISPATCH); PG_TRY(); { HeapTuple tup; TupleDesc tupdesc; bool isnull; Datum size; do { /* Establish an SPI session as a client of myself. */ if (SPI_connect() != SPI_OK_CONNECT) break; connected = true; /* Do the query. */ spiresult = SPI_execute(cmd, false, 0); /* Did the query succeed? */ if (spiresult != SPI_OK_SELECT) break; if (SPI_processed < 1) break; tup = SPI_tuptable->vals[0]; tupdesc = SPI_tuptable->tupdesc; size = heap_getattr(SPI_tuptable->vals[0], 1, SPI_tuptable->tupdesc, &isnull); if (isnull) break; result = DatumGetInt64(size); succeeded = true; } while (0); /* End recursive session. */ connected = false; SPI_finish(); if (!succeeded) elog(ERROR, "Unable to get sizes from segments"); } /* Clean up in case of error. */ PG_CATCH(); { /* End recursive session. */ if (connected) SPI_finish(); /* Carry on with error handling. */ PG_RE_THROW(); } PG_END_TRY(); return result; }
/* * ExecWorkFile_Write * write the given data from the end of the last write position. * * This function returns true if the write succeeds. Otherwise, return false. */ bool ExecWorkFile_Write(ExecWorkFile *workfile, void *data, uint64 size) { Assert(workfile != NULL); uint64 bytes; if (data == NULL || size == 0) { return false; } /* Test the per-query and per-segment limit */ if ((workfile->flags & EXEC_WORKFILE_LIMIT_SIZE) && !WorkfileDiskspace_Reserve(size)) { /* Failed to reserve additional disk space, notify caller */ workfile_mgr_report_error(); } switch(workfile->fileType) { case BUFFILE: {} BufFile *buffile = (BufFile *)workfile->file; int64 current_size = BufFileGetSize(buffile); int64 new_size = 0; PG_TRY(); { bytes = BufFileWrite(buffile, data, size); } PG_CATCH(); { new_size = BufFileGetSize(buffile); workfile->size = new_size; WorkfileDiskspace_Commit( (new_size - current_size), size, true /* update_query_size */); PG_RE_THROW(); } PG_END_TRY(); new_size = BufFileGetSize(buffile); workfile->size = new_size; WorkfileDiskspace_Commit( (new_size - current_size), size, true /* update_query_size */); workfile_update_in_progress_size(workfile, new_size - current_size); if (bytes != size) { workfile_mgr_report_error(); } break; case BFZ: PG_TRY(); { bfz_append((bfz_t *)workfile->file, data, size); } PG_CATCH(); { Assert(WorkfileDiskspace_IsFull()); WorkfileDiskspace_Commit(0, size, true /* update_query_size */); PG_RE_THROW(); } PG_END_TRY(); /* bfz_append always adds to the file size */ workfile->size += size; if ((workfile->flags & EXEC_WORKFILE_LIMIT_SIZE)) { WorkfileDiskspace_Commit(size, size, true /* update_query_size */); } workfile_update_in_progress_size(workfile, size); break; default: insist_log(false, "invalid work file type: %d", workfile->fileType); } return true; }
/* * lo_export - * exports an (inversion) large object. */ static pg_attribute_unused() Datum lo_export_pg(PG_FUNCTION_ARGS) { Oid lobjId = PG_GETARG_OID(0); text *filename = PG_GETARG_TEXT_PP(1); int fd; int nbytes, tmp; char buf[BUFSIZE]; char fnamebuf[MAXPGPATH]; LargeObjectDesc *lobj; mode_t oumask; #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to use server-side lo_export()"), errhint("Anyone can use the client-side lo_export() provided by libpq."))); #endif CreateFSContext(); /* * open the inversion object (no need to test for failure) */ lobj = inv_open(lobjId, INV_READ, fscxt); /* * open the file to be written to * * Note: we reduce backend's normal 077 umask to the slightly friendlier * 022. This code used to drop it all the way to 0, but creating * world-writable export files doesn't seem wise. */ text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); oumask = umask(S_IWGRP | S_IWOTH); PG_TRY(); { fd = OpenTransientFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); } PG_CATCH(); { umask(oumask); PG_RE_THROW(); } PG_END_TRY(); umask(oumask); if (fd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create server file \"%s\": %m", fnamebuf))); /* * read in from the inversion file and write to the filesystem */ while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) { tmp = write(fd, buf, nbytes); if (tmp != nbytes) ereport(ERROR, (errcode_for_file_access(), errmsg("could not write server file \"%s\": %m", fnamebuf))); } CloseTransientFile(fd); inv_close(lobj); PG_RETURN_INT32(1); }
static bool ServiceClientRead(ServiceClient *serviceClient, void* response, int responseLen, struct timeval *timeout) { ServiceConfig *serviceConfig; int n; int bytesRead = 0; int saved_err; char *message; bool result = false; mpp_fd_set rset; struct timeval rundownTimeout = {0,0}; // Use local variable since select modifies // the timeout parameter with remaining time. DECLARE_SAVE_SUPPRESS_PANIC(); Assert(serviceClient != NULL); serviceConfig = serviceClient->serviceConfig; Assert(serviceConfig != NULL); Assert(response != NULL); if (timeout != NULL) rundownTimeout = *timeout; PG_TRY(); { SUPPRESS_PANIC(); /* * read the response */ while (bytesRead < responseLen) { n = read(serviceClient->sockfd, ((char *)response) + bytesRead, responseLen - bytesRead); saved_err = errno; if (n == 0) { ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Connection to '%s' is closed (%d)", serviceConfig->title, serviceClient->sockfd))); } if (n < 0) { if (saved_err != EINTR && saved_err != EWOULDBLOCK) { ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Read error from '%s': %s (%d)", serviceConfig->title, strerror(saved_err), serviceClient->sockfd))); } if (saved_err == EWOULDBLOCK) { /* we shouldn't really get here since we are dealing with * small messages, but once we've read a bit of data we * need to finish out reading till we get the message (or error) */ do { MPP_FD_ZERO(&rset); MPP_FD_SET(serviceClient->sockfd, &rset); n = select(serviceClient->sockfd + 1, (fd_set *)&rset, NULL, NULL, (timeout == NULL ? NULL : &rundownTimeout)); if (n == 0) { if (timeout != NULL) { ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Read from '%s' timed out after %d.%03d seconds", serviceConfig->title, (int)timeout->tv_sec, (int)timeout->tv_usec / 1000))); } } else if (n < 0 && errno == EINTR) continue; else if (n < 0) { saved_err = errno; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Read error from '%s': %s (%d)", serviceConfig->title, strerror(saved_err), serviceClient->sockfd))); } } while (n < 1); } /* else saved_err == EINTR */ continue; } else bytesRead += n; } result = true; RESTORE_PANIC(); } PG_CATCH(); { RESTORE_PANIC(); /* Report the error to the server log */ if (!elog_demote(WARNING)) { elog(LOG,"unable to demote error"); PG_RE_THROW(); } message = elog_message(); if (message != NULL && strlen(message) + 1 < sizeof(ClientErrorString)) strcpy(ClientErrorString, message); else strcpy(ClientErrorString, ""); EmitErrorReport(); FlushErrorState(); result = false; } PG_END_TRY(); return result; }
/* * Helper function for the various SQL callable logical decoding functions. */ static Datum pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool binary) { Name name; XLogRecPtr upto_lsn; int32 upto_nchanges; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext per_query_ctx; MemoryContext oldcontext; XLogRecPtr end_of_wal; XLogRecPtr startptr; LogicalDecodingContext *ctx; ResourceOwner old_resowner = CurrentResourceOwner; ArrayType *arr; Size ndim; List *options = NIL; DecodingOutputState *p; check_permissions(); CheckLogicalDecodingRequirements(); if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("slot name must not be null"))); name = PG_GETARG_NAME(0); if (PG_ARGISNULL(1)) upto_lsn = InvalidXLogRecPtr; else upto_lsn = PG_GETARG_LSN(1); if (PG_ARGISNULL(2)) upto_nchanges = InvalidXLogRecPtr; else upto_nchanges = PG_GETARG_INT32(2); if (PG_ARGISNULL(3)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("options array must not be null"))); arr = PG_GETARG_ARRAYTYPE_P(3); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); /* state to write output to */ p = palloc0(sizeof(DecodingOutputState)); p->binary_output = binary; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* Deconstruct options array */ ndim = ARR_NDIM(arr); if (ndim > 1) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must be one-dimensional"))); } else if (array_contains_nulls(arr)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must not contain nulls"))); } else if (ndim == 1) { int nelems; Datum *datum_opts; int i; Assert(ARR_ELEMTYPE(arr) == TEXTOID); deconstruct_array(arr, TEXTOID, -1, false, 'i', &datum_opts, NULL, &nelems); if (nelems % 2 != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must have even number of elements"))); for (i = 0; i < nelems; i += 2) { char *name = TextDatumGetCString(datum_opts[i]); char *opt = TextDatumGetCString(datum_opts[i + 1]); options = lappend(options, makeDefElem(name, (Node *) makeString(opt))); } } p->tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = p->tupstore; rsinfo->setDesc = p->tupdesc; ReplicationSlotAcquire(NameStr(*name)); PG_TRY(); { /* * Passing InvalidXLogRecPtr here causes replay to start at the slot's * confirmed_flush. */ ctx = CreateDecodingContext(InvalidXLogRecPtr, options, logical_read_local_xlog_page, LogicalOutputPrepareWrite, LogicalOutputWrite); MemoryContextSwitchTo(oldcontext); /* * Check whether the output plugin writes textual output if that's * what we need. */ if (!binary && ctx->options.output_type !=OUTPUT_PLUGIN_TEXTUAL_OUTPUT) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("logical decoding output plugin \"%s\" produces binary output, but function \"%s\" expects textual data", NameStr(MyReplicationSlot->data.plugin), format_procedure(fcinfo->flinfo->fn_oid)))); ctx->output_writer_private = p; /* * We start reading xlog from the restart lsn, even though in * CreateDecodingContext we set the snapshot builder up using the * slot's confirmed_flush. This means we might read xlog we don't * actually decode rows from, but the snapshot builder might need it * to get to a consistent point. The point we start returning data to * *users* at is the confirmed_flush lsn set up in the decoding * context. */ startptr = MyReplicationSlot->data.restart_lsn; CurrentResourceOwner = ResourceOwnerCreate(CurrentResourceOwner, "logical decoding"); /* invalidate non-timetravel entries */ InvalidateSystemCaches(); if (!RecoveryInProgress()) end_of_wal = GetFlushRecPtr(); else end_of_wal = GetXLogReplayRecPtr(NULL); /* Decode until we run out of records */ while ((startptr != InvalidXLogRecPtr && startptr < end_of_wal) || (ctx->reader->EndRecPtr != InvalidXLogRecPtr && ctx->reader->EndRecPtr < end_of_wal)) { XLogRecord *record; char *errm = NULL; record = XLogReadRecord(ctx->reader, startptr, &errm); if (errm) elog(ERROR, "%s", errm); /* * Now that we've set up the xlog reader state, subsequent calls * pass InvalidXLogRecPtr to say "continue from last record" */ startptr = InvalidXLogRecPtr; /* * The {begin_txn,change,commit_txn}_wrapper callbacks above will * store the description into our tuplestore. */ if (record != NULL) LogicalDecodingProcessRecord(ctx, ctx->reader); /* check limits */ if (upto_lsn != InvalidXLogRecPtr && upto_lsn <= ctx->reader->EndRecPtr) break; if (upto_nchanges != 0 && upto_nchanges <= p->returned_rows) break; CHECK_FOR_INTERRUPTS(); } tuplestore_donestoring(tupstore); CurrentResourceOwner = old_resowner; /* * Next time, start where we left off. (Hunting things, the family * business..) */ if (ctx->reader->EndRecPtr != InvalidXLogRecPtr && confirm) LogicalConfirmReceivedLocation(ctx->reader->EndRecPtr); /* free context, call shutdown callback */ FreeDecodingContext(ctx); ReplicationSlotRelease(); InvalidateSystemCaches(); } PG_CATCH(); { /* clear all timetravel entries */ InvalidateSystemCaches(); PG_RE_THROW(); } PG_END_TRY(); return (Datum) 0; }
static text* _nls_run_strxfrm(text *string, text *locale) { char *string_str; int string_len; char *locale_str = NULL; int locale_len = 0; text *result; char *tmp = NULL; size_t size = 0; size_t rest = 0; int changed_locale = 0; /* * Save the default, server-wide locale setting. * It should not change during the life-span of the server so it * is safe to save it only once, during the first invocation. */ if (!lc_collate_cache) { if ((lc_collate_cache = setlocale(LC_COLLATE, NULL))) /* Make a copy of the locale name string. */ lc_collate_cache = strdup(lc_collate_cache); if (!lc_collate_cache) elog(ERROR, "failed to retrieve the default LC_COLLATE value"); } /* * To run strxfrm, we need a zero-terminated strings. */ string_len = VARSIZE_ANY_EXHDR(string); if (string_len < 0) return NULL; string_str = palloc(string_len + 1); memcpy(string_str, VARDATA_ANY(string), string_len); *(string_str + string_len) = '\0'; if (locale) { locale_len = VARSIZE_ANY_EXHDR(locale); } /* * If different than default locale is requested, call setlocale. */ if (locale_len > 0 && (strncmp(lc_collate_cache, VARDATA_ANY(locale), locale_len) || *(lc_collate_cache + locale_len) != '\0')) { locale_str = palloc(locale_len + 1); memcpy(locale_str, VARDATA_ANY(locale), locale_len); *(locale_str + locale_len) = '\0'; /* * Try to set correct locales. * If setlocale failed, we know the default stayed the same, * co we can safely elog. */ if (!setlocale(LC_COLLATE, locale_str)) elog(ERROR, "failed to set the requested LC_COLLATE value [%s]", locale_str); changed_locale = 1; } /* * We do TRY / CATCH / END_TRY to catch ereport / elog that might * happen during palloc. Ereport during palloc would not be * nice since it would leave the server with changed locales * setting, resulting in bad things. */ PG_TRY(); { /* * Text transformation. * Increase the buffer until the strxfrm is able to fit. */ size = string_len * multiplication + 1; tmp = palloc(size + VARHDRSZ); rest = strxfrm(tmp + VARHDRSZ, string_str, size); while (rest >= size) { pfree(tmp); size = rest + 1; tmp = palloc(size + VARHDRSZ); rest = strxfrm(tmp + VARHDRSZ, string_str, size); /* * Cache the multiplication factor so that the next * time we start with better value. */ if (string_len) multiplication = (rest / string_len) + 2; } } PG_CATCH (); { if (changed_locale) { /* * Set original locale */ if (!setlocale(LC_COLLATE, lc_collate_cache)) elog(FATAL, "failed to set back the default LC_COLLATE value [%s]", lc_collate_cache); } } PG_END_TRY (); if (changed_locale) { /* * Set original locale */ if (!setlocale(LC_COLLATE, lc_collate_cache)) elog(FATAL, "failed to set back the default LC_COLLATE value [%s]", lc_collate_cache); pfree(locale_str); } pfree(string_str); /* * If the multiplication factor went down, reset it. */ if (string_len && rest < string_len * multiplication / 4) multiplication = (rest / string_len) + 1; result = (text *) tmp; SET_VARSIZE(result, rest + VARHDRSZ); return result; }
Datum plpython_call_handler(PG_FUNCTION_ARGS) { Datum retval; PLyExecutionContext *exec_ctx; ErrorContextCallback plerrcontext; PLy_initialize(); /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); /* * Push execution context onto stack. It is important that this get * popped again, so avoid putting anything that could throw error between * here and the PG_TRY. */ exec_ctx = PLy_push_execution_context(); PG_TRY(); { Oid funcoid = fcinfo->flinfo->fn_oid; PLyProcedure *proc; /* * Setup error traceback support for ereport(). Note that the PG_TRY * structure pops this for us again at exit, so we needn't do that * explicitly, nor do we risk the callback getting called after we've * destroyed the exec_ctx. */ plerrcontext.callback = plpython_error_callback; plerrcontext.arg = exec_ctx; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; if (CALLED_AS_TRIGGER(fcinfo)) { Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation; HeapTuple trv; proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), true); exec_ctx->curr_proc = proc; trv = PLy_exec_trigger(fcinfo, proc); retval = PointerGetDatum(trv); } else { proc = PLy_procedure_get(funcoid, InvalidOid, false); exec_ctx->curr_proc = proc; retval = PLy_exec_function(fcinfo, proc); } } PG_CATCH(); { PLy_pop_execution_context(); PyErr_Clear(); PG_RE_THROW(); } PG_END_TRY(); /* Destroy the execution context */ PLy_pop_execution_context(); return retval; }
static PyObject * PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc) { PyObject *volatile arg = NULL; PyObject *volatile args = NULL; int i; PG_TRY(); { args = PyList_New(proc->nargs); for (i = 0; i < proc->nargs; i++) { if (proc->args[i].is_rowtype > 0) { if (fcinfo->argnull[i]) arg = NULL; 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); /* Set up I/O funcs if not done yet */ if (proc->args[i].is_rowtype != 1) PLy_input_tuple_funcs(&(proc->args[i]), tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc); ReleaseTupleDesc(tupdesc); } } else { if (fcinfo->argnull[i]) arg = NULL; else { arg = (proc->args[i].in.d.func) (&(proc->args[i].in.d), fcinfo->arg[i]); } } if (arg == NULL) { Py_INCREF(Py_None); arg = Py_None; } if (PyList_SetItem(args, i, arg) == -1) PLy_elog(ERROR, "PyList_SetItem() failed, while setting up arguments"); if (proc->argnames && proc->argnames[i] && PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1) PLy_elog(ERROR, "PyDict_SetItemString() failed, while setting up arguments"); arg = NULL; } /* Set up output conversion for functions returning RECORD */ if (proc->result.out.d.typoid == RECORDOID) { TupleDesc desc; if (get_call_result_type(fcinfo, NULL, &desc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); /* cache the output conversion functions */ PLy_output_record_funcs(&(proc->result), desc); } } PG_CATCH(); { Py_XDECREF(arg); Py_XDECREF(args); PG_RE_THROW(); } PG_END_TRY(); return args; }
Datum plpython_inline_handler(PG_FUNCTION_ARGS) { InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0)); FunctionCallInfoData fake_fcinfo; FmgrInfo flinfo; PLyProcedure proc; PLyExecutionContext *exec_ctx; ErrorContextCallback plerrcontext; PLy_initialize(); /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo)); MemSet(&flinfo, 0, sizeof(flinfo)); fake_fcinfo.flinfo = &flinfo; flinfo.fn_oid = InvalidOid; flinfo.fn_mcxt = CurrentMemoryContext; MemSet(&proc, 0, sizeof(PLyProcedure)); proc.pyname = PLy_strdup("__plpython_inline_block"); proc.result.out.d.typoid = VOIDOID; /* * Push execution context onto stack. It is important that this get * popped again, so avoid putting anything that could throw error between * here and the PG_TRY. */ exec_ctx = PLy_push_execution_context(); PG_TRY(); { /* * Setup error traceback support for ereport(). * plpython_inline_error_callback doesn't currently need exec_ctx, but * for consistency with plpython_call_handler we do it the same way. */ plerrcontext.callback = plpython_inline_error_callback; plerrcontext.arg = exec_ctx; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; PLy_procedure_compile(&proc, codeblock->source_text); exec_ctx->curr_proc = &proc; PLy_exec_function(&fake_fcinfo, &proc); } PG_CATCH(); { PLy_pop_execution_context(); PLy_procedure_delete(&proc); PyErr_Clear(); PG_RE_THROW(); } PG_END_TRY(); /* Destroy the execution context */ PLy_pop_execution_context(); /* Now clean up the transient procedure we made */ PLy_procedure_delete(&proc); PG_RETURN_VOID(); }
static PyObject * PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *rv) { TriggerData *tdata = (TriggerData *) fcinfo->context; PyObject *pltname, *pltevent, *pltwhen, *pltlevel, *pltrelid, *plttablename, *plttableschema; PyObject *pltargs, *pytnew, *pytold; PyObject *volatile pltdata = NULL; char *stroid; PG_TRY(); { pltdata = PyDict_New(); if (!pltdata) PLy_elog(ERROR, "could not create new dictionary while building trigger arguments"); pltname = PyString_FromString(tdata->tg_trigger->tgname); PyDict_SetItemString(pltdata, "name", pltname); Py_DECREF(pltname); stroid = DatumGetCString(DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->tg_relation->rd_id))); pltrelid = PyString_FromString(stroid); PyDict_SetItemString(pltdata, "relid", pltrelid); Py_DECREF(pltrelid); pfree(stroid); stroid = SPI_getrelname(tdata->tg_relation); plttablename = PyString_FromString(stroid); PyDict_SetItemString(pltdata, "table_name", plttablename); Py_DECREF(plttablename); pfree(stroid); stroid = SPI_getnspname(tdata->tg_relation); plttableschema = PyString_FromString(stroid); PyDict_SetItemString(pltdata, "table_schema", plttableschema); Py_DECREF(plttableschema); pfree(stroid); if (TRIGGER_FIRED_BEFORE(tdata->tg_event)) pltwhen = PyString_FromString("BEFORE"); else if (TRIGGER_FIRED_AFTER(tdata->tg_event)) pltwhen = PyString_FromString("AFTER"); else if (TRIGGER_FIRED_INSTEAD(tdata->tg_event)) pltwhen = PyString_FromString("INSTEAD OF"); else { elog(ERROR, "unrecognized WHEN tg_event: %u", tdata->tg_event); pltwhen = NULL; /* keep compiler quiet */ } PyDict_SetItemString(pltdata, "when", pltwhen); Py_DECREF(pltwhen); if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) { pltlevel = PyString_FromString("ROW"); PyDict_SetItemString(pltdata, "level", pltlevel); Py_DECREF(pltlevel); if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) { pltevent = PyString_FromString("INSERT"); PyDict_SetItemString(pltdata, "old", Py_None); pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "new", pytnew); Py_DECREF(pytnew); *rv = tdata->tg_trigtuple; } else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) { pltevent = PyString_FromString("DELETE"); PyDict_SetItemString(pltdata, "new", Py_None); pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "old", pytold); Py_DECREF(pytold); *rv = tdata->tg_trigtuple; } else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) { pltevent = PyString_FromString("UPDATE"); pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "new", pytnew); Py_DECREF(pytnew); pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "old", pytold); Py_DECREF(pytold); *rv = tdata->tg_newtuple; } else { elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event); pltevent = NULL; /* keep compiler quiet */ } PyDict_SetItemString(pltdata, "event", pltevent); Py_DECREF(pltevent); } else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event)) { pltlevel = PyString_FromString("STATEMENT"); PyDict_SetItemString(pltdata, "level", pltlevel); Py_DECREF(pltlevel); PyDict_SetItemString(pltdata, "old", Py_None); PyDict_SetItemString(pltdata, "new", Py_None); *rv = NULL; if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) pltevent = PyString_FromString("INSERT"); else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) pltevent = PyString_FromString("DELETE"); else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) pltevent = PyString_FromString("UPDATE"); else if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event)) pltevent = PyString_FromString("TRUNCATE"); else { elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event); pltevent = NULL; /* keep compiler quiet */ } PyDict_SetItemString(pltdata, "event", pltevent); Py_DECREF(pltevent); } else elog(ERROR, "unrecognized LEVEL tg_event: %u", tdata->tg_event); if (tdata->tg_trigger->tgnargs) { /* * all strings... */ int i; PyObject *pltarg; pltargs = PyList_New(tdata->tg_trigger->tgnargs); for (i = 0; i < tdata->tg_trigger->tgnargs; i++) { pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]); /* * stolen, don't Py_DECREF */ PyList_SetItem(pltargs, i, pltarg); } } else { Py_INCREF(Py_None); pltargs = Py_None; } PyDict_SetItemString(pltdata, "args", pltargs); Py_DECREF(pltargs); } PG_CATCH(); { Py_XDECREF(pltdata); PG_RE_THROW(); } PG_END_TRY(); return pltdata; }
static HeapTuple PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, HeapTuple otup) { PyObject *volatile plntup; PyObject *volatile plkeys; PyObject *volatile plval; HeapTuple rtup; int natts, i, attn, atti; int *volatile modattrs; Datum *volatile modvalues; char *volatile modnulls; TupleDesc tupdesc; ErrorContextCallback plerrcontext; plerrcontext.callback = plpython_trigger_error_callback; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; plntup = plkeys = plval = NULL; modattrs = NULL; modvalues = NULL; modnulls = NULL; PG_TRY(); { if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL) ereport(ERROR, (errmsg("TD[\"new\"] deleted, cannot modify row"))); Py_INCREF(plntup); if (!PyDict_Check(plntup)) ereport(ERROR, (errmsg("TD[\"new\"] is not a dictionary"))); plkeys = PyDict_Keys(plntup); natts = PyList_Size(plkeys); modattrs = (int *) palloc(natts * sizeof(int)); modvalues = (Datum *) palloc(natts * sizeof(Datum)); modnulls = (char *) palloc(natts * sizeof(char)); tupdesc = tdata->tg_relation->rd_att; for (i = 0; i < natts; i++) { PyObject *platt; char *plattstr; platt = PyList_GetItem(plkeys, i); if (PyString_Check(platt)) plattstr = PyString_AsString(platt); else if (PyUnicode_Check(platt)) plattstr = PLyUnicode_AsString(platt); else { ereport(ERROR, (errmsg("TD[\"new\"] dictionary key at ordinal position %d is not a string", i))); plattstr = NULL; /* keep compiler quiet */ } attn = SPI_fnumber(tupdesc, plattstr); if (attn == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row", plattstr))); atti = attn - 1; plval = PyDict_GetItem(plntup, platt); if (plval == NULL) elog(FATAL, "Python interpreter is probably corrupted"); Py_INCREF(plval); modattrs[i] = attn; if (tupdesc->attrs[atti]->attisdropped) { modvalues[i] = (Datum) 0; modnulls[i] = 'n'; } else if (plval != Py_None) { PLyObToDatum *att = &proc->result.out.r.atts[atti]; modvalues[i] = (att->func) (att, tupdesc->attrs[atti]->atttypmod, plval); modnulls[i] = ' '; } else { modvalues[i] = InputFunctionCall(&proc->result.out.r.atts[atti].typfunc, NULL, proc->result.out.r.atts[atti].typioparam, tupdesc->attrs[atti]->atttypmod); modnulls[i] = 'n'; } Py_DECREF(plval); plval = NULL; } rtup = SPI_modifytuple(tdata->tg_relation, otup, natts, modattrs, modvalues, modnulls); if (rtup == NULL) elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result); } PG_CATCH(); { Py_XDECREF(plntup); Py_XDECREF(plkeys); Py_XDECREF(plval); if (modnulls) pfree(modnulls); if (modvalues) pfree(modvalues); if (modattrs) pfree(modattrs); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(plntup); Py_DECREF(plkeys); pfree(modattrs); pfree(modvalues); pfree(modnulls); error_context_stack = plerrcontext.previous; return rtup; }