/* * Copy a ParamListInfo structure. * * The result is allocated in CurrentMemoryContext. */ ParamListInfo copyParamList(ParamListInfo from) { ParamListInfo retval; Size size; int i; if (from == NULL || from->numParams <= 0) return NULL; /* sizeof(ParamListInfoData) includes the first array element */ size = sizeof(ParamListInfoData) + (from->numParams - 1) *sizeof(ParamExternData); retval = (ParamListInfo) palloc(size); memcpy(retval, from, size); /* * Flat-copy is not good enough for pass-by-ref data values, so make a * pass over the array to copy those. */ for (i = 0; i < retval->numParams; i++) { ParamExternData *prm = &retval->params[i]; int16 typLen; bool typByVal; if (prm->isnull || !OidIsValid(prm->ptype)) continue; get_typlenbyval(prm->ptype, &typLen, &typByVal); prm->value = datumCopy(prm->value, typByVal, typLen); } return retval; }
static void fillTypeDesc(SpGistTypeDesc *desc, Oid type) { desc->type = type; if (type != InvalidOid) get_typlenbyval(type, &desc->attlen, &desc->attbyval); }
Datum orafce_dump(PG_FUNCTION_ARGS) { Oid valtype = get_fn_expr_argtype(fcinfo->flinfo, 0); List *args; int16 typlen; bool typbyval; Size length; Datum value; int format; StringInfoData str; if (!fcinfo->flinfo || !fcinfo->flinfo->fn_expr) elog(ERROR, "function is called from invalid context"); if (PG_ARGISNULL(0)) elog(ERROR, "argument is NULL"); value = PG_GETARG_DATUM(0); format = PG_GETARG_IF_EXISTS(1, INT32, 10); args = ((FuncExpr *) fcinfo->flinfo->fn_expr)->args; valtype = exprType((Node *) list_nth(args, 0)); get_typlenbyval(valtype, &typlen, &typbyval); length = datumGetSize(value, typbyval, typlen); initStringInfo(&str); appendStringInfo(&str, "Typ=%d Len=%d: ", valtype, (int) length); if (!typbyval) { appendDatum(&str, DatumGetPointer(value), length, format); } else if (length <= 1) { char v = DatumGetChar(value); appendDatum(&str, &v, sizeof(char), format); } else if (length <= 2) { int16 v = DatumGetInt16(value); appendDatum(&str, &v, sizeof(int16), format); } else if (length <= 4) { int32 v = DatumGetInt32(value); appendDatum(&str, &v, sizeof(int32), format); } else { int64 v = DatumGetInt64(value); appendDatum(&str, &v, sizeof(int64), format); } PG_RETURN_TEXT_P(cstring_to_text(str.data)); }
/* * makeNullConst - * creates a Const node representing a NULL of the specified type */ Const * makeNullConst(Oid consttype) { int16 typLen; bool typByVal; get_typlenbyval(consttype, &typLen, &typByVal); return makeConst(consttype, (int) typLen, (Datum) 0, true, typByVal); }
/* check whether the contents in the bytea is safe for a fmtransval */ void check_fmtransval(bytea * storage) { fmtransval * fmt = NULL; sortasort *st = NULL; int16 typLen = 0; bool typByVal = false; if (VARSIZE(storage) < VARHDRSZ + sizeof(fmtransval)) { elog(ERROR, "invalid transition state for fmsketch"); } fmt = (fmtransval*)VARDATA(storage); if (fmt->status != SMALL && fmt->status != BIG) { elog(ERROR, "invalid transition state for fmsketch"); } if (fmt->reserved != 0) { elog(ERROR, "invalid transition state for fmsketch"); } if (InvalidOid == fmt->typOid) { elog(ERROR, "invalid transition state for fmsketch"); } get_typlenbyval(fmt->typOid, &typLen, &typByVal); if (fmt->typByVal != typByVal || fmt->typLen != typLen) { elog(ERROR, "invalid transition state for fmsketch"); } if (fmt->typLen < -2 || fmt->typLen == 0) { elog(ERROR, "invalid transition state for fmsketch"); } if (SMALL == fmt->status) { if (VARSIZE(storage) < VARHDRSZ + sizeof(fmtransval) + sizeof(sortasort)) { elog(ERROR, "invalid transition state for fmsketch"); } st = (sortasort *)fmt->storage; if (fmt->typLen != st->typLen || fmt->typByVal != (bool)st->typByVal) { elog(ERROR, "invalid transition state for fmsketch"); } check_sortasort(st, VARSIZE(storage) - VARHDRSZ - sizeof(fmtransval)); } else { if (VARSIZE(storage) < 2*VARHDRSZ + sizeof(fmtransval)) { elog(ERROR, "invalid transition state for fmsketch"); } if (VARSIZE(storage) < VARHDRSZ + sizeof(fmtransval) + VARSIZE(&(fmt->storage))) { elog(ERROR, "invalid transition state for fmsketch"); } } }
/* * makeNullConst - * creates a Const node representing a NULL of the specified type/typmod * * This is a convenience routine that just saves a lookup of the type's * storage properties. */ Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid) { int16 typLen; bool typByVal; get_typlenbyval(consttype, &typLen, &typByVal); return makeConst(consttype, consttypmod, constcollid, (int) typLen, (Datum) 0, true, typByVal); }
/* * Copy a ParamListInfo structure. * * The result is allocated in CurrentMemoryContext. * * Note: the intent of this function is to make a static, self-contained * set of parameter values. If dynamic parameter hooks are present, we * intentionally do not copy them into the result. Rather, we forcibly * instantiate all available parameter values and copy the datum values. */ ParamListInfo copyParamList(ParamListInfo from) { ParamListInfo retval; Size size; int i; if (from == NULL || from->numParams <= 0) return NULL; /* sizeof(ParamListInfoData) includes the first array element */ size = sizeof(ParamListInfoData) + (from->numParams - 1) * sizeof(ParamExternData); retval = (ParamListInfo) palloc(size); retval->paramFetch = NULL; retval->paramFetchArg = NULL; retval->parserSetup = NULL; retval->parserSetupArg = NULL; retval->numParams = from->numParams; for (i = 0; i < from->numParams; i++) { ParamExternData *oprm = &from->params[i]; ParamExternData *nprm = &retval->params[i]; int16 typLen; bool typByVal; /* give hook a chance in case parameter is dynamic */ if (!OidIsValid(oprm->ptype) && from->paramFetch != NULL) (*from->paramFetch) (from, i + 1); /* flat-copy the parameter info */ *nprm = *oprm; /* need datumCopy in case it's a pass-by-reference datatype */ if (nprm->isnull || !OidIsValid(nprm->ptype)) continue; get_typlenbyval(nprm->ptype, &typLen, &typByVal); nprm->value = datumCopy(nprm->value, typByVal, typLen); } return retval; }
/* * SPI_cursor_open() * * Open a prepared SPI plan as a portal */ Portal SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls, bool read_only) { _SPI_plan *spiplan = (_SPI_plan *) plan; List *qtlist = spiplan->qtlist; List *ptlist = spiplan->ptlist; Query *queryTree; Plan *planTree; ParamListInfo paramLI; Snapshot snapshot; MemoryContext oldcontext; Portal portal; int k; /* Ensure that the plan contains only one query */ if (list_length(ptlist) != 1 || list_length(qtlist) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open multi-query plan as cursor"))); queryTree = (Query *) linitial((List *) linitial(qtlist)); planTree = (Plan *) linitial(ptlist); /* Must be a query that returns tuples */ switch (queryTree->commandType) { case CMD_SELECT: if (queryTree->into != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open SELECT INTO query as cursor"))); break; case CMD_UTILITY: if (!UtilityReturnsTuples(queryTree->utilityStmt)) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open non-SELECT query as cursor"))); break; default: ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open non-SELECT query as cursor"))); break; } /* Reset SPI result */ SPI_processed = 0; SPI_tuptable = NULL; _SPI_current->processed = 0; _SPI_current->tuptable = NULL; /* Create the portal */ if (name == NULL || name[0] == '\0') { /* Use a random nonconflicting name */ portal = CreateNewPortal(); } else { /* In this path, error if portal of same name already exists */ portal = CreatePortal(name, false, false); } /* Switch to portals memory and copy the parsetree and plan to there */ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); queryTree = copyObject(queryTree); planTree = copyObject(planTree); /* If the plan has parameters, set them up */ if (spiplan->nargs > 0) { paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) * sizeof(ParamListInfoData)); for (k = 0; k < spiplan->nargs; k++) { paramLI[k].kind = PARAM_NUM; paramLI[k].id = k + 1; paramLI[k].ptype = spiplan->argtypes[k]; paramLI[k].isnull = (Nulls && Nulls[k] == 'n'); if (paramLI[k].isnull) { /* nulls just copy */ paramLI[k].value = Values[k]; } else { /* pass-by-ref values must be copied into portal context */ int16 paramTypLen; bool paramTypByVal; get_typlenbyval(spiplan->argtypes[k], ¶mTypLen, ¶mTypByVal); paramLI[k].value = datumCopy(Values[k], paramTypByVal, paramTypLen); } } paramLI[k].kind = PARAM_INVALID; } else paramLI = NULL; /* * Set up the portal. */ PortalDefineQuery(portal, NULL, /* unfortunately don't have sourceText */ "SELECT", /* nor the raw parse tree... */ list_make1(queryTree), list_make1(planTree), PortalGetHeapMemory(portal)); MemoryContextSwitchTo(oldcontext); /* * Set up options for portal. */ portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL); if (planTree == NULL || ExecSupportsBackwardScan(planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; /* * Set up the snapshot to use. (PortalStart will do CopySnapshot, * so we skip that here.) */ if (read_only) snapshot = ActiveSnapshot; else { CommandCounterIncrement(); snapshot = GetTransactionSnapshot(); } /* * Start portal execution. */ PortalStart(portal, paramLI, snapshot); Assert(portal->strategy == PORTAL_ONE_SELECT || portal->strategy == PORTAL_UTIL_SELECT); /* Return the created portal */ return portal; }
Datum exec_sql_using(PG_FUNCTION_ARGS) { HeapTuple procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid)); if (!HeapTupleIsValid(procedureTuple)) ereport(ERROR, (errmsg("cache lookup failed for function %u", fcinfo->flinfo->fn_oid))); Oid* types = NULL; char** names = NULL; char* modes = NULL; int nargs = get_func_arg_info(procedureTuple, &types, &names, &modes); Oid resultTypeOid; TupleDesc tupleDesc; TypeFuncClass resultType = get_call_result_type(fcinfo, &resultTypeOid, &tupleDesc); bool returnTypeIsByValue; int16 returnTypeLen; get_typlenbyval(resultTypeOid, &returnTypeLen, &returnTypeIsByValue); if (resultType != TYPEFUNC_SCALAR && resultType != TYPEFUNC_COMPOSITE) ereport(ERROR, ( errmsg("function \"%s\" has indeterminable result type", format_procedure(fcinfo->flinfo->fn_oid)) )); bool returnVoid = resultTypeOid == VOIDOID; ReleaseSysCache(procedureTuple); if (nargs < 2) ereport(ERROR, ( errmsg("function \"%s\" has less than 2 arguments", format_procedure(fcinfo->flinfo->fn_oid)) )); else if (modes != NULL) for (int i = 0; i < nargs; i++) { if (modes[i] != PROARGMODE_IN) ereport(ERROR, ( errmsg("function \"%s\" has non-IN arguments", format_procedure(fcinfo->flinfo->fn_oid)) )); } else if (PG_ARGISNULL(0)) ereport(ERROR, ( errmsg("function \"%s\" called with NULL as first argument", format_procedure(fcinfo->flinfo->fn_oid)) )); char* stmt = NULL; if (types[0] == TEXTOID) stmt = DatumGetCString( DirectFunctionCall1(textout, PG_GETARG_DATUM(0))); else if (types[0] == VARCHAROID) stmt = DatumGetCString( DirectFunctionCall1(varcharout, PG_GETARG_DATUM(0))); else ereport(ERROR, ( errmsg("function \"%s\" does not have a leading VARCHAR/TEXT " "argument", format_procedure(fcinfo->flinfo->fn_oid)) )); char* nulls = NULL; for (int i = 1; i < nargs; i++) if (PG_ARGISNULL(i)) { if (nulls == NULL) { nulls = palloc0(sizeof(char) * (nargs - 1)); memset(nulls, ' ', nargs - 1); } nulls[i - 1] = 'n'; } SPI_connect(); SPIPlanPtr plan = SPI_prepare(stmt, nargs - 1, &types[1]); if (plan == NULL) ereport(ERROR, ( errmsg("function \"%s\" could not obtain execution plan for " "SQL statement", format_procedure(fcinfo->flinfo->fn_oid)) )); int result = SPI_execute_plan(plan, &fcinfo->arg[1], nulls, false, returnVoid ? 0 : 1); Datum returnValue = 0; bool returnNull = false; if (!returnVoid) { if (result != SPI_OK_SELECT && result != SPI_OK_INSERT_RETURNING && result != SPI_OK_DELETE_RETURNING && result == SPI_OK_UPDATE_RETURNING) ereport(ERROR, ( errmsg("function \"%s\" could not obtain result from query", format_procedure(fcinfo->flinfo->fn_oid)) )); else if (SPI_tuptable->tupdesc->natts != 1) ereport(ERROR, ( errmsg("function \"%s\" retrieved more than one column from " "query", format_procedure(fcinfo->flinfo->fn_oid)) )); else if (resultTypeOid != SPI_gettypeid(SPI_tuptable->tupdesc, 1)) ereport(ERROR, ( errmsg("function \"%s\" has different return type OID than " "what query returned", format_procedure(fcinfo->flinfo->fn_oid)) )); /* It is important to copy the value into the upper executor context, * i.e., the memory context that was current when SPI_connect was * called */ returnValue = SPI_getbinval(SPI_copytuple(SPI_tuptable->vals[0]), SPI_tuptable->tupdesc, 1, &returnNull); } SPI_freeplan(plan); if (nulls) pfree(nulls); SPI_finish(); if (result < 0) ereport(ERROR, ( errmsg("function \"%s\" encountered error %d during SQL execution", format_procedure(fcinfo->flinfo->fn_oid), result) )); if (returnVoid) PG_RETURN_VOID(); else if (returnNull) PG_RETURN_NULL(); else return returnValue; }
/*! UDA transition function for the fmsketch aggregate. */ Datum __fmsketch_trans(PG_FUNCTION_ARGS) { bytea * transblob = (bytea *)PG_GETARG_BYTEA_P(0); fmtransval *transval; Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Oid funcOid; bool typIsVarlena; Datum retval; Datum inval; if (!OidIsValid(element_type)) elog(ERROR, "could not determine data type of input"); /* * This is Postgres boilerplate for UDFs that modify the data in their own context. * Such UDFs can only be correctly called in an agg context since regular scalar * UDFs are essentially stateless across invocations. */ if (!(fcinfo->context && (IsA(fcinfo->context, AggState) #ifdef NOTGP || IsA(fcinfo->context, WindowAggState) #endif ))) elog( ERROR, "UDF call to a function that only works for aggs (destructive pass by reference)"); /* get the provided element, being careful in case it's NULL */ if (!PG_ARGISNULL(1)) { inval = PG_GETARG_DATUM(1); /* * if this is the first call, initialize transval to hold a sortasort * on the first call, we should have the empty string (if the agg was declared properly!) */ if (VARSIZE(transblob) <= VARHDRSZ) { size_t blobsz = VARHDRSZ + sizeof(fmtransval) + SORTASORT_INITIAL_STORAGE; transblob = (bytea *)palloc0(blobsz); SET_VARSIZE(transblob, blobsz); transval = (fmtransval *)VARDATA(transblob); transval->typOid = element_type; /* figure out the outfunc for this type */ getTypeOutputInfo(element_type, &funcOid, &typIsVarlena); get_typlenbyval(element_type, &(transval->typLen), &(transval->typByVal)); transval->status = SMALL; sortasort_init((sortasort *)transval->storage, MINVALS, SORTASORT_INITIAL_STORAGE, transval->typLen, transval->typByVal); } else { check_fmtransval(transblob); /* extract the existing transval from the transblob */ transval = (fmtransval *)VARDATA(transblob); if (transval->typOid != element_type) { elog(ERROR, "cannot aggregate on elements with different types"); } } /* * if we've seen < MINVALS distinct values, place datum into the sortasort * XXXX Would be cleaner to try the sortasort insert and if it fails, then continue. */ if (transval->status == SMALL && ((sortasort *)(transval->storage))->num_vals < MINVALS) { int len = ExtractDatumLen(inval, transval->typLen, transval->typByVal, -1); retval = PointerGetDatum(fmsketch_sortasort_insert( transblob, inval, len)); PG_RETURN_DATUM(retval); } /* * if we've seen exactly MINVALS distinct values, create FM bitmaps * and load the contents of the sortasort into the FM sketch */ else if (transval->status == SMALL && ((sortasort *)(transval->storage))->num_vals == MINVALS) { int i; sortasort *s = (sortasort *)(transval->storage); bytea *newblob = fm_new(transval); transval = (fmtransval *)VARDATA(newblob); /* * "catch up" on the past as if we were doing FM from the beginning: * apply the FM sketching algorithm to each value previously stored in the sortasort */ for (i = 0; i < MINVALS; i++) __fmsketch_trans_c(newblob, PointerExtractDatum(sortasort_getval(s,i), s->typByVal)); /* * XXXX would like to pfree the old transblob, but the memory allocator doesn't like it * XXXX Meanwhile we know that this memory "leak" is of fixed size and will get * XXXX deallocated "soon" when the memory context is destroyed. */ /* drop through to insert the current datum in "BIG" mode */ transblob = newblob; } /* * if we're here we've seen >=MINVALS distinct values and are in BIG mode. * Just for sanity, let's check. */ if (transval->status != BIG) elog( ERROR, "FM sketch failed internal sanity check"); /* Apply FM algorithm to this datum */ retval = __fmsketch_trans_c(transblob, inval); PG_RETURN_DATUM(retval); } else PG_RETURN_NULL(); }
/* * Compose and dispatch the MPPEXEC commands corresponding to a plan tree * within a complete parallel plan. (A plan tree will correspond either * to an initPlan or to the main plan.) * * If cancelOnError is true, then any dispatching error, a cancellation * request from the client, or an error from any of the associated QEs, * may cause the unfinished portion of the plan to be abandoned or canceled; * and in the event this occurs before all gangs have been dispatched, this * function does not return, but waits for all QEs to stop and exits to * the caller's error catcher via ereport(ERROR,...).Otherwise this * function returns normally and errors are not reported until later. * * If cancelOnError is false, the plan is to be dispatched as fully as * possible and the QEs allowed to proceed regardless of cancellation * requests, errors or connection failures from other QEs, etc. * * The CdbDispatchResults objects allocated for the plan are returned * in *pPrimaryResults. The caller, after calling * CdbCheckDispatchResult(), can examine the CdbDispatchResults * objects, can keep them as long as needed, and ultimately must free * them with cdbdisp_destroyDispatcherState() prior to deallocation of * the caller's memory context. Callers should use PG_TRY/PG_CATCH to * ensure proper cleanup. * * To wait for completion, check for errors, and clean up, it is * suggested that the caller use cdbdisp_finishCommand(). * * Note that the slice tree dispatched is the one specified in the EState * of the argument QueryDesc as es_cur__slice. * * Note that the QueryDesc params must include PARAM_EXEC_REMOTE parameters * containing the values of any initplans required by the slice to be run. * (This is handled by calls to addRemoteExecParamsToParamList() from the * functions preprocess_initplans() and ExecutorRun().) * * Each QE receives its assignment as a message of type 'M' in PostgresMain(). * The message is deserialized and processed by exec_mpp_query() in postgres.c. */ void cdbdisp_dispatchPlan(struct QueryDesc *queryDesc, bool planRequiresTxn, bool cancelOnError, struct CdbDispatcherState *ds) { char *splan, *sddesc, *sparams; int splan_len, splan_len_uncompressed, sddesc_len, sparams_len; SliceTable *sliceTbl; int rootIdx; int oldLocalSlice; PlannedStmt *stmt; bool is_SRI; DispatchCommandQueryParms queryParms; CdbComponentDatabaseInfo *qdinfo; ds->primaryResults = NULL; ds->dispatchThreads = NULL; Assert(Gp_role == GP_ROLE_DISPATCH); Assert(queryDesc != NULL && queryDesc->estate != NULL); /* * Later we'll need to operate with the slice table provided via the * EState structure in the argument QueryDesc. Cache this information * locally and assert our expectations about it. */ sliceTbl = queryDesc->estate->es_sliceTable; rootIdx = RootSliceIndex(queryDesc->estate); Assert(sliceTbl != NULL); Assert(rootIdx == 0 || (rootIdx > sliceTbl->nMotions && rootIdx <= sliceTbl->nMotions + sliceTbl->nInitPlans)); /* * Keep old value so we can restore it. We use this field as a parameter. */ oldLocalSlice = sliceTbl->localSlice; /* * This function is called only for planned statements. */ stmt = queryDesc->plannedstmt; Assert(stmt); /* * Let's evaluate STABLE functions now, so we get consistent values on the QEs * * Also, if this is a single-row INSERT statement, let's evaluate * nextval() and currval() now, so that we get the QD's values, and a * consistent value for everyone * */ is_SRI = false; if (queryDesc->operation == CMD_INSERT) { Assert(stmt->commandType == CMD_INSERT); /* * We might look for constant input relation (instead of SRI), but I'm afraid * * that wouldn't scale. */ is_SRI = IsA(stmt->planTree, Result) && stmt->planTree->lefttree == NULL; } if (!is_SRI) clear_relsize_cache(); if (queryDesc->operation == CMD_INSERT || queryDesc->operation == CMD_SELECT || queryDesc->operation == CMD_UPDATE || queryDesc->operation == CMD_DELETE) { MemoryContext oldContext; oldContext = CurrentMemoryContext; if (stmt->qdContext) { oldContext = MemoryContextSwitchTo(stmt->qdContext); } else /* * memory context of plan tree should not change */ { MemoryContext mc = GetMemoryChunkContext(stmt->planTree); oldContext = MemoryContextSwitchTo(mc); } stmt->planTree = (Plan *) exec_make_plan_constant(stmt, is_SRI); MemoryContextSwitchTo(oldContext); } /* * Cursor queries and bind/execute path queries don't run on the * writer-gang QEs; but they require snapshot-synchronization to * get started. * * initPlans, and other work (see the function pre-evaluation * above) may advance the snapshot "segmateSync" value, so we're * best off setting the shared-snapshot-ready value here. This * will dispatch to the writer gang and force it to set its * snapshot; we'll then be able to serialize the same snapshot * version (see qdSerializeDtxContextInfo() below). */ if (queryDesc->extended_query) { verify_shared_snapshot_ready(); } /* * serialized plan tree. Note that we're called for a single * slice tree (corresponding to an initPlan or the main plan), so the * parameters are fixed and we can include them in the prefix. */ splan = serializeNode((Node *) queryDesc->plannedstmt, &splan_len, &splan_len_uncompressed); uint64 plan_size_in_kb = ((uint64) splan_len_uncompressed) / (uint64) 1024; if (0 < gp_max_plan_size && plan_size_in_kb > gp_max_plan_size) { ereport(ERROR, (errcode(ERRCODE_STATEMENT_TOO_COMPLEX), (errmsg("Query plan size limit exceeded, current size: " UINT64_FORMAT "KB, max allowed size: %dKB", plan_size_in_kb, gp_max_plan_size), errhint("Size controlled by gp_max_plan_size")))); } Assert(splan != NULL && splan_len > 0 && splan_len_uncompressed > 0); if (queryDesc->params != NULL && queryDesc->params->numParams > 0) { ParamListInfoData *pli; ParamExternData *pxd; StringInfoData parambuf; Size length; int plioff; int32 iparam; /* * Allocate buffer for params */ initStringInfo(¶mbuf); /* * Copy ParamListInfoData header and ParamExternData array */ pli = queryDesc->params; length = (char *) &pli->params[pli->numParams] - (char *) pli; plioff = parambuf.len; Assert(plioff == MAXALIGN(plioff)); appendBinaryStringInfo(¶mbuf, pli, length); /* * Copy pass-by-reference param values. */ for (iparam = 0; iparam < queryDesc->params->numParams; iparam++) { int16 typlen; bool typbyval; /* * Recompute pli each time in case parambuf.data is repalloc'ed */ pli = (ParamListInfoData *) (parambuf.data + plioff); pxd = &pli->params[iparam]; if (pxd->ptype == InvalidOid) continue; /* * Does pxd->value contain the value itself, or a pointer? */ get_typlenbyval(pxd->ptype, &typlen, &typbyval); if (!typbyval) { char *s = DatumGetPointer(pxd->value); if (pxd->isnull || !PointerIsValid(s)) { pxd->isnull = true; pxd->value = 0; } else { length = datumGetSize(pxd->value, typbyval, typlen); /* * We *must* set this before we * append. Appending may realloc, which will * invalidate our pxd ptr. (obviously we could * append first if we recalculate pxd from the new * base address) */ pxd->value = Int32GetDatum(length); appendBinaryStringInfo(¶mbuf, &iparam, sizeof(iparam)); appendBinaryStringInfo(¶mbuf, s, length); } } } sparams = parambuf.data; sparams_len = parambuf.len; } else { sparams = NULL; sparams_len = 0; } sddesc = serializeNode((Node *) queryDesc->ddesc, &sddesc_len, NULL /*uncompressed_size */ ); MemSet(&queryParms, 0, sizeof(queryParms)); queryParms.strCommand = queryDesc->sourceText; queryParms.serializedQuerytree = NULL; queryParms.serializedQuerytreelen = 0; queryParms.serializedPlantree = splan; queryParms.serializedPlantreelen = splan_len; queryParms.serializedParams = sparams; queryParms.serializedParamslen = sparams_len; queryParms.serializedQueryDispatchDesc = sddesc; queryParms.serializedQueryDispatchDesclen = sddesc_len; queryParms.rootIdx = rootIdx; /* * sequence server info */ qdinfo = &(getComponentDatabases()->entry_db_info[0]); Assert(qdinfo != NULL && qdinfo->hostip != NULL); queryParms.seqServerHost = pstrdup(qdinfo->hostip); queryParms.seqServerHostlen = strlen(qdinfo->hostip) + 1; queryParms.seqServerPort = seqServerCtl->seqServerPort; /* * serialized a version of our snapshot */ /* * Generate our transction isolations. We generally want Plan * based dispatch to be in a global transaction. The executor gets * to decide if the special circumstances exist which allow us to * dispatch without starting a global xact. */ queryParms.serializedDtxContextInfo = qdSerializeDtxContextInfo(&queryParms.serializedDtxContextInfolen, true /* wantSnapshot */ , queryDesc->extended_query, mppTxnOptions(planRequiresTxn), "cdbdisp_dispatchPlan"); cdbdisp_dispatchX(&queryParms, cancelOnError, sliceTbl, ds); sliceTbl->localSlice = oldLocalSlice; }
/* * Copied from Postgres' src/backend/optimizer/util/clauses.c * * evaluate_expr: pre-evaluate a constant expression * * We use the executor's routine ExecEvalExpr() to avoid duplication of * code and ensure we get the same result as the executor would get. */ Expr * evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Oid result_collation) { EState *estate; ExprState *exprstate; MemoryContext oldcontext; Datum const_val; bool const_is_null; int16 resultTypLen; bool resultTypByVal; /* * To use the executor, we need an EState. */ estate = CreateExecutorState(); /* We can use the estate's working context to avoid memory leaks. */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* Make sure any opfuncids are filled in. */ fix_opfuncids((Node *) expr); /* * Prepare expr for execution. (Note: we can't use ExecPrepareExpr * because it'd result in recursively invoking eval_const_expressions.) */ exprstate = ExecInitExpr(expr, NULL); /* * And evaluate it. * * It is OK to use a default econtext because none of the ExecEvalExpr() * code used in this situation will use econtext. That might seem * fortuitous, but it's not so unreasonable --- a constant expression does * not depend on context, by definition, n'est ce pas? */ const_val = ExecEvalExprSwitchContext(exprstate, GetPerTupleExprContext(estate), &const_is_null, NULL); /* Get info needed about result datatype */ get_typlenbyval(result_type, &resultTypLen, &resultTypByVal); /* Get back to outer memory context */ MemoryContextSwitchTo(oldcontext); /* * Must copy result out of sub-context used by expression eval. * * Also, if it's varlena, forcibly detoast it. This protects us against * storing TOAST pointers into plans that might outlive the referenced * data. (makeConst would handle detoasting anyway, but it's worth a few * extra lines here so that we can do the copy and detoast in one step.) */ if (!const_is_null) { if (resultTypLen == -1) const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val)); else const_val = datumCopy(const_val, resultTypByVal, resultTypLen); } /* Release all the junk we just created */ FreeExecutorState(estate); /* * Make the constant result node. */ return (Expr *) makeConst(result_type, result_typmod, result_collation, resultTypLen, const_val, const_is_null, resultTypByVal); }