From af7053df5b5ed389eb454bee9a651a2265d6dfe6 Mon Sep 17 00:00:00 2001
From: Taiki Koshino <koshino@sraoss.co.jp>
Date: Fri, 30 Oct 2026 18:24:07 +0900
Subject: [PATCH v2] 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 ++++++---
 src/test/regress/expected/alter_table.out     | 22 ++++----
 src/test/regress/expected/constraints.out     | 50 +++++++++----------
 src/test/regress/expected/copy2.out           |  2 +-
 .../regress/expected/create_table_like.out    |  2 +-
 src/test/regress/expected/domain.out          |  4 +-
 .../regress/expected/generated_stored.out     |  4 +-
 .../regress/expected/generated_virtual.out    |  4 +-
 src/test/regress/expected/inherit.out         | 40 +++++++--------
 src/test/regress/expected/insert.out          |  4 +-
 src/test/regress/expected/privileges.out      |  2 +-
 .../regress/expected/sqljson_queryfuncs.out   | 12 ++---
 12 files changed, 89 insertions(+), 80 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)));
 		}
 	}
 }
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 96aa9b8e5fb..941e0b4510c 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -800,7 +800,7 @@ alter table atacc1 add constraint atacc_test1 check (test>3);
 -- should fail
 insert into atacc1 (test) values (2);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc_test1"
-DETAIL:  Failing row contains (2).
+DETAIL:  Failing row contains (2). Check constraint expression: (test > 3).
 -- should succeed
 insert into atacc1 (test) values (4);
 drop table atacc1;
@@ -827,7 +827,7 @@ alter table atacc1 add constraint atacc_test1 check (test+test2<test3*4);
 -- should fail
 insert into atacc1 (test,test2,test3) values (4,4,2);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc_test1"
-DETAIL:  Failing row contains (4, 4, 2).
+DETAIL:  Failing row contains (4, 4, 2). Check constraint expression: ((test + test2) < (test3 * 4)).
 -- should succeed
 insert into atacc1 (test,test2,test3) values (4,4,5);
 drop table atacc1;
@@ -837,7 +837,7 @@ alter table atacc1 add check (test2>test);
 -- should fail for $2
 insert into atacc1 (test2, test) values (3, 4);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc1_check"
-DETAIL:  Failing row contains (4, 3).
+DETAIL:  Failing row contains (4, 3). Check constraint expression: (test2 > test).
 drop table atacc1;
 -- inheritance related tests
 create table atacc1 (test int);
@@ -847,12 +847,12 @@ alter table atacc2 add constraint foo check (test2>0);
 -- fail and then succeed on atacc2
 insert into atacc2 (test2) values (-3);
 ERROR:  new row for relation "atacc2" violates check constraint "foo"
-DETAIL:  Failing row contains (-3).
+DETAIL:  Failing row contains (-3). Check constraint expression: (test2 > 0).
 insert into atacc2 (test2) values (3);
 -- fail and then succeed on atacc3
 insert into atacc3 (test2) values (-3);
 ERROR:  new row for relation "atacc3" violates check constraint "foo"
-DETAIL:  Failing row contains (null, -3, null).
+DETAIL:  Failing row contains (null, -3, null). Check constraint expression: (test2 > 0).
 insert into atacc3 (test2) values (3);
 drop table atacc3;
 drop table atacc2;
@@ -919,7 +919,7 @@ insert into atacc2 (test) values (-3);
 -- check constraint is there on parent
 insert into atacc1 (test) values (-3);
 ERROR:  new row for relation "atacc1" violates check constraint "foo"
-DETAIL:  Failing row contains (-3).
+DETAIL:  Failing row contains (-3). Check constraint expression: (test > 0).
 insert into atacc1 (test) values (3);
 -- fail, violating row:
 alter table atacc2 add constraint foo check (test>0) no inherit;
@@ -1654,12 +1654,12 @@ drop table atacc1;
 create table atacc1 (id serial primary key, value int check (value < 10));
 insert into atacc1(value) values (100);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc1_value_check"
-DETAIL:  Failing row contains (1, 100).
+DETAIL:  Failing row contains (1, 100). Check constraint expression: (value < 10).
 alter table atacc1 drop column value;
 alter table atacc1 add column value int check (value < 10);
 insert into atacc1(value) values (100);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc1_value_check"
-DETAIL:  Failing row contains (2, 100).
+DETAIL:  Failing row contains (2, 100). Check constraint expression: (value < 10).
 insert into atacc1(id, value) values (null, 0);
 ERROR:  null value in column "id" of relation "atacc1" violates not-null constraint
 DETAIL:  Failing row contains (null, 0).
