/* * Abort lingering subtransactions that have been explicitly started * by plpy.subtransaction().start() and not properly closed. */ static void PLy_abort_open_subtransactions(int save_subxact_level) { Assert(save_subxact_level >= 0); while (list_length(explicit_subtransactions) > save_subxact_level) { PLySubtransactionData *subtransactiondata; Assert(explicit_subtransactions != NIL); ereport(WARNING, (errmsg("forcibly aborting a subtransaction that has not been exited"))); RollbackAndReleaseCurrentSubTransaction(); SPI_restore_connection(); subtransactiondata = (PLySubtransactionData *) linitial(explicit_subtransactions); explicit_subtransactions = list_delete_first(explicit_subtransactions); MemoryContextSwitchTo(subtransactiondata->oldcontext); CurrentResourceOwner = subtransactiondata->oldowner; PLy_free(subtransactiondata); } }
/* * PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and * returns a new PLyProcedure. * * fn_oid is the OID of the function requested * fn_rel is InvalidOid or the relation this function triggers on * is_trigger denotes whether the function is a trigger function * * The reason that both fn_rel and is_trigger need to be passed is that when * trigger functions get validated we don't know which relation(s) they'll * be used with, so no sensible fn_rel can be passed. */ PLyProcedure * PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger) { bool use_cache = !(is_trigger && fn_rel == InvalidOid); HeapTuple procTup; PLyProcedureKey key; PLyProcedureEntry *volatile entry = NULL; PLyProcedure *volatile proc = NULL; bool found = false; procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid)); if (!HeapTupleIsValid(procTup)) elog(ERROR, "cache lookup failed for function %u", fn_oid); /* * Look for the function in the cache, unless we don't have the necessary * information (e.g. during validation). In that case we just don't cache * anything. */ if (use_cache) { key.fn_oid = fn_oid; key.fn_rel = fn_rel; entry = hash_search(PLy_procedure_cache, &key, HASH_ENTER, &found); proc = entry->proc; } PG_TRY(); { if (!found) { /* Haven't found it, create a new procedure */ proc = PLy_procedure_create(procTup, fn_oid, is_trigger); if (use_cache) entry->proc = proc; } else if (!PLy_procedure_valid(proc, procTup)) { /* Found it, but it's invalid, free and reuse the cache entry */ PLy_procedure_delete(proc); PLy_free(proc); proc = PLy_procedure_create(procTup, fn_oid, is_trigger); entry->proc = proc; } /* Found it and it's valid, it's fine to use it */ } PG_CATCH(); { /* Do not leave an uninitialised entry in the cache */ if (use_cache) hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL); PG_RE_THROW(); } PG_END_TRY(); ReleaseSysCache(procTup); return proc; }
void PLy_procedure_delete(PLyProcedure *proc) { int i; Py_XDECREF(proc->code); Py_XDECREF(proc->statics); Py_XDECREF(proc->globals); if (proc->proname) PLy_free(proc->proname); if (proc->pyname) PLy_free(proc->pyname); for (i = 0; i < proc->nargs; i++) { if (proc->args[i].is_rowtype == 1) { if (proc->args[i].in.r.atts) PLy_free(proc->args[i].in.r.atts); if (proc->args[i].out.r.atts) PLy_free(proc->args[i].out.r.atts); } if (proc->argnames && proc->argnames[i]) PLy_free(proc->argnames[i]); } if (proc->src) PLy_free(proc->src); if (proc->argnames) PLy_free(proc->argnames); }
static void PLy_pop_execution_context(void) { PLyExecutionContext *context = PLy_execution_contexts; if (context == NULL) elog(ERROR, "no Python function is currently executing"); PLy_execution_contexts = context->next; MemoryContextDelete(context->scratch_ctx); PLy_free(context); }
void PLy_typeinfo_dealloc(PLyTypeInfo *arg) { if (arg->is_rowtype == 1) { int i; for (i = 0; i < arg->in.r.natts; i++) { if (arg->in.r.atts[i].elm != NULL) PLy_free(arg->in.r.atts[i].elm); } if (arg->in.r.atts) PLy_free(arg->in.r.atts); for (i = 0; i < arg->out.r.natts; i++) { if (arg->out.r.atts[i].elm != NULL) PLy_free(arg->out.r.atts[i].elm); } if (arg->out.r.atts) PLy_free(arg->out.r.atts); } }
void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) { int i; if (arg->is_rowtype == 0) elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); arg->is_rowtype = 1; if (arg->out.r.natts != desc->natts) { if (arg->out.r.atts) PLy_free(arg->out.r.atts); arg->out.r.natts = desc->natts; arg->out.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb)); } Assert(OidIsValid(desc->tdtypeid)); /* * RECORDOID means we got called to create output functions for an * anonymous record type */ if (desc->tdtypeid != RECORDOID) { HeapTuple relTup; /* Get the pg_class tuple corresponding to the type of the output */ arg->typ_relid = typeidTypeRelid(desc->tdtypeid); relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid)); if (!HeapTupleIsValid(relTup)) elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid); /* Remember XMIN and TID for later validation if cache is still OK */ arg->typrel_xmin = HeapTupleHeaderGetXmin(relTup->t_data); arg->typrel_tid = relTup->t_self; ReleaseSysCache(relTup); } for (i = 0; i < desc->natts; i++) { HeapTuple typeTup; if (desc->attrs[i]->attisdropped) continue; if (arg->out.r.atts[i].typoid == desc->attrs[i]->atttypid) continue; /* already set up this entry */ typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->attrs[i]->atttypid)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", desc->attrs[i]->atttypid); PLy_output_datum_func2(&(arg->out.r.atts[i]), typeTup); ReleaseSysCache(typeTup); } }
void PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) { int i; PLyExecutionContext *exec_ctx = PLy_current_execution_context(); if (arg->is_rowtype == 0) elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); arg->is_rowtype = 1; if (arg->in.r.natts != desc->natts) { if (arg->in.r.atts) PLy_free(arg->in.r.atts); arg->in.r.natts = desc->natts; arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb)); } /* Can this be an unnamed tuple? If not, then an Assert would be enough */ if (desc->tdtypmod != -1) elog(ERROR, "received unnamed record type as input"); Assert(OidIsValid(desc->tdtypeid)); /* * RECORDOID means we got called to create input functions for a tuple * fetched by plpy.execute or for an anonymous record type */ if (desc->tdtypeid != RECORDOID) { HeapTuple relTup; /* Get the pg_class tuple corresponding to the type of the input */ arg->typ_relid = typeidTypeRelid(desc->tdtypeid); relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid)); if (!HeapTupleIsValid(relTup)) elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid); /* Remember XMIN and TID for later validation if cache is still OK */ arg->typrel_xmin = HeapTupleHeaderGetRawXmin(relTup->t_data); arg->typrel_tid = relTup->t_self; ReleaseSysCache(relTup); } for (i = 0; i < desc->natts; i++) { HeapTuple typeTup; if (desc->attrs[i]->attisdropped) continue; if (arg->in.r.atts[i].typoid == desc->attrs[i]->atttypid) continue; /* already set up this entry */ typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->attrs[i]->atttypid)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", desc->attrs[i]->atttypid); PLy_input_datum_func2(&(arg->in.r.atts[i]), desc->attrs[i]->atttypid, typeTup, exec_ctx->curr_proc->langid, exec_ctx->curr_proc->trftypes); ReleaseSysCache(typeTup); } }