From e64964aa354048fd014d09edb9a07bc87be312c3 Mon Sep 17 00:00:00 2001 From: Taiki Koshino Date: Fri, 23 Oct 2026 09:45:05 +0900 Subject: [PATCH v1] Report CHECK expression in constraint violation details CHECK constraint violations currently report the constraint name and the failing row, but not the expression that was violated. Users therefore need to inspect the table definition to determine the constraint condition. Make ExecRelCheck return the failed ConstrCheck instead of only its name. This allows ExecConstraints to deparse ccbin and include the CHECK expression in the error detail. For example: ERROR: new row for relation "staff_sales" violates check constraint "staff_age_check" DETAIL: Failing row contains (1, 15). Check constraint expression: (age >= 18). --- src/backend/executor/execMain.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 24df5d50a18..ad13bbf9747 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -63,6 +63,7 @@ #include "utils/lsyscache.h" #include "utils/partcache.h" #include "utils/rls.h" +#include "utils/ruleutils.h" #include "utils/snapmgr.h" @@ -1803,9 +1804,9 @@ ExecutePlan(QueryDesc *queryDesc, /* * ExecRelCheck --- check that tuple meets check constraints for result relation * - * Returns NULL if OK, else name of failed check constraint + * Returns NULL if OK, else struct of failed check constraint */ -static const char * +static const ConstrCheck * ExecRelCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate) { @@ -1869,7 +1870,7 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, * ExecQual. */ if (checkconstr && !ExecCheck(checkconstr, econtext)) - return check[i].ccname; + return &check[i]; } /* NULL result means no error */ @@ -2056,11 +2057,13 @@ ExecConstraints(ResultRelInfo *resultRelInfo, */ if (rel->rd_rel->relchecks > 0) { - const char *failed; + const ConstrCheck *failed; if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL) { + char *check_desc; char *val_desc; + List *context; Relation orig_rel = rel; /* @@ -2095,6 +2098,10 @@ ExecConstraints(ResultRelInfo *resultRelInfo, else modifiedCols = bms_union(ExecGetInsertedCols(resultRelInfo, estate), ExecGetUpdatedCols(resultRelInfo, estate)); + + context = deparse_context_for(RelationGetRelationName(orig_rel), RelationGetRelid(orig_rel)); + check_desc = deparse_expression(stringToNode(failed->ccbin), context, false, false); + val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel), slot, tupdesc, @@ -2103,9 +2110,11 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("new row for relation \"%s\" violates check constraint \"%s\"", - RelationGetRelationName(orig_rel), failed), - val_desc ? errdetail("Failing row contains %s.", val_desc) : 0, - errtableconstraint(orig_rel, failed))); + RelationGetRelationName(orig_rel), failed->ccname), + val_desc ? + errdetail("Failing row contains %s. Check constraint expression: %s.", + val_desc, check_desc) : 0, + errtableconstraint(orig_rel, failed->ccname))); } } } -- 2.52.0