Improve error cursor positions for problems with partition bounds.

We failed to pass down the query string to check_new_partition_bound,
so that its attempts to provide error cursor positions were for naught;
one must have the query string to get parser_errposition to do anything.
Adjust its API to require a ParseState to be passed down.

Also, improve the logic inside check_new_partition_bound so that the
cursor points at the partition bound for the specific column causing
the issue, when one can be identified.

That part is also for naught if we can't determine the query position of
the column with the problem.  Improve transformPartitionBoundValue so
that it makes sure that const-simplified partition expressions will be
properly labeled with positions.  In passing, skip calling evaluate_expr
if the value is already a Const, which is surely the most common case.

Alexandra Wang, Ashwin Agrawal, Amit Langote; reviewed by Ashutosh Bapat

Discussion: https://postgr.es/m/CACiyaSopZoqssfMzgHk6fAkp01cL6vnqBdmTw2C5_KJaFR_aMg@mail.gmail.com
Discussion: https://postgr.es/m/CAJV4CdrZ5mKuaEsRSbLf2URQ3h6iMtKD=hik8MaF5WwdmC9uZw@mail.gmail.com
This commit is contained in:
Tom Lane 2020-09-23 18:04:53 -04:00
parent 3ea7e9550e
commit 6b2c4e59d0
6 changed files with 160 additions and 58 deletions

View File

@ -543,7 +543,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,
@ -1007,7 +1008,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
@ -4718,7 +4719,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);
@ -16280,7 +16282,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;
@ -16295,6 +16298,9 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
const char *trigger_name;
Oid defaultPartOid;
List *partBoundConstraint;
ParseState *pstate = make_parsestate(NULL);
pstate->p_sourcetext = context->queryString;
/*
* We must lock the default partition if one exists, because attaching a
@ -16460,7 +16466,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
* error.
*/
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);

View File

@ -4164,7 +4164,7 @@ validateInfiniteBounds(ParseState *pstate, List *blist)
}
/*
* Transform one constant in a partition bound spec
* Transform one entry in a partition bound spec, producing a constant.
*/
static Const *
transformPartitionBoundValue(ParseState *pstate, Node *val,
@ -4176,6 +4176,13 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
/* Transform raw parsetree */
value = transformExpr(pstate, val, EXPR_KIND_PARTITION_BOUND);
/*
* transformExpr() should have already rejected column references,
* subqueries, aggregates, window functions, and SRFs, based on the
* EXPR_KIND_ of a partition bound expression.
*/
Assert(!contain_var_clause(value));
/*
* Check that the input expression's collation is compatible with one
* specified for the parent's partition key (partcollation). Don't throw
@ -4220,7 +4227,11 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
parser_errposition(pstate, exprLocation(value))));
}
/* Coerce to correct type */
/*
* Coerce to the correct type. This might cause an explicit coercion step
* to be added on top of the expression, which must be evaluated before
* returning the result to the caller.
*/
value = coerce_to_target_type(pstate,
value, exprType(value),
colType,
@ -4236,25 +4247,35 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
format_type_be(colType), colName),
parser_errposition(pstate, exprLocation(val))));
/* Simplify the expression, in case we had a coercion */
/*
* Evaluate the expression, if needed, assigning the partition key's data
* type and collation to the resulting Const node.
*/
if (!IsA(value, Const))
{
value = (Node *) expression_planner((Expr *) value);
value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod,
partCollation);
if (!IsA(value, Const))
elog(ERROR, "could not evaluate partition bound expression");
}
else
{
/*
* If the expression is already a Const, as is often the case, we can
* skip the rather expensive steps above. But we still have to insert
* the right collation, since coerce_to_target_type doesn't handle
* that.
*/
((Const *) value)->constcollid = partCollation;
}
/*
* transformExpr() should have already rejected column references,
* subqueries, aggregates, window functions, and SRFs, based on the
* EXPR_KIND_ for a default expression.
* Attach original expression's parse location to the Const, so that
* that's what will be reported for any later errors related to this
* partition bound.
*/
Assert(!contain_var_clause(value));
/*
* Evaluate the expression, assigning the partition key's collation to the
* resulting Const expression.
*/
value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod,
partCollation);
if (!IsA(value, Const))
elog(ERROR, "could not evaluate partition bound expression");
((Const *) value)->location = exprLocation(val);
return (Const *) value;
}

View File

