[PATCH] Add schema and table names to partition error
Hello,
I'm writing telemetry data into a table partitioned by time. When there
is no partition for a particular date, my app notices the constraint
violation, creates the partition, and retries the insert.
I'm used to handling constraint violations by observing the constraint
name in the error fields. However, this error had none. I set out to add
the name to the error field, but after a bit of reading my impression is
that partition constraints are more like a property of a table.
I've attached a patch that adds the schema and table name fields to
errors for my use case:
- Insert data into a partitioned table for which there is no partition.
- Insert data directly into an incorrect partition.
Thanks,
Chris
Attachments:
0001-Add-schema-and-table-names-to-partition-error.patchtext/x-patch; charset=UTF-8; name=0001-Add-schema-and-table-names-to-partition-error.patchDownload
From 8261b366c49b2d04baeb882d39dfa626b9315889 Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Sat, 29 Feb 2020 12:47:56 -0600
Subject: [PATCH] Add schema and table names to partition error
---
src/backend/executor/execMain.c | 3 ++-
src/backend/executor/execPartition.c | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git src/backend/executor/execMain.c src/backend/executor/execMain.c
index ee5c3a60ff..7cb486b211 100644
--- src/backend/executor/execMain.c
+++ src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git src/backend/executor/execPartition.c src/backend/executor/execPartition.c
index c13b1d3501..a5542b92c7 100644
--- src/backend/executor/execPartition.c
+++ src/backend/executor/execPartition.c
@@ -345,7 +345,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
--
2.11.0
Hi Chris,
On Sun, Mar 1, 2020 at 4:34 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Hello,
I'm writing telemetry data into a table partitioned by time. When there
is no partition for a particular date, my app notices the constraint
violation, creates the partition, and retries the insert.I'm used to handling constraint violations by observing the constraint
name in the error fields. However, this error had none. I set out to add
the name to the error field, but after a bit of reading my impression is
that partition constraints are more like a property of a table.
This makes sense to me. Btree code which implements unique
constraints also does this; see _bt_check_unique() function in
src/backend/access/nbtree/nbtinsert.c:
ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("duplicate key value violates
unique constraint \"%s\"",
RelationGetRelationName(rel)),
key_desc ? errdetail("Key %s already exists.",
key_desc) : 0,
errtableconstraint(heapRel,
RelationGetRelationName(rel))));
I've attached a patch that adds the schema and table name fields to
errors for my use case:
Instead of using errtable(), use errtableconstraint() like the btree
code does, if only just for consistency.
- Insert data into a partitioned table for which there is no partition.
- Insert data directly into an incorrect partition.
There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:
In ATRewriteTable():
if (partqualstate && !ExecCheck(partqualstate, econtext))
{
if (tab->validate_default)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for
default partition \"%s\" would be violated by some row",
RelationGetRelationName(oldrel))));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation
\"%s\" is violated by some row",
RelationGetRelationName(oldrel))));
}
Maybe, better fix these too for completeness.
Thanks,
Amit
On Sun, Mar 1, 2020 at 10:10 AM Amit Langote <amitlangote09@gmail.com> wrote:
Hi Chris,
On Sun, Mar 1, 2020 at 4:34 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Hello,
I'm writing telemetry data into a table partitioned by time. When there
is no partition for a particular date, my app notices the constraint
violation, creates the partition, and retries the insert.I'm used to handling constraint violations by observing the constraint
name in the error fields. However, this error had none. I set out to add
the name to the error field, but after a bit of reading my impression is
that partition constraints are more like a property of a table.This makes sense to me. Btree code which implements unique
constraints also does this; see _bt_check_unique() function in
src/backend/access/nbtree/nbtinsert.c:ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("duplicate key value violates
unique constraint \"%s\"",
RelationGetRelationName(rel)),
key_desc ? errdetail("Key %s already exists.",
key_desc) : 0,
errtableconstraint(heapRel,RelationGetRelationName(rel))));
I've attached a patch that adds the schema and table name fields to
errors for my use case:Instead of using errtable(), use errtableconstraint() like the btree
code does, if only just for consistency.
+1. We use errtableconstraint at other places where we use error code
ERRCODE_CHECK_VIOLATION.
- Insert data into a partitioned table for which there is no partition.
- Insert data directly into an incorrect partition.There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:In ATRewriteTable():
if (partqualstate && !ExecCheck(partqualstate, econtext))
{
if (tab->validate_default)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for
default partition \"%s\" would be violated by some row",
RelationGetRelationName(oldrel))));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation
\"%s\" is violated by some row",
RelationGetRelationName(oldrel))));
}Maybe, better fix these too for completeness.
Right, if we want to make a change for this, then I think we can once
check all the places where we use error code ERRCODE_CHECK_VIOLATION.
Another thing we might need to see is which of these can be
back-patched. We should also try to write the tests for cases we are
changing even if we don't want to commit those.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
Thank you both for look at this!
On 3/1/20 5:14 AM, Amit Kapila wrote:
On Sun, Mar 1, 2020 at 10:10 AM Amit Langote <amitlangote09@gmail.com> wrote:
Hi Chris,
On Sun, Mar 1, 2020 at 4:34 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Hello,
I'm writing telemetry data into a table partitioned by time. When there
is no partition for a particular date, my app notices the constraint
violation, creates the partition, and retries the insert.I'm used to handling constraint violations by observing the constraint
name in the error fields. However, this error had none. I set out to add
the name to the error field, but after a bit of reading my impression is
that partition constraints are more like a property of a table.This makes sense to me. Btree code which implements unique
constraints also does this; see _bt_check_unique() function in
src/backend/access/nbtree/nbtinsert.c:ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("duplicate key value violates
unique constraint \"%s\"",
RelationGetRelationName(rel)),
key_desc ? errdetail("Key %s already exists.",
key_desc) : 0,
errtableconstraint(heapRel,RelationGetRelationName(rel))));
I've attached a patch that adds the schema and table name fields to
errors for my use case:Instead of using errtable(), use errtableconstraint() like the btree
code does, if only just for consistency.
There are two relations in the example you give: the index, rel, and the
table, heapRel. It makes sense to me that two error fields be filled in
with those two names.
With partitions, there is no second name because there is no index nor
constraint object. My (very limited) understanding is that partition
"constraints" are entirely contained within pg_class.relpartbound of the
partition.
Are you suggesting that the table name go into the constraint name field
of the error?
+1. We use errtableconstraint at other places where we use error code
ERRCODE_CHECK_VIOLATION.
Yes, I see this function used when it is a CHECK constraint that is
being violated. In every case the constraint name is passed as the
second argument.
There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:Maybe, better fix these too for completeness.
Done. As there is no named constraint here, I used errtable again.
Right, if we want to make a change for this, then I think we can once
check all the places where we use error code ERRCODE_CHECK_VIOLATION.
I've looked at every instance of this. It is used for 1) check
constraints, 2) domain constraints, and 3) partition constraints.
1. errtableconstraint is used with the name of the constraint.
2. errdomainconstraint is used with the name of the constraint except in
one instance which deliberately uses errtablecol.
3. With the attached patch, errtable is used except for one instance in
src/backend/partitioning/partbounds.c described below.
In check_default_partition_contents of
src/backend/partitioning/partbounds.c, the default partition is checked
for any rows that should belong in the partition being added _unless_
the leaf being checked is a foreign table. There are two tables
mentioned in this warning, and I couldn't decide which, if any, deserves
to be in the error fields:
/*
* Only RELKIND_RELATION relations (i.e. leaf
partitions) need to be
* scanned.
*/
if (part_rel->rd_rel->relkind != RELKIND_RELATION)
{
if (part_rel->rd_rel->relkind ==
RELKIND_FOREIGN_TABLE)
ereport(WARNING,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("skipped
scanning foreign table \"%s\" which is a partition of default partition
\"%s\"",
RelationGetRelationName(part_rel),
RelationGetRelationName(default_rel))));
if (RelationGetRelid(default_rel) !=
RelationGetRelid(part_rel))
table_close(part_rel, NoLock);
continue;
}
Another thing we might need to see is which of these can be
back-patched. We should also try to write the tests for cases we are
changing even if we don't want to commit those.
I don't have any opinion on back-patching. Existing tests pass. I wasn't
able to find another test that checks the constraint field of errors.
There's a little bit in the tests for psql, but that is about the the
\errverbose functionality rather than specific errors and their fields.
Here's what I tested:
# CREATE TABLE t1 (i int PRIMARY KEY); INSERT INTO t1 VALUES (1), (1);
# \errverbose
...
CONSTRAINT NAME: t1_pkey
# CREATE TABLE pt1 (x int, y int, PRIMARY KEY (x,y)) PARTITIONED BY
RANGE (y);
# INSERT INTO pt1 VALUES (10,10);
# \errverbose
...
SCHEMA NAME: public
TABLE NAME: pt1
# CREATE TABLE pt1_p1 PARTITION OF pt1 FOR VALUES FROM (1) TO (5);
# INSERT INTO pt1 VALUES (10,10);
# \errverbose
...
SCHEMA NAME: public
TABLE NAME: pt1
# INSERT INTO pt1_p1 VALUES (10,10);
# \errverbose
...
SCHEMA NAME: public
TABLE NAME: pt1_p1
# CREATE TABLE pt1_dp PARTITION OF pt1 DEFAULT;
# INSERT INTO pt1 VALUES (10,10);
# CREATE TABLE pt1_p2 PARTITION OF pt1 FOR VALUES FROM (6) TO (20);
# \errverbose
...
SCHEMA NAME: public
TABLE NAME: pt1_dp
Thanks,
Chris
Attachments:
v2-0001-Add-schema-and-table-names-to-partition-errors.patchtext/x-patch; charset=UTF-8; name=v2-0001-Add-schema-and-table-names-to-partition-errors.patchDownload
From c6b39accf9f51f9c08a2fc62e848144776e23ffb Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Sat, 29 Feb 2020 12:47:56 -0600
Subject: [PATCH] Add schema and table names to partition errors
---
src/backend/commands/tablecmds.c | 6 ++++--
src/backend/executor/execMain.c | 3 ++-
src/backend/executor/execPartition.c | 3 ++-
src/backend/partitioning/partbounds.c | 3 ++-
4 files changed, 10 insertions(+), 5 deletions(-)
diff --git src/backend/commands/tablecmds.c src/backend/commands/tablecmds.c
index 02a7c04fdb..6a32812a1f 100644
--- src/backend/commands/tablecmds.c
+++ src/backend/commands/tablecmds.c
@@ -5342,12 +5342,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation \"%s\" is violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
}
/* Write the tuple out to the new relation */
diff --git src/backend/executor/execMain.c src/backend/executor/execMain.c
index ee5c3a60ff..7cb486b211 100644
--- src/backend/executor/execMain.c
+++ src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git src/backend/executor/execPartition.c src/backend/executor/execPartition.c
index c13b1d3501..a5542b92c7 100644
--- src/backend/executor/execPartition.c
+++ src/backend/executor/execPartition.c
@@ -345,7 +345,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
diff --git src/backend/partitioning/partbounds.c src/backend/partitioning/partbounds.c
index 35953f23fa..4c47f54a57 100644
--- src/backend/partitioning/partbounds.c
+++ src/backend/partitioning/partbounds.c
@@ -1366,7 +1366,8 @@ check_default_partition_contents(Relation parent, Relation default_rel,
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(default_rel))));
+ RelationGetRelationName(default_rel)),
+ errtable(default_rel)));
ResetExprContext(econtext);
CHECK_FOR_INTERRUPTS();
--
2.11.0
Hi Chris,
On Mon, Mar 2, 2020 at 8:51 AM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/1/20 5:14 AM, Amit Kapila wrote:
On Sun, Mar 1, 2020 at 10:10 AM Amit Langote <amitlangote09@gmail.com> wrote:
This makes sense to me. Btree code which implements unique
constraints also does this; see _bt_check_unique() function in
src/backend/access/nbtree/nbtinsert.c:ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("duplicate key value violates
unique constraint \"%s\"",
RelationGetRelationName(rel)),
key_desc ? errdetail("Key %s already exists.",
key_desc) : 0,
errtableconstraint(heapRel,RelationGetRelationName(rel))));
I've attached a patch that adds the schema and table name fields to
errors for my use case:Instead of using errtable(), use errtableconstraint() like the btree
code does, if only just for consistency.There are two relations in the example you give: the index, rel, and the
table, heapRel. It makes sense to me that two error fields be filled in
with those two names.
That's a good point. Index constraints are actually named after the
index and vice versa, so it's a totally valid usage of
errtableconstraint().
create table foo (a int unique);
\d foo
Table "public.foo"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
a | integer | | |
Indexes:
"foo_a_key" UNIQUE CONSTRAINT, btree (a)
select conname from pg_constraint where conrelid = 'foo'::regclass;
conname
-----------
foo_a_key
(1 row)
create table bar (a int, constraint a_uniq unique (a));
\d bar
Table "public.bar"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
a | integer | | |
Indexes:
"a_uniq" UNIQUE CONSTRAINT, btree (a)
select conname from pg_constraint where conrelid = 'bar'::regclass;
conname
---------
a_uniq
(1 row)
With partitions, there is no second name because there is no index nor
constraint object.
It's right to say that partition's case cannot really be equated with
unique indexes.
My (very limited) understanding is that partition
"constraints" are entirely contained within pg_class.relpartbound of the
partition.
That is correct.
Are you suggesting that the table name go into the constraint name field
of the error?
Yes, that's what I was thinking, at least for "partition constraint
violated" errors, but given the above that would be a misleading use
of ErrorData.constraint_name.
Maybe it's not too late to invent a new error code like
ERRCODE_PARTITION_VIOLATION or such, then maybe we can use a
hard-coded name, say, just the string "partition constraint".
There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:Maybe, better fix these too for completeness.
Done. As there is no named constraint here, I used errtable again.
Right, if we want to make a change for this, then I think we can once
check all the places where we use error code ERRCODE_CHECK_VIOLATION.I've looked at every instance of this. It is used for 1) check
constraints, 2) domain constraints, and 3) partition constraints.1. errtableconstraint is used with the name of the constraint.
2. errdomainconstraint is used with the name of the constraint except in
one instance which deliberately uses errtablecol.
3. With the attached patch, errtable is used except for one instance in
src/backend/partitioning/partbounds.c described below.In check_default_partition_contents of
src/backend/partitioning/partbounds.c, the default partition is checked
for any rows that should belong in the partition being added _unless_
the leaf being checked is a foreign table. There are two tables
mentioned in this warning, and I couldn't decide which, if any, deserves
to be in the error fields:/*
* Only RELKIND_RELATION relations (i.e. leaf
partitions) need to be
* scanned.
*/
if (part_rel->rd_rel->relkind != RELKIND_RELATION)
{
if (part_rel->rd_rel->relkind ==
RELKIND_FOREIGN_TABLE)
ereport(WARNING,(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("skipped
scanning foreign table \"%s\" which is a partition of default partition
\"%s\"",RelationGetRelationName(part_rel),
RelationGetRelationName(default_rel))));
It seems strange to see that errcode here or any errcode for that
matter, so we shouldn't really be concerned about this one.
if (RelationGetRelid(default_rel) !=
RelationGetRelid(part_rel))
table_close(part_rel, NoLock);continue;
}Another thing we might need to see is which of these can be
back-patched. We should also try to write the tests for cases we are
changing even if we don't want to commit those.I don't have any opinion on back-patching. Existing tests pass. I wasn't
able to find another test that checks the constraint field of errors.
There's a little bit in the tests for psql, but that is about the the
\errverbose functionality rather than specific errors and their fields.
Actually, it's not a bad idea to use \errverbose to test this.
Thanks,
Amit
On Mon, Mar 2, 2020 at 9:39 AM Amit Langote <amitlangote09@gmail.com> wrote:
My (very limited) understanding is that partition
"constraints" are entirely contained within pg_class.relpartbound of the
partition.That is correct.
Are you suggesting that the table name go into the constraint name field
of the error?Yes, that's what I was thinking, at least for "partition constraint
violated" errors, but given the above that would be a misleading use
of ErrorData.constraint_name.
I think it is better to use errtable in such cases.
Maybe it's not too late to invent a new error code like
ERRCODE_PARTITION_VIOLATION or such, then maybe we can use a
hard-coded name, say, just the string "partition constraint".There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:Maybe, better fix these too for completeness.
Done. As there is no named constraint here, I used errtable again.
Right, if we want to make a change for this, then I think we can once
check all the places where we use error code ERRCODE_CHECK_VIOLATION.I've looked at every instance of this. It is used for 1) check
constraints, 2) domain constraints, and 3) partition constraints.1. errtableconstraint is used with the name of the constraint.
2. errdomainconstraint is used with the name of the constraint except in
one instance which deliberately uses errtablecol.
3. With the attached patch, errtable is used except for one instance in
src/backend/partitioning/partbounds.c described below.In check_default_partition_contents of
src/backend/partitioning/partbounds.c, the default partition is checked
for any rows that should belong in the partition being added _unless_
the leaf being checked is a foreign table. There are two tables
mentioned in this warning, and I couldn't decide which, if any, deserves
to be in the error fields:/*
* Only RELKIND_RELATION relations (i.e. leaf
partitions) need to be
* scanned.
*/
if (part_rel->rd_rel->relkind != RELKIND_RELATION)
{
if (part_rel->rd_rel->relkind ==
RELKIND_FOREIGN_TABLE)
ereport(WARNING,(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("skipped
scanning foreign table \"%s\" which is a partition of default partition
\"%s\"",RelationGetRelationName(part_rel),
RelationGetRelationName(default_rel))));
It seems strange to see that errcode here or any errcode for that
matter, so we shouldn't really be concerned about this one.
Right.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On 3/1/20 10:09 PM, Amit Langote wrote:
Hi Chris,
On Mon, Mar 2, 2020 at 8:51 AM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/1/20 5:14 AM, Amit Kapila wrote:
On Sun, Mar 1, 2020 at 10:10 AM Amit Langote <amitlangote09@gmail.com> wrote:
There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:Maybe, better fix these too for completeness.
Another thing we might need to see is which of these can be
back-patched. We should also try to write the tests for cases we are
changing even if we don't want to commit those.I don't have any opinion on back-patching. Existing tests pass. I wasn't
able to find another test that checks the constraint field of errors.
There's a little bit in the tests for psql, but that is about the the
\errverbose functionality rather than specific errors and their fields.Actually, it's not a bad idea to use \errverbose to test this.
I've added a second patch with tests that cover three of the five errors
touched by the first patch. Rather than \errverbose, I simply \set
VERBOSITY verbose. I could not find a way to exclude the location field
from the output, so those lines will be likely be out of date soon--if
not already.
I couldn't find a way to exercise the errors in tablecmds.c. Does anyone
know how to instigate a table rewrite that would violate partition
constraints? I tried:
ALTER TABLE pterr1 ALTER y TYPE bigint USING (y - 5);
ERROR: 42P16: cannot alter column "y" because it is part of the
partition key of relation "pterr1"
LOCATION: ATPrepAlterColumnType, tablecmds.c:10812
Thanks,
Chris
Attachments:
v2-0001-Add-schema-and-table-names-to-partition-errors.patchtext/x-patch; charset=UTF-8; name=v2-0001-Add-schema-and-table-names-to-partition-errors.patchDownload
From c6b39accf9f51f9c08a2fc62e848144776e23ffb Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Sat, 29 Feb 2020 12:47:56 -0600
Subject: [PATCH] Add schema and table names to partition errors
---
src/backend/commands/tablecmds.c | 6 ++++--
src/backend/executor/execMain.c | 3 ++-
src/backend/executor/execPartition.c | 3 ++-
src/backend/partitioning/partbounds.c | 3 ++-
4 files changed, 10 insertions(+), 5 deletions(-)
diff --git src/backend/commands/tablecmds.c src/backend/commands/tablecmds.c
index 02a7c04fdb..6a32812a1f 100644
--- src/backend/commands/tablecmds.c
+++ src/backend/commands/tablecmds.c
@@ -5342,12 +5342,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation \"%s\" is violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
}
/* Write the tuple out to the new relation */
diff --git src/backend/executor/execMain.c src/backend/executor/execMain.c
index ee5c3a60ff..7cb486b211 100644
--- src/backend/executor/execMain.c
+++ src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git src/backend/executor/execPartition.c src/backend/executor/execPartition.c
index c13b1d3501..a5542b92c7 100644
--- src/backend/executor/execPartition.c
+++ src/backend/executor/execPartition.c
@@ -345,7 +345,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
diff --git src/backend/partitioning/partbounds.c src/backend/partitioning/partbounds.c
index 35953f23fa..4c47f54a57 100644
--- src/backend/partitioning/partbounds.c
+++ src/backend/partitioning/partbounds.c
@@ -1366,7 +1366,8 @@ check_default_partition_contents(Relation parent, Relation default_rel,
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(default_rel))));
+ RelationGetRelationName(default_rel)),
+ errtable(default_rel)));
ResetExprContext(econtext);
CHECK_FOR_INTERRUPTS();
--
2.11.0
v2-0002-Tests-for-partition-error-fields.patchtext/x-patch; charset=UTF-8; name=v2-0002-Tests-for-partition-error-fields.patchDownload
From 2d2ab39bcbc7ea13cd04986b0c8aad6f14449ebd Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Mon, 2 Mar 2020 22:13:28 -0600
Subject: [PATCH] Tests for partition error fields
---
src/test/regress/expected/partition_errors.out | 37 ++++++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/partition_errors.sql | 23 ++++++++++++++++
3 files changed, 61 insertions(+), 1 deletion(-)
create mode 100644 src/test/regress/expected/partition_errors.out
create mode 100644 src/test/regress/sql/partition_errors.sql
diff --git src/test/regress/expected/partition_errors.out src/test/regress/expected/partition_errors.out
new file mode 100644
index 0000000000..c5a3b8c815
--- /dev/null
+++ src/test/regress/expected/partition_errors.out
@@ -0,0 +1,37 @@
+--
+-- Tests for partition error fields
+--
+\set VERBOSITY verbose
+-- no partitions
+CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+INSERT INTO pterr1 VALUES (10, 10);
+ERROR: 23514: no partition of relation "pterr1" found for row
+DETAIL: Partition key of the failing row contains (y) = (10).
+SCHEMA NAME: public
+TABLE NAME: pterr1
+LOCATION: ExecFindPartition, execPartition.c:349
+-- outside the only partition
+CREATE TABLE pterr1_p1 PARTITION OF pterr1 FOR VALUES FROM (1) TO (5);
+INSERT INTO pterr1 VALUES (10, 10);
+ERROR: 23514: no partition of relation "pterr1" found for row
+DETAIL: Partition key of the failing row contains (y) = (10).
+SCHEMA NAME: public
+TABLE NAME: pterr1
+LOCATION: ExecFindPartition, execPartition.c:349
+INSERT INTO pterr1_p1 VALUES (10, 10);
+ERROR: 23514: new row for relation "pterr1_p1" violates partition constraint
+DETAIL: Failing row contains (10, 10).
+SCHEMA NAME: public
+TABLE NAME: pterr1_p1
+LOCATION: ExecPartitionCheckEmitError, execMain.c:1882
+-- conflict with default
+CREATE TABLE pterr1_default PARTITION OF pterr1 DEFAULT;
+INSERT INTO pterr1 VALUES (10, 10);
+CREATE TABLE pterr1_p2 PARTITION OF pterr1 FOR VALUES FROM (6) TO (20);
+ERROR: 23514: updated partition constraint for default partition "pterr1_default" would be violated by some row
+SCHEMA NAME: public
+TABLE NAME: pterr1_default
+LOCATION: check_default_partition_contents, partbounds.c:1370
+-- cleanup
+\set VERBOSITY default
+DROP TABLE pterr1, pterr1_default, pterr1_p1;
diff --git src/test/regress/parallel_schedule src/test/regress/parallel_schedule
index d2b17dd3ea..e1708c87ec 100644
--- src/test/regress/parallel_schedule
+++ src/test/regress/parallel_schedule
@@ -112,7 +112,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion tr
# ----------
# Another group of parallel tests
# ----------
-test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain
+test: partition_errors partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git src/test/regress/sql/partition_errors.sql src/test/regress/sql/partition_errors.sql
new file mode 100644
index 0000000000..7b9197d507
--- /dev/null
+++ src/test/regress/sql/partition_errors.sql
@@ -0,0 +1,23 @@
+--
+-- Tests for partition error fields
+--
+
+\set VERBOSITY verbose
+
+-- no partitions
+CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+INSERT INTO pterr1 VALUES (10, 10);
+
+-- outside the only partition
+CREATE TABLE pterr1_p1 PARTITION OF pterr1 FOR VALUES FROM (1) TO (5);
+INSERT INTO pterr1 VALUES (10, 10);
+INSERT INTO pterr1_p1 VALUES (10, 10);
+
+-- conflict with default
+CREATE TABLE pterr1_default PARTITION OF pterr1 DEFAULT;
+INSERT INTO pterr1 VALUES (10, 10);
+CREATE TABLE pterr1_p2 PARTITION OF pterr1 FOR VALUES FROM (6) TO (20);
+
+-- cleanup
+\set VERBOSITY default
+DROP TABLE pterr1, pterr1_default, pterr1_p1;
--
2.11.0
+\set VERBOSITY verbose +-- no partitions +CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y); +INSERT INTO pterr1 VALUES (10, 10); +ERROR: 23514: no partition of relation "pterr1" found for row +DETAIL: Partition key of the failing row contains (y) = (10). +SCHEMA NAME: public +TABLE NAME: pterr1 +LOCATION: ExecFindPartition, execPartition.c:349
This won't work well, because people would be forced to update the .out
file whenever the execPartition.c file changed to add or remove lines
before the one with the error call. Maybe if you want to verify the
schema/table names, use a plpgsql function to extract them, using
GET STACKED DIAGNOSTICS TABLE_NAME = ...
in an exception block?
I'm not sure that this *needs* to be tested, though. Don't we already
verify that errtable() works, elsewhere? I don't suppose you mean to
test that every single ereport() call that includes errtable() contains
a TABLE NAME item.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 3/3/20 10:08 AM, Alvaro Herrera wrote:
+\set VERBOSITY verbose +-- no partitions +CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y); +INSERT INTO pterr1 VALUES (10, 10); +ERROR: 23514: no partition of relation "pterr1" found for row +DETAIL: Partition key of the failing row contains (y) = (10). +SCHEMA NAME: public +TABLE NAME: pterr1 +LOCATION: ExecFindPartition, execPartition.c:349This won't work well, because people would be forced to update the .out
file whenever the execPartition.c file changed to add or remove lines
before the one with the error call.
I agree. I expected that and should have made it more clear that I
didn't intend for those tests to be committed. Others in the thread
suggested I include some form of test, even if it didn't live past
review. That being said...
Maybe if you want to verify the
schema/table names, use a plpgsql function to extract them, using
GET STACKED DIAGNOSTICS TABLE_NAME = ...
in an exception block?
This is a great idea and the result looks much cleaner than I expected.
I have no reservations about committing the attached tests.
I'm not sure that this *needs* to be tested, though. Don't we already
verify that errtable() works, elsewhere?
I looked for tests that might target errtable() or errtableconstraint()
but found none. Perhaps someone who knows the tests better could answer
this question.
I don't suppose you mean to
test that every single ereport() call that includes errtable() contains
a TABLE NAME item.
Correct. I intend only to test the few calls I'm touching in this
thread. It might be worthwhile for someone to perform a more thorough
review of existing errors, however. The documentation seems to say that
every error in SQLSTATE class 23 has one of these fields filled[1]https://www.postgresql.org/docs/current/errcodes-appendix.html. The
errors in these patches are in that class but lacked any fields.
[1]: https://www.postgresql.org/docs/current/errcodes-appendix.html
Thanks,
Chris
Attachments:
v3-0001-Add-schema-and-table-names-to-partition-errors.patchtext/x-patch; charset=UTF-8; name=v3-0001-Add-schema-and-table-names-to-partition-errors.patchDownload
From c6b39accf9f51f9c08a2fc62e848144776e23ffb Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Sat, 29 Feb 2020 12:47:56 -0600
Subject: [PATCH 1/2] Add schema and table names to partition errors
---
src/backend/commands/tablecmds.c | 6 ++++--
src/backend/executor/execMain.c | 3 ++-
src/backend/executor/execPartition.c | 3 ++-
src/backend/partitioning/partbounds.c | 3 ++-
4 files changed, 10 insertions(+), 5 deletions(-)
diff --git src/backend/commands/tablecmds.c src/backend/commands/tablecmds.c
index 02a7c04fdb..6a32812a1f 100644
--- src/backend/commands/tablecmds.c
+++ src/backend/commands/tablecmds.c
@@ -5342,12 +5342,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation \"%s\" is violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
}
/* Write the tuple out to the new relation */
diff --git src/backend/executor/execMain.c src/backend/executor/execMain.c
index ee5c3a60ff..7cb486b211 100644
--- src/backend/executor/execMain.c
+++ src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git src/backend/executor/execPartition.c src/backend/executor/execPartition.c
index c13b1d3501..a5542b92c7 100644
--- src/backend/executor/execPartition.c
+++ src/backend/executor/execPartition.c
@@ -345,7 +345,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
diff --git src/backend/partitioning/partbounds.c src/backend/partitioning/partbounds.c
index 35953f23fa..4c47f54a57 100644
--- src/backend/partitioning/partbounds.c
+++ src/backend/partitioning/partbounds.c
@@ -1366,7 +1366,8 @@ check_default_partition_contents(Relation parent, Relation default_rel,
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(default_rel))));
+ RelationGetRelationName(default_rel)),
+ errtable(default_rel)));
ResetExprContext(econtext);
CHECK_FOR_INTERRUPTS();
--
2.11.0
v3-0002-Tests-for-partition-error-fields.patchtext/x-patch; charset=UTF-8; name=v3-0002-Tests-for-partition-error-fields.patchDownload
From 19a259beeab29ca4a1398641c8e2dbdf5a3d548e Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Mon, 2 Mar 2020 22:13:28 -0600
Subject: [PATCH 2/2] Tests for partition error fields
---
src/test/regress/expected/partition_errors.out | 70 ++++++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/partition_errors.sql | 45 +++++++++++++++++
3 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 src/test/regress/expected/partition_errors.out
create mode 100644 src/test/regress/sql/partition_errors.sql
diff --git src/test/regress/expected/partition_errors.out src/test/regress/expected/partition_errors.out
new file mode 100644
index 0000000000..cbe77fd107
--- /dev/null
+++ src/test/regress/expected/partition_errors.out
@@ -0,0 +1,70 @@
+--
+-- Tests for partition error fields
+--
+\pset expanded on
+CREATE FUNCTION partition_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_schema text,
+ OUT err_table text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN OTHERS THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME;
+END;
+$$ LANGUAGE plpgsql;
+-- no partitions
+CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+-[ RECORD 1 ]+------------------------------------------------------
+err_sqlstate | 23514
+err_message | no partition of relation "pterr1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_schema | public
+err_table | pterr1
+
+-- outside the only partition
+CREATE TABLE pterr1_p1 PARTITION OF pterr1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+-[ RECORD 1 ]+------------------------------------------------------
+err_sqlstate | 23514
+err_message | no partition of relation "pterr1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_schema | public
+err_table | pterr1
+
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1_p1 VALUES (10, 10);
+$$);
+-[ RECORD 1 ]+---------------------------------------------------------------
+err_sqlstate | 23514
+err_message | new row for relation "pterr1_p1" violates partition constraint
+err_detail | Failing row contains (10, 10).
+err_schema | public
+err_table | pterr1_p1
+
+-- conflict with default
+CREATE TABLE pterr1_default PARTITION OF pterr1 DEFAULT;
+INSERT INTO pterr1 VALUES (10, 10);
+SELECT * FROM partition_error_record($$
+ CREATE TABLE pterr1_p2 PARTITION OF pterr1 FOR VALUES FROM (6) TO (20);
+$$);
+-[ RECORD 1 ]+--------------------------------------------------------------------------------------------------
+err_sqlstate | 23514
+err_message | updated partition constraint for default partition "pterr1_default" would be violated by some row
+err_detail |
+err_schema | public
+err_table | pterr1_default
+
diff --git src/test/regress/parallel_schedule src/test/regress/parallel_schedule
index d2b17dd3ea..e1708c87ec 100644
--- src/test/regress/parallel_schedule
+++ src/test/regress/parallel_schedule
@@ -112,7 +112,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion tr
# ----------
# Another group of parallel tests
# ----------
-test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain
+test: partition_errors partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git src/test/regress/sql/partition_errors.sql src/test/regress/sql/partition_errors.sql
new file mode 100644
index 0000000000..420d75994f
--- /dev/null
+++ src/test/regress/sql/partition_errors.sql
@@ -0,0 +1,45 @@
+--
+-- Tests for partition error fields
+--
+\pset expanded on
+CREATE FUNCTION partition_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_schema text,
+ OUT err_table text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN OTHERS THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME;
+END;
+$$ LANGUAGE plpgsql;
+
+-- no partitions
+CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+
+-- outside the only partition
+CREATE TABLE pterr1_p1 PARTITION OF pterr1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1_p1 VALUES (10, 10);
+$$);
+
+-- conflict with default
+CREATE TABLE pterr1_default PARTITION OF pterr1 DEFAULT;
+INSERT INTO pterr1 VALUES (10, 10);
+SELECT * FROM partition_error_record($$
+ CREATE TABLE pterr1_p2 PARTITION OF pterr1 FOR VALUES FROM (6) TO (20);
+$$);
--
2.11.0
On Wed, Mar 4, 2020 at 10:48 AM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/3/20 10:08 AM, Alvaro Herrera wrote:
+\set VERBOSITY verbose +-- no partitions +CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y); +INSERT INTO pterr1 VALUES (10, 10); +ERROR: 23514: no partition of relation "pterr1" found for row +DETAIL: Partition key of the failing row contains (y) = (10). +SCHEMA NAME: public +TABLE NAME: pterr1 +LOCATION: ExecFindPartition, execPartition.c:349This won't work well, because people would be forced to update the .out
file whenever the execPartition.c file changed to add or remove lines
before the one with the error call.I agree. I expected that and should have made it more clear that I
didn't intend for those tests to be committed. Others in the thread
suggested I include some form of test, even if it didn't live past
review.
Right, it is not for committing those tests, but rather once we try to
hit the code we are changing in this patch.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On 3/3/20 11:18 PM, Chris Bandy wrote:
On 3/3/20 10:08 AM, Alvaro Herrera wrote:
I don't suppose you mean to
test that every single ereport() call that includes errtable() contains
a TABLE NAME item.Correct. I intend only to test the few calls I'm touching in this
It might be worthwhile for someone to perform a more thorough
review of existing errors, however. The documentation seems to say that
every error in SQLSTATE class 23 has one of these fields filled[1]. The
errors in these patches are in that class but lacked any fields.[1] https://www.postgresql.org/docs/current/errcodes-appendix.html
By the power of grep I found another partition error that needed a
field. I'm pretty happy with the way the test turned out, so I've
squashed everything into a single patch.
I've also convinced myself that the number of integrity errors in the
entire codebase is manageable to test. If others think it is worthwhile,
I can spend some time over the next week to expand this test approach to
cover _all_ SQLSTATE class 23 errors.
If so,
* Should it be one regression test (file) that discusses the
significance of class 23, or
* Should it be a few test cases added to the existing test files related
to each feature?
The former allows the helper function to be defined once, while the
latter would repeat it over many files.
Thanks,
Chris
Attachments:
v4-0001-Add-object-names-to-partition-errors.patchtext/x-patch; charset=UTF-8; name=v4-0001-Add-object-names-to-partition-errors.patchDownload
From 5fbe19233ed734b52cec77d76e81282c7bf2360a Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Sat, 29 Feb 2020 12:47:56 -0600
Subject: [PATCH] Add object names to partition errors
All errors of SQLSTATE class 23 should include the name of an object
associated with the error.
---
src/backend/commands/tablecmds.c | 6 +-
src/backend/executor/execMain.c | 3 +-
src/backend/executor/execPartition.c | 3 +-
src/backend/partitioning/partbounds.c | 3 +-
src/backend/utils/adt/ri_triggers.c | 3 +-
src/test/regress/expected/partition_errors.out | 93 ++++++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/partition_errors.sql | 62 +++++++++++++++++
8 files changed, 168 insertions(+), 7 deletions(-)
create mode 100644 src/test/regress/expected/partition_errors.out
create mode 100644 src/test/regress/sql/partition_errors.sql
diff --git src/backend/commands/tablecmds.c src/backend/commands/tablecmds.c
index 02a7c04fdb..6a32812a1f 100644
--- src/backend/commands/tablecmds.c
+++ src/backend/commands/tablecmds.c
@@ -5342,12 +5342,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation \"%s\" is violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
}
/* Write the tuple out to the new relation */
diff --git src/backend/executor/execMain.c src/backend/executor/execMain.c
index 28130fbc2b..4fdffad6f3 100644
--- src/backend/executor/execMain.c
+++ src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git src/backend/executor/execPartition.c src/backend/executor/execPartition.c
index c13b1d3501..a5542b92c7 100644
--- src/backend/executor/execPartition.c
+++ src/backend/executor/execPartition.c
@@ -345,7 +345,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
diff --git src/backend/partitioning/partbounds.c src/backend/partitioning/partbounds.c
index 35953f23fa..4c47f54a57 100644
--- src/backend/partitioning/partbounds.c
+++ src/backend/partitioning/partbounds.c
@@ -1366,7 +1366,8 @@ check_default_partition_contents(Relation parent, Relation default_rel,
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(default_rel))));
+ RelationGetRelationName(default_rel)),
+ errtable(default_rel)));
ResetExprContext(econtext);
CHECK_FOR_INTERRUPTS();
diff --git src/backend/utils/adt/ri_triggers.c src/backend/utils/adt/ri_triggers.c
index 4ab7cda110..bb49e80d16 100644
--- src/backend/utils/adt/ri_triggers.c
+++ src/backend/utils/adt/ri_triggers.c
@@ -2452,7 +2452,8 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
NameStr(riinfo->conname)),
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
key_names.data, key_values.data,
- RelationGetRelationName(fk_rel))));
+ RelationGetRelationName(fk_rel)),
+ errtableconstraint(fk_rel, NameStr(riinfo->conname))));
else if (onfk)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
diff --git src/test/regress/expected/partition_errors.out src/test/regress/expected/partition_errors.out
new file mode 100644
index 0000000000..8f3da6c74f
--- /dev/null
+++ src/test/regress/expected/partition_errors.out
@@ -0,0 +1,93 @@
+--
+-- Tests for partition error fields
+--
+-- Some partition errors are emitted with SQLSTATE 23xxx. Such errors
+-- should include the name of a database object as a separate field.
+--
+-- The fields of interest are shown at the same verbosity level as
+-- volatile details such as source-code line numbers. To produce stable
+-- regression output, the following function returns a portion of the
+-- full error reported.
+\pset expanded on
+\pset tuples_only on
+CREATE FUNCTION partition_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_schema text,
+ OUT err_table text,
+ OUT err_constraint text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN integrity_constraint_violation THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME,
+ err_constraint := CONSTRAINT_NAME;
+END;
+$$ LANGUAGE plpgsql;
+-- no partitions
+CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | no partition of relation "pterr1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_schema | public
+err_table | pterr1
+err_constraint |
+
+-- outside the only partition
+CREATE TABLE pterr1_p1 PARTITION OF pterr1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | no partition of relation "pterr1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_schema | public
+err_table | pterr1
+err_constraint |
+
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1_p1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | new row for relation "pterr1_p1" violates partition constraint
+err_detail | Failing row contains (10, 10).
+err_schema | public
+err_table | pterr1_p1
+err_constraint |
+
+-- conflict with default
+CREATE TABLE pterr1_default PARTITION OF pterr1 DEFAULT;
+INSERT INTO pterr1 VALUES (10, 10);
+SELECT * FROM partition_error_record($$
+ CREATE TABLE pterr1_p2 PARTITION OF pterr1 FOR VALUES FROM (6) TO (20);
+$$);
+err_sqlstate | 23514
+err_message | updated partition constraint for default partition "pterr1_default" would be violated by some row
+err_detail |
+err_schema | public
+err_table | pterr1_default
+err_constraint |
+
+-- foreign key reference
+CREATE TABLE pterr2 (x int, y int, FOREIGN KEY (x, y) REFERENCES pterr1);
+INSERT INTO pterr2 VALUES (10, 10);
+SELECT * FROM partition_error_record($$
+ ALTER TABLE pterr1 DETACH PARTITION pterr1_default;
+$$);
+err_sqlstate | 23503
+err_message | removing partition "pterr1_default" violates foreign key constraint "pterr2_x_y_fkey2"
+err_detail | Key (x, y)=(10, 10) is still referenced from table "pterr2".
+err_schema | public
+err_table | pterr2
+err_constraint | pterr2_x_y_fkey2
+
diff --git src/test/regress/parallel_schedule src/test/regress/parallel_schedule
index d2b17dd3ea..e1708c87ec 100644
--- src/test/regress/parallel_schedule
+++ src/test/regress/parallel_schedule
@@ -112,7 +112,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion tr
# ----------
# Another group of parallel tests
# ----------
-test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain
+test: partition_errors partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git src/test/regress/sql/partition_errors.sql src/test/regress/sql/partition_errors.sql
new file mode 100644
index 0000000000..054ec2e059
--- /dev/null
+++ src/test/regress/sql/partition_errors.sql
@@ -0,0 +1,62 @@
+--
+-- Tests for partition error fields
+--
+-- Some partition errors are emitted with SQLSTATE 23xxx. Such errors
+-- should include the name of a database object as a separate field.
+--
+-- The fields of interest are shown at the same verbosity level as
+-- volatile details such as source-code line numbers. To produce stable
+-- regression output, the following function returns a portion of the
+-- full error reported.
+\pset expanded on
+\pset tuples_only on
+CREATE FUNCTION partition_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_schema text,
+ OUT err_table text,
+ OUT err_constraint text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN integrity_constraint_violation THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME,
+ err_constraint := CONSTRAINT_NAME;
+END;
+$$ LANGUAGE plpgsql;
+
+-- no partitions
+CREATE TABLE pterr1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+
+-- outside the only partition
+CREATE TABLE pterr1_p1 PARTITION OF pterr1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1 VALUES (10, 10);
+$$);
+SELECT * FROM partition_error_record($$
+ INSERT INTO pterr1_p1 VALUES (10, 10);
+$$);
+
+-- conflict with default
+CREATE TABLE pterr1_default PARTITION OF pterr1 DEFAULT;
+INSERT INTO pterr1 VALUES (10, 10);
+SELECT * FROM partition_error_record($$
+ CREATE TABLE pterr1_p2 PARTITION OF pterr1 FOR VALUES FROM (6) TO (20);
+$$);
+
+-- foreign key reference
+CREATE TABLE pterr2 (x int, y int, FOREIGN KEY (x, y) REFERENCES pterr1);
+INSERT INTO pterr2 VALUES (10, 10);
+SELECT * FROM partition_error_record($$
+ ALTER TABLE pterr1 DETACH PARTITION pterr1_default;
+$$);
--
2.11.0
On 3/4/20 2:54 AM, Chris Bandy wrote:
I've also convinced myself that the number of integrity errors in the
entire codebase is manageable to test. If others think it is worthwhile,
I can spend some time over the next week to expand this test approach to
cover _all_ SQLSTATE class 23 errors.
Done. Please find attached two patches that (1) test all but one reports
of integrity violations and (2) attach object names to the handful that
lacked them.
I decided to include error messages in the tests so that the next person
to change the message would be mindful of the attached fields and vice
versa. I thought these might be impacted by locale, but `make check
LANG=de_DE.utf8` passes for me. Is that command the right way to verify
that?
With these patches, behavior matches the documentation which states:
"[object] names are supplied in separate fields of the error report
message so that applications need not try to extract them from the
possibly-localized human-readable text of the message. As of PostgreSQL
9.3, complete coverage for this feature exists only for errors in
SQLSTATE class 23..."
Thanks,
Chris
Attachments:
v5-0001-Add-tests-for-integrity-violation-error-fields.patchtext/x-patch; charset=UTF-8; name=v5-0001-Add-tests-for-integrity-violation-error-fields.patchDownload
From 101bb413634f4be82a6d934660ceda99e4f4cc53 Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Fri, 6 Mar 2020 20:48:55 -0600
Subject: [PATCH 1/2] Add tests for integrity violation error fields
The documentation states that all errors of SQLSTATE class 23 should
include the name of an object associated with the error.
---
src/test/regress/expected/integrity_errors.out | 408 +++++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/integrity_errors.sql | 193 ++++++++++++
3 files changed, 602 insertions(+), 1 deletion(-)
create mode 100644 src/test/regress/expected/integrity_errors.out
create mode 100644 src/test/regress/sql/integrity_errors.sql
diff --git a/src/test/regress/expected/integrity_errors.out b/src/test/regress/expected/integrity_errors.out
new file mode 100644
index 0000000000..e75e6b722f
--- /dev/null
+++ b/src/test/regress/expected/integrity_errors.out
@@ -0,0 +1,408 @@
+--
+-- Tests for integrity violation error fields
+--
+-- Errors in SQLSTATE class 23 (integrity constraint violation) should
+-- include the name of a database object as a separate field.
+--
+-- The fields of interest are shown at the same verbosity level as
+-- volatile details such as source code line numbers. To produce stable
+-- regression output, the following function returns a portion of the
+-- full error reported.
+CREATE FUNCTION integrity_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_datatype text,
+ OUT err_schema text,
+ OUT err_table text,
+ OUT err_column text,
+ OUT err_constraint text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN integrity_constraint_violation THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_datatype := PG_DATATYPE_NAME,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME,
+ err_column := COLUMN_NAME,
+ err_constraint := CONSTRAINT_NAME;
+END;
+$$ LANGUAGE plpgsql;
+\pset expanded on
+\pset tuples_only on
+-- table not null
+CREATE TABLE ivnt1 (n int NOT NULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivnt1 VALUES (NULL);
+$$);
+err_sqlstate | 23502
+err_message | null value in column "n" of relation "ivnt1" violates not-null constraint
+err_detail | Failing row contains (null).
+err_datatype |
+err_schema | public
+err_table | ivnt1
+err_column | n
+err_constraint |
+
+-- alter table not null
+CREATE TABLE ivnt2 (n int);
+INSERT INTO ivnt2 VALUES (NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivnt2 ALTER n SET NOT NULL;
+$$);
+err_sqlstate | 23502
+err_message | column "n" of relation "ivnt2" contains null values
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivnt2
+err_column | n
+err_constraint |
+
+DROP TABLE ivnt1, ivnt2;
+-- table unique
+CREATE TABLE ivpkt1 (x int, y int, PRIMARY KEY (x, y));
+INSERT INTO ivpkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpkt1 VALUES (1, 2);
+$$);
+err_sqlstate | 23505
+err_message | duplicate key value violates unique constraint "ivpkt1_pkey"
+err_detail | Key (x, y)=(1, 2) already exists.
+err_datatype |
+err_schema | public
+err_table | ivpkt1
+err_column |
+err_constraint | ivpkt1_pkey
+
+-- alter table unique
+CREATE TABLE ivpkt2 (x int, y int);
+INSERT INTO ivpkt2 VALUES (1, 2), (1, 2);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpkt2 ADD PRIMARY KEY (x, y);
+$$);
+err_sqlstate | 23505
+err_message | could not create unique index "ivpkt2_pkey"
+err_detail | Key (x, y)=(1, 2) is duplicated.
+err_datatype |
+err_schema | public
+err_table | ivpkt2
+err_column |
+err_constraint | ivpkt2_pkey
+
+-- table foreign key reference
+CREATE TABLE ivfkt1 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt1 VALUES (10, 10);
+$$);
+err_sqlstate | 23503
+err_message | insert or update on table "ivfkt1" violates foreign key constraint "ivfkt1_x_y_fkey"
+err_detail | Key (x, y)=(10, 10) is not present in table "ivpkt1".
+err_datatype |
+err_schema | public
+err_table | ivfkt1
+err_column |
+err_constraint | ivfkt1_x_y_fkey
+
+INSERT INTO ivfkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ DELETE FROM ivpkt1;
+$$);
+err_sqlstate | 23503
+err_message | update or delete on table "ivpkt1" violates foreign key constraint "ivfkt1_x_y_fkey" on table "ivfkt1"
+err_detail | Key (x, y)=(1, 2) is still referenced from table "ivfkt1".
+err_datatype |
+err_schema | public
+err_table | ivfkt1
+err_column |
+err_constraint | ivfkt1_x_y_fkey
+
+-- foreign key reference match full
+CREATE TABLE ivfkt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt2 VALUES (1, NULL);
+$$);
+err_sqlstate | 23503
+err_message | insert or update on table "ivfkt2" violates foreign key constraint "ivfkt2_x_y_fkey"
+err_detail | MATCH FULL does not allow mixing of null and nonnull key values.
+err_datatype |
+err_schema | public
+err_table | ivfkt2
+err_column |
+err_constraint | ivfkt2_x_y_fkey
+
+CREATE TABLE ivfkt3 (x int, y int);
+INSERT INTO ivfkt3 VALUES (1, NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivfkt3 ADD FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL;
+$$);
+err_sqlstate | 23503
+err_message | insert or update on table "ivfkt3" violates foreign key constraint "ivfkt3_x_y_fkey"
+err_detail | MATCH FULL does not allow mixing of null and nonnull key values.
+err_datatype |
+err_schema | public
+err_table | ivfkt3
+err_column |
+err_constraint | ivfkt3_x_y_fkey
+
+DROP TABLE ivfkt1, ivfkt2, ivfkt3, ivpkt1, ivpkt2;
+-- table exclusion
+CREATE TABLE ivet1 (n int, EXCLUDE (n WITH =));
+INSERT INTO ivet1 VALUES (1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivet1 VALUES (1);
+$$);
+err_sqlstate | 23P01
+err_message | conflicting key value violates exclusion constraint "ivet1_n_excl"
+err_detail | Key (n)=(1) conflicts with existing key (n)=(1).
+err_datatype |
+err_schema | public
+err_table | ivet1
+err_column |
+err_constraint | ivet1_n_excl
+
+-- alter table exclusion
+CREATE TABLE ivet2 (n int);
+INSERT INTO ivet2 VALUES (1), (1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivet2 ADD EXCLUDE (n WITH =);
+$$);
+err_sqlstate | 23P01
+err_message | could not create exclusion constraint "ivet2_n_excl"
+err_detail | Key (n)=(1) conflicts with key (n)=(1).
+err_datatype |
+err_schema | public
+err_table | ivet2
+err_column |
+err_constraint | ivet2_n_excl
+
+DROP TABLE ivet1, ivet2;
+-- domain
+CREATE DOMAIN ivd1 int NOT NULL CHECK (VALUE < 5);
+CREATE TABLE ivdt1 (n ivd1);
+SELECT * FROM integrity_error_record($$
+ SELECT NULL::ivd1;
+$$);
+err_sqlstate | 23502
+err_message | domain ivd1 does not allow null values
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ SELECT 10::ivd1;
+$$);
+err_sqlstate | 23514
+err_message | value for domain ivd1 violates check constraint "ivd1_check"
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint | ivd1_check
+
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":null}');
+$$);
+err_sqlstate | 23502
+err_message | domain ivd1 does not allow null values
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":10}');
+$$);
+err_sqlstate | 23514
+err_message | value for domain ivd1 violates check constraint "ivd1_check"
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint | ivd1_check
+
+-- alter domain
+CREATE DOMAIN ivd2 int;
+CREATE TABLE ivdt2 (n ivd2);
+INSERT INTO ivdt2 VALUES (NULL), (10);
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 SET NOT NULL;
+$$);
+err_sqlstate | 23502
+err_message | column "n" of table "ivdt2" contains null values
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivdt2
+err_column | n
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 ADD CHECK (VALUE < 5);
+$$);
+err_sqlstate | 23514
+err_message | column "n" of table "ivdt2" contains values that violate the new constraint
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivdt2
+err_column | n
+err_constraint |
+
+DROP TABLE ivdt1, ivdt2;
+DROP DOMAIN ivd1, ivd2;
+-- table check
+CREATE TABLE ivct1 (n int, CHECK (n < 5));
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivct1 VALUES (10);
+$$);
+err_sqlstate | 23514
+err_message | new row for relation "ivct1" violates check constraint "ivct1_n_check"
+err_detail | Failing row contains (10).
+err_datatype |
+err_schema | public
+err_table | ivct1
+err_column |
+err_constraint | ivct1_n_check
+
+-- alter table check
+CREATE TABLE ivct2 (n int);
+INSERT INTO ivct2 VALUES (10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 ADD CHECK (n < 5);
+$$);
+err_sqlstate | 23514
+err_message | check constraint "ivct2_n_check" of relation "ivct2" is violated by some row
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivct2
+err_column |
+err_constraint | ivct2_n_check
+
+-- alter table validate check
+ALTER TABLE ivct2 ADD CONSTRAINT ivct2_check CHECK (n < 5) NOT VALID;
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 VALIDATE CONSTRAINT ivct2_check;
+$$);
+err_sqlstate | 23514
+err_message | check constraint "ivct2_check" of relation "ivct2" is violated by some row
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivct2
+err_column |
+err_constraint | ivct2_check
+
+DROP TABLE ivct1, ivct2;
+-- no partitions
+CREATE TABLE ivpt1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | no partition of relation "ivpt1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- partition constraint
+CREATE TABLE ivpt1_p1 PARTITION OF ivpt1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | no partition of relation "ivpt1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1_p1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | new row for relation "ivpt1_p1" violates partition constraint
+err_detail | Failing row contains (10, 10).
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- alter partition constraint
+CREATE TABLE ivpt1_p2 (LIKE ivpt1);
+INSERT INTO ivpt1_p2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p2 FOR VALUES FROM (6) TO (10);
+$$);
+err_sqlstate | 23514
+err_message | partition constraint of relation "ivpt1_p2" is violated by some row
+err_detail |
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- conflict with default partition
+CREATE TABLE ivpt1_default PARTITION OF ivpt1 DEFAULT;
+INSERT INTO ivpt1 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ CREATE TABLE ivpt1_p3 PARTITION OF ivpt1 FOR VALUES FROM (10) TO (20);
+$$);
+err_sqlstate | 23514
+err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
+err_detail |
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+CREATE TABLE ivpt1_p3 (LIKE ivpt1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p3 FOR VALUES FROM (10) TO (20);
+$$);
+err_sqlstate | 23514
+err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
+err_detail |
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- partition foreign key reference
+CREATE TABLE ivpt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpt1);
+INSERT INTO ivpt2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 DETACH PARTITION ivpt1_default;
+$$);
+err_sqlstate | 23503
+err_message | removing partition "ivpt1_default" violates foreign key constraint "ivpt2_x_y_fkey2"
+err_detail | Key (x, y)=(10, 10) is still referenced from table "ivpt2".
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+DROP TABLE ivpt1, ivpt1_p2, ivpt1_p2, ivpt1_p3, ivpt2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..4fc2b0b467 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -55,7 +55,7 @@ test: create_index create_index_spgist create_view index_including index_includi
# ----------
# Another group of parallel tests
# ----------
-test: create_aggregate create_function_3 create_cast constraints triggers select inherit typed_table vacuum drop_if_exists updatable_views roleattributes create_am hash_func errors
+test: create_aggregate create_function_3 create_cast constraints triggers select inherit typed_table vacuum drop_if_exists updatable_views roleattributes create_am hash_func errors integrity_errors
# ----------
# sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/sql/integrity_errors.sql b/src/test/regress/sql/integrity_errors.sql
new file mode 100644
index 0000000000..1616d216ff
--- /dev/null
+++ b/src/test/regress/sql/integrity_errors.sql
@@ -0,0 +1,193 @@
+--
+-- Tests for integrity violation error fields
+--
+-- Errors in SQLSTATE class 23 (integrity constraint violation) should
+-- include the name of a database object as a separate field.
+--
+-- The fields of interest are shown at the same verbosity level as
+-- volatile details such as source code line numbers. To produce stable
+-- regression output, the following function returns a portion of the
+-- full error reported.
+CREATE FUNCTION integrity_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_datatype text,
+ OUT err_schema text,
+ OUT err_table text,
+ OUT err_column text,
+ OUT err_constraint text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN integrity_constraint_violation THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_datatype := PG_DATATYPE_NAME,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME,
+ err_column := COLUMN_NAME,
+ err_constraint := CONSTRAINT_NAME;
+END;
+$$ LANGUAGE plpgsql;
+
+\pset expanded on
+\pset tuples_only on
+
+-- table not null
+CREATE TABLE ivnt1 (n int NOT NULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivnt1 VALUES (NULL);
+$$);
+
+-- alter table not null
+CREATE TABLE ivnt2 (n int);
+INSERT INTO ivnt2 VALUES (NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivnt2 ALTER n SET NOT NULL;
+$$);
+DROP TABLE ivnt1, ivnt2;
+
+-- table unique
+CREATE TABLE ivpkt1 (x int, y int, PRIMARY KEY (x, y));
+INSERT INTO ivpkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpkt1 VALUES (1, 2);
+$$);
+
+-- alter table unique
+CREATE TABLE ivpkt2 (x int, y int);
+INSERT INTO ivpkt2 VALUES (1, 2), (1, 2);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpkt2 ADD PRIMARY KEY (x, y);
+$$);
+
+-- table foreign key reference
+CREATE TABLE ivfkt1 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt1 VALUES (10, 10);
+$$);
+INSERT INTO ivfkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ DELETE FROM ivpkt1;
+$$);
+
+-- foreign key reference match full
+CREATE TABLE ivfkt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt2 VALUES (1, NULL);
+$$);
+CREATE TABLE ivfkt3 (x int, y int);
+INSERT INTO ivfkt3 VALUES (1, NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivfkt3 ADD FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL;
+$$);
+DROP TABLE ivfkt1, ivfkt2, ivfkt3, ivpkt1, ivpkt2;
+
+-- table exclusion
+CREATE TABLE ivet1 (n int, EXCLUDE (n WITH =));
+INSERT INTO ivet1 VALUES (1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivet1 VALUES (1);
+$$);
+
+-- alter table exclusion
+CREATE TABLE ivet2 (n int);
+INSERT INTO ivet2 VALUES (1), (1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivet2 ADD EXCLUDE (n WITH =);
+$$);
+DROP TABLE ivet1, ivet2;
+
+-- domain
+CREATE DOMAIN ivd1 int NOT NULL CHECK (VALUE < 5);
+CREATE TABLE ivdt1 (n ivd1);
+SELECT * FROM integrity_error_record($$
+ SELECT NULL::ivd1;
+$$);
+SELECT * FROM integrity_error_record($$
+ SELECT 10::ivd1;
+$$);
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":null}');
+$$);
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":10}');
+$$);
+
+-- alter domain
+CREATE DOMAIN ivd2 int;
+CREATE TABLE ivdt2 (n ivd2);
+INSERT INTO ivdt2 VALUES (NULL), (10);
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 SET NOT NULL;
+$$);
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 ADD CHECK (VALUE < 5);
+$$);
+DROP TABLE ivdt1, ivdt2;
+DROP DOMAIN ivd1, ivd2;
+
+-- table check
+CREATE TABLE ivct1 (n int, CHECK (n < 5));
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivct1 VALUES (10);
+$$);
+
+-- alter table check
+CREATE TABLE ivct2 (n int);
+INSERT INTO ivct2 VALUES (10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 ADD CHECK (n < 5);
+$$);
+
+-- alter table validate check
+ALTER TABLE ivct2 ADD CONSTRAINT ivct2_check CHECK (n < 5) NOT VALID;
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 VALIDATE CONSTRAINT ivct2_check;
+$$);
+DROP TABLE ivct1, ivct2;
+
+-- no partitions
+CREATE TABLE ivpt1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+
+-- partition constraint
+CREATE TABLE ivpt1_p1 PARTITION OF ivpt1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1_p1 VALUES (10, 10);
+$$);
+
+-- alter partition constraint
+CREATE TABLE ivpt1_p2 (LIKE ivpt1);
+INSERT INTO ivpt1_p2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p2 FOR VALUES FROM (6) TO (10);
+$$);
+
+-- conflict with default partition
+CREATE TABLE ivpt1_default PARTITION OF ivpt1 DEFAULT;
+INSERT INTO ivpt1 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ CREATE TABLE ivpt1_p3 PARTITION OF ivpt1 FOR VALUES FROM (10) TO (20);
+$$);
+CREATE TABLE ivpt1_p3 (LIKE ivpt1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p3 FOR VALUES FROM (10) TO (20);
+$$);
+
+-- partition foreign key reference
+CREATE TABLE ivpt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpt1);
+INSERT INTO ivpt2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 DETACH PARTITION ivpt1_default;
+$$);
+DROP TABLE ivpt1, ivpt1_p2, ivpt1_p2, ivpt1_p3, ivpt2;
--
2.11.0
v5-0002-Add-object-names-to-partition-integrity-violations.patchtext/x-patch; charset=UTF-8; name=v5-0002-Add-object-names-to-partition-integrity-violations.patchDownload
From 6f2ccc824c7ef82408a3c35ff73b9e0eb4c23486 Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Fri, 6 Mar 2020 21:02:52 -0600
Subject: [PATCH 2/2] Add object names to partition integrity violations
All errors of SQLSTATE class 23 should include the name of an object
associated with the error.
---
src/backend/commands/tablecmds.c | 6 ++++--
src/backend/executor/execMain.c | 3 ++-
src/backend/executor/execPartition.c | 3 ++-
src/backend/partitioning/partbounds.c | 3 ++-
src/backend/utils/adt/ri_triggers.c | 3 ++-
src/test/regress/expected/integrity_errors.out | 30 +++++++++++++-------------
6 files changed, 27 insertions(+), 21 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7a13b97164..054db26099 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5342,12 +5342,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation \"%s\" is violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
}
/* Write the tuple out to the new relation */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 28130fbc2b..4fdffad6f3 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index c13b1d3501..a5542b92c7 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -345,7 +345,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 35953f23fa..4c47f54a57 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -1366,7 +1366,8 @@ check_default_partition_contents(Relation parent, Relation default_rel,
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(default_rel))));
+ RelationGetRelationName(default_rel)),
+ errtable(default_rel)));
ResetExprContext(econtext);
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 4ab7cda110..bb49e80d16 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2452,7 +2452,8 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
NameStr(riinfo->conname)),
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
key_names.data, key_values.data,
- RelationGetRelationName(fk_rel))));
+ RelationGetRelationName(fk_rel)),
+ errtableconstraint(fk_rel, NameStr(riinfo->conname))));
else if (onfk)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
diff --git a/src/test/regress/expected/integrity_errors.out b/src/test/regress/expected/integrity_errors.out
index e75e6b722f..14796a99c9 100644
--- a/src/test/regress/expected/integrity_errors.out
+++ b/src/test/regress/expected/integrity_errors.out
@@ -316,8 +316,8 @@ err_sqlstate | 23514
err_message | no partition of relation "ivpt1" found for row
err_detail | Partition key of the failing row contains (y) = (10).
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1
err_column |
err_constraint |
@@ -330,8 +330,8 @@ err_sqlstate | 23514
err_message | no partition of relation "ivpt1" found for row
err_detail | Partition key of the failing row contains (y) = (10).
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1
err_column |
err_constraint |
@@ -342,8 +342,8 @@ err_sqlstate | 23514
err_message | new row for relation "ivpt1_p1" violates partition constraint
err_detail | Failing row contains (10, 10).
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_p1
err_column |
err_constraint |
@@ -357,8 +357,8 @@ err_sqlstate | 23514
err_message | partition constraint of relation "ivpt1_p2" is violated by some row
err_detail |
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_p2
err_column |
err_constraint |
@@ -372,8 +372,8 @@ err_sqlstate | 23514
err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
err_detail |
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_default
err_column |
err_constraint |
@@ -385,8 +385,8 @@ err_sqlstate | 23514
err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
err_detail |
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_default
err_column |
err_constraint |
@@ -400,9 +400,9 @@ err_sqlstate | 23503
err_message | removing partition "ivpt1_default" violates foreign key constraint "ivpt2_x_y_fkey2"
err_detail | Key (x, y)=(10, 10) is still referenced from table "ivpt2".
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt2
err_column |
-err_constraint |
+err_constraint | ivpt2_x_y_fkey2
DROP TABLE ivpt1, ivpt1_p2, ivpt1_p2, ivpt1_p3, ivpt2;
--
2.11.0
On Tue, Mar 3, 2020 at 10:05 AM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/1/20 10:09 PM, Amit Langote wrote:
Hi Chris,
On Mon, Mar 2, 2020 at 8:51 AM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/1/20 5:14 AM, Amit Kapila wrote:
On Sun, Mar 1, 2020 at 10:10 AM Amit Langote <amitlangote09@gmail.com> wrote:
There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:Maybe, better fix these too for completeness.
Another thing we might need to see is which of these can be
back-patched. We should also try to write the tests for cases we are
changing even if we don't want to commit those.I don't have any opinion on back-patching. Existing tests pass. I wasn't
able to find another test that checks the constraint field of errors.
There's a little bit in the tests for psql, but that is about the the
\errverbose functionality rather than specific errors and their fields.Actually, it's not a bad idea to use \errverbose to test this.
I've added a second patch with tests that cover three of the five errors
touched by the first patch. Rather than \errverbose, I simply \set
VERBOSITY verbose. I could not find a way to exclude the location field
from the output, so those lines will be likely be out of date soon--if
not already.I couldn't find a way to exercise the errors in tablecmds.c. Does anyone
know how to instigate a table rewrite that would violate partition
constraints? I tried:
When I tried to apply your patch on HEAD with patch -p1 <
<path_to_patch>, I am getting below errors
(Stripping trailing CRs from patch; use --binary to disable.)
can't find file to patch at input line 17
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
..
I have tried with git am as well, but it failed. I am not sure what
is the reason. Can you please once check at your end? Also, see, if
it applies till 9.5 as I think we should backpatch this.
IIUC, this patch is mainly to get the table name, schema name in case
of the error paths, so that your application can handle errors in case
partition constraint violation, right?
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
Amit,
On 3/11/20 6:29 AM, Amit Kapila wrote:
On Tue, Mar 3, 2020 at 10:05 AM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/1/20 10:09 PM, Amit Langote wrote:
Hi Chris,
On Mon, Mar 2, 2020 at 8:51 AM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/1/20 5:14 AM, Amit Kapila wrote:
On Sun, Mar 1, 2020 at 10:10 AM Amit Langote <amitlangote09@gmail.com> wrote:
There are couple more instances in src/backend/command/tablecmds.c
where partition constraint is checked:Maybe, better fix these too for completeness.
Another thing we might need to see is which of these can be
back-patched. We should also try to write the tests for cases we are
changing even if we don't want to commit those.I don't have any opinion on back-patching. Existing tests pass. I wasn't
able to find another test that checks the constraint field of errors.
There's a little bit in the tests for psql, but that is about the the
\errverbose functionality rather than specific errors and their fields.Actually, it's not a bad idea to use \errverbose to test this.
I've added a second patch with tests that cover three of the five errors
touched by the first patch. Rather than \errverbose, I simply \set
VERBOSITY verbose. I could not find a way to exclude the location field
from the output, so those lines will be likely be out of date soon--if
not already.I couldn't find a way to exercise the errors in tablecmds.c. Does anyone
know how to instigate a table rewrite that would violate partition
constraints? I tried:When I tried to apply your patch on HEAD with patch -p1 <
<path_to_patch>, I am getting below errors(Stripping trailing CRs from patch; use --binary to disable.)
can't find file to patch at input line 17
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
..I have tried with git am as well, but it failed. I am not sure what
is the reason. Can you please once check at your end?
Yes, sorry. This set (and v3 and v4) should work with -p0. Any following
patches from me will use the normal -p1.
Also, see, if
it applies till 9.5 as I think we should backpatch this.IIUC, this patch is mainly to get the table name, schema name in case
of the error paths, so that your application can handle errors in case
partition constraint violation, right?
Yes, that is correct. Which also means it doesn't apply to 9.5 (no
partitions!) Later in this thread I created a test that covers all
integrity violation errors.[1]/messages/by-id/0731def8-978e-0285-04ee-582762729b38@gmail.com *That* can be backpatched, if you'd like.
For an approach limited to partitions only, I recommend looking at v4
rather than v2 or v3.[2]/messages/by-id/7985cf2f-5082-22d9-1bb4-6b280150eeae@gmail.com
[1]: /messages/by-id/0731def8-978e-0285-04ee-582762729b38@gmail.com
[2]: /messages/by-id/7985cf2f-5082-22d9-1bb4-6b280150eeae@gmail.com
Thanks,
Chris
On Wed, Mar 11, 2020 at 8:51 PM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/11/20 6:29 AM, Amit Kapila wrote:
I have tried with git am as well, but it failed. I am not sure what
is the reason. Can you please once check at your end?Yes, sorry. This set (and v3 and v4) should work with -p0. Any following
patches from me will use the normal -p1.
Okay.
Also, see, if
it applies till 9.5 as I think we should backpatch this.IIUC, this patch is mainly to get the table name, schema name in case
of the error paths, so that your application can handle errors in case
partition constraint violation, right?Yes, that is correct. Which also means it doesn't apply to 9.5 (no
partitions!) Later in this thread I created a test that covers all
integrity violation errors.[1] *That* can be backpatched, if you'd like.For an approach limited to partitions only, I recommend looking at v4
rather than v2 or v3.[2]
It is strange that I didn't receive your email which has a v4 version.
I will look into it, but I don't think we need to add the tests for
error conditions. Those are good for testing, but I think if we start
adding tests for all error conditions, then it might increase the
number of tests that are not of very high value.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Thu, Mar 12, 2020 at 7:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Wed, Mar 11, 2020 at 8:51 PM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/11/20 6:29 AM, Amit Kapila wrote:
I have tried with git am as well, but it failed. I am not sure what
is the reason. Can you please once check at your end?Yes, sorry. This set (and v3 and v4) should work with -p0. Any following
patches from me will use the normal -p1.Okay.
I again tried the latest patch v5 both with -p1 and -p0, but it gives
an error while applying the patch. Can you send a patch that we can
apply with patch -p1 or git-am?
[1]: /messages/by-id/0731def8-978e-0285-04ee-582762729b38@gmail.com
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On 3/18/20 6:56 AM, Amit Kapila wrote:
On Thu, Mar 12, 2020 at 7:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Wed, Mar 11, 2020 at 8:51 PM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/11/20 6:29 AM, Amit Kapila wrote:
I have tried with git am as well, but it failed. I am not sure what
is the reason. Can you please once check at your end?Yes, sorry. This set (and v3 and v4) should work with -p0. Any following
patches from me will use the normal -p1.Okay.
I again tried the latest patch v5 both with -p1 and -p0, but it gives
an error while applying the patch. Can you send a patch that we can
apply with patch -p1 or git-am?[1] - /messages/by-id/0731def8-978e-0285-04ee-582762729b38@gmail.com
Sorry for these troubles. Attached are patches created using `git
format-patch -n -v6` on master at 487e9861d0.
Thanks,
Chris
Attachments:
v6-0001-Add-tests-for-integrity-violation-error-fields.patchtext/x-patch; charset=UTF-8; name=v6-0001-Add-tests-for-integrity-violation-error-fields.patchDownload
From 6e5e47dc9a816c8d3e3453759da5ea0dcbeea31a Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Fri, 6 Mar 2020 20:48:55 -0600
Subject: [PATCH v6 1/2] Add tests for integrity violation error fields
The documentation states that all errors of SQLSTATE class 23 should
include the name of an object associated with the error.
---
src/test/regress/expected/integrity_errors.out | 408 +++++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/integrity_errors.sql | 193 ++++++++++++
3 files changed, 602 insertions(+), 1 deletion(-)
create mode 100644 src/test/regress/expected/integrity_errors.out
create mode 100644 src/test/regress/sql/integrity_errors.sql
diff --git a/src/test/regress/expected/integrity_errors.out b/src/test/regress/expected/integrity_errors.out
new file mode 100644
index 0000000000..e75e6b722f
--- /dev/null
+++ b/src/test/regress/expected/integrity_errors.out
@@ -0,0 +1,408 @@
+--
+-- Tests for integrity violation error fields
+--
+-- Errors in SQLSTATE class 23 (integrity constraint violation) should
+-- include the name of a database object as a separate field.
+--
+-- The fields of interest are shown at the same verbosity level as
+-- volatile details such as source code line numbers. To produce stable
+-- regression output, the following function returns a portion of the
+-- full error reported.
+CREATE FUNCTION integrity_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_datatype text,
+ OUT err_schema text,
+ OUT err_table text,
+ OUT err_column text,
+ OUT err_constraint text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN integrity_constraint_violation THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_datatype := PG_DATATYPE_NAME,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME,
+ err_column := COLUMN_NAME,
+ err_constraint := CONSTRAINT_NAME;
+END;
+$$ LANGUAGE plpgsql;
+\pset expanded on
+\pset tuples_only on
+-- table not null
+CREATE TABLE ivnt1 (n int NOT NULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivnt1 VALUES (NULL);
+$$);
+err_sqlstate | 23502
+err_message | null value in column "n" of relation "ivnt1" violates not-null constraint
+err_detail | Failing row contains (null).
+err_datatype |
+err_schema | public
+err_table | ivnt1
+err_column | n
+err_constraint |
+
+-- alter table not null
+CREATE TABLE ivnt2 (n int);
+INSERT INTO ivnt2 VALUES (NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivnt2 ALTER n SET NOT NULL;
+$$);
+err_sqlstate | 23502
+err_message | column "n" of relation "ivnt2" contains null values
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivnt2
+err_column | n
+err_constraint |
+
+DROP TABLE ivnt1, ivnt2;
+-- table unique
+CREATE TABLE ivpkt1 (x int, y int, PRIMARY KEY (x, y));
+INSERT INTO ivpkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpkt1 VALUES (1, 2);
+$$);
+err_sqlstate | 23505
+err_message | duplicate key value violates unique constraint "ivpkt1_pkey"
+err_detail | Key (x, y)=(1, 2) already exists.
+err_datatype |
+err_schema | public
+err_table | ivpkt1
+err_column |
+err_constraint | ivpkt1_pkey
+
+-- alter table unique
+CREATE TABLE ivpkt2 (x int, y int);
+INSERT INTO ivpkt2 VALUES (1, 2), (1, 2);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpkt2 ADD PRIMARY KEY (x, y);
+$$);
+err_sqlstate | 23505
+err_message | could not create unique index "ivpkt2_pkey"
+err_detail | Key (x, y)=(1, 2) is duplicated.
+err_datatype |
+err_schema | public
+err_table | ivpkt2
+err_column |
+err_constraint | ivpkt2_pkey
+
+-- table foreign key reference
+CREATE TABLE ivfkt1 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt1 VALUES (10, 10);
+$$);
+err_sqlstate | 23503
+err_message | insert or update on table "ivfkt1" violates foreign key constraint "ivfkt1_x_y_fkey"
+err_detail | Key (x, y)=(10, 10) is not present in table "ivpkt1".
+err_datatype |
+err_schema | public
+err_table | ivfkt1
+err_column |
+err_constraint | ivfkt1_x_y_fkey
+
+INSERT INTO ivfkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ DELETE FROM ivpkt1;
+$$);
+err_sqlstate | 23503
+err_message | update or delete on table "ivpkt1" violates foreign key constraint "ivfkt1_x_y_fkey" on table "ivfkt1"
+err_detail | Key (x, y)=(1, 2) is still referenced from table "ivfkt1".
+err_datatype |
+err_schema | public
+err_table | ivfkt1
+err_column |
+err_constraint | ivfkt1_x_y_fkey
+
+-- foreign key reference match full
+CREATE TABLE ivfkt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt2 VALUES (1, NULL);
+$$);
+err_sqlstate | 23503
+err_message | insert or update on table "ivfkt2" violates foreign key constraint "ivfkt2_x_y_fkey"
+err_detail | MATCH FULL does not allow mixing of null and nonnull key values.
+err_datatype |
+err_schema | public
+err_table | ivfkt2
+err_column |
+err_constraint | ivfkt2_x_y_fkey
+
+CREATE TABLE ivfkt3 (x int, y int);
+INSERT INTO ivfkt3 VALUES (1, NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivfkt3 ADD FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL;
+$$);
+err_sqlstate | 23503
+err_message | insert or update on table "ivfkt3" violates foreign key constraint "ivfkt3_x_y_fkey"
+err_detail | MATCH FULL does not allow mixing of null and nonnull key values.
+err_datatype |
+err_schema | public
+err_table | ivfkt3
+err_column |
+err_constraint | ivfkt3_x_y_fkey
+
+DROP TABLE ivfkt1, ivfkt2, ivfkt3, ivpkt1, ivpkt2;
+-- table exclusion
+CREATE TABLE ivet1 (n int, EXCLUDE (n WITH =));
+INSERT INTO ivet1 VALUES (1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivet1 VALUES (1);
+$$);
+err_sqlstate | 23P01
+err_message | conflicting key value violates exclusion constraint "ivet1_n_excl"
+err_detail | Key (n)=(1) conflicts with existing key (n)=(1).
+err_datatype |
+err_schema | public
+err_table | ivet1
+err_column |
+err_constraint | ivet1_n_excl
+
+-- alter table exclusion
+CREATE TABLE ivet2 (n int);
+INSERT INTO ivet2 VALUES (1), (1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivet2 ADD EXCLUDE (n WITH =);
+$$);
+err_sqlstate | 23P01
+err_message | could not create exclusion constraint "ivet2_n_excl"
+err_detail | Key (n)=(1) conflicts with key (n)=(1).
+err_datatype |
+err_schema | public
+err_table | ivet2
+err_column |
+err_constraint | ivet2_n_excl
+
+DROP TABLE ivet1, ivet2;
+-- domain
+CREATE DOMAIN ivd1 int NOT NULL CHECK (VALUE < 5);
+CREATE TABLE ivdt1 (n ivd1);
+SELECT * FROM integrity_error_record($$
+ SELECT NULL::ivd1;
+$$);
+err_sqlstate | 23502
+err_message | domain ivd1 does not allow null values
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ SELECT 10::ivd1;
+$$);
+err_sqlstate | 23514
+err_message | value for domain ivd1 violates check constraint "ivd1_check"
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint | ivd1_check
+
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":null}');
+$$);
+err_sqlstate | 23502
+err_message | domain ivd1 does not allow null values
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":10}');
+$$);
+err_sqlstate | 23514
+err_message | value for domain ivd1 violates check constraint "ivd1_check"
+err_detail |
+err_datatype | ivd1
+err_schema | public
+err_table |
+err_column |
+err_constraint | ivd1_check
+
+-- alter domain
+CREATE DOMAIN ivd2 int;
+CREATE TABLE ivdt2 (n ivd2);
+INSERT INTO ivdt2 VALUES (NULL), (10);
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 SET NOT NULL;
+$$);
+err_sqlstate | 23502
+err_message | column "n" of table "ivdt2" contains null values
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivdt2
+err_column | n
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 ADD CHECK (VALUE < 5);
+$$);
+err_sqlstate | 23514
+err_message | column "n" of table "ivdt2" contains values that violate the new constraint
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivdt2
+err_column | n
+err_constraint |
+
+DROP TABLE ivdt1, ivdt2;
+DROP DOMAIN ivd1, ivd2;
+-- table check
+CREATE TABLE ivct1 (n int, CHECK (n < 5));
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivct1 VALUES (10);
+$$);
+err_sqlstate | 23514
+err_message | new row for relation "ivct1" violates check constraint "ivct1_n_check"
+err_detail | Failing row contains (10).
+err_datatype |
+err_schema | public
+err_table | ivct1
+err_column |
+err_constraint | ivct1_n_check
+
+-- alter table check
+CREATE TABLE ivct2 (n int);
+INSERT INTO ivct2 VALUES (10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 ADD CHECK (n < 5);
+$$);
+err_sqlstate | 23514
+err_message | check constraint "ivct2_n_check" of relation "ivct2" is violated by some row
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivct2
+err_column |
+err_constraint | ivct2_n_check
+
+-- alter table validate check
+ALTER TABLE ivct2 ADD CONSTRAINT ivct2_check CHECK (n < 5) NOT VALID;
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 VALIDATE CONSTRAINT ivct2_check;
+$$);
+err_sqlstate | 23514
+err_message | check constraint "ivct2_check" of relation "ivct2" is violated by some row
+err_detail |
+err_datatype |
+err_schema | public
+err_table | ivct2
+err_column |
+err_constraint | ivct2_check
+
+DROP TABLE ivct1, ivct2;
+-- no partitions
+CREATE TABLE ivpt1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | no partition of relation "ivpt1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- partition constraint
+CREATE TABLE ivpt1_p1 PARTITION OF ivpt1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | no partition of relation "ivpt1" found for row
+err_detail | Partition key of the failing row contains (y) = (10).
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1_p1 VALUES (10, 10);
+$$);
+err_sqlstate | 23514
+err_message | new row for relation "ivpt1_p1" violates partition constraint
+err_detail | Failing row contains (10, 10).
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- alter partition constraint
+CREATE TABLE ivpt1_p2 (LIKE ivpt1);
+INSERT INTO ivpt1_p2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p2 FOR VALUES FROM (6) TO (10);
+$$);
+err_sqlstate | 23514
+err_message | partition constraint of relation "ivpt1_p2" is violated by some row
+err_detail |
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- conflict with default partition
+CREATE TABLE ivpt1_default PARTITION OF ivpt1 DEFAULT;
+INSERT INTO ivpt1 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ CREATE TABLE ivpt1_p3 PARTITION OF ivpt1 FOR VALUES FROM (10) TO (20);
+$$);
+err_sqlstate | 23514
+err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
+err_detail |
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+CREATE TABLE ivpt1_p3 (LIKE ivpt1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p3 FOR VALUES FROM (10) TO (20);
+$$);
+err_sqlstate | 23514
+err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
+err_detail |
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+-- partition foreign key reference
+CREATE TABLE ivpt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpt1);
+INSERT INTO ivpt2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 DETACH PARTITION ivpt1_default;
+$$);
+err_sqlstate | 23503
+err_message | removing partition "ivpt1_default" violates foreign key constraint "ivpt2_x_y_fkey2"
+err_detail | Key (x, y)=(10, 10) is still referenced from table "ivpt2".
+err_datatype |
+err_schema |
+err_table |
+err_column |
+err_constraint |
+
+DROP TABLE ivpt1, ivpt1_p2, ivpt1_p2, ivpt1_p3, ivpt2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..4fc2b0b467 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -55,7 +55,7 @@ test: create_index create_index_spgist create_view index_including index_includi
# ----------
# Another group of parallel tests
# ----------
-test: create_aggregate create_function_3 create_cast constraints triggers select inherit typed_table vacuum drop_if_exists updatable_views roleattributes create_am hash_func errors
+test: create_aggregate create_function_3 create_cast constraints triggers select inherit typed_table vacuum drop_if_exists updatable_views roleattributes create_am hash_func errors integrity_errors
# ----------
# sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/sql/integrity_errors.sql b/src/test/regress/sql/integrity_errors.sql
new file mode 100644
index 0000000000..1616d216ff
--- /dev/null
+++ b/src/test/regress/sql/integrity_errors.sql
@@ -0,0 +1,193 @@
+--
+-- Tests for integrity violation error fields
+--
+-- Errors in SQLSTATE class 23 (integrity constraint violation) should
+-- include the name of a database object as a separate field.
+--
+-- The fields of interest are shown at the same verbosity level as
+-- volatile details such as source code line numbers. To produce stable
+-- regression output, the following function returns a portion of the
+-- full error reported.
+CREATE FUNCTION integrity_error_record(
+ dml text,
+ OUT err_sqlstate text,
+ OUT err_message text,
+ OUT err_detail text,
+ OUT err_datatype text,
+ OUT err_schema text,
+ OUT err_table text,
+ OUT err_column text,
+ OUT err_constraint text)
+AS $$
+BEGIN
+ EXECUTE $1;
+EXCEPTION
+ WHEN integrity_constraint_violation THEN GET STACKED DIAGNOSTICS
+ err_sqlstate := RETURNED_SQLSTATE,
+ err_message := MESSAGE_TEXT,
+ err_detail := PG_EXCEPTION_DETAIL,
+ err_datatype := PG_DATATYPE_NAME,
+ err_schema := SCHEMA_NAME,
+ err_table := TABLE_NAME,
+ err_column := COLUMN_NAME,
+ err_constraint := CONSTRAINT_NAME;
+END;
+$$ LANGUAGE plpgsql;
+
+\pset expanded on
+\pset tuples_only on
+
+-- table not null
+CREATE TABLE ivnt1 (n int NOT NULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivnt1 VALUES (NULL);
+$$);
+
+-- alter table not null
+CREATE TABLE ivnt2 (n int);
+INSERT INTO ivnt2 VALUES (NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivnt2 ALTER n SET NOT NULL;
+$$);
+DROP TABLE ivnt1, ivnt2;
+
+-- table unique
+CREATE TABLE ivpkt1 (x int, y int, PRIMARY KEY (x, y));
+INSERT INTO ivpkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpkt1 VALUES (1, 2);
+$$);
+
+-- alter table unique
+CREATE TABLE ivpkt2 (x int, y int);
+INSERT INTO ivpkt2 VALUES (1, 2), (1, 2);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpkt2 ADD PRIMARY KEY (x, y);
+$$);
+
+-- table foreign key reference
+CREATE TABLE ivfkt1 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt1 VALUES (10, 10);
+$$);
+INSERT INTO ivfkt1 VALUES (1, 2);
+SELECT * FROM integrity_error_record($$
+ DELETE FROM ivpkt1;
+$$);
+
+-- foreign key reference match full
+CREATE TABLE ivfkt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivfkt2 VALUES (1, NULL);
+$$);
+CREATE TABLE ivfkt3 (x int, y int);
+INSERT INTO ivfkt3 VALUES (1, NULL);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivfkt3 ADD FOREIGN KEY (x, y) REFERENCES ivpkt1 MATCH FULL;
+$$);
+DROP TABLE ivfkt1, ivfkt2, ivfkt3, ivpkt1, ivpkt2;
+
+-- table exclusion
+CREATE TABLE ivet1 (n int, EXCLUDE (n WITH =));
+INSERT INTO ivet1 VALUES (1);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivet1 VALUES (1);
+$$);
+
+-- alter table exclusion
+CREATE TABLE ivet2 (n int);
+INSERT INTO ivet2 VALUES (1), (1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivet2 ADD EXCLUDE (n WITH =);
+$$);
+DROP TABLE ivet1, ivet2;
+
+-- domain
+CREATE DOMAIN ivd1 int NOT NULL CHECK (VALUE < 5);
+CREATE TABLE ivdt1 (n ivd1);
+SELECT * FROM integrity_error_record($$
+ SELECT NULL::ivd1;
+$$);
+SELECT * FROM integrity_error_record($$
+ SELECT 10::ivd1;
+$$);
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":null}');
+$$);
+SELECT * FROM integrity_error_record($$
+ SELECT json_populate_record(NULL::ivdt1, '{"n":10}');
+$$);
+
+-- alter domain
+CREATE DOMAIN ivd2 int;
+CREATE TABLE ivdt2 (n ivd2);
+INSERT INTO ivdt2 VALUES (NULL), (10);
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 SET NOT NULL;
+$$);
+SELECT * FROM integrity_error_record($$
+ ALTER DOMAIN ivd2 ADD CHECK (VALUE < 5);
+$$);
+DROP TABLE ivdt1, ivdt2;
+DROP DOMAIN ivd1, ivd2;
+
+-- table check
+CREATE TABLE ivct1 (n int, CHECK (n < 5));
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivct1 VALUES (10);
+$$);
+
+-- alter table check
+CREATE TABLE ivct2 (n int);
+INSERT INTO ivct2 VALUES (10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 ADD CHECK (n < 5);
+$$);
+
+-- alter table validate check
+ALTER TABLE ivct2 ADD CONSTRAINT ivct2_check CHECK (n < 5) NOT VALID;
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivct2 VALIDATE CONSTRAINT ivct2_check;
+$$);
+DROP TABLE ivct1, ivct2;
+
+-- no partitions
+CREATE TABLE ivpt1 (x int, y int, PRIMARY KEY (x, y)) PARTITION BY RANGE (y);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+
+-- partition constraint
+CREATE TABLE ivpt1_p1 PARTITION OF ivpt1 FOR VALUES FROM (1) TO (5);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1 VALUES (10, 10);
+$$);
+SELECT * FROM integrity_error_record($$
+ INSERT INTO ivpt1_p1 VALUES (10, 10);
+$$);
+
+-- alter partition constraint
+CREATE TABLE ivpt1_p2 (LIKE ivpt1);
+INSERT INTO ivpt1_p2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p2 FOR VALUES FROM (6) TO (10);
+$$);
+
+-- conflict with default partition
+CREATE TABLE ivpt1_default PARTITION OF ivpt1 DEFAULT;
+INSERT INTO ivpt1 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ CREATE TABLE ivpt1_p3 PARTITION OF ivpt1 FOR VALUES FROM (10) TO (20);
+$$);
+CREATE TABLE ivpt1_p3 (LIKE ivpt1);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 ATTACH PARTITION ivpt1_p3 FOR VALUES FROM (10) TO (20);
+$$);
+
+-- partition foreign key reference
+CREATE TABLE ivpt2 (x int, y int, FOREIGN KEY (x, y) REFERENCES ivpt1);
+INSERT INTO ivpt2 VALUES (10, 10);
+SELECT * FROM integrity_error_record($$
+ ALTER TABLE ivpt1 DETACH PARTITION ivpt1_default;
+$$);
+DROP TABLE ivpt1, ivpt1_p2, ivpt1_p2, ivpt1_p3, ivpt2;
--
2.11.0
v6-0002-Add-object-names-to-partition-integrity-violation.patchtext/x-patch; charset=UTF-8; name=v6-0002-Add-object-names-to-partition-integrity-violation.patchDownload
From 6e65ef7d5cb1f8b722b0fabad8b2e4b0e965563c Mon Sep 17 00:00:00 2001
From: Chris Bandy <bandy.chris@gmail.com>
Date: Fri, 6 Mar 2020 21:02:52 -0600
Subject: [PATCH v6 2/2] Add object names to partition integrity violations
All errors of SQLSTATE class 23 should include the name of an object
associated with the error.
---
src/backend/commands/tablecmds.c | 6 ++++--
src/backend/executor/execMain.c | 3 ++-
src/backend/executor/execPartition.c | 3 ++-
src/backend/partitioning/partbounds.c | 3 ++-
src/backend/utils/adt/ri_triggers.c | 3 ++-
src/test/regress/expected/integrity_errors.out | 30 +++++++++++++-------------
6 files changed, 27 insertions(+), 21 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 729025470d..8e35c5bd1a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5343,12 +5343,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation \"%s\" is violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
}
/* Write the tuple out to the new relation */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 28130fbc2b..4fdffad6f3 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index ef74ad85ff..fb6ce49056 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -346,7 +346,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 35953f23fa..4c47f54a57 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -1366,7 +1366,8 @@ check_default_partition_contents(Relation parent, Relation default_rel,
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(default_rel))));
+ RelationGetRelationName(default_rel)),
+ errtable(default_rel)));
ResetExprContext(econtext);
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 4ab7cda110..bb49e80d16 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2452,7 +2452,8 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
NameStr(riinfo->conname)),
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
key_names.data, key_values.data,
- RelationGetRelationName(fk_rel))));
+ RelationGetRelationName(fk_rel)),
+ errtableconstraint(fk_rel, NameStr(riinfo->conname))));
else if (onfk)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
diff --git a/src/test/regress/expected/integrity_errors.out b/src/test/regress/expected/integrity_errors.out
index e75e6b722f..14796a99c9 100644
--- a/src/test/regress/expected/integrity_errors.out
+++ b/src/test/regress/expected/integrity_errors.out
@@ -316,8 +316,8 @@ err_sqlstate | 23514
err_message | no partition of relation "ivpt1" found for row
err_detail | Partition key of the failing row contains (y) = (10).
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1
err_column |
err_constraint |
@@ -330,8 +330,8 @@ err_sqlstate | 23514
err_message | no partition of relation "ivpt1" found for row
err_detail | Partition key of the failing row contains (y) = (10).
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1
err_column |
err_constraint |
@@ -342,8 +342,8 @@ err_sqlstate | 23514
err_message | new row for relation "ivpt1_p1" violates partition constraint
err_detail | Failing row contains (10, 10).
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_p1
err_column |
err_constraint |
@@ -357,8 +357,8 @@ err_sqlstate | 23514
err_message | partition constraint of relation "ivpt1_p2" is violated by some row
err_detail |
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_p2
err_column |
err_constraint |
@@ -372,8 +372,8 @@ err_sqlstate | 23514
err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
err_detail |
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_default
err_column |
err_constraint |
@@ -385,8 +385,8 @@ err_sqlstate | 23514
err_message | updated partition constraint for default partition "ivpt1_default" would be violated by some row
err_detail |
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt1_default
err_column |
err_constraint |
@@ -400,9 +400,9 @@ err_sqlstate | 23503
err_message | removing partition "ivpt1_default" violates foreign key constraint "ivpt2_x_y_fkey2"
err_detail | Key (x, y)=(10, 10) is still referenced from table "ivpt2".
err_datatype |
-err_schema |
-err_table |
+err_schema | public
+err_table | ivpt2
err_column |
-err_constraint |
+err_constraint | ivpt2_x_y_fkey2
DROP TABLE ivpt1, ivpt1_p2, ivpt1_p2, ivpt1_p3, ivpt2;
--
2.11.0
On Thu, Mar 19, 2020 at 3:55 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Sorry for these troubles. Attached are patches created using `git
format-patch -n -v6` on master at 487e9861d0.
No problem. I have extracted your code changes as a separate patch
(see attached) as I am not sure we want to add tests for these cases.
This doesn't apply in back-branches, but I think that is small work
and we can do that if required. The real question is do we want to
back-patch this? Basically, this improves the errors in certain cases
by providing additional information that otherwise the user might need
to extract from error messages. So, there doesn't seem to be pressing
need to back-patch this but OTOH, we have mentioned in docs that we
support to display this information for all SQLSTATE class 23
(integrity constraint violation) errors which is not true as we forgot
to adhere to that in some parts of code.
What do you think? Anybody else has an opinion on whether to
back-patch this or not?
[1]: https://www.postgresql.org/docs/devel/errcodes-appendix.html
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
Attachments:
v7-0001-Add-object-names-to-partition-integrity-violation.patchapplication/octet-stream; name=v7-0001-Add-object-names-to-partition-integrity-violation.patchDownload
From a414bc232ff3b161478e987b41dfe35f175ddc62 Mon Sep 17 00:00:00 2001
From: Amit Kapila <akapila@postgresql.org>
Date: Thu, 19 Mar 2020 09:30:13 +0530
Subject: [PATCH v7] Add object names to partition integrity violations.
All errors of SQLSTATE class 23 should include the name of an object
associated with the error in separate fields of the error report message.
We do this so that applications need not try to extract them from the
possibly-localized human-readable text of the message.
Reported-by: Chris Bandy
Author: Chris Bandy
Reviewed-by: Amit Kapila and Amit Langote
Discussion: https://postgr.es/m/0aa113a3-3c7f-db48-bcd8-f9290b2269ae@gmail.com
---
src/backend/commands/tablecmds.c | 6 ++++--
src/backend/executor/execMain.c | 3 ++-
src/backend/executor/execPartition.c | 3 ++-
src/backend/partitioning/partbounds.c | 3 ++-
src/backend/utils/adt/ri_triggers.c | 3 ++-
5 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7290254..8e35c5b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5343,12 +5343,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
else
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint of relation \"%s\" is violated by some row",
- RelationGetRelationName(oldrel))));
+ RelationGetRelationName(oldrel)),
+ errtable(oldrel)));
}
/* Write the tuple out to the new relation */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 28130fb..4fdffad 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1878,7 +1878,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("new row for relation \"%s\" violates partition constraint",
RelationGetRelationName(resultRelInfo->ri_RelationDesc)),
- val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
+ val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+ errtable(resultRelInfo->ri_RelationDesc)));
}
/*
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index ef74ad8..fb6ce49 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -346,7 +346,8 @@ ExecFindPartition(ModifyTableState *mtstate,
RelationGetRelationName(rel)),
val_desc ?
errdetail("Partition key of the failing row contains %s.",
- val_desc) : 0));
+ val_desc) : 0,
+ errtable(rel)));
}
if (partdesc->is_leaf[partidx])
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 35953f2..4c47f54 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -1366,7 +1366,8 @@ check_default_partition_contents(Relation parent, Relation default_rel,
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
- RelationGetRelationName(default_rel))));
+ RelationGetRelationName(default_rel)),
+ errtable(default_rel)));
ResetExprContext(econtext);
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 4ab7cda..bb49e80 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2452,7 +2452,8 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
NameStr(riinfo->conname)),
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
key_names.data, key_values.data,
- RelationGetRelationName(fk_rel))));
+ RelationGetRelationName(fk_rel)),
+ errtableconstraint(fk_rel, NameStr(riinfo->conname))));
else if (onfk)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
--
1.8.3.1
Thank you Chris, Amit.
On Thu, Mar 19, 2020 at 1:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Thu, Mar 19, 2020 at 3:55 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Sorry for these troubles. Attached are patches created using `git
format-patch -n -v6` on master at 487e9861d0.No problem. I have extracted your code changes as a separate patch
(see attached) as I am not sure we want to add tests for these cases.
This doesn't apply in back-branches, but I think that is small work
and we can do that if required. The real question is do we want to
back-patch this? Basically, this improves the errors in certain cases
by providing additional information that otherwise the user might need
to extract from error messages. So, there doesn't seem to be pressing
need to back-patch this but OTOH, we have mentioned in docs that we
support to display this information for all SQLSTATE class 23
(integrity constraint violation) errors which is not true as we forgot
to adhere to that in some parts of code.What do you think? Anybody else has an opinion on whether to
back-patch this or not?
As nobody except Chris complained about this so far, maybe no?
--
Thank you,
Amit
On 3/18/20 11:46 PM, Amit Kapila wrote:
On Thu, Mar 19, 2020 at 3:55 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Sorry for these troubles. Attached are patches created using `git
format-patch -n -v6` on master at 487e9861d0.No problem. I have extracted your code changes as a separate patch
(see attached) as I am not sure we want to add tests for these cases.
Patch looks good.
My last pitch to keep the tests: These would be the first and only
automated tests that verify errtable, errtableconstraint, etc.
This doesn't apply in back-branches, but I think that is small work
and we can do that if required.
It looks like the only failing hunk on REL_12_STABLE is in tablecmds.c.
The ereport is near line 5090 there. The partition code has changed
quite a bit compared the older branches. ;-)
Thanks,
Chris
On Thu, Mar 19, 2020 at 8:21 PM Chris Bandy <bandy.chris@gmail.com> wrote:
On 3/18/20 11:46 PM, Amit Kapila wrote:
On Thu, Mar 19, 2020 at 3:55 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Sorry for these troubles. Attached are patches created using `git
format-patch -n -v6` on master at 487e9861d0.No problem. I have extracted your code changes as a separate patch
(see attached) as I am not sure we want to add tests for these cases.Patch looks good.
My last pitch to keep the tests: These would be the first and only
automated tests that verify errtable, errtableconstraint, etc.
I don't object to those tests. However, I don't feel adding just for
this patch is advisable. I suggest you start a new thread for these
tests and let us see what others think about them.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Thu, Mar 19, 2020 at 3:34 PM Amit Langote <amitlangote09@gmail.com> wrote:
Thank you Chris, Amit.
On Thu, Mar 19, 2020 at 1:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Thu, Mar 19, 2020 at 3:55 AM Chris Bandy <bandy.chris@gmail.com> wrote:
Sorry for these troubles. Attached are patches created using `git
format-patch -n -v6` on master at 487e9861d0.No problem. I have extracted your code changes as a separate patch
(see attached) as I am not sure we want to add tests for these cases.
This doesn't apply in back-branches, but I think that is small work
and we can do that if required. The real question is do we want to
back-patch this? Basically, this improves the errors in certain cases
by providing additional information that otherwise the user might need
to extract from error messages. So, there doesn't seem to be pressing
need to back-patch this but OTOH, we have mentioned in docs that we
support to display this information for all SQLSTATE class 23
(integrity constraint violation) errors which is not true as we forgot
to adhere to that in some parts of code.What do you think? Anybody else has an opinion on whether to
back-patch this or not?As nobody except Chris complained about this so far, maybe no?
Fair enough, unless I see any other opinions, I will push this on Monday.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Fri, Mar 20, 2020 at 12:22 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Thu, Mar 19, 2020 at 3:34 PM Amit Langote <amitlangote09@gmail.com> wrote:
What do you think? Anybody else has an opinion on whether to
back-patch this or not?As nobody except Chris complained about this so far, maybe no?
Fair enough, unless I see any other opinions, I will push this on Monday.
Pushed.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com