diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index b31c272..47e5495 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -70,10 +70,11 @@
  * is an upper bound.
  */
 
-/* Ternary value to represent what's contained in a range bound datum */
+/* Value to represent what's contained in a range bound datum */
 typedef enum RangeDatumContent
 {
 	RANGE_DATUM_FINITE = 0,		/* actual datum stored elsewhere */
+	RANGE_DATUM_DEFAULT,		/* default */
 	RANGE_DATUM_NEG_INF,		/* negative infinity */
 	RANGE_DATUM_POS_INF			/* positive infinity */
 } RangeDatumContent;
@@ -136,7 +137,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,
@@ -177,11 +180,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;
@@ -356,6 +359,9 @@ RelationBuildPartitionDesc(Relation rel)
 				if (spec->strategy != PARTITION_STRATEGY_RANGE)
 					elog(ERROR, "invalid strategy in partition bound spec");
 
+				if (spec->is_default)
+					default_index = i;
+
 				lower = make_one_range_bound(key, i, spec->lowerdatums,
 											 true);
 				upper = make_one_range_bound(key, i, spec->upperdatums,
@@ -396,9 +402,23 @@ RelationBuildPartitionDesc(Relation rel)
 						break;
 					}
 
+					if (cur->content[i] == RANGE_DATUM_DEFAULT)
+					{
+						/*
+						 * Both the upper and lower bound of default partition
+						 * are same, and the upper bound is used to set the
+						 * mapping later, hence only the upper bound is
+						 * considered distinct.
+						 */
+						if (!cur->lower)
+							is_distinct = true;
+						break;
+					}
+
 					/*
 					 * If either of them has infinite element, we can't equate
-					 * them.  Even when both are infinite, they'd have
+					 * them. One of them could be default and the other
+					 * infinte and even if both are infinite, they'd have
 					 * opposite signs, because only one of cur and prev is a
 					 * lower bound).
 					 */
@@ -595,6 +615,8 @@ RelationBuildPartitionDesc(Relation rel)
 						}
 					}
 					boundinfo->indexes[i] = -1;
+					if (default_index != -1)
+						boundinfo->default_index = mapping[default_index];
 					break;
 				}
 
@@ -783,6 +805,7 @@ check_new_partition_bound(char *relname, Relation parent,
 						   *upper;
 
 				Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
+
 				lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
 				upper = make_one_range_bound(key, -1, spec->upperdatums, false);
 
@@ -895,10 +918,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);
 
@@ -1098,7 +1121,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:
@@ -1720,6 +1743,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
  *
@@ -1760,11 +1830,15 @@ get_range_key_properties(PartitionKey key, int keynum,
  * does not really have a constraint, except the IS NOT NULL constraint for
  * partition keys.
  *
+ * 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,
@@ -1775,10 +1849,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,
@@ -1788,44 +1862,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
@@ -2039,7 +2139,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;
 }
@@ -2207,8 +2309,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;
@@ -2252,24 +2353,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.
@@ -2374,12 +2457,22 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
 
 	bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
 	bound->index = index;
-	bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
+	bound->datums = datums ? (Datum *) palloc0(key->partnatts * sizeof(Datum))
+		: NULL;
 	bound->content = (RangeDatumContent *) palloc0(key->partnatts *
 												   sizeof(RangeDatumContent));
 	bound->lower = lower;
 
 	i = 0;
+
+	/* datums are NULL for default */
+	if (datums == NULL)
+	{
+		for (i = 0; i < key->partnatts; i++)
+			bound->content[i] = RANGE_DATUM_DEFAULT;
+		return bound;
+	}
+
 	foreach(lc, datums)
 	{
 		PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc));
