From 88facc56669ad3b5af84a94d442ab6ada67201a3 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Thu, 24 May 2018 11:58:33 +0300
Subject: [PATCH v9] Compile check constraints for domains into separate
 ExprState nodes

Because the plans for check constraints of the CoerceToDomain nodes are handled
dynamically they are compiled into separate ExprState nodes so they can be used
with lists of their own cached expressions that are compiled as PARAM_EXEC.
---
 src/backend/executor/execExpr.c       | 58 +++++++++++++++++------------------
 src/backend/executor/execExprInterp.c | 22 ++++++++++---
 src/include/executor/execExpr.h       |  9 ++++--
 3 files changed, 53 insertions(+), 36 deletions(-)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd7..29c21c3 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2673,14 +2673,9 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 	DomainConstraintRef *constraint_ref;
 	Datum	   *domainval = NULL;
 	bool	   *domainnull = NULL;
-	Datum	   *save_innermost_domainval;
-	bool	   *save_innermost_domainnull;
 	ListCell   *l;
 
 	scratch->d.domaincheck.resulttype = ctest->resulttype;
-	/* we'll allocate workspace only if needed */
-	scratch->d.domaincheck.checkvalue = NULL;
-	scratch->d.domaincheck.checknull = NULL;
 
 	/*
 	 * Evaluate argument - it's fine to directly store it into resv/resnull,
@@ -2721,6 +2716,8 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 	foreach(l, constraint_ref->constraints)
 	{
 		DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+		Expr	   *check_expr = con->check_expr;
+		ExprState  *check_exprstate;
 
 		scratch->d.domaincheck.constraintname = con->name;
 
@@ -2728,18 +2725,10 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 		{
 			case DOM_CONSTRAINT_NOTNULL:
 				scratch->opcode = EEOP_DOMAIN_NOTNULL;
+				scratch->d.domaincheck.check_exprstate = NULL;
 				ExprEvalPushStep(state, scratch);
 				break;
 			case DOM_CONSTRAINT_CHECK:
-				/* Allocate workspace for CHECK output if we didn't yet */
-				if (scratch->d.domaincheck.checkvalue == NULL)
-				{
-					scratch->d.domaincheck.checkvalue =
-						(Datum *) palloc(sizeof(Datum));
-					scratch->d.domaincheck.checknull =
-						(bool *) palloc(sizeof(bool));
-				}
-
 				/*
 				 * If first time through, determine where CoerceToDomainValue
 				 * nodes should read from.
@@ -2772,27 +2761,38 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 					}
 				}
 
-				/*
-				 * Set up value to be returned by CoerceToDomainValue nodes.
-				 * We must save and restore innermost_domainval/null fields,
-				 * in case this node is itself within a check expression for
-				 * another domain.
-				 */
-				save_innermost_domainval = state->innermost_domainval;
-				save_innermost_domainnull = state->innermost_domainnull;
-				state->innermost_domainval = domainval;
-				state->innermost_domainnull = domainnull;
+				check_exprstate = makeNode(ExprState);
+				check_exprstate->expr = check_expr;
+				check_exprstate->parent = state->parent;
+				check_exprstate->ext_params = state->ext_params;
+
+				/* Set up value to be returned by CoerceToDomainValue nodes */
+				check_exprstate->innermost_domainval = domainval;
+				check_exprstate->innermost_domainnull = domainnull;
 
 				/* evaluate check expression value */
-				ExecInitExprRec(con->check_expr, state,
-								scratch->d.domaincheck.checkvalue,
-								scratch->d.domaincheck.checknull);
+				ExecInitExprRec(check_expr, check_exprstate,
+								&check_exprstate->resvalue,
+								&check_exprstate->resnull);
 
-				state->innermost_domainval = save_innermost_domainval;
-				state->innermost_domainnull = save_innermost_domainnull;
+				if (check_exprstate->steps_len == 1 &&
+					check_exprstate->steps[0].opcode == EEOP_DOMAIN_TESTVAL)
+				{
+					/* Trivial, so we need no check work at runtime */
+					check_exprstate = NULL;
+				}
+				else
+				{
+					/* Not trivial, so append a DONE step */
+					scratch->opcode = EEOP_DONE;
+					ExprEvalPushStep(check_exprstate, scratch);
+					/* and ready the subexpression */
+					ExecReadyExpr(check_exprstate);
+				}
 
 				/* now test result */
 				scratch->opcode = EEOP_DOMAIN_CHECK;
+				scratch->d.domaincheck.check_exprstate = check_exprstate;
 				ExprEvalPushStep(state, scratch);
 
 				break;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25a..f0246ce 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -1455,7 +1455,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		EEO_CASE(EEOP_DOMAIN_CHECK)
 		{
 			/* too complex for an inline implementation */
-			ExecEvalConstraintCheck(state, op);
+			ExecEvalConstraintCheck(state, op, econtext);
 
 			EEO_NEXT();
 		}
@@ -3508,10 +3508,24 @@ ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
  * Evaluate a CHECK domain constraint.
  */
 void
-ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
+ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op,
+						ExprContext *econtext)
 {
-	if (!*op->d.domaincheck.checknull &&
-		!DatumGetBool(*op->d.domaincheck.checkvalue))
+	if (op->d.domaincheck.check_exprstate)
+	{
+		op->d.domaincheck.checkvalue = ExecEvalExpr(
+											op->d.domaincheck.check_exprstate,
+											econtext,
+											&op->d.domaincheck.checknull);
+	}
+	else
+	{
+		op->d.domaincheck.checkvalue = *(op->resvalue);
+		op->d.domaincheck.checknull = *(op->resnull);
+	}
+
+	if (!op->d.domaincheck.checknull &&
+		!DatumGetBool(op->d.domaincheck.checkvalue))
 		ereport(ERROR,
 				(errcode(ERRCODE_CHECK_VIOLATION),
 				 errmsg("value for domain %s violates check constraint \"%s\"",
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77..0ec8947 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -509,10 +509,12 @@ typedef struct ExprEvalStep
 			/* name of constraint */
 			char	   *constraintname;
 			/* where the result of a CHECK constraint will be stored */
-			Datum	   *checkvalue;
-			bool	   *checknull;
+			Datum		checkvalue;
+			bool		checknull;
 			/* OID of domain type */
 			Oid			resulttype;
+			/* NULL for EEOP_DOMAIN_NOTNULL */
+			ExprState  *check_exprstate;
 		}			domaincheck;
 
 		/* for EEOP_CONVERT_ROWTYPE */
@@ -726,7 +728,8 @@ extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op,
 					   ExprContext *econtext);
 extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
-extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op,
+						ExprContext *econtext);
 extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op,
-- 
2.7.4

