Esempio n. 1
0
/*
 * 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;
}
Esempio n. 2
0
static void
fillTypeDesc(SpGistTypeDesc *desc, Oid type)
{
    desc->type = type;

    if (type != InvalidOid)
        get_typlenbyval(type, &desc->attlen, &desc->attbyval);
}
Esempio n. 3
0
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));
}
Esempio n. 4
0
/*
 * 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);
}
Esempio n. 5
0
File: fm.c Progetto: AI-Org/madlib
/* 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");
        }
    }
}
Esempio n. 6
0
/*
 * 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);
}
Esempio n. 7
0
File: params.c Progetto: GisKook/Gis
/*
 * 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;
}
Esempio n. 8
0
/*
 * 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],
								&paramTypLen, &paramTypByVal);
				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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
File: fm.c Progetto: AI-Org/madlib
/*! 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();
}
Esempio n. 11
0
/*
 * 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(&parambuf);

		/*
		 * 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(&parambuf, 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(&parambuf, &iparam, sizeof(iparam));
					appendBinaryStringInfo(&parambuf, 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;
}
Esempio n. 12
0
/*
 * 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);
}