@@ -1956,7 +1956,7 @@ NOTICE:  merging definition of column "f2" for child "c1"
 insert into p1 values (1,2,'abc');
 insert into c1 values(11,'xyz',33,0); -- should fail
 ERROR:  new row for relation "c1" violates check constraint "p1_a1_check"
-DETAIL:  Failing row contains (11, xyz, 33, 0).
+DETAIL:  Failing row contains (11, xyz, 33, 0). Check constraint expression: (a1 > 0).
 insert into c1 values(11,'xyz',33,22);
 select * from p1;
  f1 | a1 | f2  
@@ -2048,7 +2048,7 @@ select * from anothertab;
 
 insert into anothertab (atcol1, atcol2) values (45, null); -- fails
 ERROR:  new row for relation "anothertab" violates check constraint "anothertab_chk"
-DETAIL:  Failing row contains (45, null).
+DETAIL:  Failing row contains (45, null). Check constraint expression: (atcol1 <= 3).
 insert into anothertab (atcol1, atcol2) values (default, null);
 select * from anothertab;
  atcol1 | atcol2 
@@ -3418,7 +3418,7 @@ ALTER TABLE ONLY test_drop_constr_parent DROP CONSTRAINT "test_drop_constr_paren
 -- should fail
 INSERT INTO test_drop_constr_child (c) VALUES (NULL);
 ERROR:  new row for relation "test_drop_constr_child" violates check constraint "test_drop_constr_parent_c_check"
-DETAIL:  Failing row contains (null).
+DETAIL:  Failing row contains (null). Check constraint expression: (c IS NOT NULL).
 DROP TABLE test_drop_constr_parent CASCADE;
 NOTICE:  drop cascades to table test_drop_constr_child
 --
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 83f97f684d5..a0f7488b610 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -71,14 +71,14 @@ INSERT INTO CHECK_TBL VALUES (5);
 INSERT INTO CHECK_TBL VALUES (4);
 INSERT INTO CHECK_TBL VALUES (3);
 ERROR:  new row for relation "check_tbl" violates check constraint "check_con"
-DETAIL:  Failing row contains (3).
+DETAIL:  Failing row contains (3). Check constraint expression: (x > 3).
 INSERT INTO CHECK_TBL VALUES (2);
 ERROR:  new row for relation "check_tbl" violates check constraint "check_con"
-DETAIL:  Failing row contains (2).
+DETAIL:  Failing row contains (2). Check constraint expression: (x > 3).
 INSERT INTO CHECK_TBL VALUES (6);
 INSERT INTO CHECK_TBL VALUES (1);
 ERROR:  new row for relation "check_tbl" violates check constraint "check_con"
-DETAIL:  Failing row contains (1).
+DETAIL:  Failing row contains (1). Check constraint expression: (x > 3).
 SELECT * FROM CHECK_TBL;
  x 
 ---
