*** a/src/backend/access/common/tupdesc.c --- b/src/backend/access/common/tupdesc.c *************** *** 203,208 **** CreateTupleDescCopyConstr(TupleDesc tupdesc) --- 203,209 ---- cpy->check[i].ccname = pstrdup(constr->check[i].ccname); if (constr->check[i].ccbin) cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin); + cpy->check[i].ccnode = NULL; /* recompute lazily */ cpy->check[i].ccvalid = constr->check[i].ccvalid; } } *************** *** 253,258 **** FreeTupleDesc(TupleDesc tupdesc) --- 254,261 ---- pfree(check[i].ccname); if (check[i].ccbin) pfree(check[i].ccbin); + if (check[i].ccnode) + pfree(check[i].ccnode); } pfree(check); } *************** *** 416,421 **** equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) --- 419,425 ---- */ for (j = 0; j < n; check2++, j++) { + /* Don't compare ccnode, a mere derivative of ccbin. */ if (strcmp(check1->ccname, check2->ccname) == 0 && strcmp(check1->ccbin, check2->ccbin) == 0) break; *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 1669,1676 **** MergeAttributes(List *schema, List *supers, char relpersistence, continue; /* Adjust Vars to match new table's column numbering */ ! expr = map_variable_attnos(stringToNode(check[i].ccbin), ! 1, 0, newattno, tupleDesc->natts, &found_whole_row); --- 1669,1676 ---- continue; /* Adjust Vars to match new table's column numbering */ ! expr = copyObject(RelationGetConstraint(relation, i)); ! expr = map_variable_attnos(expr, 1, 0, newattno, tupleDesc->natts, &found_whole_row); *** a/src/backend/executor/execMain.c --- b/src/backend/executor/execMain.c *************** *** 1512,1520 **** ExecRelCheck(ResultRelInfo *resultRelInfo, int i; /* ! * If first time through for this result relation, build expression ! * nodetrees for rel's constraint expressions. Keep them in the per-query ! * memory context so they'll survive throughout the query. */ if (resultRelInfo->ri_ConstraintExprs == NULL) { --- 1512,1521 ---- int i; /* ! * If first time through for this result relation, prepare expressions for ! * rel's constraints. Keep them in the per-query memory context so ! * they'll survive throughout the query. We assume make_ands_implicit() ! * does not modify its argument. */ if (resultRelInfo->ri_ConstraintExprs == NULL) { *************** *** 1524,1530 **** ExecRelCheck(ResultRelInfo *resultRelInfo, for (i = 0; i < ncheck; i++) { /* ExecQual wants implicit-AND form */ ! qual = make_ands_implicit(stringToNode(check[i].ccbin)); resultRelInfo->ri_ConstraintExprs[i] = (List *) ExecPrepareExpr((Expr *) qual, estate); } --- 1525,1531 ---- for (i = 0; i < ncheck; i++) { /* ExecQual wants implicit-AND form */ ! qual = make_ands_implicit((Expr *) RelationGetConstraint(rel, i)); resultRelInfo->ri_ConstraintExprs[i] = (List *) ExecPrepareExpr((Expr *) qual, estate); } *** a/src/backend/optimizer/util/plancat.c --- b/src/backend/optimizer/util/plancat.c *************** *** 671,677 **** get_relation_constraints(PlannerInfo *root, if (!constr->check[i].ccvalid) continue; ! cexpr = stringToNode(constr->check[i].ccbin); /* * Run each expression through const-simplification and --- 671,677 ---- if (!constr->check[i].ccvalid) continue; ! cexpr = copyObject(RelationGetConstraint(relation, i)); /* * Run each expression through const-simplification and *** a/src/backend/parser/parse_utilcmd.c --- b/src/backend/parser/parse_utilcmd.c *************** *** 836,850 **** transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) { char *ccname = tupleDesc->constr->check[ccnum].ccname; - char *ccbin = tupleDesc->constr->check[ccnum].ccbin; Constraint *n = makeNode(Constraint); ! Node *ccbin_node; bool found_whole_row; ! ccbin_node = map_variable_attnos(stringToNode(ccbin), ! 1, 0, ! attmap, tupleDesc->natts, ! &found_whole_row); /* * We reject whole-row variables because the whole point of LIKE --- 836,849 ---- for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) { char *ccname = tupleDesc->constr->check[ccnum].ccname; Constraint *n = makeNode(Constraint); ! Node *ccnode; bool found_whole_row; ! ccnode = copyObject(RelationGetConstraint(relation, ccnum)); ! ccnode = map_variable_attnos(ccnode, 1, 0, ! attmap, tupleDesc->natts, ! &found_whole_row); /* * We reject whole-row variables because the whole point of LIKE *************** *** 864,870 **** transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla n->location = -1; n->conname = pstrdup(ccname); n->raw_expr = NULL; ! n->cooked_expr = nodeToString(ccbin_node); cxt->ckconstraints = lappend(cxt->ckconstraints, n); /* Copy comment on constraint */ --- 863,869 ---- n->location = -1; n->conname = pstrdup(ccname); n->raw_expr = NULL; ! n->cooked_expr = nodeToString(ccnode); cxt->ckconstraints = lappend(cxt->ckconstraints, n); /* Copy comment on constraint */ *** a/src/backend/utils/cache/relcache.c --- b/src/backend/utils/cache/relcache.c *************** *** 3414,3419 **** CheckConstraintFetch(Relation relation) --- 3414,3422 ---- check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, TextDatumGetCString(val)); + + check[found].ccnode = NULL; + found++; } *************** *** 3426,3431 **** CheckConstraintFetch(Relation relation) --- 3429,3456 ---- } /* + * RelationGetConstraint -- get stringToNode() form of a CHECK constraint + * + * This function implements an on-demand cache. The "idx" argument indexes + * the "check" array of the TupleConstr. Callers shall not modify the result. + */ + Node * + RelationGetConstraint(Relation relation, int idx) + { + ConstrCheck *check = relation->rd_att->constr->check + idx; + + Assert(check->ccbin != NULL); + if (check->ccnode == NULL) + { + MemoryContext oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + check->ccnode = stringToNode(check->ccbin); + MemoryContextSwitchTo(oldcxt); + } + + return check->ccnode; + } + + /* * RelationGetIndexList -- get a list of OIDs of indexes on this relation * * The index list is created only if someone requests it. We scan pg_index *** a/src/include/access/tupdesc.h --- b/src/include/access/tupdesc.h *************** *** 29,34 **** typedef struct constrCheck --- 29,35 ---- { char *ccname; char *ccbin; /* nodeToString representation of expr */ + Node *ccnode; /* internal; use RelationGetConstraint() */ bool ccvalid; bool ccnoinherit; /* this is a non-inheritable constraint */ } ConstrCheck; *** a/src/include/utils/relcache.h --- b/src/include/utils/relcache.h *************** *** 37,42 **** extern void RelationClose(Relation relation); --- 37,43 ---- /* * Routines to compute/retrieve additional cached information */ + extern Node *RelationGetConstraint(Relation relation, int idx); extern List *RelationGetIndexList(Relation relation); extern Oid RelationGetOidIndex(Relation relation); extern List *RelationGetIndexExpressions(Relation relation);