/* -------------------------------- * ExecSetSlotDescriptor * * This function is used to set the tuple descriptor associated * with the slot's tuple. * -------------------------------- */ void ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ TupleDesc tupdesc, /* new tuple descriptor */ bool shouldFree) /* is desc owned by slot? */ { /* For safety, make sure slot is empty before changing it */ ExecClearTuple(slot); /* * Release any old descriptor. Also release old Datum/isnull arrays if * present (we don't bother to check if they could be re-used). */ if (slot->tts_shouldFreeDesc) FreeTupleDesc(slot->tts_tupleDescriptor); if (slot->tts_values) pfree(slot->tts_values); if (slot->tts_isnull) pfree(slot->tts_isnull); /* * Set up the new descriptor */ slot->tts_tupleDescriptor = tupdesc; slot->tts_shouldFreeDesc = shouldFree; /* * Allocate Datum/isnull arrays of the appropriate size. These must have * the same lifetime as the slot, so allocate in the slot's own context. */ slot->tts_values = (Datum *) MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum)); slot->tts_isnull = (bool *) MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool)); }
/* * TupleDesc_FromNamesAndOids - create a TupleDesc from some basic elements * * Given a count of attributes, an array of names and type Oids, build and * return a TupleDesc. */ TupleDesc TupleDesc_FromNamesAndOids(int ac, const char **names, Oid *types) { unsigned int i; volatile TupleDesc td = NULL; PG_TRY(); { td = CreateTemplateTupleDesc(ac, false); for (i = 0; i < ac; ++i) { const char *name = NULL; if (names) name = names[i]; if (name == NULL) name = ""; TupleDescInitEntry(td, i + 1, name, types[i], -1, 0); } } PG_CATCH(); { if (td != NULL) FreeTupleDesc(td); PG_RE_THROW(); } PG_END_TRY(); return(td); }
void plpgsql_check_recval_release(PLpgSQL_rec *rec) { #if PG_VERSION_NUM >= 110000 Assert(rec->dtype == PLPGSQL_DTYPE_REC); if (rec->erh) DeleteExpandedObject(ExpandedRecordGetDatum(rec->erh)); rec->erh = NULL; #else if (rec->freetup) heap_freetuple(rec->tup); if (rec->freetupdesc) FreeTupleDesc(rec->tupdesc); rec->freetup = false; rec->freetupdesc = false; #endif }
/* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = pointer; FreeTupleDesc((TupleDesc)p2l.ptrVal); END_NATIVE }
/* * Decrement the reference count of a tupdesc, remove the corresponding * reference from CurrentResourceOwner, and free the tupdesc if no more * references remain. * * Do not apply this to tupdescs that are not being refcounted. (Use the * macro ReleaseTupleDesc for tupdescs of uncertain status.) */ void DecrTupleDescRefCount(TupleDesc tupdesc) { Assert(tupdesc->tdrefcount > 0); ResourceOwnerForgetTupleDesc(CurrentResourceOwner, tupdesc); if (--tupdesc->tdrefcount == 0) FreeTupleDesc(tupdesc); }
static void tupd_dealloc(PyObj self) { MemoryContext former = CurrentMemoryContext; Py_XDECREF(PyPgTupleDesc_GetNames(self)); PyPgTupleDesc_SetNames(self, NULL); Py_XDECREF(PyPgTupleDesc_GetNameMap(self)); PyPgTupleDesc_SetNameMap(self, NULL); Py_XDECREF(PyPgTupleDesc_GetTypesTuple(self)); PyPgTupleDesc_SetTypesTuple(self, NULL); PG_TRY(); { TupleDesc td; int *map; td = PyPgTupleDesc_GetTupleDesc(self); PyPgTupleDesc_SetTupleDesc(self, NULL); if (td != NULL) FreeTupleDesc(td); map = PyPgTupleDesc_GetIndexMap(self); PyPgTupleDesc_SetIndexMap(self, NULL); if (map != NULL) pfree(map); map = PyPgTupleDesc_GetFreeMap(self); PyPgTupleDesc_SetFreeMap(self, NULL); if (map != NULL) pfree(map); /* * When PLPY_STRANGE_THINGS is defined. */ RaiseAStrangeError } PG_CATCH(); { PyErr_EmitPgErrorAsWarning("could not deallocate Postgres.TupleDesc fields"); } PG_END_TRY(); /* * Normally, this doesn't matter, but it is possible for the context * to be switched by the above code.. */ MemoryContextSwitchTo(former); Py_TYPE(self)->tp_free(self); }
/* * There should be only one. */ void EmptyPyPgTupleDesc_Initialize(void) { PG_TRY(); { TupleDesc td; td = CreateTemplateTupleDesc(0, false); EmptyPyPgTupleDesc = PyPgTupleDesc_FromCopy(td); FreeTupleDesc(td); } PG_CATCH(); { PyErr_SetPgError(true); } PG_END_TRY(); }
static void PLy_result_dealloc(PyObject *arg) { PLyResultObject *ob = (PLyResultObject *) arg; Py_XDECREF(ob->nrows); Py_XDECREF(ob->rows); Py_XDECREF(ob->status); if (ob->tupdesc) { FreeTupleDesc(ob->tupdesc); ob->tupdesc = NULL; } arg->ob_type->tp_free(arg); }
void plproxy_free_composite(ProxyComposite *rec) { int i; int natts = rec->tupdesc->natts; for (i = 0; i < natts; i++) { plproxy_free_type(rec->type_list[i]); if (rec->name_list[i]) pfree(rec->name_list[i]); } pfree(rec->type_list); pfree(rec->name_list); FreeTupleDesc(rec->tupdesc); pfree(rec); }
/* -------------------------------- * ExecDropSingleTupleTableSlot * * Release a TupleTableSlot made with MakeSingleTupleTableSlot. * -------------------------------- */ void ExecDropSingleTupleTableSlot(TupleTableSlot *slot) { /* * sanity checks */ Assert(slot != NULL); ExecClearTuple(slot); if (slot->tts_shouldFreeDesc) FreeTupleDesc(slot->tts_tupleDescriptor); if (slot->tts_values) pfree(slot->tts_values); if (slot->tts_isnull) pfree(slot->tts_isnull); pfree(slot); }
/* -------------------------------- * ExecDropTupleTable * * This frees the storage used by the tuple table itself * and optionally frees the contents of the table also. * It is expected that this routine be called by EndPlan(). * -------------------------------- */ void ExecDropTupleTable(TupleTable table, /* tuple table */ bool shouldFree) /* true if we should free slot * contents */ { /* * sanity checks */ Assert(table != NULL); /* * first free all the valid pointers in the tuple array and drop refcounts * of any referenced buffers, if that's what the caller wants. (There is * probably no good reason for the caller ever not to want it!) */ if (shouldFree) { int next = table->next; int i; for (i = 0; i < next; i++) { TupleTableSlot *slot = &(table->array[i]); ExecClearTuple(slot); if (slot->tts_shouldFreeDesc) FreeTupleDesc(slot->tts_tupleDescriptor); if (slot->tts_values) pfree(slot->tts_values); if (slot->tts_isnull) pfree(slot->tts_isnull); } } /* * finally free the tuple table itself. */ pfree(table); }
void TupleFormerTerm(TupleFormer *former) { if (former->typId) pfree(former->typId); if (former->typIOParam) pfree(former->typIOParam); if (former->typInput) pfree(former->typInput); if (former->values) pfree(former->values); if (former->isnull) pfree(former->isnull); if (former->attnum) pfree(former->attnum); if (former->desc) FreeTupleDesc(former->desc); }
/* * ExecEndRecScan * * Ends the SeqScan and then does some additional things. */ void ExecEndRecScan(RecScanState *node) { /* End the normal scan. */ switch(nodeTag(node->subscan)) { case T_SeqScanState: ExecEndSeqScan((SeqScanState *) node->subscan); break; default: elog(ERROR, "invalid RecScan subscan type: %d", (int) nodeTag(node->subscan)); break; } /* Now for extra stuff. */ if (node->itemList) pfree(node->itemList); if (node->fullItemList) pfree(node->fullItemList); if (node->userFeatures) pfree(node->userFeatures); if (node->base_slot) FreeTupleDesc(node->base_slot); }
static void lint_func_beg( PLpgSQL_execstate * estate, PLpgSQL_function * func ) { const char *err_text = estate->err_text; if (plpgsql_lint_enable) { int i; PLpgSQL_rec *saved_records; PLpgSQL_var *saved_vars; MemoryContext oldcontext; ResourceOwner oldowner; /* * inside control a rec and vars variables are modified, so we should to save their * content */ saved_records = palloc(sizeof(PLpgSQL_rec) * estate->ndatums); saved_vars = palloc(sizeof(PLpgSQL_var) * estate->ndatums); for (i = 0; i < estate->ndatums; i++) { if (estate->datums[i]->dtype == PLPGSQL_DTYPE_REC) { PLpgSQL_rec *rec = (PLpgSQL_rec *) estate->datums[i]; saved_records[i].tup = rec->tup; saved_records[i].tupdesc = rec->tupdesc; saved_records[i].freetup = rec->freetup; saved_records[i].freetupdesc = rec->freetupdesc; /* don't release a original tupdesc and original tup */ rec->freetup = false; rec->freetupdesc = false; } else if (estate->datums[i]->dtype == PLPGSQL_DTYPE_VAR) { PLpgSQL_var *var = (PLpgSQL_var *) estate->datums[i]; saved_vars[i].value = var->value; saved_vars[i].isnull = var->isnull; saved_vars[i].freeval = var->freeval; var->freeval = false; } } estate->err_text = NULL; /* * Raised exception should be trapped in outer functtion. Protection * against outer trap is QUERY_CANCELED exception. */ oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PG_TRY(); { lint_stmt(estate, func, (PLpgSQL_stmt *) func->action); } PG_CATCH(); { ErrorData *edata; /* Save error info */ MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); CurrentResourceOwner = oldowner; edata->sqlerrcode = ERRCODE_QUERY_CANCELED; ReThrowError(edata); } PG_END_TRY(); estate->err_text = err_text; estate->err_stmt = NULL; /* return back a original rec variables */ for (i = 0; i < estate->ndatums; i++) { if (estate->datums[i]->dtype == PLPGSQL_DTYPE_REC) { PLpgSQL_rec *rec = (PLpgSQL_rec *) estate->datums[i]; if (rec->freetupdesc) FreeTupleDesc(rec->tupdesc); rec->tup = saved_records[i].tup; rec->tupdesc = saved_records[i].tupdesc; rec->freetup = saved_records[i].freetup; rec->freetupdesc = saved_records[i].freetupdesc; } else if (estate->datums[i]->dtype == PLPGSQL_DTYPE_VAR) { PLpgSQL_var *var = (PLpgSQL_var *) estate->datums[i]; var->value = saved_vars[i].value; var->isnull = saved_vars[i].isnull; var->freeval = saved_vars[i].freeval; } } pfree(saved_records); pfree(saved_vars); } }
void EndMotionLayerNode(MotionLayerState *mlStates, int16 motNodeID, bool flushCommLayer) { MotionNodeEntry *pMNEntry; ChunkSorterEntry *pCSEntry; int i; pMNEntry = getMotionNodeEntry(mlStates, motNodeID, "EndMotionLayerNode"); #ifdef AMS_VERBOSE_LOGGING elog(DEBUG5, "Cleaning up Motion Layer details for motion node %d.", motNodeID); #endif /* * Iterate through all entries in the motion layer's chunk-sort map, to * see if we have gotten end-of-stream from all senders. */ if (pMNEntry->preserve_order && pMNEntry->ready_tuple_lists != NULL) { for (i=0; i < GpIdentity.numsegments; i++) { pCSEntry = &pMNEntry->ready_tuple_lists[i]; /* * QD should not expect end-of-stream comes from QEs who is not members of * direct dispatch */ if (!pCSEntry->init) continue; if (pMNEntry->preserve_order && gp_log_interconnect >= GPVARS_VERBOSITY_DEBUG) { /* Print chunk-sorter entry statistics. */ elog(DEBUG4, "Chunk-sorter entry [route=%d,node=%d] statistics:\n" "\tAvailable Tuples High-Watermark: " UINT64_FORMAT, i, pMNEntry->motion_node_id, pMNEntry->stat_tuples_available_hwm); } if (!pMNEntry->stopped && !pCSEntry->end_of_stream) { if (flushCommLayer) { elog(FATAL, "Motion layer node %d cleanup - did not receive" " end-of-stream from sender %d.", motNodeID, i); /*** TODO - get chunks until end-of-stream comes in. ***/ } else { elog(LOG, "Motion layer node %d cleanup - did not receive" " end-of-stream from sender %d.", motNodeID, i); } } else { /* End-of-stream is marked for this entry. */ /*** TODO - do more than just complain! ***/ if (pCSEntry->chunk_list.num_chunks > 0) { elog(LOG, "Motion layer node %d cleanup - there are still" " %d chunks enqueued from sender %d.", motNodeID, pCSEntry->chunk_list.num_chunks, i ); } /*** TODO - Make sure there are no outstanding tuples in the tuple-store. ***/ } /* * Clean up the chunk-sorter entry, then remove it from the hash * table. */ clearTCList(&pMNEntry->ser_tup_info.chunkCache, &pCSEntry->chunk_list); if (pMNEntry->preserve_order) /* Clean up the tuple-store. */ htfifo_destroy(pCSEntry->ready_tuples); } } pMNEntry->cleanedUp = true; /* Clean up the motion-node entry, then remove it from the hash table. */ if (gp_log_interconnect >= GPVARS_VERBOSITY_VERBOSE) { if (pMNEntry->stat_total_bytes_sent > 0 || pMNEntry->sel_wr_wait > 0) { elog(LOG, "Interconnect seg%d slice%d sent " UINT64_FORMAT " tuples, " UINT64_FORMAT " total bytes, " UINT64_FORMAT " tuple bytes, " UINT64_FORMAT " chunks; waited " UINT64_FORMAT " usec.", Gp_segment, currentSliceId, pMNEntry->stat_total_sends, pMNEntry->stat_total_bytes_sent, pMNEntry->stat_tuple_bytes_sent, pMNEntry->stat_total_chunks_sent, pMNEntry->sel_wr_wait ); } if (pMNEntry->stat_total_bytes_recvd > 0 || pMNEntry->sel_rd_wait > 0) { elog(LOG, "Interconnect seg%d slice%d received from slice%d: " UINT64_FORMAT " tuples, " UINT64_FORMAT " total bytes, " UINT64_FORMAT " tuple bytes, " UINT64_FORMAT " chunks; waited " UINT64_FORMAT " usec.", Gp_segment, currentSliceId, motNodeID, pMNEntry->stat_total_recvs, pMNEntry->stat_total_bytes_recvd, pMNEntry->stat_tuple_bytes_recvd, pMNEntry->stat_total_chunks_recvd, pMNEntry->sel_rd_wait ); } } CleanupSerTupInfo(&pMNEntry->ser_tup_info); FreeTupleDesc(pMNEntry->tuple_desc); if (!pMNEntry->preserve_order) htfifo_destroy(pMNEntry->ready_tuples); pMNEntry->valid = false; }
TupleCheckStatus FilterInit(Filter *filter, TupleDesc desc, Oid collation) { int i; ParsedFunction func; HeapTuple ftup; HeapTuple ltup; Form_pg_proc pp; Form_pg_language lp; TupleCheckStatus status = NEED_COERCION_CHECK; if (filter->funcstr == NULL) return NO_COERCION; /* parse filter function */ func = ParseFunction(filter->funcstr, true); filter->funcid = func.oid; filter->nargs = func.nargs; for (i = 0; i < filter->nargs; i++) { /* Check for polymorphic types and internal pseudo-type argument */ if (IsPolymorphicType(func.argtypes[i]) || func.argtypes[i] == INTERNALOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("filter function does not support a polymorphic function and having a internal pseudo-type argument function: %s", get_func_name(filter->funcid)))); filter->argtypes[i] = func.argtypes[i]; } ftup = SearchSysCache(PROCOID, ObjectIdGetDatum(filter->funcid), 0, 0, 0); pp = (Form_pg_proc) GETSTRUCT(ftup); if (pp->proretset) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("filter function must not return set"))); /* Check data type of the function result value */ if (pp->prorettype == desc->tdtypeid && pp->prorettype != RECORDOID) status = NO_COERCION; else if (pp->prorettype == RECORDOID) { TupleDesc resultDesc = NULL; /* Check for OUT parameters defining a RECORD result */ resultDesc = build_function_result_tupdesc_t(ftup); if (resultDesc) { if (tupledesc_match(desc, resultDesc)) status = NO_COERCION; FreeTupleDesc(resultDesc); } } else if (get_typtype(pp->prorettype) != TYPTYPE_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return data type and target table data type do not match"))); /* Get default values */ #if PG_VERSION_NUM >= 80400 filter->fn_ndargs = pp->pronargdefaults; if (filter->fn_ndargs > 0) { Datum proargdefaults; bool isnull; char *str; List *defaults; ListCell *l; filter->defaultValues = palloc(sizeof(Datum) * filter->fn_ndargs); filter->defaultIsnull = palloc(sizeof(bool) * filter->fn_ndargs); proargdefaults = SysCacheGetAttr(PROCOID, ftup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); str = TextDatumGetCString(proargdefaults); defaults = (List *) stringToNode(str); Assert(IsA(defaults, List)); pfree(str); filter->econtext = CreateStandaloneExprContext(); i = 0; foreach(l, defaults) { Expr *expr = (Expr *) lfirst(l); ExprState *argstate; ExprDoneCond thisArgIsDone; argstate = ExecInitExpr(expr, NULL); filter->defaultValues[i] = ExecEvalExpr(argstate, filter->econtext, &filter->defaultIsnull[i], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); i++; }
/* * ExecMakeTableFunctionResult * * Evaluate a table function, producing a materialized result in a Tuplestore * object. * * This is used by nodeFunctionscan.c. */ Tuplestorestate * ExecMakeTableFunctionResult(SetExprState *setexpr, ExprContext *econtext, MemoryContext argContext, TupleDesc expectedDesc, bool randomAccess) { Tuplestorestate *tupstore = NULL; TupleDesc tupdesc = NULL; Oid funcrettype; bool returnsTuple; bool returnsSet = false; FunctionCallInfoData fcinfo; PgStat_FunctionCallUsage fcusage; ReturnSetInfo rsinfo; HeapTupleData tmptup; MemoryContext callerContext; MemoryContext oldcontext; bool first_time = true; callerContext = CurrentMemoryContext; funcrettype = exprType((Node *) setexpr->expr); returnsTuple = type_is_rowtype(funcrettype); /* * Prepare a resultinfo node for communication. We always do this even if * not expecting a set result, so that we can pass expectedDesc. In the * generic-expression case, the expression doesn't actually get to see the * resultinfo, but set it up anyway because we use some of the fields as * our own state variables. */ rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = expectedDesc; rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred); if (randomAccess) rsinfo.allowedModes |= (int) SFRM_Materialize_Random; rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; /* * Normally the passed expression tree will be a SetExprState, since the * grammar only allows a function call at the top level of a table * function reference. However, if the function doesn't return set then * the planner might have replaced the function call via constant-folding * or inlining. So if we see any other kind of expression node, execute * it via the general ExecEvalExpr() code; the only difference is that we * don't get a chance to pass a special ReturnSetInfo to any functions * buried in the expression. */ if (!setexpr->elidedFuncState) { /* * This path is similar to ExecMakeFunctionResultSet. */ returnsSet = setexpr->funcReturnsSet; InitFunctionCallInfoData(fcinfo, &(setexpr->func), list_length(setexpr->args), setexpr->fcinfo_data.fncollation, NULL, (Node *) &rsinfo); /* * Evaluate the function's argument list. * * We can't do this in the per-tuple context: the argument values * would disappear when we reset that context in the inner loop. And * the caller's CurrentMemoryContext is typically a query-lifespan * context, so we don't want to leak memory there. We require the * caller to pass a separate memory context that can be used for this, * and can be reset each time through to avoid bloat. */ MemoryContextReset(argContext); oldcontext = MemoryContextSwitchTo(argContext); ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext); MemoryContextSwitchTo(oldcontext); /* * If function is strict, and there are any NULL arguments, skip * calling the function and act like it returned NULL (or an empty * set, in the returns-set case). */ if (setexpr->func.fn_strict) { int i; for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) goto no_function_result; } } } else { /* Treat setexpr as a generic expression */ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL); } /* * Switch to short-lived context for calling the function or expression. */ MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Loop to handle the ValuePerCall protocol (which is also the same * behavior needed in the generic ExecEvalExpr path). */ for (;;) { Datum result; CHECK_FOR_INTERRUPTS(); /* * reset per-tuple memory context before each call of the function or * expression. This cleans up any local memory the function may leak * when called. */ ResetExprContext(econtext); /* Call the function or expression one time */ if (!setexpr->elidedFuncState) { pgstat_init_function_usage(&fcinfo, &fcusage); fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); } else { result = ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull); rsinfo.isDone = ExprSingleResult; } /* Which protocol does function want to use? */ if (rsinfo.returnMode == SFRM_ValuePerCall) { /* * Check for end of result set. */ if (rsinfo.isDone == ExprEndResult) break; /* * If first time through, build tuplestore for result. For a * scalar function result type, also make a suitable tupdesc. */ if (first_time) { oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); rsinfo.setResult = tupstore; if (!returnsTuple) { tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "column", funcrettype, -1, 0); rsinfo.setDesc = tupdesc; } MemoryContextSwitchTo(oldcontext); } /* * Store current resultset item. */ if (returnsTuple) { if (!fcinfo.isnull) { HeapTupleHeader td = DatumGetHeapTupleHeader(result); if (tupdesc == NULL) { /* * This is the first non-NULL result from the * function. Use the type info embedded in the * rowtype Datum to look up the needed tupdesc. Make * a copy for the query. */ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); rsinfo.setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); } else { /* * Verify all later returned rows have same subtype; * necessary in case the type is RECORD. */ if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid || HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("rows returned by function are not all of the same row type"))); } /* * tuplestore_puttuple needs a HeapTuple not a bare * HeapTupleHeader, but it doesn't need all the fields. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuplestore_puttuple(tupstore, &tmptup); } else { /* * NULL result from a tuple-returning function; expand it * to a row of all nulls. We rely on the expectedDesc to * form such rows. (Note: this would be problematic if * tuplestore_putvalues saved the tdtypeid/tdtypmod from * the provided descriptor, since that might not match * what we get from the function itself. But it doesn't.) */ int natts = expectedDesc->natts; bool *nullflags; nullflags = (bool *) palloc(natts * sizeof(bool)); memset(nullflags, true, natts * sizeof(bool)); tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags); } } else { /* Scalar-type case: just store the function result */ tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull); } /* * Are we done? */ if (rsinfo.isDone != ExprMultipleResult) break; } else if (rsinfo.returnMode == SFRM_Materialize) { /* check we're on the same page as the function author */ if (!first_time || rsinfo.isDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("table-function protocol for materialize mode was not followed"))); /* Done evaluating the set result */ break; } else ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("unrecognized table-function returnMode: %d", (int) rsinfo.returnMode))); first_time = false; } no_function_result: /* * If we got nothing from the function (ie, an empty-set or NULL result), * we have to create the tuplestore to return, and if it's a * non-set-returning function then insert a single all-nulls row. As * above, we depend on the expectedDesc to manufacture the dummy row. */ if (rsinfo.setResult == NULL) { MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); rsinfo.setResult = tupstore; if (!returnsSet) { int natts = expectedDesc->natts; bool *nullflags; MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); nullflags = (bool *) palloc(natts * sizeof(bool)); memset(nullflags, true, natts * sizeof(bool)); tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags); } } /* * If function provided a tupdesc, cross-check it. We only really need to * do this for functions returning RECORD, but might as well do it always. */ if (rsinfo.setDesc) { tupledesc_match(expectedDesc, rsinfo.setDesc); /* * If it is a dynamically-allocated TupleDesc, free it: it is * typically allocated in a per-query context, so we must avoid * leaking it across multiple usages. */ if (rsinfo.setDesc->tdrefcount == -1) FreeTupleDesc(rsinfo.setDesc); } MemoryContextSwitchTo(callerContext); /* All done, pass back the tuplestore */ return rsinfo.setResult; }
/* ---------- * toast_flatten_tuple_attribute - * * If a Datum is of composite type, "flatten" it to contain no toasted fields. * This must be invoked on any potentially-composite field that is to be * inserted into a tuple. Doing this preserves the invariant that toasting * goes only one level deep in a tuple. * ---------- */ Datum toast_flatten_tuple_attribute(Datum value, Oid typeId, int32 typeMod) { TupleDesc tupleDesc; HeapTupleHeader olddata; HeapTupleHeader new_data; int32 new_len; HeapTupleData tmptup; Form_pg_attribute *att; int numAttrs; int i; bool need_change = false; bool has_nulls = false; Datum toast_values[MaxTupleAttributeNumber]; bool toast_isnull[MaxTupleAttributeNumber]; bool toast_free[MaxTupleAttributeNumber]; /* * See if it's a composite type, and get the tupdesc if so. */ tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, typeMod, true); if (tupleDesc == NULL) return value; /* not a composite type */ tupleDesc = CreateTupleDescCopy(tupleDesc); att = tupleDesc->attrs; numAttrs = tupleDesc->natts; /* * Break down the tuple into fields. */ olddata = DatumGetHeapTupleHeader(value); Assert(typeId == HeapTupleHeaderGetTypeId(olddata)); Assert(typeMod == HeapTupleHeaderGetTypMod(olddata)); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(olddata); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = olddata; Assert(numAttrs <= MaxTupleAttributeNumber); heap_deform_tuple(&tmptup, tupleDesc, toast_values, toast_isnull); memset(toast_free, 0, numAttrs * sizeof(bool)); for (i = 0; i < numAttrs; i++) { /* * Look at non-null varlena attributes */ if (toast_isnull[i]) has_nulls = true; else if (att[i]->attlen == -1) { varattrib *new_value; new_value = (varattrib *) DatumGetPointer(toast_values[i]); if (VARATT_IS_EXTENDED(new_value)) { new_value = heap_tuple_untoast_attr(new_value); toast_values[i] = PointerGetDatum(new_value); toast_free[i] = true; need_change = true; } } } /* * If nothing to untoast, just return the original tuple. */ if (!need_change) { FreeTupleDesc(tupleDesc); return value; } /* * Calculate the new size of the tuple. Header size should not change, * but data size might. */ new_len = offsetof(HeapTupleHeaderData, t_bits); if (has_nulls) new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_len += heap_compute_data_size(tupleDesc, toast_values, toast_isnull); new_data = (HeapTupleHeader) palloc0(new_len); /* * Put the tuple header and the changed values into place */ memcpy(new_data, olddata, olddata->t_hoff); HeapTupleHeaderSetDatumLength(new_data, new_len); heap_fill_tuple(tupleDesc, toast_values, toast_isnull, (char *) new_data + olddata->t_hoff, &(new_data->t_infomask), has_nulls ? new_data->t_bits : NULL); /* * Free allocated temp values */ for (i = 0; i < numAttrs; i++) if (toast_free[i]) pfree(DatumGetPointer(toast_values[i])); FreeTupleDesc(tupleDesc); return PointerGetDatum(new_data); }
static PyObj func_new_from_oid(PyTypeObject *subtype, Oid fn_oid, PyObj fn_oid_int, PyObj fn_oid_str) { volatile HeapTuple ht = NULL; volatile PyObj rob = NULL; Assert(OidIsValid(fn_oid)); Assert(fn_oid_int != NULL); Assert(fn_oid_str != NULL); rob = subtype->tp_alloc(subtype, 0); if (rob == NULL) return(NULL); PyPgFunction_SetOid(rob, fn_oid); PyPgFunction_SetStateful(rob, false); Py_INCREF(fn_oid_int); Py_INCREF(fn_oid_str); PyPgFunction_SetPyLongOid(rob, fn_oid_int); PyPgFunction_SetPyUnicodeOid(rob, fn_oid_str); /* * Collect the Function information from the system cache */ PG_TRY(); { Form_pg_proc ps; Form_pg_namespace ns; FmgrInfo flinfo; text *prosrc; Datum prosrc_datum; bool isnull = true; const char *filename = NULL, *nspname, *q_nspname; TupleDesc argdesc = NULL, result_desc = NULL; Oid prorettype = InvalidOid; PyObj id_str_ob = NULL, nspname_str_ob = NULL; PyObj filename_str_ob = NULL, q_nspname_str_ob = NULL; PyObj output = NULL, src = NULL; PyObj input; ht = SearchSysCache(PROCOID, fn_oid, 0, 0, 0); if (!HeapTupleIsValid(ht)) { ereport(ERROR,( errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("failed to find function at oid %d", fn_oid) )); } PyPgFunction_SetXMin(rob, HeapTupleHeaderGetXmin(ht->t_data)); PyPgFunction_SetItemPointer(rob, &(ht->t_self)); ps = (Form_pg_proc) GETSTRUCT(ht); PyPgFunction_SetNamespace(rob, ps->pronamespace); PyPgFunction_SetLanguage(rob, ps->prolang); PyPgFunction_SetReturnsSet(rob, ps->proretset); PyPgFunction_SetVolatile(rob, ps->provolatile); prorettype = ps->prorettype; prosrc_datum = SysCacheGetAttr( PROCOID, ht, Anum_pg_proc_prosrc, &isnull); if (!isnull) { prosrc = DatumGetTextPCopy(prosrc_datum); src = PyUnicode_FromTEXT(prosrc); PyPgFunction_SetSource(rob, src); pfree(prosrc); prosrc = NULL; } else { src = Py_None; Py_INCREF(src); PyPgFunction_SetSource(rob, src); } if (src == NULL) PyErr_RelayException(); /* * Get the function's address. */ fmgr_info(fn_oid, &flinfo); PyPgFunction_SetPGFunction(rob, flinfo.fn_addr); /* * Build function parameters TupleDesc */ if (ps->pronargs > 0) { argdesc = TupleDesc_From_pg_proc_arginfo(ht); input = PyPgTupleDesc_FromCopy(argdesc); if (input == NULL) PyErr_RelayException(); PyPgFunction_SetInput(rob, input); FreeTupleDesc(argdesc); } else { Py_INCREF(EmptyPyPgTupleDesc); PyPgFunction_SetInput(rob, EmptyPyPgTupleDesc); } /* * If it's a registered composite, * PyPgType_FromOid will resolve that below. */ if (prorettype == RECORDOID) { /* * Otherwise, build out a function result tupdesc. */ result_desc = build_function_result_tupdesc_t(ht); if (result_desc != NULL) { /* * Anonymous composite returned by function. */ output = PyPgType_FromTupleDesc(result_desc); PyPgFunction_SetOutput(rob, output); FreeTupleDesc(result_desc); /* * We will certainly be using it, so bless it right now iff * it's *not* polymorphic. */ if (output && !PyPgType_IsPolymorphic(output)) BlessTupleDesc(PyPgType_GetTupleDesc(output)); } else { /* * ew.. */ goto lookup_output_type; } } else { lookup_output_type: output = PyPgType_FromOid(prorettype); if (output == NULL) PyErr_RelayException(); PyPgFunction_SetOutput(rob, output); } RELEASESYSCACHE(&ht); /* * Don't worry *too* much about leaking memory. */ filename = format_procedure(fn_oid); Assert(filename != NULL); ht = SearchSysCache(NAMESPACEOID, PyPgFunction_GetNamespace(rob), 0, 0, 0); if (!HeapTupleIsValid(ht)) { pfree((char *) filename); elog(ERROR, "function %u namespace %u does not exist", fn_oid, PyPgFunction_GetNamespace(rob)); } ns = (Form_pg_namespace) GETSTRUCT(ht); nspname = pstrdup(NameStr(ns->nspname)); RELEASESYSCACHE(&ht); /* * Build the filename string. */ q_nspname = quote_identifier(nspname); nspname_str_ob = PyUnicode_FromCString(nspname); PyPgFunction_SetNamespaceName(rob, nspname_str_ob); if (nspname_str_ob == NULL) { /* * Invalid encoded string? */ if (nspname != q_nspname) pfree((char *) q_nspname); pfree((char *) nspname); PyErr_RelayException(); } q_nspname_str_ob = PyUnicode_FromCString(q_nspname); if (nspname != q_nspname) pfree((char *) q_nspname); pfree((char *) nspname); /* * Ignore the potential exception for a moment. */ id_str_ob = PyUnicode_FromCString(filename); /* * Skip the filename_str_ob if either of the above failed. */ if (id_str_ob != NULL && q_nspname_str_ob != NULL) { if (FunctionIsVisible(fn_oid)) filename_str_ob = PyUnicode_FromFormat("%U.%U", q_nspname_str_ob, id_str_ob); else { filename_str_ob = id_str_ob; Py_INCREF(id_str_ob); } } PyPgFunction_SetFilename(rob, filename_str_ob); Py_XDECREF(q_nspname_str_ob); Py_XDECREF(id_str_ob); pfree((char *) filename); if (filename_str_ob == NULL) PyErr_RelayException(); } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); if (ht != NULL) ReleaseSysCache(ht); } PG_END_TRY(); return(rob); }
static void FunctionParserInit(FunctionParser *self, Checker *checker, const char *infile, TupleDesc desc, bool multi_process, Oid collation) { int i; ParsedFunction function; int nargs; Oid funcid; HeapTuple ftup; Form_pg_proc pp; bool tupledesc_matched = false; if (pg_strcasecmp(infile, "stdin") == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot load from STDIN in the case of \"TYPE = FUNCTION\""))); if (checker->encoding != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("does not support parameter \"ENCODING\" in \"TYPE = FUNCTION\""))); function = ParseFunction(infile, false); funcid = function.oid; fmgr_info(funcid, &self->flinfo); if (!self->flinfo.fn_retset) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function must return set"))); ftup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); pp = (Form_pg_proc) GETSTRUCT(ftup); /* Check data type of the function result value */ if (pp->prorettype == desc->tdtypeid && desc->tdtypeid != RECORDOID) tupledesc_matched = true; else if (pp->prorettype == RECORDOID) { TupleDesc resultDesc = NULL; /* Check for OUT parameters defining a RECORD result */ resultDesc = build_function_result_tupdesc_t(ftup); if (resultDesc) { tupledesc_match(desc, resultDesc); tupledesc_matched = true; FreeTupleDesc(resultDesc); } } else if (get_typtype(pp->prorettype) != TYPTYPE_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return data type and target table data type do not match"))); if (tupledesc_matched && checker->tchecker) checker->tchecker->status = NO_COERCION; /* * assign arguments */ nargs = function.nargs; for (i = 0; #if PG_VERSION_NUM >= 80400 i < nargs - function.nvargs; #else i < nargs; #endif ++i) { if (function.args[i] == NULL) { if (self->flinfo.fn_strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function is strict, but argument %d is NULL", i))); self->fcinfo.argnull[i] = true; } else { Oid typinput; Oid typioparam; getTypeInputInfo(pp->proargtypes.values[i], &typinput, &typioparam); self->fcinfo.arg[i] = OidInputFunctionCall(typinput, (char *) function.args[i], typioparam, -1); self->fcinfo.argnull[i] = false; pfree(function.args[i]); } } /* * assign variadic arguments */ #if PG_VERSION_NUM >= 80400 if (function.nvargs > 0) { int nfixedarg; Oid func; Oid element_type; int16 elmlen; bool elmbyval; char elmalign; char elmdelim; Oid elmioparam; Datum *elems; bool *nulls; int dims[1]; int lbs[1]; ArrayType *arry; nfixedarg = i; element_type = pp->provariadic; /* * Get info about element type, including its input conversion proc */ get_type_io_data(element_type, IOFunc_input, &elmlen, &elmbyval, &elmalign, &elmdelim, &elmioparam, &func); elems = (Datum *) palloc(function.nvargs * sizeof(Datum)); nulls = (bool *) palloc0(function.nvargs * sizeof(bool)); for (i = 0; i < function.nvargs; i++) { if (function.args[nfixedarg + i] == NULL) nulls[i] = true; else { elems[i] = OidInputFunctionCall(func, (char *) function.args[nfixedarg + i], elmioparam, -1); pfree(function.args[nfixedarg + i]); } } dims[0] = function.nvargs; lbs[0] = 1; arry = construct_md_array(elems, nulls, 1, dims, lbs, element_type, elmlen, elmbyval, elmalign); self->fcinfo.arg[nfixedarg] = PointerGetDatum(arry); } /* * assign default arguments */ if (function.ndargs > 0) { Datum proargdefaults; bool isnull; char *str; List *defaults; int ndelete; ListCell *l; /* shouldn't happen, FuncnameGetCandidates messed up */ if (function.ndargs > pp->pronargdefaults) elog(ERROR, "not enough default arguments"); proargdefaults = SysCacheGetAttr(PROCOID, ftup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); str = TextDatumGetCString(proargdefaults); defaults = (List *) stringToNode(str); Assert(IsA(defaults, List)); pfree(str); /* Delete any unused defaults from the returned list */ ndelete = list_length(defaults) - function.ndargs; while (ndelete-- > 0) defaults = list_delete_first(defaults); self->arg_econtext = CreateStandaloneExprContext(); foreach(l, defaults) { Expr *expr = (Expr *) lfirst(l); ExprState *argstate; ExprDoneCond thisArgIsDone; /* probably shouldn't happen ... */ if (nargs >= FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("cannot pass more than %d arguments to a function", FUNC_MAX_ARGS))); argstate = ExecInitExpr(expr, NULL); self->fcinfo.arg[nargs] = ExecEvalExpr(argstate, self->arg_econtext, &self->fcinfo.argnull[nargs], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); nargs++; }