1 /*
2  * citus_clauses.c
3  *
4  * Routines roughly equivalent to postgres' util/clauses.
5  *
6  * Copyright (c) Citus Data, Inc.
7  */
8 
9 #include "postgres.h"
10 
11 #include "distributed/citus_clauses.h"
12 #include "distributed/insert_select_planner.h"
13 #include "distributed/metadata_cache.h"
14 #include "distributed/multi_router_planner.h"
15 #include "distributed/version_compat.h"
16 
17 #include "catalog/pg_proc.h"
18 #include "catalog/pg_type.h"
19 #include "executor/executor.h"
20 #include "nodes/makefuncs.h"
21 #include "nodes/nodeFuncs.h"
22 #include "nodes/nodes.h"
23 #include "nodes/primnodes.h"
24 #include "optimizer/clauses.h"
25 #include "optimizer/optimizer.h"
26 #include "optimizer/planmain.h"
27 #include "utils/datum.h"
28 #include "utils/lsyscache.h"
29 #include "utils/syscache.h"
30 
31 
32 /* private function declarations */
33 static bool IsVariableExpression(Node *node);
34 static Expr * citus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
35 								  Oid result_collation,
36 								  CoordinatorEvaluationContext *
37 								  coordinatorEvaluationContext);
38 static bool CitusIsVolatileFunctionIdChecker(Oid func_id, void *context);
39 static bool CitusIsMutableFunctionIdChecker(Oid func_id, void *context);
40 static bool ShouldEvaluateExpression(Expr *expression);
41 static bool ShouldEvaluateFunctions(CoordinatorEvaluationContext *evaluationContext);
42 static void FixFunctionArguments(Node *expr);
43 static bool FixFunctionArgumentsWalker(Node *expr, void *context);
44 
45 
46 /*
47  * RequiresCoordinatorEvaluation returns the executor needs to reparse and
48  * try to execute this query, which is the case if the query contains
49  * any stable or volatile function.
50  */
51 bool
RequiresCoordinatorEvaluation(Query * query)52 RequiresCoordinatorEvaluation(Query *query)
53 {
54 	if (query->commandType == CMD_SELECT && !query->hasModifyingCTE)
55 	{
56 		return false;
57 	}
58 
59 	return FindNodeMatchingCheckFunction((Node *) query, CitusIsMutableFunction);
60 }
61 
62 
63 /*
64  * ExecuteCoordinatorEvaluableExpressions evaluates expressions and parameters
65  * that can be resolved to a constant.
66  */
67 void
ExecuteCoordinatorEvaluableExpressions(Query * query,PlanState * planState)68 ExecuteCoordinatorEvaluableExpressions(Query *query, PlanState *planState)
69 {
70 	CoordinatorEvaluationContext coordinatorEvaluationContext;
71 
72 	coordinatorEvaluationContext.planState = planState;
73 	if (query->commandType == CMD_SELECT)
74 	{
75 		coordinatorEvaluationContext.evaluationMode = EVALUATE_PARAMS;
76 	}
77 	else
78 	{
79 		coordinatorEvaluationContext.evaluationMode = EVALUATE_FUNCTIONS_PARAMS;
80 	}
81 
82 	PartiallyEvaluateExpression((Node *) query, &coordinatorEvaluationContext);
83 }
84 
85 
86 /*
87  * PartiallyEvaluateExpression descends into an expression tree to evaluate
88  * expressions that can be resolved to a constant on the master. Expressions
89  * containing a Var are skipped, since the value of the Var is not known
90  * on the master.
91  */
92 Node *
PartiallyEvaluateExpression(Node * expression,CoordinatorEvaluationContext * coordinatorEvaluationContext)93 PartiallyEvaluateExpression(Node *expression,
94 							CoordinatorEvaluationContext *coordinatorEvaluationContext)
95 {
96 	if (expression == NULL || IsA(expression, Const))
97 	{
98 		return expression;
99 	}
100 
101 	NodeTag nodeTag = nodeTag(expression);
102 	if (nodeTag == T_Param)
103 	{
104 		Param *param = (Param *) expression;
105 		if (param->paramkind == PARAM_SUBLINK)
106 		{
107 			/* ExecInitExpr cannot handle PARAM_SUBLINK */
108 			return expression;
109 		}
110 
111 		return (Node *) citus_evaluate_expr((Expr *) expression,
112 											exprType(expression),
113 											exprTypmod(expression),
114 											exprCollation(expression),
115 											coordinatorEvaluationContext);
116 	}
117 	else if (ShouldEvaluateExpression((Expr *) expression) &&
118 			 ShouldEvaluateFunctions(coordinatorEvaluationContext))
119 	{
120 		/*
121 		 * The planner normally evaluates constant expressions, but we may be
122 		 * working on the original query tree. We could rely on
123 		 * citus_evaluate_expr to evaluate constant expressions, but there are
124 		 * certain node types that citus_evaluate_expr does not expect because
125 		 * the planner normally replaces them (in particular, CollateExpr).
126 		 * Hence, we first evaluate constant expressions using
127 		 * eval_const_expressions before continuing.
128 		 *
129 		 * NOTE: We do not use expression_planner here, since all it does
130 		 * apart from calling eval_const_expressions is call fix_opfuncids.
131 		 * We do not need this, since that is already called in
132 		 * citus_evaluate_expr. So we won't needlessly traverse the expression
133 		 * tree by calling it another time.
134 		 */
135 		expression = eval_const_expressions(NULL, expression);
136 
137 		/*
138 		 * It's possible that after evaluating const expressions we
139 		 * actually don't need to evaluate this expression anymore e.g:
140 		 *
141 		 * 1 = 0 AND now() > timestamp '10-10-2000 00:00'
142 		 *
143 		 * This statement would simply resolve to false, because 1 = 0 is
144 		 * false. That's why we now check again if we should evaluate the
145 		 * expression and only continue if we still do.
146 		 */
147 		if (!ShouldEvaluateExpression((Expr *) expression))
148 		{
149 			return (Node *) expression_tree_mutator(expression,
150 													PartiallyEvaluateExpression,
151 													coordinatorEvaluationContext);
152 		}
153 
154 		if (FindNodeMatchingCheckFunction(expression, IsVariableExpression))
155 		{
156 			/*
157 			 * The expression contains a variable expression (e.g. a stable function,
158 			 * which has a column reference as its input). That means that we cannot
159 			 * evaluate the expression on the coordinator, since the result depends
160 			 * on the input.
161 			 *
162 			 * Skipping function evaluation for these expressions is safe in most
163 			 * cases, since the function will always be re-evaluated for every input
164 			 * value. An exception is function calls that call another stable function
165 			 * that should not be re-evaluated, such as now().
166 			 */
167 			return (Node *) expression_tree_mutator(expression,
168 													PartiallyEvaluateExpression,
169 													coordinatorEvaluationContext);
170 		}
171 
172 		return (Node *) citus_evaluate_expr((Expr *) expression,
173 											exprType(expression),
174 											exprTypmod(expression),
175 											exprCollation(expression),
176 											coordinatorEvaluationContext);
177 	}
178 	else if (nodeTag == T_Query)
179 	{
180 		Query *query = (Query *) expression;
181 		CoordinatorEvaluationContext subContext = *coordinatorEvaluationContext;
182 		if (query->commandType != CMD_SELECT)
183 		{
184 			/*
185 			 * Currently INSERT SELECT evaluates stable functions on master,
186 			 * while a plain SELECT does not. For evaluating SELECT evaluationMode is
187 			 * EVALUATE_PARAMS, but if recursing into a modifying CTE switch into
188 			 * EVALUATE_FUNCTIONS_PARAMS.
189 			 */
190 			subContext.evaluationMode = EVALUATE_FUNCTIONS_PARAMS;
191 		}
192 
193 		return (Node *) query_tree_mutator(query,
194 										   PartiallyEvaluateExpression,
195 										   &subContext,
196 										   QTW_DONT_COPY_QUERY);
197 	}
198 	else
199 	{
200 		return (Node *) expression_tree_mutator(expression,
201 												PartiallyEvaluateExpression,
202 												coordinatorEvaluationContext);
203 	}
204 
205 	return expression;
206 }
207 
208 
209 /*
210  * ShouldEvaluateFunctions is a helper function which is used to
211  * decide whether the function/expression should be evaluated with the input
212  * coordinatorEvaluationContext.
213  */
214 static bool
ShouldEvaluateFunctions(CoordinatorEvaluationContext * evaluationContext)215 ShouldEvaluateFunctions(CoordinatorEvaluationContext *evaluationContext)
216 {
217 	if (evaluationContext == NULL)
218 	{
219 		/* if no context provided, evaluate, which is the default behaviour */
220 		return true;
221 	}
222 
223 	return evaluationContext->evaluationMode == EVALUATE_FUNCTIONS_PARAMS;
224 }
225 
226 
227 /*
228  * ShouldEvaluateExpression returns true if Citus should evaluate the
229  * input node on the coordinator.
230  */
231 static bool
ShouldEvaluateExpression(Expr * expression)232 ShouldEvaluateExpression(Expr *expression)
233 {
234 	NodeTag nodeTag = nodeTag(expression);
235 
236 	switch (nodeTag)
237 	{
238 		case T_FuncExpr:
239 		{
240 			FuncExpr *funcExpr = (FuncExpr *) expression;
241 
242 			/* we cannot evaluate set returning functions */
243 			bool isSetReturningFunction = funcExpr->funcretset;
244 			return !isSetReturningFunction;
245 		}
246 
247 		case T_OpExpr:
248 		case T_DistinctExpr:
249 		case T_NullIfExpr:
250 		case T_CoerceViaIO:
251 		case T_ArrayCoerceExpr:
252 		case T_ScalarArrayOpExpr:
253 		case T_RowExpr:
254 		case T_RowCompareExpr:
255 		case T_RelabelType:
256 		case T_CoerceToDomain:
257 		{
258 			return true;
259 		}
260 
261 		default:
262 			return false;
263 	}
264 }
265 
266 
267 /*
268  * IsVariableExpression returns whether the given node is a variable expression,
269  * meaning its result depends on the input data and is not constant for the whole
270  * query.
271  */
272 static bool
IsVariableExpression(Node * node)273 IsVariableExpression(Node *node)
274 {
275 	if (IsA(node, Aggref))
276 	{
277 		return true;
278 	}
279 
280 	if (IsA(node, WindowFunc))
281 	{
282 		return true;
283 	}
284 
285 	if (IsA(node, Param))
286 	{
287 		/* ExecInitExpr cannot handle PARAM_SUBLINK */
288 		return ((Param *) node)->paramkind == PARAM_SUBLINK;
289 	}
290 
291 	return IsA(node, Var);
292 }
293 
294 
295 /*
296  * a copy of pg's evaluate_expr, pre-evaluate a constant expression
297  *
298  * We use the executor's routine ExecEvalExpr() to avoid duplication of
299  * code and ensure we get the same result as the executor would get.
300  *
301  * *INDENT-OFF*
302  */
303 static Expr *
citus_evaluate_expr(Expr * expr,Oid result_type,int32 result_typmod,Oid result_collation,CoordinatorEvaluationContext * coordinatorEvaluationContext)304 citus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
305 					Oid result_collation,
306 					CoordinatorEvaluationContext *coordinatorEvaluationContext)
307 {
308 	PlanState *planState = NULL;
309 	EState     *estate;
310 	ExprState  *exprstate;
311 	Datum		const_val;
312 	bool		const_is_null;
313 	int16		resultTypLen;
314 	bool		resultTypByVal;
315 
316 	if (coordinatorEvaluationContext)
317 	{
318 		planState = coordinatorEvaluationContext->planState;
319 
320 		if (IsA(expr, Param))
321 		{
322 			if (coordinatorEvaluationContext->evaluationMode == EVALUATE_NONE)
323 			{
324 				/* bail out, the caller doesn't want params to be evaluated  */
325 				return expr;
326 			}
327 		}
328 		else if (coordinatorEvaluationContext->evaluationMode != EVALUATE_FUNCTIONS_PARAMS)
329 		{
330 			/* should only get here for node types we should evaluate */
331 			Assert(ShouldEvaluateExpression(expr));
332 
333 			/* bail out, the caller doesn't want functions/expressions to be evaluated */
334 			return expr;
335 		}
336 	}
337 
338 	/*
339 	 * To use the executor, we need an EState.
340 	 */
341 	estate = CreateExecutorState();
342 
343 	/* We can use the estate's working context to avoid memory leaks. */
344 	MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
345 
346 	/* handles default values */
347 	FixFunctionArguments((Node *) expr);
348 
349 	/* Make sure any opfuncids are filled in. */
350 	fix_opfuncids((Node *) expr);
351 
352 	/*
353 	 * Prepare expr for execution.  (Note: we can't use ExecPrepareExpr
354 	 * because it'd result in recursively invoking eval_const_expressions.)
355 	 */
356 	exprstate = ExecInitExpr(expr, planState);
357 
358 	/*
359 	 * Get short lived per tuple context as evaluate_expr does. Here we don't
360 	 * use planState->ExprContext as it might cause double-free'ing executor
361 	 * state.
362 	 */
363 	ExprContext *econtext = GetPerTupleExprContext(estate);
364 	if (planState)
365 	{
366 		/*
367 		 * If planState exists, then we add es_param_list_info to per tuple
368 		 * ExprContext as we need them when evaluating prepared statements.
369 		 */
370 		econtext->ecxt_param_list_info = planState->state->es_param_list_info;
371 	}
372 
373 	/*
374 	 * And evaluate it.
375 	 */
376 	const_val = ExecEvalExprSwitchContext(exprstate, econtext, &const_is_null);
377 
378 	/* Get info needed about result datatype */
379 	get_typlenbyval(result_type, &resultTypLen, &resultTypByVal);
380 
381 	/* Get back to outer memory context */
382 	MemoryContextSwitchTo(oldcontext);
383 
384 	/*
385 	 * Must copy result out of sub-context used by expression eval.
386 	 *
387 	 * Also, if it's varlena, forcibly detoast it.  This protects us against
388 	 * storing TOAST pointers into plans that might outlive the referenced
389 	 * data.  (makeConst would handle detoasting anyway, but it's worth a few
390 	 * extra lines here so that we can do the copy and detoast in one step.)
391 	 */
392 	if (!const_is_null)
393 	{
394 		if (resultTypLen == -1)
395 			const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val));
396 		else
397 			const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
398 	}
399 
400 	/* Release all the junk we just created */
401 	FreeExecutorState(estate);
402 
403 	/*
404 	 * Make the constant result node.
405 	 */
406 	return (Expr *) makeConst(result_type, result_typmod, result_collation,
407 							  resultTypLen,
408 							  const_val, const_is_null,
409 							  resultTypByVal);
410 }
411 
412 /* *INDENT-ON* */
413 
414 
415 /*
416  * CitusIsVolatileFunctionIdChecker checks if the given function id is
417  * a volatile function other than read_intermediate_result().
418  */
419 static bool
CitusIsVolatileFunctionIdChecker(Oid func_id,void * context)420 CitusIsVolatileFunctionIdChecker(Oid func_id, void *context)
421 {
422 	if (func_id == CitusReadIntermediateResultFuncId() ||
423 		func_id == CitusReadIntermediateResultArrayFuncId())
424 	{
425 		return false;
426 	}
427 
428 	return (func_volatile(func_id) == PROVOLATILE_VOLATILE);
429 }
430 
431 
432 /*
433  * CitusIsVolatileFunction checks if the given node is a volatile function
434  * other than Citus's internal functions.
435  */
436 bool
CitusIsVolatileFunction(Node * node)437 CitusIsVolatileFunction(Node *node)
438 {
439 	/* Check for volatile functions in node itself */
440 	if (check_functions_in_node(node, CitusIsVolatileFunctionIdChecker, NULL))
441 	{
442 		return true;
443 	}
444 
445 	if (IsA(node, NextValueExpr))
446 	{
447 		/* NextValueExpr is volatile */
448 		return true;
449 	}
450 
451 	return false;
452 }
453 
454 
455 /*
456  * CitusIsMutableFunctionIdChecker checks if the given function id is
457  * a mutable function other than read_intermediate_result().
458  */
459 static bool
CitusIsMutableFunctionIdChecker(Oid func_id,void * context)460 CitusIsMutableFunctionIdChecker(Oid func_id, void *context)
461 {
462 	if (func_id == CitusReadIntermediateResultFuncId() ||
463 		func_id == CitusReadIntermediateResultArrayFuncId())
464 	{
465 		return false;
466 	}
467 	else
468 	{
469 		return (func_volatile(func_id) != PROVOLATILE_IMMUTABLE);
470 	}
471 }
472 
473 
474 /*
475  * CitusIsMutableFunction checks if the given node is a mutable function
476  * other than Citus's internal functions.
477  */
478 bool
CitusIsMutableFunction(Node * node)479 CitusIsMutableFunction(Node *node)
480 {
481 	/* Check for mutable functions in node itself */
482 	if (check_functions_in_node(node, CitusIsMutableFunctionIdChecker, NULL))
483 	{
484 		return true;
485 	}
486 
487 	if (IsA(node, SQLValueFunction))
488 	{
489 		/* all variants of SQLValueFunction are stable */
490 		return true;
491 	}
492 
493 	if (IsA(node, NextValueExpr))
494 	{
495 		/* NextValueExpr is volatile */
496 		return true;
497 	}
498 
499 	return false;
500 }
501 
502 
503 /* FixFunctionArguments applies expand_function_arguments to all function calls. */
504 static void
FixFunctionArguments(Node * expr)505 FixFunctionArguments(Node *expr)
506 {
507 	FixFunctionArgumentsWalker(expr, NULL);
508 }
509 
510 
511 /* FixFunctionArgumentsWalker is the helper function for fix_funcargs. */
512 static bool
FixFunctionArgumentsWalker(Node * expr,void * context)513 FixFunctionArgumentsWalker(Node *expr, void *context)
514 {
515 	if (expr == NULL)
516 	{
517 		return false;
518 	}
519 
520 	if (IsA(expr, FuncExpr))
521 	{
522 		FuncExpr *funcExpr = castNode(FuncExpr, expr);
523 		HeapTuple func_tuple =
524 			SearchSysCache1(PROCOID, ObjectIdGetDatum(funcExpr->funcid));
525 		if (!HeapTupleIsValid(func_tuple))
526 		{
527 			elog(ERROR, "cache lookup failed for function %u", funcExpr->funcid);
528 		}
529 
530 		funcExpr->args = expand_function_arguments_compat(funcExpr->args, false,
531 														  funcExpr->funcresulttype,
532 														  func_tuple);
533 
534 		ReleaseSysCache(func_tuple);
535 	}
536 
537 	return expression_tree_walker(expr, FixFunctionArgumentsWalker, NULL);
538 }
539