@@ -113,16 +113,16 @@ CREATE TABLE CHECK2_TBL (x int, y text, z int,
 INSERT INTO CHECK2_TBL VALUES (4, 'check ok', -2);
 INSERT INTO CHECK2_TBL VALUES (1, 'x check failed', -2);
 ERROR:  new row for relation "check2_tbl" violates check constraint "sequence_con"
-DETAIL:  Failing row contains (1, x check failed, -2).
+DETAIL:  Failing row contains (1, x check failed, -2). Check constraint expression: ((x > 3) AND (y <> 'check failed'::text) AND (z < 8)).
 INSERT INTO CHECK2_TBL VALUES (5, 'z check failed', 10);
 ERROR:  new row for relation "check2_tbl" violates check constraint "sequence_con"
-DETAIL:  Failing row contains (5, z check failed, 10).
+DETAIL:  Failing row contains (5, z check failed, 10). Check constraint expression: ((x > 3) AND (y <> 'check failed'::text) AND (z < 8)).
 INSERT INTO CHECK2_TBL VALUES (0, 'check failed', -2);
 ERROR:  new row for relation "check2_tbl" violates check constraint "sequence_con"
-DETAIL:  Failing row contains (0, check failed, -2).
+DETAIL:  Failing row contains (0, check failed, -2). Check constraint expression: ((x > 3) AND (y <> 'check failed'::text) AND (z < 8)).
 INSERT INTO CHECK2_TBL VALUES (6, 'check failed', 11);
 ERROR:  new row for relation "check2_tbl" violates check constraint "sequence_con"
-DETAIL:  Failing row contains (6, check failed, 11).
+DETAIL:  Failing row contains (6, check failed, 11). Check constraint expression: ((x > 3) AND (y <> 'check failed'::text) AND (z < 8)).
 INSERT INTO CHECK2_TBL VALUES (7, 'check ok', 7);
 SELECT * from CHECK2_TBL;
  x |    y     | z  
@@ -143,7 +143,7 @@ CREATE TABLE INSERT_TBL (x INT DEFAULT nextval('insert_seq'),
 	CONSTRAINT NE_INSERT_TBL_CON CHECK (x + z = 1) NOT ENFORCED);
 INSERT INTO INSERT_TBL(x,z) VALUES (2, -2);
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (2, -NULL-, -2).
+DETAIL:  Failing row contains (2, -NULL-, -2). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 SELECT * FROM INSERT_TBL;
  x | y | z 
 ---+---+---
@@ -157,15 +157,15 @@ SELECT 'one' AS one, nextval('insert_seq');
 
 INSERT INTO INSERT_TBL(y) VALUES ('Y');
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (2, Y, -2).
+DETAIL:  Failing row contains (2, Y, -2). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 INSERT INTO INSERT_TBL(y) VALUES ('Y');
 INSERT INTO INSERT_TBL(x,z) VALUES (1, -2);
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_check"
-DETAIL:  Failing row contains (1, -NULL-, -2).
+DETAIL:  Failing row contains (1, -NULL-, -2). Check constraint expression: ((x + z) = 0).
 INSERT INTO INSERT_TBL(z,x) VALUES (-7,  7);
 INSERT INTO INSERT_TBL VALUES (5, 'check failed', -5);
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (5, check failed, -5).
+DETAIL:  Failing row contains (5, check failed, -5). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 INSERT INTO INSERT_TBL VALUES (7, '!check failed', -7);
 INSERT INTO INSERT_TBL(y) VALUES ('-!NULL-');
 SELECT * FROM INSERT_TBL;
@@ -179,10 +179,10 @@ SELECT * FROM INSERT_TBL;
 
 INSERT INTO INSERT_TBL(y,z) VALUES ('check failed', 4);
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_check"
-DETAIL:  Failing row contains (5, check failed, 4).
+DETAIL:  Failing row contains (5, check failed, 4). Check constraint expression: ((x + z) = 0).
 INSERT INTO INSERT_TBL(x,y) VALUES (5, 'check failed');
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (5, check failed, -5).
+DETAIL:  Failing row contains (5, check failed, -5). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 INSERT INTO INSERT_TBL(x,y) VALUES (5, '!check failed');
 INSERT INTO INSERT_TBL(y) VALUES ('-!NULL-');
 SELECT * FROM INSERT_TBL;
@@ -204,7 +204,7 @@ SELECT 'seven' AS one, nextval('insert_seq');
 
 INSERT INTO INSERT_TBL(y) VALUES ('Y');
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (8, Y, -8).
+DETAIL:  Failing row contains (8, Y, -8). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 SELECT 'eight' AS one, currval('insert_seq');
   one  | currval 
 -------+---------
@@ -236,7 +236,7 @@ CREATE TABLE SYS_COL_CHECK_TBL (city text, state text, is_capital bool,
 INSERT INTO SYS_COL_CHECK_TBL VALUES ('Seattle', 'Washington', false, 100);
 INSERT INTO SYS_COL_CHECK_TBL VALUES ('Olympia', 'Washington', true, 100);
 ERROR:  new row for relation "sys_col_check_tbl" violates check constraint "sys_col_check_tbl_check"
-DETAIL:  Failing row contains (Olympia, Washington, t, 100).
+DETAIL:  Failing row contains (Olympia, Washington, t, 100). Check constraint expression: (NOT (is_capital AND (((tableoid)::regclass)::text = 'sys_col_check_tbl'::text))).
 SELECT *, tableoid::regclass::text FROM SYS_COL_CHECK_TBL;
   city   |   state    | is_capital | altitude |     tableoid      
 ---------+------------+------------+----------+-------------------
@@ -262,13 +262,13 @@ CREATE TABLE INSERT_CHILD (cx INT default 42,
 INSERT INTO INSERT_CHILD(x,z,cy) VALUES (7,-7,11);
 INSERT INTO INSERT_CHILD(x,z,cy) VALUES (7,-7,6);
 ERROR:  new row for relation "insert_child" violates check constraint "insert_child_check"
-DETAIL:  Failing row contains (7, -NULL-, -7, 42, 6).
+DETAIL:  Failing row contains (7, -NULL-, -7, 42, 6). Check constraint expression: (cy > x).
 INSERT INTO INSERT_CHILD(x,z,cy) VALUES (6,-7,7);
 ERROR:  new row for relation "insert_child" violates check constraint "insert_tbl_check"
-DETAIL:  Failing row contains (6, -NULL-, -7, 42, 7).
+DETAIL:  Failing row contains (6, -NULL-, -7, 42, 7). Check constraint expression: ((x + z) = 0).
 INSERT INTO INSERT_CHILD(x,y,z,cy) VALUES (6,'check failed',-6,7);
 ERROR:  new row for relation "insert_child" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (6, check failed, -6, 42, 7).
+DETAIL:  Failing row contains (6, check failed, -6, 42, 7). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 SELECT * FROM INSERT_CHILD;
  x |   y    | z  | cx | cy 
 ---+--------+----+----+----
@@ -287,7 +287,7 @@ INSERT INTO ATACC2 (TEST) VALUES (-3);
 -- check constraint is there on parent
 INSERT INTO ATACC1 (TEST) VALUES (-3);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc1_test_check"
-DETAIL:  Failing row contains (-3).
+DETAIL:  Failing row contains (-3). Check constraint expression: (test > 0).
 DROP TABLE ATACC1 CASCADE;
 NOTICE:  drop cascades to table atacc2
 CREATE TABLE ATACC1 (TEST INT, TEST2 INT
@@ -296,17 +296,17 @@ CREATE TABLE ATACC2 () INHERITS (ATACC1);
 -- check constraint is there on child
 INSERT INTO ATACC2 (TEST) VALUES (-3);
 ERROR:  new row for relation "atacc2" violates check constraint "atacc1_test_check"
-DETAIL:  Failing row contains (-3, null).
+DETAIL:  Failing row contains (-3, null). Check constraint expression: (test > 0).
 -- check constraint is there on parent
 INSERT INTO ATACC1 (TEST) VALUES (-3);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc1_test_check"
-DETAIL:  Failing row contains (-3, null).
+DETAIL:  Failing row contains (-3, null). Check constraint expression: (test > 0).
 -- check constraint is not there on child
 INSERT INTO ATACC2 (TEST2) VALUES (3);
 -- check constraint is there on parent
 INSERT INTO ATACC1 (TEST2) VALUES (3);
 ERROR:  new row for relation "atacc1" violates check constraint "atacc1_test2_check"
-DETAIL:  Failing row contains (null, 3).
+DETAIL:  Failing row contains (null, 3). Check constraint expression: (test2 > 10).
 DROP TABLE ATACC1 CASCADE;
 NOTICE:  drop cascades to table atacc2
 --
@@ -331,7 +331,7 @@ INSERT INTO INSERT_TBL SELECT * FROM tmp WHERE yd = 'try again';
 INSERT INTO INSERT_TBL(y,z) SELECT yd, -7 FROM tmp WHERE yd = 'try again';
 INSERT INTO INSERT_TBL(y,z) SELECT yd, -8 FROM tmp WHERE yd = 'try again';
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (8, try again, -8).
+DETAIL:  Failing row contains (8, try again, -8). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 SELECT * FROM INSERT_TBL;
  x |       y       | z  
 ---+---------------+----
@@ -351,7 +351,7 @@ UPDATE INSERT_TBL SET x = 6 WHERE x = 6;
 UPDATE INSERT_TBL SET x = -z, z = -x;
 UPDATE INSERT_TBL SET x = z, z = x;
 ERROR:  new row for relation "insert_tbl" violates check constraint "insert_tbl_con"
-DETAIL:  Failing row contains (-4, Y, 4).
+DETAIL:  Failing row contains (-4, Y, 4). Check constraint expression: ((x >= 3) AND (y <> 'check failed'::text) AND (x < 8)).
 SELECT * FROM INSERT_TBL;
  x |       y       | z  
 ---+---------------+----
@@ -381,7 +381,7 @@ SELECT * FROM COPY_TBL;
 \set filename :abs_srcdir '/data/constrf.data'
 COPY COPY_TBL FROM :'filename';
 ERROR:  new row for relation "copy_tbl" violates check constraint "copy_con"
-DETAIL:  Failing row contains (7, check failed, 6).
+DETAIL:  Failing row contains (7, check failed, 6). Check constraint expression: ((x > 3) AND (y <> 'check failed'::text) AND (x < 7)).
 CONTEXT:  COPY copy_tbl, line 2: "7	check failed	6"
 SELECT * FROM COPY_TBL;
  x |       y       | z 
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index df6fcd91799..546fb239e54 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -658,7 +658,7 @@ NOTICE:  input = {"f1":null}
 copy check_con_tbl from stdin;
 NOTICE:  input = {"f1":0}
 ERROR:  new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check"
-DETAIL:  Failing row contains (0).
+DETAIL:  Failing row contains (0). Check constraint expression: check_con_function(check_con_tbl.*).
 CONTEXT:  COPY check_con_tbl, line 1: "0"
 select * from check_con_tbl;
  f1 
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 76069bde756..b95ec91264e 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -57,7 +57,7 @@ INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */
 INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds -- Unique constraints not copied */
 INSERT INTO inhg VALUES ('x', 'foo',  'y');  /* fails due to constraint */
 ERROR:  new row for relation "inhg" violates check constraint "foo"
-DETAIL:  Failing row contains (x, foo, y).
+DETAIL:  Failing row contains (x, foo, y). Check constraint expression: (xx = 'text'::text).
 SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */
  x |  xx  | y 
 ---+------+---
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 62a48a523a2..c2c892306af 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -690,7 +690,7 @@ insert into nulltest values ('a', 'b', 'c', 'd', NULL);
 ERROR:  domain dcheck does not allow null values
 insert into nulltest values ('a', 'b', 'c', 'd', 'a');
 ERROR:  new row for relation "nulltest" violates check constraint "nulltest_col5_check"
-DETAIL:  Failing row contains (a, b, c, d, a).
+DETAIL:  Failing row contains (a, b, c, d, a). Check constraint expression: ((col5)::text = ANY ((ARRAY['c'::character varying, 'd'::character varying])::text[])).
 INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
 ERROR:  domain dnotnull does not allow null values
 INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c');
@@ -710,7 +710,7 @@ CONTEXT:  COPY nulltest, line 1, column col5: null input
 -- Last row is bad
 COPY nulltest FROM stdin;
 ERROR:  new row for relation "nulltest" violates check constraint "nulltest_col5_check"
-DETAIL:  Failing row contains (a, b, c, null, a).
+DETAIL:  Failing row contains (a, b, c, null, a). Check constraint expression: ((col5)::text = ANY ((ARRAY['c'::character varying, 'd'::character varying])::text[])).
 CONTEXT:  COPY nulltest, line 3: "a	b	c	\N	a"
 select * from nulltest;
  col1 | col2 | col3 | col4 | col5 
diff --git a/src/test/regress/expected/generated_stored.out b/src/test/regress/expected/generated_stored.out
index fd6caf1cf2d..ecada434168 100644
--- a/src/test/regress/expected/generated_stored.out
+++ b/src/test/regress/expected/generated_stored.out
@@ -662,7 +662,7 @@ CREATE TABLE gtest20 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORE
 INSERT INTO gtest20 (a) VALUES (10);  -- ok
 INSERT INTO gtest20 (a) VALUES (30);  -- violates constraint
 ERROR:  new row for relation "gtest20" violates check constraint "gtest20_b_check"
-DETAIL:  Failing row contains (30, 60).
+DETAIL:  Failing row contains (30, 60). Check constraint expression: (b < 50).
 ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100);  -- violates constraint
 ERROR:  check constraint "gtest20_b_check" of relation "gtest20" is violated by some row
 ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3);  -- ok
@@ -687,7 +687,7 @@ ALTER TABLE gtest20c ADD CONSTRAINT whole_row_check CHECK (gtest20c IS NOT NULL)
 INSERT INTO gtest20c VALUES (1);  -- ok
 INSERT INTO gtest20c VALUES (NULL);  -- fails
 ERROR:  new row for relation "gtest20c" violates check constraint "whole_row_check"
-DETAIL:  Failing row contains (null, null).
+DETAIL:  Failing row contains (null, null). Check constraint expression: (gtest20c.* IS NOT NULL).
 ALTER TABLE gtest20c ALTER COLUMN b SET EXPRESSION AS (NULL::int);  -- violates constraint
 ERROR:  check constraint "whole_row_check" of relation "gtest20c" is violated by some row
 -- index with whole-row reference needs rebuild
diff --git a/src/test/regress/expected/generated_virtual.out b/src/test/regress/expected/generated_virtual.out
index b2f8e16e63b..26a0dc66c10 100644
--- a/src/test/regress/expected/generated_virtual.out
+++ b/src/test/regress/expected/generated_virtual.out
@@ -647,7 +647,7 @@ CREATE TABLE gtest20 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) VIRTU
 INSERT INTO gtest20 (a) VALUES (10);  -- ok
 INSERT INTO gtest20 (a) VALUES (30);  -- violates constraint
 ERROR:  new row for relation "gtest20" violates check constraint "gtest20_b_check"
-DETAIL:  Failing row contains (30, virtual).
+DETAIL:  Failing row contains (30, virtual). Check constraint expression: (b < 50).
 ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100);  -- violates constraint
 ERROR:  check constraint "gtest20_b_check" of relation "gtest20" is violated by some row
 ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3);  -- ok
