/*
* Read from location identified by innermost_caseval. Note
* that innermost_caseval could be NULL, if this node isn't
- * actually within a CASE structure; some parts of the system
- * abuse CaseTestExpr to cause a read of a value externally
- * supplied in econtext->caseValue_datum. We'll take care of
- * that scenario at runtime.
+ * actually within a CaseExpr, ArrayCoerceExpr, etc structure.
+ * That can happen because some parts of the system abuse
+ * CaseTestExpr to cause a read of a value externally supplied
+ * in econtext->caseValue_datum. We'll take care of that
+ * scenario at runtime.
*/
scratch.opcode = EEOP_CASE_TESTVAL;
scratch.d.casetest.value = state->innermost_caseval;
* CaseTestExpr nodes must appear directly within the corresponding CaseExpr,
* not nested within another one, or they'll see the wrong test value. If one
* appears "bare" in the arguments of a SQL function, then we can't inline the
- * SQL function for fear of creating such a situation.
+ * SQL function for fear of creating such a situation. The same applies for
+ * CaseTestExpr used within the elemexpr of an ArrayCoerceExpr.
*
* CoerceToDomainValue would have the same issue if domain CHECK expressions
* could get inlined into larger expressions, but presently that's impossible.
return contain_context_dependent_node_walker(clause, &flags);
}
-#define CCDN_IN_CASEEXPR 0x0001 /* CaseTestExpr okay here? */
+#define CCDN_CASETESTEXPR_OK 0x0001 /* CaseTestExpr okay here? */
static bool
contain_context_dependent_node_walker(Node *node, int *flags)
if (node == NULL)
return false;
if (IsA(node, CaseTestExpr))
- return !(*flags & CCDN_IN_CASEEXPR);
- if (IsA(node, CaseExpr))
+ return !(*flags & CCDN_CASETESTEXPR_OK);
+ else if (IsA(node, CaseExpr))
{
CaseExpr *caseexpr = (CaseExpr *) node;
* seem worth any extra code. If there are any bare CaseTestExprs
* elsewhere in the CASE, something's wrong already.
*/
- *flags |= CCDN_IN_CASEEXPR;
+ *flags |= CCDN_CASETESTEXPR_OK;
res = expression_tree_walker(node,
contain_context_dependent_node_walker,
(void *) flags);
return res;
}
}
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *ac = (ArrayCoerceExpr *) node;
+ int save_flags;
+ bool res;
+
+ /* Check the array expression */
+ if (contain_context_dependent_node_walker((Node *) ac->arg, flags))
+ return true;
+
+ /* Check the elemexpr, which is allowed to contain CaseTestExpr */
+ save_flags = *flags;
+ *flags |= CCDN_CASETESTEXPR_OK;
+ res = contain_context_dependent_node_walker((Node *) ac->elemexpr,
+ flags);
+ *flags = save_flags;
+ return res;
+ }
return expression_tree_walker(node, contain_context_dependent_node_walker,
(void *) flags);
}
}
case T_ArrayCoerceExpr:
{
- ArrayCoerceExpr *ac;
+ ArrayCoerceExpr *ac = makeNode(ArrayCoerceExpr);
+ Node *save_case_val;
- /* Copy the node and const-simplify its arguments */
- ac = (ArrayCoerceExpr *) ece_generic_processing(node);
+ /*
+ * Copy the node and const-simplify its arguments. We can't
+ * use ece_generic_processing() here because we need to mess
+ * with case_val only while processing the elemexpr.
+ */
+ memcpy(ac, node, sizeof(ArrayCoerceExpr));
+ ac->arg = (Expr *)
+ eval_const_expressions_mutator((Node *) ac->arg,
+ context);
+
+ /*
+ * Set up for the CaseTestExpr node contained in the elemexpr.
+ * We must prevent it from absorbing any outer CASE value.
+ */
+ save_case_val = context->case_val;
+ context->case_val = NULL;
+
+ ac->elemexpr = (Expr *)
+ eval_const_expressions_mutator((Node *) ac->elemexpr,
+ context);
+
+ context->case_val = save_case_val;
/*
* If constant argument and the per-element expression is
ac->elemexpr && !IsA(ac->elemexpr, CoerceToDomain) &&
!contain_mutable_functions((Node *) ac->elemexpr))
return ece_evaluate_expr(ac);
+
return (Node *) ac;
}
case T_CollateExpr:
* This is effectively like a Param, but can be implemented more simply
* since we need only one replacement value at a time.
*
- * We also use this in nested UPDATE expressions.
- * See transformAssignmentIndirection().
+ * We also abuse this node type for some other purposes, including:
+ * * Placeholder for the current array element value in ArrayCoerceExpr;
+ * see build_coercion_expression().
+ * * Nested FieldStore/ArrayRef assignment expressions in INSERT/UPDATE;
+ * see transformAssignmentIndirection().
+ *
+ * The uses in CaseExpr and ArrayCoerceExpr are safe only to the extent that
+ * there is not any other CaseExpr or ArrayCoerceExpr between the value source
+ * node and its child CaseTestExpr(s). This is true in the parse analysis
+ * output, but the planner's function-inlining logic has to be careful not to
+ * break it.
+ *
+ * The nested-assignment-expression case is safe because the only node types
+ * that can be above such CaseTestExprs are FieldStore and ArrayRef.
*/
typedef struct CaseTestExpr
{