citus/src/backend/distributed/utils/citus_clauses.c

318 lines
7.2 KiB
C

/*
* citus_clauses.c
*
* Routines roughly equivalent to postgres' util/clauses.
*
* Copyright (c) Citus Data, Inc.
*/
#include "postgres.h"
#include "distributed/citus_clauses.h"
#include "distributed/insert_select_planner.h"
#include "distributed/metadata_cache.h"
#include "distributed/multi_router_planner.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/nodes.h"
#include "nodes/primnodes.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
/* private function declarations */
static bool IsVarNode(Node *node);
static Expr * citus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation, PlanState *planState);
static bool CitusIsVolatileFunctionIdChecker(Oid func_id, void *context);
static bool CitusIsMutableFunctionIdChecker(Oid func_id, void *context);
/*
* RequiresMastereEvaluation returns the executor needs to reparse and
* try to execute this query, which is the case if the query contains
* any stable or volatile function.
*/
bool
RequiresMasterEvaluation(Query *query)
{
return FindNodeCheck((Node *) query, CitusIsMutableFunction);
}
/*
* ExecuteMasterEvaluableFunctions evaluates expressions that can be resolved
* to a constant.
*/
void
ExecuteMasterEvaluableFunctions(Query *query, PlanState *planState)
{
PartiallyEvaluateExpression((Node *) query, planState);
}
/*
* PartiallyEvaluateExpression descend into an expression tree to evaluate
* expressions that can be resolved to a constant on the master. Expressions
* containing a Var are skipped, since the value of the Var is not known
* on the master.
*/
Node *
PartiallyEvaluateExpression(Node *expression, PlanState *planState)
{
if (expression == NULL || IsA(expression, Const))
{
return expression;
}
switch (nodeTag(expression))
{
case T_Param:
{
Param *param = (Param *) expression;
if (param->paramkind == PARAM_SUBLINK)
{
/* ExecInitExpr cannot handle PARAM_SUBLINK */
return expression;
}
}
/* fallthrough */
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
case T_NullIfExpr:
case T_CoerceViaIO:
case T_ArrayCoerceExpr:
case T_ScalarArrayOpExpr:
case T_RowCompareExpr:
case T_RelabelType:
case T_CoerceToDomain:
{
if (FindNodeCheck(expression, IsVarNode))
{
return (Node *) expression_tree_mutator(expression,
PartiallyEvaluateExpression,
planState);
}
return (Node *) citus_evaluate_expr((Expr *) expression,
exprType(expression),
exprTypmod(expression),
exprCollation(expression),
planState);
}
case T_Query:
{
return (Node *) query_tree_mutator((Query *) expression,
PartiallyEvaluateExpression,
planState, QTW_DONT_COPY_QUERY);
}
default:
{
return (Node *) expression_tree_mutator(expression,
PartiallyEvaluateExpression,
planState);
}
}
return expression;
}
/*
* IsVarNode returns whether a node is a Var (column reference).
*/
static bool
IsVarNode(Node *node)
{
return IsA(node, Var);
}
/*
* a copy of pg's 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.
*
* *INDENT-OFF*
*/
static Expr *
citus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation, PlanState *planState)
{
EState *estate;
ExprState *exprstate;
ExprContext *econtext;
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, planState);
if (planState != NULL)
{
/* use executor's context to pass down parameters */
econtext = planState->ps_ExprContext;
}
else
{
/* when called from a function, use a default context */
econtext = GetPerTupleExprContext(estate);
}
/*
* And evaluate it.
*/
const_val = ExecEvalExprSwitchContext(exprstate, econtext, &const_is_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);
}
/*
* CitusIsVolatileFunctionIdChecker checks if the given function id is
* a volatile function other than read_intermediate_result().
*/
static bool
CitusIsVolatileFunctionIdChecker(Oid func_id, void *context)
{
if (func_id == CitusReadIntermediateResultFuncId())
{
return false;
}
return (func_volatile(func_id) == PROVOLATILE_VOLATILE);
}
/*
* CitusIsVolatileFunction checks if the given node is a volatile function
* other than Citus's internal functions.
*/
bool
CitusIsVolatileFunction(Node *node)
{
/* Check for volatile functions in node itself */
if (check_functions_in_node(node, CitusIsVolatileFunctionIdChecker, NULL))
{
return true;
}
if (IsA(node, NextValueExpr))
{
/* NextValueExpr is volatile */
return true;
}
return false;
}
/*
* CitusIsMutableFunctionIdChecker checks if the given function id is
* a mutable function other than read_intermediate_result().
*/
static bool
CitusIsMutableFunctionIdChecker(Oid func_id, void *context)
{
if (func_id == CitusReadIntermediateResultFuncId())
{
return false;
}
else
{
return (func_volatile(func_id) != PROVOLATILE_IMMUTABLE);
}
}
/*
* CitusIsMutableFunction checks if the given node is a mutable function
* other than Citus's internal functions.
*/
bool
CitusIsMutableFunction(Node *node)
{
/* Check for mutable functions in node itself */
if (check_functions_in_node(node, CitusIsMutableFunctionIdChecker, NULL))
{
return true;
}
if (IsA(node, SQLValueFunction))
{
/* all variants of SQLValueFunction are stable */
return true;
}
if (IsA(node, NextValueExpr))
{
/* NextValueExpr is volatile */
return true;
}
return false;
}
/* *INDENT-ON* */