@@ -693,7 +693,7 @@ ALTER TABLE gtest20c ADD CONSTRAINT whole_row_check CHECK (gtest20c IS NOT NULL)
 INSERT INTO gtest20c VALUES (1);  -- ok
 INSERT INTO gtest20c VALUES (NULL);  -- fails
 ERROR:  new row for relation "gtest20c" violates check constraint "whole_row_check"
-DETAIL:  Failing row contains (null, virtual).
+DETAIL:  Failing row contains (null, virtual). Check constraint expression: (gtest20c.* IS NOT NULL).
 ALTER TABLE gtest20c ALTER COLUMN b SET EXPRESSION AS (NULL::int);  -- violates constraint
 ERROR:  check constraint "whole_row_check" of relation "gtest20c" is violated by some row
 -- index with whole-row reference needs rebuild
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 3c2ff55d3ba..fded025bf1c 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -867,10 +867,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg
 
 insert into ac (aa) values (NULL);
 ERROR:  new row for relation "ac" violates check constraint "ac_check"
-DETAIL:  Failing row contains (null).
+DETAIL:  Failing row contains (null). Check constraint expression: (aa IS NOT NULL).
 insert into bc (aa) values (NULL);
 ERROR:  new row for relation "bc" violates check constraint "ac_check"
-DETAIL:  Failing row contains (null, null).
+DETAIL:  Failing row contains (null, null). Check constraint expression: (aa IS NOT NULL).
 alter table bc drop constraint ac_check;  -- fail, disallowed
 ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
 alter table ac drop constraint ac_check;
