default_range_partition_v10.patch
application/octet-stream
Filename: default_range_partition_v10.patch
Type: application/octet-stream
Part: 0
Message:
Re: Default Partition for Range
Patch
Format: unified
Series: patch v10
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 163 | 98 |
| src/backend/parser/parse_utilcmd.c | 0 | 5 |
| src/backend/utils/adt/ruleutils.c | 0 | 1 |
| src/test/regress/expected/alter_table.out | 13 | 0 |
| src/test/regress/expected/create_table.out | 11 | 3 |
| src/test/regress/expected/insert.out | 48 | 1 |
| src/test/regress/expected/sanity_check.out | 4 | 0 |
| src/test/regress/expected/update.out | 9 | 0 |
| src/test/regress/sql/alter_table.sql | 15 | 0 |
| src/test/regress/sql/create_table.sql | 12 | 2 |
| src/test/regress/sql/insert.sql | 30 | 1 |
| src/test/regress/sql/update.sql | 8 | 0 |
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index e0b9dec..75b03d2 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -127,7 +127,9 @@ static void get_range_key_properties(PartitionKey key, int keynum,
Expr **keyCol,
Const **lower_val, Const **upper_val);
static List *get_qual_for_list(Relation parent, PartitionBoundSpec *spec);
-static List *get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec);
+static List *get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
+ bool for_default);
+static List *get_range_nulltest(PartitionKey key);
static List *generate_partition_qual(Relation rel);
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
@@ -168,11 +170,11 @@ RelationBuildPartitionDesc(Relation rel)
MemoryContext oldcxt;
int ndatums = 0;
+ int default_index = -1;
/* List partitioning specific */
PartitionListValue **all_values = NULL;
int null_index = -1;
- int default_index = -1;
/* Range partitioning specific */
PartitionRangeBound **rbounds = NULL;
@@ -322,21 +324,18 @@ RelationBuildPartitionDesc(Relation rel)
}
else if (key->strategy == PARTITION_STRATEGY_RANGE)
{
- int j,
- k;
+ int k;
PartitionRangeBound **all_bounds,
*prev;
- bool *distinct_indexes;
all_bounds = (PartitionRangeBound **) palloc0(2 * nparts *
sizeof(PartitionRangeBound *));
- distinct_indexes = (bool *) palloc(2 * nparts * sizeof(bool));
/*
* Create a unified list of range bounds across all the
* partitions.
*/
- i = j = 0;
+ i = ndatums = 0;
foreach(cell, boundspecs)
{
PartitionBoundSpec *spec = castNode(PartitionBoundSpec,
@@ -347,30 +346,41 @@ RelationBuildPartitionDesc(Relation rel)
if (spec->strategy != PARTITION_STRATEGY_RANGE)
elog(ERROR, "invalid strategy in partition bound spec");
+ /*
+ * Note the index of the partition bound spec for the default
+ * partition. There's no datum to add to the allbounds array
+ * for this partition.
+ */
+ if (spec->is_default)
+ {
+ default_index = i++;
+ continue;
+ }
+
lower = make_one_range_bound(key, i, spec->lowerdatums,
true);
upper = make_one_range_bound(key, i, spec->upperdatums,
false);
- all_bounds[j] = lower;
- all_bounds[j + 1] = upper;
- j += 2;
+ all_bounds[ndatums++] = lower;
+ all_bounds[ndatums++] = upper;
i++;
}
- Assert(j == 2 * nparts);
+
+ Assert(ndatums == nparts * 2 ||
+ (default_index != -1 && ndatums == (nparts - 1) * 2));
/* Sort all the bounds in ascending order */
- qsort_arg(all_bounds, 2 * nparts,
+ qsort_arg(all_bounds, ndatums,
sizeof(PartitionRangeBound *),
qsort_partition_rbound_cmp,
(void *) key);
- /*
- * Count the number of distinct bounds to allocate an array of
- * that size.
- */
- ndatums = 0;
+ /* Pick out distinct datums and store it in a new array. */
+ rbounds = (PartitionRangeBound **) palloc(ndatums *
+ sizeof(PartitionRangeBound *));
+ k = 0;
prev = NULL;
- for (i = 0; i < 2 * nparts; i++)
+ for (i = 0; i < ndatums; i++)
{
PartitionRangeBound *cur = all_bounds[i];
bool is_distinct = false;
@@ -407,34 +417,17 @@ RelationBuildPartitionDesc(Relation rel)
}
/*
- * Count the current bound if it is distinct from the previous
- * one. Also, store if the index i contains a distinct bound
- * that we'd like put in the relcache array.
+ * If distinct, save into array from where they will be copied
+ * into the relcache.
*/
if (is_distinct)
- {
- distinct_indexes[i] = true;
- ndatums++;
- }
- else
- distinct_indexes[i] = false;
+ rbounds[k++] = all_bounds[i];
prev = cur;
}
- /*
- * Finally save them in an array from where they will be copied
- * into the relcache.
- */
- rbounds = (PartitionRangeBound **) palloc(ndatums *
- sizeof(PartitionRangeBound *));
- k = 0;
- for (i = 0; i < 2 * nparts; i++)
- {
- if (distinct_indexes[i])
- rbounds[k++] = all_bounds[i];
- }
- Assert(k == ndatums);
+ /* Update ndatums to hold the count of distinct datums. */
+ ndatums = k;
}
else
elog(ERROR, "unexpected partition strategy: %d",
@@ -581,6 +574,14 @@ RelationBuildPartitionDesc(Relation rel)
boundinfo->indexes[i] = mapping[orig_index];
}
}
+
+ /* Assign mapped index for the default partition. */
+ if (default_index != -1)
+ {
+ Assert(default_index >= 0 && mapping[default_index] == -1);
+ mapping[default_index] = next_index++;
+ boundinfo->default_index = mapping[default_index];
+ }
boundinfo->indexes[i] = -1;
break;
}
@@ -787,8 +788,10 @@ check_new_partition_bound(char *relname, Relation parent,
int offset;
bool equal;
- Assert(boundinfo && boundinfo->ndatums > 0 &&
- boundinfo->strategy == PARTITION_STRATEGY_RANGE);
+ Assert(boundinfo &&
+ boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
+ (boundinfo->ndatums > 0 ||
+ partition_bound_has_default(boundinfo)));
/*
* Test whether the new lower bound (which is treated
@@ -880,10 +883,10 @@ check_default_allows_bound(Relation parent, Relation default_rel,
List *all_parts;
ListCell *lc;
- /* Currently default partition is supported only for LIST partition. */
- Assert(new_spec->strategy == PARTITION_STRATEGY_LIST);
- new_part_constraints = get_qual_for_list(parent, new_spec);
+ new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
+ ? get_qual_for_list(parent, new_spec)
+ : get_qual_for_range(parent, new_spec, false);
def_part_constraints =
get_default_part_validation_constraint(new_part_constraints);
@@ -1083,7 +1086,7 @@ get_qual_from_partbound(Relation rel, Relation parent,
case PARTITION_STRATEGY_RANGE:
Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
- my_qual = get_qual_for_range(key, spec);
+ my_qual = get_qual_for_range(parent, spec, false);
break;
default:
@@ -1706,6 +1709,53 @@ get_range_key_properties(PartitionKey key, int keynum,
*upper_val = NULL;
}
+ /*
+ * get_range_nulltest
+ *
+ * A non-default range partition table does not currently allow partition
+ * keys to be null, so emit an IS NOT NULL expression for each key column.
+ */
+static List *
+get_range_nulltest(PartitionKey key)
+{
+ List *result = NIL;
+ NullTest *nulltest;
+ ListCell *partexprs_item;
+ int i;
+
+ partexprs_item = list_head(key->partexprs);
+ for (i = 0; i < key->partnatts; i++)
+ {
+ Expr *keyCol;
+
+ if (key->partattrs[i] != 0)
+ {
+ keyCol = (Expr *) makeVar(1,
+ key->partattrs[i],
+ key->parttypid[i],
+ key->parttypmod[i],
+ key->parttypcoll[i],
+ 0);
+ }
+ else
+ {
+ if (partexprs_item == NULL)
+ elog(ERROR, "wrong number of partition key expressions");
+ keyCol = copyObject(lfirst(partexprs_item));
+ partexprs_item = lnext(partexprs_item);
+ }
+
+ nulltest = makeNode(NullTest);
+ nulltest->arg = keyCol;
+ nulltest->nulltesttype = IS_NOT_NULL;
+ nulltest->argisrow = false;
+ nulltest->location = -1;
+ result = lappend(result, nulltest);
+ }
+
+ return result;
+}
+
/*
* get_qual_for_range
*
@@ -1744,11 +1794,15 @@ get_range_key_properties(PartitionKey key, int keynum,
* In most common cases with only one partition column, say a, the following
* expression tree will be generated: a IS NOT NULL AND a >= al AND a < au
*
+ * For default partition, it returns the negation of the constraints of all
+ * the other partitions.
+ *
* If we end up with an empty result list, we return a single-member list
* containing a constant TRUE, because callers expect a non-empty list.
*/
static List *
-get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
+get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
+ bool for_default)
{
List *result = NIL;
ListCell *cell1,
@@ -1759,10 +1813,10 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
j;
PartitionRangeDatum *ldatum,
*udatum;
+ PartitionKey key = RelationGetPartitionKey(parent);
Expr *keyCol;
Const *lower_val,
*upper_val;
- NullTest *nulltest;
List *lower_or_arms,
*upper_or_arms;
int num_or_arms,
@@ -1772,44 +1826,70 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
bool need_next_lower_arm,
need_next_upper_arm;
- lower_or_start_datum = list_head(spec->lowerdatums);
- upper_or_start_datum = list_head(spec->upperdatums);
- num_or_arms = key->partnatts;
-
- /*
- * A range-partitioned table does not currently allow partition keys to be
- * null, so emit an IS NOT NULL expression for each key column.
- */
- partexprs_item = list_head(key->partexprs);
- for (i = 0; i < key->partnatts; i++)
+ if (spec->is_default)
{
- Expr *keyCol;
+ List *or_expr_args = NIL;
+ PartitionDesc pdesc = RelationGetPartitionDesc(parent);
+ Oid *inhoids = pdesc->oids;
+ int nparts = pdesc->nparts,
+ i;
- if (key->partattrs[i] != 0)
+ for (i = 0; i < nparts; i++)
{
- keyCol = (Expr *) makeVar(1,
- key->partattrs[i],
- key->parttypid[i],
- key->parttypmod[i],
- key->parttypcoll[i],
- 0);
+ Oid inhrelid = inhoids[i];
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull;
+ PartitionBoundSpec *bspec;
+
+ tuple = SearchSysCache1(RELOID, inhrelid);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", inhrelid);
+
+ datum = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_relpartbound,
+ &isnull);
+
+ Assert(!isnull);
+ bspec = (PartitionBoundSpec *) stringToNode(TextDatumGetCString(datum));
+
+ if (!bspec->is_default)
+ {
+ List *part_qual = get_qual_for_range(parent, bspec, true);
+
+ /*
+ * AND the constraints of the partition and add to
+ * or_expr_args
+ */
+ or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
+ ? makeBoolExpr(AND_EXPR, part_qual, -1)
+ : linitial(part_qual));
+ }
+ ReleaseSysCache(tuple);
}
- else
+
+ if (or_expr_args != NIL)
{
- if (partexprs_item == NULL)
- elog(ERROR, "wrong number of partition key expressions");
- keyCol = copyObject(lfirst(partexprs_item));
- partexprs_item = lnext(partexprs_item);
+ /* OR all the non-default partition constraints; then negate it */
+ result = lappend(result,
+ list_length(or_expr_args) > 1
+ ? makeBoolExpr(OR_EXPR, or_expr_args, -1)
+ : linitial(or_expr_args));
+ result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
}
+ else /* default is the only partition */
+ result = list_make1(makeBoolConst(true, false));
- nulltest = makeNode(NullTest);
- nulltest->arg = keyCol;
- nulltest->nulltesttype = IS_NOT_NULL;
- nulltest->argisrow = false;
- nulltest->location = -1;
- result = lappend(result, nulltest);
+ return result;
}
+ lower_or_start_datum = list_head(spec->lowerdatums);
+ upper_or_start_datum = list_head(spec->upperdatums);
+ num_or_arms = key->partnatts;
+
+ if (!for_default)
+ result = get_range_nulltest(key);
+
/*
* Iterate over the key columns and check if the corresponding lower and
* upper datums are equal using the btree equality operator for the
@@ -2033,7 +2113,9 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
/* As noted above, caller expects the list to be non-empty. */
if (result == NIL)
- result = list_make1(makeBoolConst(true, false));
+ result = for_default
+ ? get_range_nulltest(key)
+ : list_make1(makeBoolConst(true, false));
return result;
}
@@ -2201,8 +2283,7 @@ get_partition_for_tuple(PartitionDispatch *pd,
Datum values[PARTITION_MAX_KEYS];
bool isnull[PARTITION_MAX_KEYS];
int cur_offset,
- cur_index;
- int i,
+ cur_index = -1,
result;
ExprContext *ecxt = GetPerTupleExprContext(estate);
TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
@@ -2246,24 +2327,6 @@ get_partition_for_tuple(PartitionDispatch *pd,
ecxt->ecxt_scantuple = slot;
FormPartitionKeyDatum(parent, slot, estate, values, isnull);
- if (key->strategy == PARTITION_STRATEGY_RANGE)
- {
- /*
- * Since we cannot route tuples with NULL partition keys through a
- * range-partitioned table, simply return that no partition exists
- */
- for (i = 0; i < key->partnatts; i++)
- {
- if (isnull[i])
- {
- *failed_at = parent;
- *failed_slot = slot;
- result = -1;
- goto error_exit;
- }
- }
- }
-
/*
* If this is a NULL, route it to the null-accepting partition.
* Otherwise, route by searching the array of partition bounds.
@@ -2366,6 +2429,8 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
ListCell *lc;
int i;
+ Assert(datums != NULL);
+
bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
bound->index = index;
bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6830b64..6157613 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3307,11 +3307,6 @@ transformPartitionBound(ParseState *pstate, Relation parent,
if (spec->is_default)
{
- if (strategy != PARTITION_STRATEGY_LIST)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("default partition is supported only for a list partitioned table")));
-
/*
* In case of the default partition, parser had no way to identify the
* partition strategy. Assign the parent strategy to the default
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index e066159..67a5c62 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8700,7 +8700,6 @@ get_rule_expr(Node *node, deparse_context *context,
if (spec->is_default)
{
- Assert(strategy == PARTITION_STRATEGY_LIST);
appendStringInfoString(buf, "DEFAULT");
break;
}
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 9a5f6bf..6855d71 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3356,6 +3356,19 @@ CREATE TABLE part2 (
);
ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20);
INFO: partition constraint for table "part2" is implied by existing constraints
+-- Create default partition
+CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT;
+-- Only one default partition is allowed, hence, following should give error
+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"
+-- Overlapping partitions cannot be attached, hence, following should give error
+INSERT INTO partr_def1 VALUES (2, 10);
+CREATE TABLE part3 (LIKE range_parted);
+ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (2, 10) TO (2, 20);
+ERROR: updated partition constraint for default partition would be violated by some row
+-- Attaching partitions should be successful when there are no overlapping rows
+ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (3, 10) TO (3, 20);
-- check that leaf partitions are scanned when attaching a partitioned
-- table
CREATE TABLE part_5 (
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 9615c07..55914e4 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -517,9 +517,6 @@ ERROR: TO must specify exactly one value per partitioning column
-- cannot specify null values in range bounds
CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (null) TO (maxvalue);
ERROR: cannot specify NULL in range bound
--- range partition cannot have default partition
-CREATE TABLE fail_part PARTITION OF range_parted DEFAULT;
-ERROR: default partition is supported only for a list partitioned table
-- check if compatible with the specified parent
-- cannot create as partition of a non-partitioned table
CREATE TABLE unparted (
@@ -594,6 +591,16 @@ CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
ERROR: partition "fail_part" would overlap partition "part2"
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
ERROR: partition "fail_part" would overlap partition "part2"
+-- 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"
+-- 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);
+ERROR: updated partition constraint for default partition "range2_default" would be violated by some row
+CREATE TABLE part4 PARTITION OF range_parted2 FOR VALUES FROM (90) TO (100);
-- now check for multi-column range partition key
CREATE TABLE range_parted3 (
a int,
@@ -607,6 +614,7 @@ 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"
+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
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 3319f6d..cc28bdc 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -271,6 +271,18 @@ insert into range_parted values ('b', 10);
insert into range_parted values ('a');
ERROR: no partition of relation "range_parted" found for row
DETAIL: Partition key of the failing row contains (a, (b + 0)) = (a, null).
+-- Check default partition
+create table part_def partition of range_parted default;
+-- fail
+insert into part_def values ('b', 10);
+ERROR: new row for relation "part_def" violates partition constraint
+DETAIL: Failing row contains (b, 10).
+-- ok
+insert into part_def values ('c', 10);
+insert into range_parted values (null, null);
+insert into range_parted values ('a', null);
+insert into range_parted values (null, 19);
+insert into range_parted values ('b', 20);
select tableoid::regclass, * from range_parted;
tableoid | a | b
----------+---+----
@@ -280,7 +292,12 @@ select tableoid::regclass, * from range_parted;
part3 | b | 1
part4 | b | 10
part4 | b | 10
-(6 rows)
+ part_def | c | 10
+ part_def | |
+ part_def | a |
+ part_def | | 19
+ part_def | b | 20
+(11 rows)
-- ok
insert into list_parted values (null, 1);
@@ -467,6 +484,36 @@ insert into mlparted5 (a, b, c) values (1, 40, 'a');
ERROR: new row for relation "mlparted5a" violates partition constraint
DETAIL: Failing row contains (b, 1, 40).
drop table mlparted5;
+alter table mlparted drop constraint check_b;
+-- Check multi-level default partition
+create table mlparted_def partition of mlparted default partition by range(a);
+create table mlparted_def1 partition of mlparted_def for values from (40) to (50);
+create table mlparted_def2 partition of mlparted_def for values from (50) to (60);
+insert into mlparted values (40, 100);
+insert into mlparted_def1 values (42, 100);
+insert into mlparted_def2 values (54, 50);
+-- fail
+insert into mlparted values (70, 100);
+ERROR: no partition of relation "mlparted_def" found for row
+DETAIL: Partition key of the failing row contains (a) = (70).
+insert into mlparted_def1 values (52, 50);
+ERROR: new row for relation "mlparted_def1" violates partition constraint
+DETAIL: Failing row contains (52, 50, null).
+insert into mlparted_def2 values (34, 50);
+ERROR: new row for relation "mlparted_def2" violates partition constraint
+DETAIL: Failing row contains (34, 50, null).
+-- ok
+create table mlparted_defd partition of mlparted_def default;
+insert into mlparted values (70, 100);
+select tableoid::regclass, * from mlparted_def;
+ tableoid | a | b | c
+---------------+----+-----+---
+ mlparted_def1 | 40 | 100 |
+ mlparted_def1 | 42 | 100 |
+ mlparted_def2 | 54 | 50 |
+ mlparted_defd | 70 | 100 |
+(4 rows)
+
-- check that message shown after failure to find a partition shows the
-- appropriate key description (or none) in various situations
create table key_desc (a int, b int) partition by list ((a+0));
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 6750152..e996640 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -77,6 +77,10 @@ mlparted12|f
mlparted2|f
mlparted3|f
mlparted4|f
+mlparted_def|f
+mlparted_def1|f
+mlparted_def2|f
+mlparted_defd|f
money_data|f
num_data|f
num_exp_add|t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index 9912ef2..cfb6aa8 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -218,6 +218,15 @@ ERROR: new row for relation "part_b_10_b_20" violates partition constraint
DETAIL: Failing row contains (b, 9).
-- ok
update range_parted set b = b + 1 where b = 10;
+-- Creating default partition for range
+create table part_def partition of range_parted default;
+insert into range_parted values ('c', 9);
+-- ok
+update part_def set a = 'd' where a = 'c';
+-- fail
+update part_def set a = 'a' where a = 'd';
+ERROR: new row for relation "part_def" violates partition constraint
+DETAIL: Failing row contains (a, 9).
create table list_parted (
a text,
b int
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 935b24b..d4ca7f1 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2181,6 +2181,21 @@ CREATE TABLE part2 (
);
ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20);
+-- Create default partition
+CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT;
+
+-- Only one default partition is allowed, hence, following should give error
+CREATE TABLE partr_def2 (LIKE part1 INCLUDING CONSTRAINTS);
+ALTER 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);
+ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (2, 10) TO (2, 20);
+
+-- Attaching partitions should be successful when there are no overlapping rows
+ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (3, 10) TO (3, 20);
+
-- check that leaf partitions are scanned when attaching a partitioned
-- table
CREATE TABLE part_5 (
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
index 1eefa0e..e4fde85 100644
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -486,8 +486,6 @@ CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a') TO ('z',
-- cannot specify null values in range bounds
CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (null) TO (maxvalue);
--- range partition cannot have default partition
-CREATE TABLE fail_part PARTITION OF range_parted DEFAULT;
-- check if compatible with the specified parent
@@ -554,6 +552,17 @@ 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);
CREATE TABLE fail_part 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;
+
+-- 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);
+CREATE TABLE part4 PARTITION OF range_parted2 FOR VALUES FROM (90) TO (100);
+
-- now check for multi-column range partition key
CREATE TABLE range_parted3 (
a int,
@@ -567,6 +576,7 @@ CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO
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);
+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
diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql
index 7bf1778..6f5689f 100644
--- a/src/test/regress/sql/insert.sql
+++ b/src/test/regress/sql/insert.sql
@@ -168,8 +168,19 @@ insert into range_parted values ('b', 1);
insert into range_parted values ('b', 10);
-- fail (partition key (b+0) is null)
insert into range_parted values ('a');
-select tableoid::regclass, * from range_parted;
+-- Check default partition
+create table part_def partition of range_parted default;
+-- fail
+insert into part_def values ('b', 10);
+-- ok
+insert into part_def values ('c', 10);
+insert into range_parted values (null, null);
+insert into range_parted values ('a', null);
+insert into range_parted values (null, 19);
+insert into range_parted values ('b', 20);
+
+select tableoid::regclass, * from range_parted;
-- ok
insert into list_parted values (null, 1);
insert into list_parted (a) values ('aA');
@@ -299,6 +310,24 @@ create function mlparted5abrtrig_func() returns trigger as $$ begin new.c = 'b';
create trigger mlparted5abrtrig before insert on mlparted5a for each row execute procedure mlparted5abrtrig_func();
insert into mlparted5 (a, b, c) values (1, 40, 'a');
drop table mlparted5;
+alter table mlparted drop constraint check_b;
+
+-- Check multi-level default partition
+create table mlparted_def partition of mlparted default partition by range(a);
+create table mlparted_def1 partition of mlparted_def for values from (40) to (50);
+create table mlparted_def2 partition of mlparted_def for values from (50) to (60);
+insert into mlparted values (40, 100);
+insert into mlparted_def1 values (42, 100);
+insert into mlparted_def2 values (54, 50);
+-- fail
+insert into mlparted values (70, 100);
+insert into mlparted_def1 values (52, 50);
+insert into mlparted_def2 values (34, 50);
+-- ok
+create table mlparted_defd partition of mlparted_def default;
+insert into mlparted values (70, 100);
+
+select tableoid::regclass, * from mlparted_def;
-- check that message shown after failure to find a partition shows the
-- appropriate key description (or none) in various situations
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 44fb0dc..1ce7a94 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -125,6 +125,14 @@ update range_parted set b = b - 1 where b = 10;
-- ok
update range_parted set b = b + 1 where b = 10;
+-- Creating default partition for range
+create table part_def partition of range_parted default;
+insert into range_parted values ('c', 9);
+-- ok
+update part_def set a = 'd' where a = 'c';
+-- fail
+update part_def set a = 'a' where a = 'd';
+
create table list_parted (
a text,
b int