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