@@ -890,10 +890,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg
 
 insert into ac (aa) values (NULL);
 ERROR:  new row for relation "ac" violates check constraint "ac_aa_check"
-DETAIL:  Failing row contains (null).
+DETAIL:  Failing row contains (null). Check constraint expression: (aa IS NOT NULL).
 insert into bc (aa) values (NULL);
 ERROR:  new row for relation "bc" violates check constraint "ac_aa_check"
-DETAIL:  Failing row contains (null, null).
+DETAIL:  Failing row contains (null, null). Check constraint expression: (aa IS NOT NULL).
 alter table bc drop constraint ac_aa_check;  -- fail, disallowed
 ERROR:  cannot drop inherited constraint "ac_aa_check" of relation "bc"
 alter table ac drop constraint ac_aa_check;
@@ -980,7 +980,7 @@ insert into c1 values(1,1,2);
 alter table p2 add check (f2>0);
 insert into c1 values(1,-1,2);  -- fail
 ERROR:  new row for relation "c1" violates check constraint "p2_f2_check"
-DETAIL:  Failing row contains (1, -1, 2).
+DETAIL:  Failing row contains (1, -1, 2). Check constraint expression: (f2 > 0).
 create table c2(f3 int) inherits(p1,p2);
 \d c2
                  Table "public.c2"