@@ -2443,13 +2536,19 @@ partition_rbound_cmp(PartitionKey key,
 	for (i = 0; i < key->partnatts; i++)
 	{
 		/*
-		 * First, handle cases where the column is unbounded, which should not
-		 * invoke the comparison procedure, and should not consider any later
-		 * columns.
+		 * First, handle cases where the column is unbounded or default, which
+		 * should not invoke the comparison procedure, and should not consider
+		 * any later columns.
 		 */
 		if (content1[i] != RANGE_DATUM_FINITE ||
 			content2[i] != RANGE_DATUM_FINITE)
 		{
+
+			/* Return zero if both are default */
+			if (content1[i] == RANGE_DATUM_DEFAULT &&
+				content2[i] == RANGE_DATUM_DEFAULT)
+				return 0;
+
 			/*
 			 * If the bound values are equal, fall through and compare whether
 			 * they are upper or lower bounds.
@@ -2457,15 +2556,20 @@ partition_rbound_cmp(PartitionKey key,
 			if (content1[i] == content2[i])
 				break;
 
-			/* Otherwise, one bound is definitely larger than the other */
-			if (content1[i] == RANGE_DATUM_NEG_INF)
+			/*
+			 * Otherwise, one bound is definitely larger than the other. The
+			 * order is - DEFAULT, NEG_INF, FINTE, POS_INF
+			 */
+			if (content1[i] == RANGE_DATUM_DEFAULT)
 				return -1;
 			else if (content1[i] == RANGE_DATUM_POS_INF)
 				return 1;
-			else if (content2[i] == RANGE_DATUM_NEG_INF)
+			else if (content2[i] == RANGE_DATUM_DEFAULT)
 				return 1;
 			else if (content2[i] == RANGE_DATUM_POS_INF)
 				return -1;
+			else				/* compare NEG_INF and FINITE */
+				return (content1[i] == RANGE_DATUM_NEG_INF ? -1 : 1);
 		}
 
 		cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
@@ -2504,6 +2608,9 @@ partition_rbound_datum_cmp(PartitionKey key,
 
 	for (i = 0; i < key->partnatts; i++)
 	{
+		if (rb_content[i] == RANGE_DATUM_DEFAULT)
+			continue;
+
 		if (rb_content[i] != RANGE_DATUM_FINITE)
 			return rb_content[i] == RANGE_DATUM_NEG_INF ? -1 : 1;
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 57c09c2..0b70919 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 b59ea5c..d5f66ef 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8668,7 +8668,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 c8e0bf4..b7da692 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 (unbounded);
 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
 -- cannot specify finite values after UNBOUNDED has been specified
 CREATE TABLE range_parted_multicol (a int, b int, c int) PARTITION BY RANGE (a, b, c);
 CREATE TABLE fail_part PARTITION OF range_parted_multicol FOR VALUES FROM (1, UNBOUNDED, 1) TO (UNBOUNDED, 1, 1);
@@ -601,6 +598,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,
@@ -614,6 +621,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, unbounded);
 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 72290cc..76a246d 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);
@@ -452,6 +469,35 @@ with ins (a, b, c) as
  mlparted4  | 1 |  30 |  39
 (5 rows)
 
+-- 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).
+insert into mlparted_def2 values (34, 50);
+ERROR:  new row for relation "mlparted_def2" violates partition constraint
+DETAIL:  Failing row contains (34, 50).
+-- 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  
+---------------+----+-----
+ 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 f44c0e0..2011cbf 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 (unbounded);
--- range partition cannot have default partition
-CREATE TABLE fail_part PARTITION OF range_parted DEFAULT;
 
 -- cannot specify finite values after UNBOUNDED has been specified
 CREATE TABLE range_parted_multicol (a int, b int, c int) PARTITION BY RANGE (a, b, c);
@@ -559,6 +557,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,
@@ -572,6 +581,7 @@ CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, unbounded) 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, unbounded);
 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 624f171..e2f6dff 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');
@@ -288,6 +299,23 @@ with ins (a, b, c) as
   (insert into mlparted (b, a) select s.a, 1 from generate_series(2, 39) s(a) returning tableoid::regclass, *)
   select a, b, min(c), max(c) from ins group by a, b order by 1;
 
+-- 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
 create table key_desc (a int, b int) partition by list ((a+0));
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
