Пример #1
0
/*
 * GetMultiPlanString returns either NULL, if the plan is not a distributed
 * one, or the string representing the distributed plan.
 */
static char *
GetMultiPlanString(PlannedStmt *result)
{
	FunctionScan *fauxFunctionScan = NULL;
	RangeTblFunction *fauxFunction = NULL;
	FuncExpr *fauxFuncExpr = NULL;
	Const *multiPlanData = NULL;

	if (!IsA(result->planTree, FunctionScan))
	{
		return NULL;
	}

	fauxFunctionScan = (FunctionScan *) result->planTree;

	if (list_length(fauxFunctionScan->functions) != 1)
	{
		return NULL;
	}

	fauxFunction = linitial(fauxFunctionScan->functions);

	if (!IsA(fauxFunction->funcexpr, FuncExpr))
	{
		return NULL;
	}

	fauxFuncExpr = (FuncExpr *) fauxFunction->funcexpr;

	if (fauxFuncExpr->funcid != CitusExtraDataContainerFuncId())
	{
		return NULL;
	}

	if (list_length(fauxFuncExpr->args) != 1)
	{
		ereport(ERROR, (errmsg("unexpected number of function arguments to "
							   "citus_extradata_container")));
	}

	multiPlanData = (Const *) linitial(fauxFuncExpr->args);
	Assert(IsA(multiPlanData, Const));
	Assert(multiPlanData->consttype == CSTRINGOID);

	return DatumGetCString(multiPlanData->constvalue);
}
Пример #2
0
/*
 * SetRangeTblExtraData adds additional data to a RTE, overwriting previous
 * values, if present.
 *
 * The data is stored as RTE_FUNCTION type RTE of a special
 * citus_extradata_container function, with the extra data serialized into the
 * function arguments. That works, because these RTEs aren't used by Postgres
 * to any significant degree, and Citus' variant of ruleutils.c knows how to
 * deal with these extended RTEs. Note that rte->eref needs to be set prior
 * to calling SetRangeTblExtraData to ensure the funccolcount can be set
 * correctly.
 *
 * NB: If used for postgres defined RTEKinds, fields specific to that RTEKind
 * will not be handled by out/readfuncs.c. For the current uses that's ok.
 */
void
SetRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind rteKind,
					 char *fragmentSchemaName, char *fragmentTableName,
					 List *tableIdList)
{
	RangeTblFunction *fauxFunction = NULL;
	FuncExpr *fauxFuncExpr = NULL;
	Const *rteKindData = NULL;
	Const *fragmentSchemaData = NULL;
	Const *fragmentTableData = NULL;
	Const *tableIdListData = NULL;

	Assert(rte->eref);

	/* store RTE kind as a plain int4 */
	rteKindData = makeNode(Const);
	rteKindData->consttype = INT4OID;
	rteKindData->constlen = 4;
	rteKindData->constvalue = Int32GetDatum(rteKind);
	rteKindData->constbyval = true;
	rteKindData->constisnull = false;
	rteKindData->location = -1;

	/* store the fragment schema as a cstring */
	fragmentSchemaData = makeNode(Const);
	fragmentSchemaData->consttype = CSTRINGOID;
	fragmentSchemaData->constlen = -2;
	fragmentSchemaData->constvalue = CStringGetDatum(fragmentSchemaName);
	fragmentSchemaData->constbyval = false;
	fragmentSchemaData->constisnull = fragmentSchemaName == NULL;
	fragmentSchemaData->location = -1;

	/* store the fragment name as a cstring */
	fragmentTableData = makeNode(Const);
	fragmentTableData->consttype = CSTRINGOID;
	fragmentTableData->constlen = -2;
	fragmentTableData->constvalue = CStringGetDatum(fragmentTableName);
	fragmentTableData->constbyval = false;
	fragmentTableData->constisnull = fragmentTableName == NULL;
	fragmentTableData->location = -1;

	/* store the table id list as an array of integers: FIXME */
	tableIdListData = makeNode(Const);
	tableIdListData->consttype = CSTRINGOID;
	tableIdListData->constbyval = false;
	tableIdListData->constlen = -2;
	tableIdListData->location = -1;

	/* serialize tableIdList to a string, seems simplest that way */
	if (tableIdList != NIL)
	{
		char *serializedList = nodeToString(tableIdList);
		tableIdListData->constisnull = false;
		tableIdListData->constvalue = CStringGetDatum(serializedList);
	}
	else
	{
		tableIdListData->constisnull = true;
	}

	/* create function expression to store our faux arguments in */
	fauxFuncExpr = makeNode(FuncExpr);
	fauxFuncExpr->funcid = CitusExtraDataContainerFuncId();
	fauxFuncExpr->funcretset = true;
	fauxFuncExpr->location = -1;
	fauxFuncExpr->args = list_make4(rteKindData, fragmentSchemaData,
									fragmentTableData, tableIdListData);

	fauxFunction = makeNode(RangeTblFunction);
	fauxFunction->funcexpr = (Node *) fauxFuncExpr;

	/* set the column count to pass ruleutils checks, not used elsewhere */
	fauxFunction->funccolcount = list_length(rte->eref->colnames);

	rte->rtekind = RTE_FUNCTION;
	rte->functions = list_make1(fauxFunction);
}
Пример #3
0
/*
 * ExtractRangeTblExtraData extracts extra data stored for a range table entry
 * that previously has been stored with
 * Set/ModifyRangeTblExtraData. Parameters can be NULL if unintersting. It is
 * valid to use the function on a RTE without extra data.
 */