@@ -1592,7 +1592,7 @@ alter table p1_c1 inherit p1;
 insert into p1 values(-1); --ok
 insert into p1_c1 values(-1); --error
 ERROR:  new row for relation "p1_c1" violates check constraint "p1_a_check"
-DETAIL:  Failing row contains (-1).
+DETAIL:  Failing row contains (-1). Check constraint expression: (f1 > 0).
 alter table p1 alter constraint p1_a_check enforced; --error
 ERROR:  check constraint "p1_a_check" of relation "p1" is violated by some row
 truncate p1;
@@ -1743,10 +1743,10 @@ alter table invalid_check_con add constraint inh_check_constraint check(f1 > 0)
 NOTICE:  merging constraint "inh_check_constraint" with inherited definition
 insert into invalid_check_con values(0); -- fail
 ERROR:  new row for relation "invalid_check_con" violates check constraint "inh_check_constraint"
-DETAIL:  Failing row contains (0).
+DETAIL:  Failing row contains (0). Check constraint expression: (f1 > 0).
 insert into invalid_check_con_child values(0); -- fail
 ERROR:  new row for relation "invalid_check_con_child" violates check constraint "inh_check_constraint"
-DETAIL:  Failing row contains (0).
+DETAIL:  Failing row contains (0). Check constraint expression: (f1 > 0).
 select conrelid::regclass::text as relname, conname,
        convalidated, conislocal, coninhcount, connoinherit
 from pg_constraint where conname like 'inh\_check\_constraint%'
