Report error position in partition bound check
Hi,
I'm playing with partitioned tables and found a minor thing with the
error reporting of bounds checking when create partitions.
In function check_new_partition_bound(), there are three places where
we call ereport() with a parser_errposition(pstate, spec->location)
argument. However, that pstate is a dummy ParseState made from NULL,
so the error message never reports the position of the error in the
source query line.
I have attached a patch to pass in a ParseState to
check_new_partition_bound() to enable the reporting of the error
position. Below is what the error message looks like before and after
applying the patch.
-- Create parent table
create table foo (a int, b date) partition by range (b);
-- Before:
create table foo_part_1 partition of foo for values from (date
'2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to
upper bound ('2006-01-01').
-- After:
create table foo_part_1 partition of foo for values from (date
'2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
LINE 1: ...eate table foo_part_1 partition of foo for values from (date...
^
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to
upper bound ('2006-01-01').
Another option is to not pass the parser_errposition() argument at all
to ereport() in this function, since the query is relatively short and
the error message is already descriptive enough.
Alex and Ashwin
Attachments:
0001-Report-error-position-in-partition-bound-check.patchtext/x-patch; charset=US-ASCII; name=0001-Report-error-position-in-partition-bound-check.patchDownload
From b1aedddaf7ae0f609910ff82af136d93c0132c2e Mon Sep 17 00:00:00 2001
From: Alexandra Wang <lewang@pivotal.io>
Date: Wed, 8 Apr 2020 16:07:28 -0700
Subject: [PATCH] Report error position in partition bound check
We have been passing a dummy ParseState to ereport(). Without the source
text in the ParseState ereport does not report the error position even
if a error location is supplied. This patch passes a ParseState to
check_new_partition_bound() when it is available.
-- Create parent table
create table foo (a int, b date) partition by range (b);
-- Before:
create table foo_part_1 partition of foo for values from (date '2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to upper bound ('2006-01-01').
-- After:
create table foo_part_1 partition of foo for values from (date '2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
LINE 1: ...eate table foo_part_1 partition of foo for values from (date...
^
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to upper bound ('2006-01-01').
Co-authored-by: Ashwin Agrawal<aagrawal@pivotal.io>
---
src/backend/commands/tablecmds.c | 4 ++--
src/backend/partitioning/partbounds.c | 3 +--
src/include/partitioning/partbounds.h | 4 +++-
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6162fb018c..46df40bee8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1004,7 +1004,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* Check first that the new partition's bound is valid and does not
* overlap with any of existing partitions of the parent.
*/
- check_new_partition_bound(relname, parent, bound);
+ check_new_partition_bound(relname, parent, bound, pstate);
/*
* If the default partition exists, its partition constraints will
@@ -16268,7 +16268,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
* error.
*/
check_new_partition_bound(RelationGetRelationName(attachrel), rel,
- cmd->bound);
+ cmd->bound, NULL);
/* OK to create inheritance. Rest of the checks performed there */
CreateInheritance(attachrel, rel);
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 7607501fe7..dd56832efc 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -2803,12 +2803,11 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts)
*/
void
check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec)
+ PartitionBoundSpec *spec, ParseState *pstate)
{
PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent);
PartitionBoundInfo boundinfo = partdesc->boundinfo;
- ParseState *pstate = make_parsestate(NULL);
int with = -1;
bool overlap = false;
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index dfc720720b..c82f77d02f 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -14,6 +14,7 @@
#include "fmgr.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
+#include "parser/parse_node.h"
#include "partitioning/partdefs.h"
#include "utils/relcache.h"
struct RelOptInfo; /* avoid including pathnodes.h here */
@@ -98,7 +99,8 @@ extern PartitionBoundInfo partition_bounds_merge(int partnatts,
List **inner_parts);
extern bool partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts);
extern void check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec);
+ PartitionBoundSpec *spec,
+ ParseState *pstate);
extern void check_default_partition_contents(Relation parent,
Relation defaultRel,
PartitionBoundSpec *new_spec);
--
2.26.0
Forgot to run make installcheck. Here's the new version of the patch that
updated the test answer file.
Attachments:
v2-0001-Report-error-position-in-partition-bound-check.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Report-error-position-in-partition-bound-check.patchDownload
From 9071918648412383e41976a01106257bd6a2539e Mon Sep 17 00:00:00 2001
From: Alexandra Wang <lewang@pivotal.io>
Date: Wed, 8 Apr 2020 16:07:28 -0700
Subject: [PATCH v2] Report error position in partition bound check
We have been passing a dummy ParseState to ereport(). Without the source
text in the ParseState ereport does not report the error position even
if a error location is supplied. This patch passes a ParseState to
check_new_partition_bound() when it is available.
-- Create parent table
create table foo (a int, b date) partition by range (b);
-- Before:
create table foo_part_1 partition of foo for values from (date '2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to upper bound ('2006-01-01').
-- After:
create table foo_part_1 partition of foo for values from (date '2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
LINE 1: ...eate table foo_part_1 partition of foo for values from (date...
^
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to upper bound ('2006-01-01').
Co-authored-by: Ashwin Agrawal<aagrawal@pivotal.io>
---
src/backend/commands/tablecmds.c | 4 +--
src/backend/partitioning/partbounds.c | 3 +--
src/include/partitioning/partbounds.h | 4 ++-
src/test/regress/expected/create_table.out | 30 ++++++++++++++++++++++
4 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6162fb018c..46df40bee8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1004,7 +1004,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* Check first that the new partition's bound is valid and does not
* overlap with any of existing partitions of the parent.
*/
- check_new_partition_bound(relname, parent, bound);
+ check_new_partition_bound(relname, parent, bound, pstate);
/*
* If the default partition exists, its partition constraints will
@@ -16268,7 +16268,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
* error.
*/
check_new_partition_bound(RelationGetRelationName(attachrel), rel,
- cmd->bound);
+ cmd->bound, NULL);
/* OK to create inheritance. Rest of the checks performed there */
CreateInheritance(attachrel, rel);
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 7607501fe7..dd56832efc 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -2803,12 +2803,11 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts)
*/
void
check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec)
+ PartitionBoundSpec *spec, ParseState *pstate)
{
PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent);
PartitionBoundInfo boundinfo = partdesc->boundinfo;
- ParseState *pstate = make_parsestate(NULL);
int with = -1;
bool overlap = false;
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index dfc720720b..c82f77d02f 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -14,6 +14,7 @@
#include "fmgr.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
+#include "parser/parse_node.h"
#include "partitioning/partdefs.h"
#include "utils/relcache.h"
struct RelOptInfo; /* avoid including pathnodes.h here */
@@ -98,7 +99,8 @@ extern PartitionBoundInfo partition_bounds_merge(int partnatts,
List **inner_parts);
extern bool partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts);
extern void check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec);
+ PartitionBoundSpec *spec,
+ ParseState *pstate);
extern void check_default_partition_contents(Relation parent,
Relation defaultRel,
PartitionBoundSpec *new_spec);
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 1c72f23bc9..b8de012536 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -677,6 +677,8 @@ LINE 1: ...BLE fail_part PARTITION OF list_parted FOR VALUES WITH (MODU...
CREATE TABLE part_default PARTITION OF list_parted DEFAULT;
CREATE TABLE fail_default_part PARTITION OF list_parted DEFAULT;
ERROR: partition "fail_default_part" conflicts with existing default partition "part_default"
+LINE 1: ...TE TABLE fail_default_part PARTITION OF list_parted DEFAULT;
+ ^
-- specified literal can't be cast to the partition column data type
CREATE TABLE bools (
a bool
@@ -702,6 +704,8 @@ CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10);
-- fails due to overlap:
CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
ERROR: partition "bigintp_10_2" would overlap partition "bigintp_10"
+LINE 1: ...ABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
+ ^
DROP TABLE bigintp;
CREATE TABLE range_parted (
a date
@@ -823,8 +827,12 @@ CREATE TABLE part_ab PARTITION OF list_parted2 FOR VALUES IN ('a', 'b');
CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT;
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
ERROR: partition "fail_part" would overlap partition "part_null_z"
+LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
+ ^
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
ERROR: partition "fail_part" would overlap partition "part_ab"
+LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', '...
+ ^
-- check default partition overlap
INSERT INTO list_parted2 VALUES('X');
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('W', 'X', 'Y');
@@ -835,28 +843,42 @@ CREATE TABLE range_parted2 (
-- trying to create range partition with empty range
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (0).
-- note that the range '[1, 1)' has no elements
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (1).
CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2);
ERROR: partition "fail_part" would overlap partition "part0"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (minv...
+ ^
CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (maxvalue);
ERROR: partition "fail_part" would overlap partition "part1"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) T...
+ ^
CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30);
CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
ERROR: partition "fail_part" would overlap partition "part2"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
+ ^
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
ERROR: partition "fail_part" would overlap partition "part2"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
+ ^
-- Create a default partition for range partitioned table
CREATE TABLE range2_default PARTITION OF range_parted2 DEFAULT;
-- More than one default partition is not allowed, so this should give error
CREATE TABLE fail_default_part PARTITION OF range_parted2 DEFAULT;
ERROR: partition "fail_default_part" conflicts with existing default partition "range2_default"
+LINE 1: ... TABLE fail_default_part PARTITION OF range_parted2 DEFAULT;
+ ^
-- Check if the range for default partitions overlap
INSERT INTO range_parted2 VALUES (85);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (80) TO (90);
@@ -870,17 +892,23 @@ CREATE TABLE range_parted3 (
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, 1);
ERROR: partition "fail_part" would overlap partition "part00"
+LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, m...
+ ^
CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, 1);
CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10);
CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
ERROR: partition "fail_part" would overlap partition "part12"
+LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 1...
+ ^
CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT;
-- cannot create a partition that says column b is allowed to range
-- from -infinity to +infinity, while there exist partitions that have
-- more specific ranges
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, maxvalue);
ERROR: partition "fail_part" would overlap partition "part10"
+LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, m...
+ ^
-- check for partition bound overlap and other invalid specifications for the hash partition
CREATE TABLE hash_parted2 (
a varchar
@@ -892,6 +920,8 @@ CREATE TABLE h2part_4 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMA
-- overlap with part_4
CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 1);
ERROR: partition "fail_part" would overlap partition "h2part_4"
+LINE 1: ...LE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODU...
+ ^
-- modulus must be greater than zero
CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REMAINDER 1);
ERROR: modulus for hash partition must be a positive integer
--
2.26.0
On Wed, Apr 08, 2020 at 05:15:57PM -0700, Alexandra Wang wrote:
I have attached a patch to pass in a ParseState to
check_new_partition_bound() to enable the reporting of the error
position. Below is what the error message looks like before and after
applying the patch.Another option is to not pass the parser_errposition() argument at all
to ereport() in this function, since the query is relatively short and
the error message is already descriptive enough.
It depends on the complexity of the relation definition, so adding a
position looks like a good idea to me. Anyway, even if this looks
like an oversight to me, we are post feature freeze for 13 and that's
an improvement, so this looks like material for PG14 to me. Are there
more opinions on the matter?
Please note that you forgot to update the regression test output.
--
Michael
On Wed, Apr 8, 2020 at 6:11 PM Michael Paquier <michael@paquier.xyz> wrote:
Please note that you forgot to update the regression test output.
Yep thanks! Please see my previous email for the updated patch.
On Wed, Apr 08, 2020 at 08:17:55PM -0700, Alexandra Wang wrote:
On Wed, Apr 8, 2020 at 6:11 PM Michael Paquier <michael@paquier.xyz> wrote:
Please note that you forgot to update the regression test output.
Yep thanks! Please see my previous email for the updated patch.
Thanks, I saw the update. It looks like my email was a couple of
minutes too late :)
Could you add this patch to the next commit fest [1]https://commitfest.postgresql.org/28/ -- Michael?
[1]: https://commitfest.postgresql.org/28/ -- Michael
--
Michael
Hi Alexandra,
As Michael said it will be considered for the next commitfest. But
from a quick glance, a suggestion.
Instead of passing NULL parsestate from ATExecAttachPartition, pass
make_parsestate(NULL). parse_errorposition() takes care of NULL parse
state input, but it might be safer this way. Better if we could cook
up a parse state with the query text available in
AlterTableUtilityContext available in ATExecCmd().
On Thu, Apr 9, 2020 at 6:36 AM Alexandra Wang <lewang@pivotal.io> wrote:
Forgot to run make installcheck. Here's the new version of the patch that updated the test answer file.
--
Best Wishes,
Ashutosh Bapat
While I'm quite on board with providing useful error cursors,
the example cases in this patch don't seem all that useful:
-- trying to create range partition with empty range
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (0).
As best I can tell from these examples, the cursor will always
point at the FROM keyword, making it pretty unhelpful. It seems
like in addition to getting the query string passed down, you
need to do some work on the code that's actually reporting the
error position. I'd expect at a minimum that the pointer allows
identifying which column of a multi-column partition key is
giving trouble. The phrasing of this particular message, for
example, suggests that it ought to point at the "1" expression.
regards, tom lane
On Thu, Apr 9, 2020 at 10:51 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:
Hi Alexandra,
As Michael said it will be considered for the next commitfest. But
from a quick glance, a suggestion.
Instead of passing NULL parsestate from ATExecAttachPartition, pass
make_parsestate(NULL). parse_errorposition() takes care of NULL parse
state input, but it might be safer this way. Better if we could cook
up a parse state with the query text available in
AlterTableUtilityContext available in ATExecCmd().
+1. Maybe pass the *context* down to ATExecAttachPartition() from
ATExecCmd() rather than a ParseState.
--
Amit Langote
EnterpriseDB: http://www.enterprisedb.com
On Thu, Apr 9, 2020 at 11:04 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
While I'm quite on board with providing useful error cursors,
the example cases in this patch don't seem all that useful:-- trying to create range partition with empty range CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0); ERROR: empty range bound specified for partition "fail_part" +LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T... + ^ DETAIL: Specified lower bound (1) is greater than or equal to upper bound (0).As best I can tell from these examples, the cursor will always
point at the FROM keyword, making it pretty unhelpful. It seems
like in addition to getting the query string passed down, you
need to do some work on the code that's actually reporting the
error position. I'd expect at a minimum that the pointer allows
identifying which column of a multi-column partition key is
giving trouble. The phrasing of this particular message, for
example, suggests that it ought to point at the "1" expression.
I agree with that. Tried that in the attached 0002, although trying
to get the cursor to point to exactly the offending column seems a bit
tough for partition overlap errors. The patch does allow to single
out which one of the lower and upper bounds is causing the overlap
with an existing partition, which is better than now and seems helpful
enough.
Also, updated Alexandra's patch to incorporate Ashutosh's comment such
that we get the same output with ATTACH PARTITION commands too.
--
Amit Langote
EnterpriseDB: http://www.enterprisedb.com
Attachments:
v3-0001-Report-error-position-in-partition-bound-check.patchapplication/octet-stream; name=v3-0001-Report-error-position-in-partition-bound-check.patchDownload
From e3c2b8c652135d72c33dd58bc7ae55577dbc2e8d Mon Sep 17 00:00:00 2001
From: Alexandra Wang <lewang@pivotal.io>
Date: Wed, 8 Apr 2020 16:07:28 -0700
Subject: [PATCH v3 1/2] Report error position in partition bound check
We have been passing a dummy ParseState to ereport(). Without the source
text in the ParseState ereport does not report the error position even
if a error location is supplied. This patch passes a ParseState to
check_new_partition_bound() when it is available.
-- Create parent table
create table foo (a int, b date) partition by range (b);
-- Before:
create table foo_part_1 partition of foo for values from (date '2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to upper bound ('2006-01-01').
-- After:
create table foo_part_1 partition of foo for values from (date '2007-01-01') to (date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
LINE 1: ...eate table foo_part_1 partition of foo for values from (date...
^
DETAIL: Specified lower bound ('2007-01-01') is greater than or equal to upper bound ('2006-01-01').
Co-authored-by: Ashwin Agrawal<aagrawal@pivotal.io>
---
src/backend/commands/tablecmds.c | 15 ++++++++++-----
src/backend/partitioning/partbounds.c | 3 +--
src/include/partitioning/partbounds.h | 4 +++-
src/test/regress/expected/alter_table.out | 10 ++++++++++
src/test/regress/expected/create_table.out | 30 ++++++++++++++++++++++++++++++
5 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 037d457..69c70ab 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -541,7 +541,8 @@ static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partPa
static void CreateInheritance(Relation child_rel, Relation parent_rel);
static void RemoveInheritance(Relation child_rel, Relation parent_rel);
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
- PartitionCmd *cmd);
+ PartitionCmd *cmd,
+ AlterTableUtilityContext * context);
static void AttachPartitionEnsureIndexes(Relation rel, Relation attachrel);
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
List *partConstraint,
@@ -1004,7 +1005,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* Check first that the new partition's bound is valid and does not
* overlap with any of existing partitions of the parent.
*/
- check_new_partition_bound(relname, parent, bound);
+ check_new_partition_bound(relname, parent, bound, pstate);
/*
* If the default partition exists, its partition constraints will
@@ -4593,7 +4594,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
cur_pass, context);
Assert(cmd != NULL);
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
- ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
+ ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
+ context);
else
ATExecAttachPartitionIdx(wqueue, rel,
((PartitionCmd *) cmd->def)->name);
@@ -16088,7 +16090,8 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
* Return the address of the newly attached partition.
*/
static ObjectAddress
-ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
+ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
+ AlterTableUtilityContext * context)
{
Relation attachrel,
catalog;
@@ -16103,6 +16106,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
const char *trigger_name;
Oid defaultPartOid;
List *partBoundConstraint;
+ ParseState *pstate = make_parsestate(NULL);
/*
* We must lock the default partition if one exists, because attaching a
@@ -16267,8 +16271,9 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
* of existing partitions of the parent - note that it does not return on
* error.
*/
+ pstate->p_sourcetext = context->queryString;
check_new_partition_bound(RelationGetRelationName(attachrel), rel,
- cmd->bound);
+ cmd->bound, pstate);
/* OK to create inheritance. Rest of the checks performed there */
CreateInheritance(attachrel, rel);
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 7607501..dd56832 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -2803,12 +2803,11 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts)
*/
void
check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec)
+ PartitionBoundSpec *spec, ParseState *pstate)
{
PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent);
PartitionBoundInfo boundinfo = partdesc->boundinfo;
- ParseState *pstate = make_parsestate(NULL);
int with = -1;
bool overlap = false;
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index dfc7207..c82f77d 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -14,6 +14,7 @@
#include "fmgr.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
+#include "parser/parse_node.h"
#include "partitioning/partdefs.h"
#include "utils/relcache.h"
struct RelOptInfo; /* avoid including pathnodes.h here */
@@ -98,7 +99,8 @@ extern PartitionBoundInfo partition_bounds_merge(int partnatts,
List **inner_parts);
extern bool partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts);
extern void check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec);
+ PartitionBoundSpec *spec,
+ ParseState *pstate);
extern void check_default_partition_contents(Relation parent,
Relation defaultRel,
PartitionBoundSpec *new_spec);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index f343f9b..576f19b 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3792,6 +3792,8 @@ SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_1'::reg
CREATE TABLE fail_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
ERROR: partition "fail_part" would overlap partition "part_1"
+LINE 1: ...LE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
+ ^
DROP TABLE fail_part;
-- check that an existing table can be attached as a default partition
CREATE TABLE def_part (LIKE list_parted INCLUDING CONSTRAINTS);
@@ -3801,6 +3803,8 @@ ALTER TABLE list_parted ATTACH PARTITION def_part DEFAULT;
CREATE TABLE fail_def_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT;
ERROR: partition "fail_def_part" conflicts with existing default partition "def_part"
+LINE 1: ...ER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT;
+ ^
-- check validation when attaching list partitions
CREATE TABLE list_parted2 (
a int,
@@ -3870,6 +3874,8 @@ CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT;
CREATE TABLE partr_def2 (LIKE part1 INCLUDING CONSTRAINTS);
ALTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT;
ERROR: partition "partr_def2" conflicts with existing default partition "partr_def1"
+LINE 1: ...LTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT;
+ ^
-- Overlapping partitions cannot be attached, hence, following should give error
INSERT INTO partr_def1 VALUES (2, 10);
CREATE TABLE part3 (LIKE range_parted);
@@ -3990,8 +3996,12 @@ CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAIN
CREATE TABLE fail_part (LIKE hpart_1);
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 4);
ERROR: partition "fail_part" would overlap partition "hpart_1"
+LINE 1: ...hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODU...
+ ^
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 0);
ERROR: partition "fail_part" would overlap partition "hpart_1"
+LINE 1: ...hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODU...
+ ^
DROP TABLE fail_part;
-- check validation when attaching hash partitions
-- check that violating rows are correctly reported
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 1c72f23..b8de012 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -677,6 +677,8 @@ LINE 1: ...BLE fail_part PARTITION OF list_parted FOR VALUES WITH (MODU...
CREATE TABLE part_default PARTITION OF list_parted DEFAULT;
CREATE TABLE fail_default_part PARTITION OF list_parted DEFAULT;
ERROR: partition "fail_default_part" conflicts with existing default partition "part_default"
+LINE 1: ...TE TABLE fail_default_part PARTITION OF list_parted DEFAULT;
+ ^
-- specified literal can't be cast to the partition column data type
CREATE TABLE bools (
a bool
@@ -702,6 +704,8 @@ CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10);
-- fails due to overlap:
CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
ERROR: partition "bigintp_10_2" would overlap partition "bigintp_10"
+LINE 1: ...ABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
+ ^
DROP TABLE bigintp;
CREATE TABLE range_parted (
a date
@@ -823,8 +827,12 @@ CREATE TABLE part_ab PARTITION OF list_parted2 FOR VALUES IN ('a', 'b');
CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT;
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
ERROR: partition "fail_part" would overlap partition "part_null_z"
+LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
+ ^
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
ERROR: partition "fail_part" would overlap partition "part_ab"
+LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', '...
+ ^
-- check default partition overlap
INSERT INTO list_parted2 VALUES('X');
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('W', 'X', 'Y');
@@ -835,28 +843,42 @@ CREATE TABLE range_parted2 (
-- trying to create range partition with empty range
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (0).
-- note that the range '[1, 1)' has no elements
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (1).
CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2);
ERROR: partition "fail_part" would overlap partition "part0"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (minv...
+ ^
CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (maxvalue);
ERROR: partition "fail_part" would overlap partition "part1"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) T...
+ ^
CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30);
CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
ERROR: partition "fail_part" would overlap partition "part2"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
+ ^
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
ERROR: partition "fail_part" would overlap partition "part2"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
+ ^
-- Create a default partition for range partitioned table
CREATE TABLE range2_default PARTITION OF range_parted2 DEFAULT;
-- More than one default partition is not allowed, so this should give error
CREATE TABLE fail_default_part PARTITION OF range_parted2 DEFAULT;
ERROR: partition "fail_default_part" conflicts with existing default partition "range2_default"
+LINE 1: ... TABLE fail_default_part PARTITION OF range_parted2 DEFAULT;
+ ^
-- Check if the range for default partitions overlap
INSERT INTO range_parted2 VALUES (85);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (80) TO (90);
@@ -870,17 +892,23 @@ CREATE TABLE range_parted3 (
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, 1);
ERROR: partition "fail_part" would overlap partition "part00"
+LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, m...
+ ^
CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, 1);
CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10);
CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
ERROR: partition "fail_part" would overlap partition "part12"
+LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 1...
+ ^
CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT;
-- cannot create a partition that says column b is allowed to range
-- from -infinity to +infinity, while there exist partitions that have
-- more specific ranges
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, maxvalue);
ERROR: partition "fail_part" would overlap partition "part10"
+LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, m...
+ ^
-- check for partition bound overlap and other invalid specifications for the hash partition
CREATE TABLE hash_parted2 (
a varchar
@@ -892,6 +920,8 @@ CREATE TABLE h2part_4 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMA
-- overlap with part_4
CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 1);
ERROR: partition "fail_part" would overlap partition "h2part_4"
+LINE 1: ...LE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODU...
+ ^
-- modulus must be greater than zero
CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REMAINDER 1);
ERROR: modulus for hash partition must be a positive integer
--
1.8.3.1
v1-0002-Improve-check_new_partition_bound-error-position-.patchapplication/octet-stream; name=v1-0002-Improve-check_new_partition_bound-error-position-.patchDownload
From 6ff4cd9c1614b089f01cb633220d37305719f46d Mon Sep 17 00:00:00 2001
From: amitlan <amitlangote09@gmail.com>
Date: Fri, 10 Apr 2020 16:38:21 +0900
Subject: [PATCH v3 2/2] Improve check_new_partition_bound error position
reporting
---
src/backend/parser/parse_utilcmd.c | 3 +++
src/backend/partitioning/partbounds.c | 40 ++++++++++++++++++++++--------
src/test/regress/expected/alter_table.out | 2 +-
src/test/regress/expected/create_table.out | 28 ++++++++++-----------
4 files changed, 48 insertions(+), 25 deletions(-)
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 75c122f..72113bb 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -4170,5 +4170,8 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
if (!IsA(value, Const))
elog(ERROR, "could not evaluate partition bound expression");
+ /* Preserve parser location information. */
+ ((Const *) value)->location = exprLocation(val);
+
return (Const *) value;
}
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index dd56832..feb3357 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -2810,6 +2810,7 @@ check_new_partition_bound(char *relname, Relation parent,
PartitionBoundInfo boundinfo = partdesc->boundinfo;
int with = -1;
bool overlap = false;
+ int overlap_location = 0;
if (spec->is_default)
{
@@ -2904,6 +2905,7 @@ check_new_partition_bound(char *relname, Relation parent,
if (boundinfo->indexes[remainder] != -1)
{
overlap = true;
+ overlap_location = spec->location;
with = boundinfo->indexes[remainder];
break;
}
@@ -2932,6 +2934,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
Const *val = castNode(Const, lfirst(cell));
+ overlap_location = val->location;
if (!val->constisnull)
{
int offset;
@@ -2965,6 +2968,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
PartitionRangeBound *lower,
*upper;
+ int cmpval;
Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
@@ -2974,10 +2978,16 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
- key->partcollation, lower->datums,
- lower->kind, true, upper) >= 0)
+ cmpval = partition_rbound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation, lower->datums,
+ lower->kind, true, upper);
+ if (cmpval >= 0)
{
+ /* Fetch the problem bound from lower datums list. */
+ PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
+ cmpval - 1);
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("empty range bound specified for partition \"%s\"",
@@ -2985,7 +2995,7 @@ check_new_partition_bound(char *relname, Relation parent,
errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
get_range_partbound_string(spec->lowerdatums),
get_range_partbound_string(spec->upperdatums)),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, datum->location)));
}
if (partdesc->nparts > 0)
@@ -3051,6 +3061,8 @@ check_new_partition_bound(char *relname, Relation parent,
* offset + 2.
*/
overlap = true;
+ overlap_location = ((PartitionRangeDatum *)
+ linitial(spec->upperdatums))->location;
with = boundinfo->indexes[offset + 2];
}
}
@@ -3062,6 +3074,8 @@ check_new_partition_bound(char *relname, Relation parent,
* partition between offset and offset + 1.
*/
overlap = true;
+ overlap_location = ((PartitionRangeDatum *)
+ linitial(spec->lowerdatums))->location;
with = boundinfo->indexes[offset + 1];
}
}
@@ -3077,11 +3091,12 @@ check_new_partition_bound(char *relname, Relation parent,
if (overlap)
{
Assert(with >= 0);
+ Assert(overlap_location > 0);
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("partition \"%s\" would overlap partition \"%s\"",
relname, get_rel_name(partdesc->oids[with])),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, overlap_location)));
}
}
@@ -3315,7 +3330,9 @@ make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
* partition_rbound_cmp
*
* Return for two range bounds whether the 1st one (specified in datums1,
- * kind1, and lower1) is <, =, or > the bound specified in *b2.
+ * kind1, and lower1) is <, =, or > the bound specified in *b2. 0 is returned if
+ * equal and the 1-based index of the first mismatching bound if unequal;
+ * multiplied by -1 if the 1st bound is smaller.
*
* partnatts, partsupfunc and partcollation give the number of attributes in the
* bounds to be compared, comparison function to be used and the collations of
@@ -3335,6 +3352,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
bool lower1, PartitionRangeBound *b2)
{
int32 cmpval = 0; /* placate compiler */
+ int result = 0;
int i;
Datum *datums2 = b2->datums;
PartitionRangeDatumKind *kind2 = b2->kind;
@@ -3342,6 +3360,8 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
for (i = 0; i < partnatts; i++)
{
+ result++;
+
/*
* First, handle cases where the column is unbounded, which should not
* invoke the comparison procedure, and should not consider any later
@@ -3349,9 +3369,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* compare the same way as the values they represent.
*/
if (kind1[i] < kind2[i])
- return -1;
+ return -result;
else if (kind1[i] > kind2[i])
- return 1;
+ return result;
else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
/*
@@ -3376,9 +3396,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* two.
*/
if (cmpval == 0 && lower1 != lower2)
- cmpval = lower1 ? 1 : -1;
+ cmpval = lower1 ? result : -result;
- return cmpval;
+ return cmpval == 0 ? 0 : (cmpval < 0 ? -result : result);
}
/*
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 576f19b..b317469 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3793,7 +3793,7 @@ CREATE TABLE fail_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
ERROR: partition "fail_part" would overlap partition "part_1"
LINE 1: ...LE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
- ^
+ ^
DROP TABLE fail_part;
-- check that an existing table can be attached as a default partition
CREATE TABLE def_part (LIKE list_parted INCLUDING CONSTRAINTS);
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index b8de012..bc5a660 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -705,7 +705,7 @@ CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10);
CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
ERROR: partition "bigintp_10_2" would overlap partition "bigintp_10"
LINE 1: ...ABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
- ^
+ ^
DROP TABLE bigintp;
CREATE TABLE range_parted (
a date
@@ -828,10 +828,10 @@ CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT;
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
ERROR: partition "fail_part" would overlap partition "part_null_z"
LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
- ^
+ ^
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
ERROR: partition "fail_part" would overlap partition "part_ab"
-LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', '...
+LINE 1: ...ail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
^
-- check default partition overlap
INSERT INTO list_parted2 VALUES('X');
@@ -843,35 +843,35 @@ CREATE TABLE range_parted2 (
-- trying to create range partition with empty range
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
ERROR: empty range bound specified for partition "fail_part"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (0).
-- note that the range '[1, 1)' has no elements
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
ERROR: empty range bound specified for partition "fail_part"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (1).
CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2);
ERROR: partition "fail_part" would overlap partition "part0"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (minv...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) ...
^
CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (maxvalue);
ERROR: partition "fail_part" would overlap partition "part1"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) T...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (max...
^
CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30);
CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
ERROR: partition "fail_part" would overlap partition "part2"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
- ^
+LINE 1: ...art PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
+ ^
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
ERROR: partition "fail_part" would overlap partition "part2"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
- ^
+LINE 1: ...art PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
+ ^
-- Create a default partition for range partitioned table
CREATE TABLE range2_default PARTITION OF range_parted2 DEFAULT;
-- More than one default partition is not allowed, so this should give error
@@ -892,14 +892,14 @@ CREATE TABLE range_parted3 (
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, 1);
ERROR: partition "fail_part" would overlap partition "part00"
-LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, m...
+LINE 1: ..._part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalu...
^
CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, 1);
CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10);
CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
ERROR: partition "fail_part" would overlap partition "part12"
-LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 1...
+LINE 1: ..._part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO ...
^
CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT;
-- cannot create a partition that says column b is allowed to range
@@ -907,7 +907,7 @@ CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT;
-- more specific ranges
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, maxvalue);
ERROR: partition "fail_part" would overlap partition "part10"
-LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, m...
+LINE 1: ..._part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalu...
^
-- check for partition bound overlap and other invalid specifications for the hash partition
CREATE TABLE hash_parted2 (
--
1.8.3.1
On Fri, 10 Apr 2020 at 14:31, Amit Langote <amitlangote09@gmail.com> wrote:
On Thu, Apr 9, 2020 at 11:04 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
While I'm quite on board with providing useful error cursors,
the example cases in this patch don't seem all that useful:-- trying to create range partition with empty range
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1)TO (0);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1)T...
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upperbound (0).
As best I can tell from these examples, the cursor will always
point at the FROM keyword, making it pretty unhelpful. It seems
like in addition to getting the query string passed down, you
need to do some work on the code that's actually reporting the
error position. I'd expect at a minimum that the pointer allows
identifying which column of a multi-column partition key is
giving trouble. The phrasing of this particular message, for
example, suggests that it ought to point at the "1" expression.I agree with that. Tried that in the attached 0002, although trying
to get the cursor to point to exactly the offending column seems a bit
tough for partition overlap errors. The patch does allow to single
out which one of the lower and upper bounds is causing the overlap
with an existing partition, which is better than now and seems helpful
enough.Also, updated Alexandra's patch to incorporate Ashutosh's comment such
that we get the same output with ATTACH PARTITION commands too.
I looked at this briefly. It looks good, but I will review more in the next
CF. Do we have entry there yet? To nit-pick: for a multi-key value the ^
points to the first column and the reader may think that that's the
problematci column. Should it instead point to ( ?
--
Best Wishes,
Ashutosh
On Fri, 10 Apr 2020 at 14:31, Amit Langote <amitlangote09@gmail.com> wrote:
I agree with that. Tried that in the attached 0002, although trying
to get the cursor to point to exactly the offending column seems a bit
tough for partition overlap errors. The patch does allow to single
out which one of the lower and upper bounds is causing the overlap
with an existing partition, which is better than now and seems helpful
enough.Also, updated Alexandra's patch to incorporate Ashutosh's comment such
that we get the same output with ATTACH PARTITION commands too.
Thank you Amit for updating the patches, the cursor looks much helpful now.
I
created the commitfest entry https://commitfest.postgresql.org/28/2533/
On Fri, Apr 10, 2020 at 8:37 AM Ashutosh Bapat <
ashutosh.bapat@2ndquadrant.com> wrote:
for a multi-key value the ^
points to the first column and the reader may think that that's the
problematci column. Should it instead point to ( ?
I attached a v2 of Amit's 0002 patch to also report the exact column
for the partition overlap errors.
Attachments:
v2-0002-Improve-check-new-partition-bound-error-position-.patchtext/x-patch; charset=US-ASCII; name=v2-0002-Improve-check-new-partition-bound-error-position-.patchDownload
From f7cf86fcf7c1a26895e68f357717244f776fe5cd Mon Sep 17 00:00:00 2001
From: Alexandra Wang <lewang@pivotal.io>
Date: Fri, 10 Apr 2020 13:51:53 -0700
Subject: [PATCH v2] Improve check new partition bound error position report
---
src/backend/parser/parse_utilcmd.c | 3 ++
src/backend/partitioning/partbounds.c | 60 +++++++++++++++-------
src/test/regress/expected/alter_table.out | 2 +-
src/test/regress/expected/create_table.out | 28 +++++-----
4 files changed, 59 insertions(+), 34 deletions(-)
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 75c122fe34..72113bb7ff 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -4170,5 +4170,8 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
if (!IsA(value, Const))
elog(ERROR, "could not evaluate partition bound expression");
+ /* Preserve parser location information. */
+ ((Const *) value)->location = exprLocation(val);
+
return (Const *) value;
}
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index dd56832efc..0ef0f6ed07 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -223,7 +223,8 @@ static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal);
+ PartitionRangeBound *probe, bool *is_equal,
+ int32 *cmpval);
static int get_partition_bound_num_indexes(PartitionBoundInfo b);
static Expr *make_partition_op_expr(PartitionKey key, int keynum,
uint16 strategy, Expr *arg1, Expr *arg2);
@@ -2810,6 +2811,7 @@ check_new_partition_bound(char *relname, Relation parent,
PartitionBoundInfo boundinfo = partdesc->boundinfo;
int with = -1;
bool overlap = false;
+ int overlap_location = 0;
if (spec->is_default)
{
@@ -2904,6 +2906,7 @@ check_new_partition_bound(char *relname, Relation parent,
if (boundinfo->indexes[remainder] != -1)
{
overlap = true;
+ overlap_location = spec->location;
with = boundinfo->indexes[remainder];
break;
}
@@ -2932,6 +2935,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
Const *val = castNode(Const, lfirst(cell));
+ overlap_location = val->location;
if (!val->constisnull)
{
int offset;
@@ -2965,6 +2969,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
PartitionRangeBound *lower,
*upper;
+ int cmpval;
Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
@@ -2974,10 +2979,16 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
- key->partcollation, lower->datums,
- lower->kind, true, upper) >= 0)
+ cmpval = partition_rbound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation, lower->datums,
+ lower->kind, true, upper);
+ if (cmpval >= 0)
{
+ /* Fetch the problem bound from lower datums list. */
+ PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
+ cmpval - 1);
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("empty range bound specified for partition \"%s\"",
@@ -2985,7 +2996,7 @@ check_new_partition_bound(char *relname, Relation parent,
errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
get_range_partbound_string(spec->lowerdatums),
get_range_partbound_string(spec->upperdatums)),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, datum->location)));
}
if (partdesc->nparts > 0)
@@ -3017,7 +3028,7 @@ check_new_partition_bound(char *relname, Relation parent,
key->partsupfunc,
key->partcollation,
boundinfo, lower,
- &equal);
+ &equal, &cmpval);
if (boundinfo->indexes[offset + 1] < 0)
{
@@ -3029,7 +3040,6 @@ check_new_partition_bound(char *relname, Relation parent,
*/
if (offset + 1 < boundinfo->ndatums)
{
- int32 cmpval;
Datum *datums;
PartitionRangeDatumKind *kind;
bool is_lower;
@@ -3051,6 +3061,8 @@ check_new_partition_bound(char *relname, Relation parent,
* offset + 2.
*/
overlap = true;
+ overlap_location = ((PartitionRangeDatum *)
+ list_nth(spec->upperdatums, -cmpval - 1))->location;
with = boundinfo->indexes[offset + 2];
}
}
@@ -3061,7 +3073,13 @@ check_new_partition_bound(char *relname, Relation parent,
* The new partition overlaps with the existing
* partition between offset and offset + 1.
*/
+ Datum *datum;
+
overlap = true;
+ Assert(cmpval >= 0);
+ datum = cmpval == 0 ? linitial(spec->lowerdatums):
+ list_nth(spec->lowerdatums, cmpval - 1);
+ overlap_location = ((PartitionRangeDatum *)datum)->location;
with = boundinfo->indexes[offset + 1];
}
}
@@ -3077,11 +3095,12 @@ check_new_partition_bound(char *relname, Relation parent,
if (overlap)
{
Assert(with >= 0);
+ Assert(overlap_location > 0);
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("partition \"%s\" would overlap partition \"%s\"",
relname, get_rel_name(partdesc->oids[with])),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, overlap_location)));
}
}
@@ -3315,7 +3334,9 @@ make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
* partition_rbound_cmp
*
* Return for two range bounds whether the 1st one (specified in datums1,
- * kind1, and lower1) is <, =, or > the bound specified in *b2.
+ * kind1, and lower1) is <, =, or > the bound specified in *b2. 0 is returned if
+ * equal and the 1-based index of the first mismatching bound if unequal;
+ * multiplied by -1 if the 1st bound is smaller.
*
* partnatts, partsupfunc and partcollation give the number of attributes in the
* bounds to be compared, comparison function to be used and the collations of
@@ -3335,6 +3356,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
bool lower1, PartitionRangeBound *b2)
{
int32 cmpval = 0; /* placate compiler */
+ int result = 0;
int i;
Datum *datums2 = b2->datums;
PartitionRangeDatumKind *kind2 = b2->kind;
@@ -3342,6 +3364,8 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
for (i = 0; i < partnatts; i++)
{
+ result++;
+
/*
* First, handle cases where the column is unbounded, which should not
* invoke the comparison procedure, and should not consider any later
@@ -3349,9 +3373,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* compare the same way as the values they represent.
*/
if (kind1[i] < kind2[i])
- return -1;
+ return -result;
else if (kind1[i] > kind2[i])
- return 1;
+ return result;
else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
/*
@@ -3376,9 +3400,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* two.
*/
if (cmpval == 0 && lower1 != lower2)
- cmpval = lower1 ? 1 : -1;
+ cmpval = lower1 ? result : -result;
- return cmpval;
+ return cmpval == 0 ? 0 : (cmpval < 0 ? -result : result);;
}
/*
@@ -3490,7 +3514,7 @@ static int
partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal)
+ PartitionRangeBound *probe, bool *is_equal, int32 *cmpval)
{
int lo,
hi,
@@ -3500,19 +3524,17 @@ partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
hi = boundinfo->ndatums - 1;
while (lo < hi)
{
- int32 cmpval;
-
mid = (lo + hi + 1) / 2;
- cmpval = partition_rbound_cmp(partnatts, partsupfunc,
+ *cmpval = partition_rbound_cmp(partnatts, partsupfunc,
partcollation,
boundinfo->datums[mid],
boundinfo->kind[mid],
(boundinfo->indexes[mid] == -1),
probe);
- if (cmpval <= 0)
+ if (*cmpval <= 0)
{
lo = mid;
- *is_equal = (cmpval == 0);
+ *is_equal = (*cmpval == 0);
if (*is_equal)
break;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 576f19b38e..b3174694e3 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3793,7 +3793,7 @@ CREATE TABLE fail_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
ERROR: partition "fail_part" would overlap partition "part_1"
LINE 1: ...LE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
- ^
+ ^
DROP TABLE fail_part;
-- check that an existing table can be attached as a default partition
CREATE TABLE def_part (LIKE list_parted INCLUDING CONSTRAINTS);
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index b8de012536..41dce69cc4 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -705,7 +705,7 @@ CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10);
CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
ERROR: partition "bigintp_10_2" would overlap partition "bigintp_10"
LINE 1: ...ABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
- ^
+ ^
DROP TABLE bigintp;
CREATE TABLE range_parted (
a date
@@ -828,10 +828,10 @@ CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT;
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
ERROR: partition "fail_part" would overlap partition "part_null_z"
LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
- ^
+ ^
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
ERROR: partition "fail_part" would overlap partition "part_ab"
-LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', '...
+LINE 1: ...ail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
^
-- check default partition overlap
INSERT INTO list_parted2 VALUES('X');
@@ -843,35 +843,35 @@ CREATE TABLE range_parted2 (
-- trying to create range partition with empty range
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
ERROR: empty range bound specified for partition "fail_part"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (0).
-- note that the range '[1, 1)' has no elements
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
ERROR: empty range bound specified for partition "fail_part"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) T...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (1).
CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2);
ERROR: partition "fail_part" would overlap partition "part0"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (minv...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) ...
^
CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (maxvalue);
ERROR: partition "fail_part" would overlap partition "part1"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) T...
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (max...
^
CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30);
CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
ERROR: partition "fail_part" would overlap partition "part2"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
- ^
+LINE 1: ...art PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
+ ^
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
ERROR: partition "fail_part" would overlap partition "part2"
-LINE 1: ...E fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) ...
- ^
+LINE 1: ...art PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
+ ^
-- Create a default partition for range partitioned table
CREATE TABLE range2_default PARTITION OF range_parted2 DEFAULT;
-- More than one default partition is not allowed, so this should give error
@@ -892,14 +892,14 @@ CREATE TABLE range_parted3 (
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, 1);
ERROR: partition "fail_part" would overlap partition "part00"
-LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, m...
+LINE 1: ..._part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalu...
^
CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, 1);
CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10);
CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
ERROR: partition "fail_part" would overlap partition "part12"
-LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 1...
+LINE 1: ...rt PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1,...
^
CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT;
-- cannot create a partition that says column b is allowed to range
@@ -907,7 +907,7 @@ CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT;
-- more specific ranges
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, maxvalue);
ERROR: partition "fail_part" would overlap partition "part10"
-LINE 1: ...E fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, m...
+LINE 1: ..._part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalu...
^
-- check for partition bound overlap and other invalid specifications for the hash partition
CREATE TABLE hash_parted2 (
--
2.26.0
On 10 Apr 2020, at 23:50, Alexandra Wang <lewang@pivotal.io> wrote:
On Fri, Apr 10, 2020 at 8:37 AM Ashutosh Bapat <ashutosh.bapat@2ndquadrant.com <mailto:ashutosh.bapat@2ndquadrant.com>> wrote:
for a multi-key value the ^
points to the first column and the reader may think that that's the
problematci column. Should it instead point to ( ?I attached a v2 of Amit's 0002 patch to also report the exact column
for the partition overlap errors.
This patch fails to apply to HEAD due to conflicts in the create_table expected
output. Can you please submit a rebased version? I'm marking the CF entry
Waiting on Author in the meantime.
cheers ./daniel
On 2 July 2020, at 06:39, Daniel Gustafsson <daniel@yesql.se> wrote:
On 10 Apr 2020, at 23:50, Alexandra Wang <lewang@pivotal.io> wrote:
On Fri, Apr 10, 2020 at 8:37 AM Ashutosh Bapat <ashutosh.bapat@2ndquadrant.com <mailto:ashutosh.bapat@2ndquadrant.com>> wrote:
for a multi-key value the ^
points to the first column and the reader may think that that's the
problematci column. Should it instead point to ( ?I attached a v2 of Amit's 0002 patch to also report the exact column
for the partition overlap errors.This patch fails to apply to HEAD due to conflicts in the create_table expected
output. Can you please submit a rebased version? I'm marking the CF entry
Waiting on Author in the meantime.
Thank you Daniel. Here's the rebased patch. I also squashed the two
patches into one so it's easier to review.
--
Alex
Attachments:
v3-0001-Improve-check-new-partition-bound-error-position-rep.patchtext/x-patch; name=v3-0001-Improve-check-new-partition-bound-error-position-rep.patchDownload
From 334bb4ea93448073778930201c0b959c5acab924 Mon Sep 17 00:00:00 2001
From: Alexandra Wang <walexandra@vmware.com>
Date: Mon, 13 Jul 2020 10:28:04 -0700
Subject: [PATCH] Improve check new partition bound error position report
We have been passing a dummy ParseState to ereport(). Without the source
text in the ParseState ereport does not report the error position even
if a error location is supplied. This patch passes a ParseState to
check_new_partition_bound() when it is available.
-- Create parent table
create table foo (a int, b int, c date) partition by range (b,c);
-- Before:
create table foo_part_1 partition of foo for values from (1, date '2007-01-01') to (1, date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
DETAIL: Specified lower bound (1, '2007-01-01') is greater than or equal to upper bound (1, '2006-01-01').
-- After:
create table foo_part_1 partition of foo for values from (1, date '2007-01-01') to (1, date '2006-01-01');
ERROR: empty range bound specified for partition "foo_part_1"
LINE 1: ...e foo_part_1 partition of foo for values from (1, date '2007...
^
DETAIL: Specified lower bound (1, '2007-01-01') is greater than or equal to upper bound (1, '2006-01-01').
Co-authored-by: Ashwin Agrawal <aashwin@vmware.com>
Co-authored-by: Amit Langote <amitlangote09@gmail.com>
---
src/backend/commands/tablecmds.c | 15 ++++--
src/backend/parser/parse_utilcmd.c | 3 ++
src/backend/partitioning/partbounds.c | 63 ++++++++++++++--------
src/include/partitioning/partbounds.h | 4 +-
src/test/regress/expected/alter_table.out | 10 ++++
src/test/regress/expected/create_table.out | 30 +++++++++++
6 files changed, 98 insertions(+), 27 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ed553f7384..4cd7709d33 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -541,7 +541,8 @@ static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partPa
static void CreateInheritance(Relation child_rel, Relation parent_rel);
static void RemoveInheritance(Relation child_rel, Relation parent_rel);
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
- PartitionCmd *cmd);
+ PartitionCmd *cmd,
+ AlterTableUtilityContext * context);
static void AttachPartitionEnsureIndexes(Relation rel, Relation attachrel);
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
List *partConstraint,
@@ -1005,7 +1006,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* Check first that the new partition's bound is valid and does not
* overlap with any of existing partitions of the parent.
*/
- check_new_partition_bound(relname, parent, bound);
+ check_new_partition_bound(relname, parent, bound, pstate);
/*
* If the default partition exists, its partition constraints will
@@ -4646,7 +4647,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
cur_pass, context);
Assert(cmd != NULL);
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
- ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
+ ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
+ context);
else
ATExecAttachPartitionIdx(wqueue, rel,
((PartitionCmd *) cmd->def)->name);
@@ -16186,7 +16188,8 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
* Return the address of the newly attached partition.
*/
static ObjectAddress
-ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
+ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
+ AlterTableUtilityContext * context)
{
Relation attachrel,
catalog;
@@ -16201,6 +16204,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
const char *trigger_name;
Oid defaultPartOid;
List *partBoundConstraint;
+ ParseState *pstate = make_parsestate(NULL);
/*
* We must lock the default partition if one exists, because attaching a
@@ -16365,8 +16369,9 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
* of existing partitions of the parent - note that it does not return on
* error.
*/
+ pstate->p_sourcetext = context->queryString;
check_new_partition_bound(RelationGetRelationName(attachrel), rel,
- cmd->bound);
+ cmd->bound, pstate);
/* OK to create inheritance. Rest of the checks performed there */
CreateInheritance(attachrel, rel);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 25abc544fc..2a13fe2fd8 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -4170,5 +4170,8 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
if (!IsA(value, Const))
elog(ERROR, "could not evaluate partition bound expression");
+ /* Preserve parser location information. */
+ ((Const *) value)->location = exprLocation(val);
+
return (Const *) value;
}
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 7553d55987..3cade9e68c 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -223,7 +223,8 @@ static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal);
+ PartitionRangeBound *probe, bool *is_equal,
+ int32 *cmpval);
static int get_partition_bound_num_indexes(PartitionBoundInfo b);
static Expr *make_partition_op_expr(PartitionKey key, int keynum,
uint16 strategy, Expr *arg1, Expr *arg2);
@@ -2807,14 +2808,14 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts)
*/
void
check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec)
+ PartitionBoundSpec *spec, ParseState *pstate)
{
PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent);
PartitionBoundInfo boundinfo = partdesc->boundinfo;
- ParseState *pstate = make_parsestate(NULL);
int with = -1;
bool overlap = false;
+ int overlap_location = 0;
if (spec->is_default)
{
@@ -2909,6 +2910,7 @@ check_new_partition_bound(char *relname, Relation parent,
if (boundinfo->indexes[remainder] != -1)
{
overlap = true;
+ overlap_location = spec->location;
with = boundinfo->indexes[remainder];
break;
}
@@ -2937,6 +2939,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
Const *val = castNode(Const, lfirst(cell));
+ overlap_location = val->location;
if (!val->constisnull)
{
int offset;
@@ -2970,6 +2973,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
PartitionRangeBound *lower,
*upper;
+ int cmpval;
Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
@@ -2979,10 +2983,16 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
- key->partcollation, lower->datums,
- lower->kind, true, upper) >= 0)
+ cmpval = partition_rbound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation, lower->datums,
+ lower->kind, true, upper);
+ if (cmpval >= 0)
{
+ /* Fetch the problem bound from lower datums list. */
+ PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
+ cmpval - 1);
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("empty range bound specified for partition \"%s\"",
@@ -2990,7 +3000,7 @@ check_new_partition_bound(char *relname, Relation parent,
errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
get_range_partbound_string(spec->lowerdatums),
get_range_partbound_string(spec->upperdatums)),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, datum->location)));
}
if (partdesc->nparts > 0)
@@ -3022,7 +3032,7 @@ check_new_partition_bound(char *relname, Relation parent,
key->partsupfunc,
key->partcollation,
boundinfo, lower,
- &equal);
+ &equal, &cmpval);
if (boundinfo->indexes[offset + 1] < 0)
{
@@ -3034,7 +3044,6 @@ check_new_partition_bound(char *relname, Relation parent,
*/
if (offset + 1 < boundinfo->ndatums)
{
- int32 cmpval;
Datum *datums;
PartitionRangeDatumKind *kind;
bool is_lower;
@@ -3056,6 +3065,8 @@ check_new_partition_bound(char *relname, Relation parent,
* offset + 2.
*/
overlap = true;
+ overlap_location = ((PartitionRangeDatum *)
+ list_nth(spec->upperdatums, -cmpval - 1))->location;
with = boundinfo->indexes[offset + 2];
}
}
@@ -3066,7 +3077,13 @@ check_new_partition_bound(char *relname, Relation parent,
* The new partition overlaps with the existing
* partition between offset and offset + 1.
*/
+ Datum *datum;
+
overlap = true;
+ Assert(cmpval >= 0);
+ datum = cmpval == 0 ? linitial(spec->lowerdatums):
+ list_nth(spec->lowerdatums, cmpval - 1);
+ overlap_location = ((PartitionRangeDatum *)datum)->location;
with = boundinfo->indexes[offset + 1];
}
}
@@ -3082,11 +3099,12 @@ check_new_partition_bound(char *relname, Relation parent,
if (overlap)
{
Assert(with >= 0);
+ Assert(overlap_location > 0);
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("partition \"%s\" would overlap partition \"%s\"",
relname, get_rel_name(partdesc->oids[with])),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, overlap_location)));
}
}
@@ -3320,7 +3338,9 @@ make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
* partition_rbound_cmp
*
* Return for two range bounds whether the 1st one (specified in datums1,
- * kind1, and lower1) is <, =, or > the bound specified in *b2.
+ * kind1, and lower1) is <, =, or > the bound specified in *b2. 0 is returned if
+ * equal and the 1-based index of the first mismatching bound if unequal;
+ * multiplied by -1 if the 1st bound is smaller.
*
* partnatts, partsupfunc and partcollation give the number of attributes in the
* bounds to be compared, comparison function to be used and the collations of
@@ -3340,6 +3360,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
bool lower1, PartitionRangeBound *b2)
{
int32 cmpval = 0; /* placate compiler */
+ int result = 0;
int i;
Datum *datums2 = b2->datums;
PartitionRangeDatumKind *kind2 = b2->kind;
@@ -3347,6 +3368,8 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
for (i = 0; i < partnatts; i++)
{
+ result++;
+
/*
* First, handle cases where the column is unbounded, which should not
* invoke the comparison procedure, and should not consider any later
@@ -3354,9 +3377,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* compare the same way as the values they represent.
*/
if (kind1[i] < kind2[i])
- return -1;
+ return -result;
else if (kind1[i] > kind2[i])
- return 1;
+ return result;
else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
/*
@@ -3381,9 +3404,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* two.
*/
if (cmpval == 0 && lower1 != lower2)
- cmpval = lower1 ? 1 : -1;
+ cmpval = lower1 ? result : -result;
- return cmpval;
+ return cmpval == 0 ? 0 : (cmpval < 0 ? -result : result);;
}
/*
@@ -3495,7 +3518,7 @@ static int
partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal)
+ PartitionRangeBound *probe, bool *is_equal, int32 *cmpval)
{
int lo,
hi,
@@ -3505,19 +3528,17 @@ partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
hi = boundinfo->ndatums - 1;
while (lo < hi)
{
- int32 cmpval;
-
mid = (lo + hi + 1) / 2;
- cmpval = partition_rbound_cmp(partnatts, partsupfunc,
+ *cmpval = partition_rbound_cmp(partnatts, partsupfunc,
partcollation,
boundinfo->datums[mid],
boundinfo->kind[mid],
(boundinfo->indexes[mid] == -1),
probe);
- if (cmpval <= 0)
+ if (*cmpval <= 0)
{
lo = mid;
- *is_equal = (cmpval == 0);
+ *is_equal = (*cmpval == 0);
if (*is_equal)
break;
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index dfc720720b..c82f77d02f 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -14,6 +14,7 @@
#include "fmgr.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
+#include "parser/parse_node.h"
#include "partitioning/partdefs.h"
#include "utils/relcache.h"
struct RelOptInfo; /* avoid including pathnodes.h here */
@@ -98,7 +99,8 @@ extern PartitionBoundInfo partition_bounds_merge(int partnatts,
List **inner_parts);
extern bool partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts);
extern void check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec);
+ PartitionBoundSpec *spec,
+ ParseState *pstate);
extern void check_default_partition_contents(Relation parent,
Relation defaultRel,
PartitionBoundSpec *new_spec);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 002079601f..4fae37e022 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3812,6 +3812,8 @@ SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_1'::reg
CREATE TABLE fail_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
ERROR: partition "fail_part" would overlap partition "part_1"
+LINE 1: ...LE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
+ ^
DROP TABLE fail_part;
-- check that an existing table can be attached as a default partition
CREATE TABLE def_part (LIKE list_parted INCLUDING CONSTRAINTS);
@@ -3821,6 +3823,8 @@ ALTER TABLE list_parted ATTACH PARTITION def_part DEFAULT;
CREATE TABLE fail_def_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT;
ERROR: partition "fail_def_part" conflicts with existing default partition "def_part"
+LINE 1: ...ER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT;
+ ^
-- check validation when attaching list partitions
CREATE TABLE list_parted2 (
a int,
@@ -3890,6 +3894,8 @@ CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT;
CREATE TABLE partr_def2 (LIKE part1 INCLUDING CONSTRAINTS);
ALTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT;
ERROR: partition "partr_def2" conflicts with existing default partition "partr_def1"
+LINE 1: ...LTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT;
+ ^
-- Overlapping partitions cannot be attached, hence, following should give error
INSERT INTO partr_def1 VALUES (2, 10);
CREATE TABLE part3 (LIKE range_parted);
@@ -4010,8 +4016,12 @@ CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAIN
CREATE TABLE fail_part (LIKE hpart_1);
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 4);
ERROR: partition "fail_part" would overlap partition "hpart_1"
+LINE 1: ...hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODU...
+ ^
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 0);
ERROR: partition "fail_part" would overlap partition "hpart_1"
+LINE 1: ...hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODU...
+ ^
DROP TABLE fail_part;
-- check validation when attaching hash partitions
-- check that violating rows are correctly reported
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 1c72f23bc9..41dce69cc4 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -677,6 +677,8 @@ LINE 1: ...BLE fail_part PARTITION OF list_parted FOR VALUES WITH (MODU...
CREATE TABLE part_default PARTITION OF list_parted DEFAULT;
CREATE TABLE fail_default_part PARTITION OF list_parted DEFAULT;
ERROR: partition "fail_default_part" conflicts with existing default partition "part_default"
+LINE 1: ...TE TABLE fail_default_part PARTITION OF list_parted DEFAULT;
+ ^
-- specified literal can't be cast to the partition column data type
CREATE TABLE bools (
a bool
@@ -702,6 +704,8 @@ CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10);
-- fails due to overlap:
CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
ERROR: partition "bigintp_10_2" would overlap partition "bigintp_10"
+LINE 1: ...ABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10');
+ ^
DROP TABLE bigintp;
CREATE TABLE range_parted (
a date
@@ -823,8 +827,12 @@ CREATE TABLE part_ab PARTITION OF list_parted2 FOR VALUES IN ('a', 'b');
CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT;
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
ERROR: partition "fail_part" would overlap partition "part_null_z"
+LINE 1: ...LE fail_part PARTITION OF list_parted2 FOR VALUES IN (null);
+ ^
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
ERROR: partition "fail_part" would overlap partition "part_ab"
+LINE 1: ...ail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c');
+ ^
-- check default partition overlap
INSERT INTO list_parted2 VALUES('X');
CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('W', 'X', 'Y');
@@ -835,28 +843,42 @@ CREATE TABLE range_parted2 (
-- trying to create range partition with empty range
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (0).
-- note that the range '[1, 1)' has no elements
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
ERROR: empty range bound specified for partition "fail_part"
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
+ ^
DETAIL: Specified lower bound (1) is greater than or equal to upper bound (1).
CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2);
ERROR: partition "fail_part" would overlap partition "part0"
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) ...
+ ^
CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (maxvalue);
ERROR: partition "fail_part" would overlap partition "part1"
+LINE 1: ..._part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (max...
+ ^
CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30);
CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
ERROR: partition "fail_part" would overlap partition "part2"
+LINE 1: ...art PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
+ ^
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
ERROR: partition "fail_part" would overlap partition "part2"
+LINE 1: ...art PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
+ ^
-- Create a default partition for range partitioned table
CREATE TABLE range2_default PARTITION OF range_parted2 DEFAULT;
-- More than one default partition is not allowed, so this should give error
CREATE TABLE fail_default_part PARTITION OF range_parted2 DEFAULT;
ERROR: partition "fail_default_part" conflicts with existing default partition "range2_default"
+LINE 1: ... TABLE fail_default_part PARTITION OF range_parted2 DEFAULT;
+ ^
-- Check if the range for default partitions overlap
INSERT INTO range_parted2 VALUES (85);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (80) TO (90);
@@ -870,17 +892,23 @@ CREATE TABLE range_parted3 (
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, 1);
ERROR: partition "fail_part" would overlap partition "part00"
+LINE 1: ..._part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalu...
+ ^
CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, 1);
CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10);
CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, maxvalue);
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
ERROR: partition "fail_part" would overlap partition "part12"
+LINE 1: ...rt PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1,...
+ ^
CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT;
-- cannot create a partition that says column b is allowed to range
-- from -infinity to +infinity, while there exist partitions that have
-- more specific ranges
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, maxvalue);
ERROR: partition "fail_part" would overlap partition "part10"
+LINE 1: ..._part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalu...
+ ^
-- check for partition bound overlap and other invalid specifications for the hash partition
CREATE TABLE hash_parted2 (
a varchar
@@ -892,6 +920,8 @@ CREATE TABLE h2part_4 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMA
-- overlap with part_4
CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 1);
ERROR: partition "fail_part" would overlap partition "h2part_4"
+LINE 1: ...LE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODU...
+ ^
-- modulus must be greater than zero
CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REMAINDER 1);
ERROR: modulus for hash partition must be a positive integer
--
2.27.0