void
ExtractRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind *rteKind,
						 char **fragmentSchemaName, char **fragmentTableName,
						 List **tableIdList)
{
	RangeTblFunction *fauxFunction = NULL;
	FuncExpr *fauxFuncExpr = NULL;
	Const *tmpConst = NULL;

	/* set base rte kind first, so this can be used for 'non-extended' RTEs as well */
	if (rteKind != NULL)
	{
		*rteKind = (CitusRTEKind) rte->rtekind;
	}

	/* reset values of optionally-present fields, will later be overwritten, if present */
	if (fragmentSchemaName != NULL)
	{
		*fragmentSchemaName = NULL;
	}

	if (fragmentTableName != NULL)
	{
		*fragmentTableName = NULL;
	}

	if (tableIdList != NULL)
	{
		*tableIdList = NIL;
	}


	/* only function RTEs have our special extra data */
	if (rte->rtekind != RTE_FUNCTION)
	{
		return;
	}

	/* we only ever generate one argument */
	if (list_length(rte->functions) != 1)
	{
		return;
	}

	/* should pretty much always be a FuncExpr, but be liberal in what we expect... */
	fauxFunction = linitial(rte->functions);
	if (!IsA(fauxFunction->funcexpr, FuncExpr))
	{
		return;
	}

	fauxFuncExpr = (FuncExpr *) fauxFunction->funcexpr;

	/*
	 * There will never be a range table entry with this function id, but for
	 * the purpose of this file.
	 */
	if (fauxFuncExpr->funcid != CitusExtraDataContainerFuncId())
	{
		return;
	}

	/*
	 * Extra data for rtes is stored in the function arguments. The first
	 * argument stores the rtekind, second fragmentSchemaName, third
	 * fragmentTableName, fourth tableIdList.
	 */
	if (list_length(fauxFuncExpr->args) != 4)
	{
		ereport(ERROR, (errmsg("unexpected number of function arguments to "
							   "citus_extradata_container")));
		return;
	}

	/* extract rteKind */
	tmpConst = (Const *) linitial(fauxFuncExpr->args);
	Assert(IsA(tmpConst, Const));
	Assert(tmpConst->consttype == INT4OID);
	if (rteKind != NULL)
	{
		*rteKind = DatumGetInt32(tmpConst->constvalue);
	}

	/* extract fragmentSchemaName */
	tmpConst = (Const *) lsecond(fauxFuncExpr->args);
	Assert(IsA(tmpConst, Const));
	Assert(tmpConst->consttype == CSTRINGOID);
	if (fragmentSchemaName != NULL && !tmpConst->constisnull)
	{
		*fragmentSchemaName = DatumGetCString(tmpConst->constvalue);
	}

	/* extract fragmentTableName */
	tmpConst = (Const *) lthird(fauxFuncExpr->args);
	Assert(IsA(tmpConst, Const));
	Assert(tmpConst->consttype == CSTRINGOID);
	if (fragmentTableName != NULL && !tmpConst->constisnull)
	{
		*fragmentTableName = DatumGetCString(tmpConst->constvalue);
	}

	/* extract tableIdList, stored as a serialized integer list */
	tmpConst = (Const *) lfourth(fauxFuncExpr->args);
	Assert(IsA(tmpConst, Const));
	Assert(tmpConst->consttype == CSTRINGOID);
	if (tableIdList != NULL && !tmpConst->constisnull)
	{
		Node *deserializedList = stringToNode(DatumGetCString(tmpConst->constvalue));
		Assert(IsA(deserializedList, IntList));

		*tableIdList = (List *) deserializedList;
	}
}
Пример #4
0
/*
 * CreateCitusToplevelNode creates the top-level planTree node for a
 * distributed statement. That top-level node is a) recognizable by the
 * executor hooks, allowing them to redirect execution, b) contains the
 * parameters required for distributed execution.
 *
 * The exact representation of the top-level node is an implementation detail
 * which should not be referred to outside this file, as it's likely to become
 * version dependant. Use GetMultiPlan() and HasCitusToplevelNode() to access.
 *
 * Internally the data is stored as arguments to a 'citus_extradata_container'
 * function, which has to be removed from the really executed plan tree before
 * query execution.
 */