@@ -3959,13 +3959,13 @@ INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '5');
 -- insert with child check constraint error
 INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', '10');
 ERROR:  new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check"
-DETAIL:  Failing row contains (0, 1, 10).
+DETAIL:  Failing row contains (0, 1, 10). Check constraint expression: (data < 10).
 INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '10');
 ERROR:  new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check"
-DETAIL:  Failing row contains (10, 1, 10).
+DETAIL:  Failing row contains (10, 1, 10). Check constraint expression: (data < 10).
 INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '10');
 ERROR:  new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check"
-DETAIL:  Failing row contains (20, 1, 10).
+DETAIL:  Failing row contains (20, 1, 10). Check constraint expression: (data < 10).
 -- insert with child not null constraint error
 INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', NULL);
 ERROR:  null value in column "data" of relation "errtst_child_fastdef" violates not-null constraint
@@ -3979,13 +3979,13 @@ DETAIL:  Failing row contains (20, 1, null).
 -- insert with shared check constraint error
 INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '5', '5');
 ERROR:  new row for relation "errtst_child_fastdef" violates check constraint "shdata_small"
-DETAIL:  Failing row contains (0, 5, 5).
+DETAIL:  Failing row contains (0, 5, 5). Check constraint expression: (shdata < 3).
 INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '5', '5');
 ERROR:  new row for relation "errtst_child_plaindef" violates check constraint "shdata_small"
-DETAIL:  Failing row contains (10, 5, 5).
+DETAIL:  Failing row contains (10, 5, 5). Check constraint expression: (shdata < 3).
 INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '5', '5');
 ERROR:  new row for relation "errtst_child_reorder" violates check constraint "shdata_small"
-DETAIL:  Failing row contains (20, 5, 5).
+DETAIL:  Failing row contains (20, 5, 5). Check constraint expression: (shdata < 3).
 -- within partition update without child check constraint violation
 BEGIN;
 UPDATE errtst_parent SET data = data + 1 WHERE partid = 0;
@@ -3995,13 +3995,13 @@ ROLLBACK;
 -- within partition update with child check constraint violation
 UPDATE errtst_parent SET data = data + 10 WHERE partid = 0;
 ERROR:  new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check"
-DETAIL:  Failing row contains (0, 1, 15).
+DETAIL:  Failing row contains (0, 1, 15). Check constraint expression: (data < 10).
 UPDATE errtst_parent SET data = data + 10 WHERE partid = 10;
 ERROR:  new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check"
-DETAIL:  Failing row contains (10, 1, 15).
+DETAIL:  Failing row contains (10, 1, 15). Check constraint expression: (data < 10).
 UPDATE errtst_parent SET data = data + 10 WHERE partid = 20;
 ERROR:  new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check"
-DETAIL:  Failing row contains (20, 1, 15).
+DETAIL:  Failing row contains (20, 1, 15). Check constraint expression: (data < 10).
 -- direct leaf partition update, without partition id violation
 BEGIN;
 UPDATE errtst_child_fastdef SET partid = 1 WHERE partid = 0;
@@ -4027,13 +4027,13 @@ ROLLBACK;
 -- partition move, with child check constraint violation
 UPDATE errtst_parent SET partid = 10, data = data + 10 WHERE partid = 0;
 ERROR:  new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check"
-DETAIL:  Failing row contains (10, 1, 15).
+DETAIL:  Failing row contains (10, 1, 15). Check constraint expression: (data < 10).
 UPDATE errtst_parent SET partid = 20, data = data + 10 WHERE partid = 10;
 ERROR:  new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check"
-DETAIL:  Failing row contains (20, 1, 15).
+DETAIL:  Failing row contains (20, 1, 15). Check constraint expression: (data < 10).
 UPDATE errtst_parent SET partid = 0, data = data + 10 WHERE partid = 20;
 ERROR:  new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check"
-DETAIL:  Failing row contains (0, 1, 15).
+DETAIL:  Failing row contains (0, 1, 15). Check constraint expression: (data < 10).
 -- partition move, without target partition
 UPDATE errtst_parent SET partid = 30, data = data + 10 WHERE partid = 20;
 ERROR:  no partition of relation "errtst_parent" found for row
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 75b8de79fce..ce6df9aaaf7 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -660,7 +660,7 @@ create trigger mlparted11_trig before insert ON mlparted11
 -- to the BR trigger mlparted11_trig_fn)
 insert into mlparted values (1, 2);
 ERROR:  new row for relation "mlparted11" violates check constraint "check_b"