@ -223,7 +223,7 @@ 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, 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);
@ -2805,14 +2805,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 = -1;
if (spec->is_default)
{
@ -2907,6 +2907,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;
}
@ -2935,6 +2936,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
Const *val = castNode(Const, lfirst(cell));
overlap_location = val->location;
if (!val->constisnull)
{
int offset;
@ -2968,6 +2970,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);
@ -2977,10 +2980,17 @@ 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 problematic key from the 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\"",
@ -2988,13 +2998,12 @@ 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)
{
int offset;
bool equal;
Assert(boundinfo &&
boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
@ -3020,7 +3029,7 @@ check_new_partition_bound(char *relname, Relation parent,
key->partsupfunc,
key->partcollation,
boundinfo, lower,
&equal);
&cmpval);
if (boundinfo->indexes[offset + 1] < 0)
{
@ -3032,7 +3041,6 @@ check_new_partition_bound(char *relname, Relation parent,
*/
if (offset + 1 < boundinfo->ndatums)
{
int32 cmpval;
Datum *datums;
PartitionRangeDatumKind *kind;
bool is_lower;
@ -3048,12 +3056,20 @@ check_new_partition_bound(char *relname, Relation parent,
is_lower, upper);
if (cmpval < 0)
{
/*
* Fetch the problematic key from the upper
* datums list.
*/
PartitionRangeDatum *datum =
list_nth(spec->upperdatums, -cmpval - 1);
/*
* The new partition overlaps with the
* existing partition between offset + 1 and
* offset + 2.
*/
overlap = true;
overlap_location = datum->location;
with = boundinfo->indexes[offset + 2];
}
}
@ -3064,7 +3080,20 @@ check_new_partition_bound(char *relname, Relation parent,
* The new partition overlaps with the existing
* partition between offset and offset + 1.
*/
PartitionRangeDatum *datum;
/*
* Fetch the problematic key from the lower datums
* list. Given the way partition_range_bsearch()
* works, the new lower bound is certainly >= the
* bound at offset. If the bound matches exactly, we
* flag the 1st key.
*/
Assert(cmpval >= 0);
datum = cmpval == 0 ? linitial(spec->lowerdatums) :
list_nth(spec->lowerdatums, cmpval - 1);
overlap = true;
overlap_location = datum->location;
with = boundinfo->indexes[offset + 1];
}
}
@ -3084,7 +3113,7 @@ check_new_partition_bound(char *relname, Relation parent,
(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)));
}
}
@ -3317,8 +3346,12 @@ 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.
* For two range bounds this decides whether the 1st one (specified by
* datums1, kind1, and lower1) is <, =, or > the bound specified in *b2.
*
* 0 is returned if they are equal, otherwise a non-zero integer whose sign
* indicates the ordering, and whose absolute value gives the 1-based
* partition key number of the first mismatching column.
*
* partnatts, partsupfunc and partcollation give the number of attributes in the
* bounds to be compared, comparison function to be used and the collations of
@ -3337,6 +3370,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
Datum *datums1, PartitionRangeDatumKind *kind1,
bool lower1, PartitionRangeBound *b2)
{
int32 colnum = 0;
int32 cmpval = 0; /* placate compiler */
int i;
Datum *datums2 = b2->datums;
@ -3345,6 +3379,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
for (i = 0; i < partnatts; i++)
{
/* Track column number in case we need it for result */
colnum++;
/*
* First, handle cases where the column is unbounded, which should not
* invoke the comparison procedure, and should not consider any later
@ -3352,9 +3389,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 -colnum;
else if (kind1[i] > kind2[i])
return 1;
return colnum;
else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
/*
@ -3381,7 +3418,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
if (cmpval == 0 && lower1 != lower2)
cmpval = lower1 ? 1 : -1;
return cmpval;
return cmpval == 0 ? 0 : (cmpval < 0 ? -colnum : colnum);
}
/*
@ -3393,7 +3430,6 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* n_tuple_datums, partsupfunc and partcollation give number of attributes in
* the bounds to be compared, comparison function to be used and the collations
* of attributes resp.
*
*/
int32
partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
@ -3486,14 +3522,17 @@ partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
* equal to the given range bound or -1 if all of the range bounds are
* greater
*
* *is_equal is set to true if the range bound at the returned index is equal
* to the input range bound
* Upon return from this function, *cmpval is set to 0 if the bound at the
* returned index matches the input range bound exactly, otherwise a
* non-zero integer whose sign indicates the ordering, and whose absolute
* value gives the 1-based partition key number of the first mismatching
* column.
*/
static int
partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
PartitionRangeBound *probe, bool *is_equal)
PartitionRangeBound *probe, int32 *cmpval)
{
int lo,
hi,
@ -3503,21 +3542,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,
partcollation,
boundinfo->datums[mid],
boundinfo->kind[mid],
(boundinfo->indexes[mid] == -1),
probe);
if (cmpval <= 0)
*cmpval = partition_rbound_cmp(partnatts, partsupfunc,
partcollation,
boundinfo->datums[mid],
boundinfo->kind[mid],
(boundinfo->indexes[mid] == -1),
probe);
if (*cmpval <= 0)
{
lo = mid;
*is_equal = (cmpval == 0);
if (*is_equal)
if (*cmpval == 0)
break;
}
else
@ -3528,7 +3563,7 @@ partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
}
/*
* partition_range_bsearch
* partition_range_datum_bsearch
* Returns the index of the greatest range bound that is less than or
* equal to the given tuple or -1 if all of the range bounds are greater
*

View File

@ -12,10 +12,9 @@
#define PARTBOUNDS_H
#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 +97,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);

View File

@ -3868,6 +3868,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);
@ -3877,6 +3879,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,
@ -3946,6 +3950,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);
@ -4066,8 +4072,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

View File

@ -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