PlannedStmt *
MultiQueryContainerNode(PlannedStmt *result, MultiPlan *multiPlan)
{
	FunctionScan *fauxFunctionScan = NULL;
	RangeTblFunction *fauxFunction = NULL;
	FuncExpr *fauxFuncExpr = NULL;
	Const *multiPlanData = NULL;
	char *serializedPlan = NULL;

	/* pass multiPlan serialized as a constant function argument */
	serializedPlan = CitusNodeToString(multiPlan);
	multiPlanData = makeNode(Const);
	multiPlanData->consttype = CSTRINGOID;
	multiPlanData->constlen = strlen(serializedPlan);
	multiPlanData->constvalue = CStringGetDatum(serializedPlan);
	multiPlanData->constbyval = false;
	multiPlanData->location = -1;

	fauxFuncExpr = makeNode(FuncExpr);
	fauxFuncExpr->funcid = CitusExtraDataContainerFuncId();
	fauxFuncExpr->funcretset = true;
	fauxFuncExpr->location = -1;

	fauxFuncExpr->args = list_make1(multiPlanData);
	fauxFunction = makeNode(RangeTblFunction);
	fauxFunction->funcexpr = (Node *) fauxFuncExpr;

	fauxFunctionScan = makeNode(FunctionScan);
	fauxFunctionScan->functions = lappend(fauxFunctionScan->functions, fauxFunction);

	/* copy original targetlist, accessed for RETURNING queries  */
	fauxFunctionScan->scan.plan.targetlist = copyObject(result->planTree->targetlist);

	/*
	 * Add set returning function to target list if the original (postgres
	 * created) plan doesn't support backward scans; doing so prevents
	 * backward scans being supported by the new plantree as well.  This is
	 * ugly as hell, but until we can rely on custom scans (which can signal
	 * this via CUSTOMPATH_SUPPORT_BACKWARD_SCAN), there's not really a pretty
	 * method to achieve this.
	 *
	 * FIXME: This should really be done on the master select plan.
	 */
	if (!ExecSupportsBackwardScan(result->planTree))
	{
		FuncExpr *funcExpr = makeNode(FuncExpr);
		TargetEntry *targetEntry = NULL;
		bool resjunkAttribute = true;

		funcExpr->funcretset = true;

		targetEntry = makeTargetEntry((Expr *) funcExpr, InvalidAttrNumber, NULL,
									  resjunkAttribute);

		fauxFunctionScan->scan.plan.targetlist =
			lappend(fauxFunctionScan->scan.plan.targetlist,
					targetEntry);
	}

	result->planTree = (Plan *) fauxFunctionScan;

	return result;
}