-DETAIL:  Failing row contains (1, 4).
+DETAIL:  Failing row contains (1, 4). Check constraint expression: (b = 3).
 drop trigger mlparted11_trig on mlparted11;
 drop function mlparted11_trig_fn();
 -- check that inserting into an internal partition successfully results in
@@ -706,7 +706,7 @@ alter table mlparted attach partition mlparted5 for values from (1, 40) to (1, 5
 alter table mlparted add constraint check_b check (a = 1 and b < 45);
 insert into mlparted values (1, 45, 'a');
 ERROR:  new row for relation "mlparted5a" violates check constraint "check_b"
-DETAIL:  Failing row contains (1, 45, a).
+DETAIL:  Failing row contains (1, 45, a). Check constraint expression: ((a = 1) AND (b < 45)).
 create function mlparted5abrtrig_func() returns trigger as $$ begin new.c = 'b'; return new; end; $$ language plpgsql;
 create trigger mlparted5abrtrig before insert on mlparted5a for each row execute procedure mlparted5abrtrig_func();
 insert into mlparted5 (a, b, c) values (1, 40, 'a');
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 9b7153c9cd5..163101acf7c 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1112,7 +1112,7 @@ ERROR:  null value in column "c2" of relation "t1" violates not-null constraint
 DETAIL:  Failing row contains (c1) = (5).
 UPDATE t1 SET c3 = 10; -- fail, but see columns with SELECT rights, or being modified
 ERROR:  new row for relation "t1" violates check constraint "t1_c3_check"
-DETAIL:  Failing row contains (c1, c3) = (1, 10).
+DETAIL:  Failing row contains (c1, c3) = (1, 10). Check constraint expression: (c3 < 5).
 SET SESSION AUTHORIZATION regress_priv_user1;
 DROP TABLE t1;
 -- check error reporting with column privs on a partitioned table
diff --git a/src/test/regress/expected/sqljson_queryfuncs.out b/src/test/regress/expected/sqljson_queryfuncs.out
index fe8aa927b5e..fd7918a1386 100644
--- a/src/test/regress/expected/sqljson_queryfuncs.out
+++ b/src/test/regress/expected/sqljson_queryfuncs.out
@@ -1151,22 +1151,22 @@ ORDER BY 1;
 
 INSERT INTO test_jsonb_constraints VALUES ('', 1);
 ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint1"
-DETAIL:  Failing row contains (, 1, [1, 2]).
+DETAIL:  Failing row contains (, 1, [1, 2]). Check constraint expression: (js IS JSON).
 INSERT INTO test_jsonb_constraints VALUES ('1', 1);
 ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint2"
-DETAIL:  Failing row contains (1, 1, [1, 2]).
+DETAIL:  Failing row contains (1, 1, [1, 2]). Check constraint expression: JSON_EXISTS((js)::jsonb, '$."a"' PASSING (i + 5) AS "int", (i)::text AS "TXT", ARRAY[1, 2, 3] AS arr).
 INSERT INTO test_jsonb_constraints VALUES ('[]');
 ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint2"
-DETAIL:  Failing row contains ([], null, [1, 2]).
+DETAIL:  Failing row contains ([], null, [1, 2]). Check constraint expression: JSON_EXISTS((js)::jsonb, '$."a"' PASSING (i + 5) AS "int", (i)::text AS "TXT", ARRAY[1, 2, 3] AS arr).
 INSERT INTO test_jsonb_constraints VALUES ('{"b": 1}', 1);
 ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint2"
-DETAIL:  Failing row contains ({"b": 1}, 1, [1, 2]).
+DETAIL:  Failing row contains ({"b": 1}, 1, [1, 2]). Check constraint expression: JSON_EXISTS((js)::jsonb, '$."a"' PASSING (i + 5) AS "int", (i)::text AS "TXT", ARRAY[1, 2, 3] AS arr).
 INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
 ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint3"
-DETAIL:  Failing row contains ({"a": 1}, 1, [1, 2]).
+DETAIL:  Failing row contains ({"a": 1}, 1, [1, 2]). Check constraint expression: (JSON_VALUE((js)::jsonb, '$."a"' RETURNING integer DEFAULT 12 ON EMPTY ERROR ON ERROR) > i).
 INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
 ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4"
-DETAIL:  Failing row contains ({"a": 10}, 1, [1, 2]).
+DETAIL:  Failing row contains ({"a": 10}, 1, [1, 2]). Check constraint expression: (JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) = '[10]'::jsonb).
 DROP TABLE test_jsonb_constraints;
 -- Test mutability of query functions
 CREATE TABLE test_jsonb_mutability(js jsonb, b int);
-- 
2.52.0

