diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 0d7b133..c66c38c 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -72,7 +72,8 @@ typedef enum RangeDatumContent
 {
 	RANGE_DATUM_FINITE = 0,		/* actual datum stored elsewhere */
 	RANGE_DATUM_NEG_INF,		/* negative infinity */
-	RANGE_DATUM_POS_INF			/* positive infinity */
+	RANGE_DATUM_POS_INF,		/* positive infinity */
+	RANGE_DATUM_DEFAULT			/* default */
 } RangeDatumContent;
 
 typedef struct PartitionBoundInfoData
@@ -137,7 +138,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,
@@ -180,11 +183,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;
@@ -359,6 +362,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,
@@ -556,6 +562,7 @@ RelationBuildPartitionDesc(Relation rel)
 												sizeof(RangeDatumContent *));
 					boundinfo->indexes = (int *) palloc((ndatums + 1) *
 														sizeof(int));
+					boundinfo->default_index = -1;
 
 					for (i = 0; i < ndatums; i++)
 					{
@@ -577,6 +584,13 @@ RelationBuildPartitionDesc(Relation rel)
 							boundinfo->content[i][j] = rbounds[i]->content[j];
 						}
 
+						/* Assign mapping index for default partition. */
+						if (default_index != -1 && mapping[default_index] == -1)
+						{
+							mapping[default_index] = next_index++;
+							boundinfo->default_index = mapping[default_index];
+						}
+
 						/*
 						 * There is no mapping for invalid indexes.
 						 *
@@ -599,10 +613,8 @@ RelationBuildPartitionDesc(Relation rel)
 							boundinfo->indexes[i] = mapping[orig_index];
 						}
 					}
-					boundinfo->indexes[i] = -1;
 
-					/* As of now, we do not support default range partition. */
-					boundinfo->default_index = -1;
+					boundinfo->indexes[i] = -1;
 					break;
 				}
 
@@ -728,7 +740,7 @@ check_new_partition_bound(char *relname, Relation parent,
 			{
 				Assert(spec->strategy == PARTITION_STRATEGY_LIST);
 
-				if (partdesc->nparts > 0)
+				if (!spec->is_default && partdesc->nparts > 0)
 				{
 					ListCell   *cell;
 
@@ -738,20 +750,6 @@ check_new_partition_bound(char *relname, Relation parent,
 							partition_bound_accepts_nulls(boundinfo) ||
 							partition_bound_has_default(boundinfo)));
 
-					/*
-					 * Default partition cannot be added if there already
-					 * exists one.
-					 */
-					if (spec->is_default)
-					{
-						if (partition_bound_has_default(boundinfo))
-						{
-							overlap = true;
-							with = boundinfo->default_index;
-						}
-
-						break;
-					}
 
 					foreach(cell, spec->listdatums)
 					{
@@ -797,14 +795,15 @@ 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, lower->datums, lower->content, true,
-										 upper) >= 0)
+				if (!spec->is_default &&
+					partition_rbound_cmp(key, lower->datums,
+										 lower->content, true, upper) >= 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					errmsg("cannot create range partition with empty range"),
 							 parser_errposition(pstate, spec->location)));
 
-				if (partdesc->nparts > 0)
+				if (!spec->is_default && partdesc->nparts > 0)
 				{
 					PartitionBoundInfo boundinfo = partdesc->boundinfo;
 					int			off1,
@@ -815,8 +814,8 @@ check_new_partition_bound(char *relname, Relation parent,
 						   boundinfo->strategy == PARTITION_STRATEGY_RANGE);
 
 					/*
-					 * Firstly, find the greatest range bound that is less
-					 * than or equal to the new lower bound.
+					 * Find the greatest range bound that is less than or
+					 * equal to the new lower bound.
 					 */
 					off1 = partition_bound_bsearch(key, boundinfo, lower, true,
 												   &equal);
@@ -890,17 +889,19 @@ check_new_partition_bound(char *relname, Relation parent,
 				 (int) key->strategy);
 	}
 
-	if (overlap)
+	/* Default partition cannot be added if there already exists one. */
+	if (spec->is_default)
 	{
-		Assert(with >= 0);
-
-		if (spec->is_default)
+		if (partition_bound_has_default(boundinfo))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("a default partition \"%s\" already exists",
-							get_rel_name(partdesc->oids[with])),
+							get_rel_name(partdesc->oids[boundinfo->default_index])),
 					 parser_errposition(pstate, spec->location)));
-
+	}
+	else if (overlap)
+	{
+		Assert(with >= 0);
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("partition \"%s\" would overlap partition \"%s\"",
@@ -934,11 +935,9 @@ check_default_allows_bound(Relation parent, PartitionBoundSpec *new_spec)
 	List	   *all_parts;
 	ListCell   *lc;
 
-	/* Currently default partition is supported only for LIST partition. */
-	if (new_spec->strategy != PARTITION_STRATEGY_LIST)
-		return;
-
-	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);
 	new_part_constraints = (List *) eval_const_expressions(NULL,
 											  (Node *) new_part_constraints);
 	new_part_constraints =
@@ -1102,7 +1101,8 @@ 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:
@@ -1684,6 +1684,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
  *
@@ -1724,11 +1771,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,
@@ -1739,10 +1790,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,
@@ -1752,44 +1803,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
@@ -2006,7 +2083,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;
 }
@@ -2174,8 +2253,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;
@@ -2219,24 +2297,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;
-				}
-			}
-		}
-
 		/*
 		 * Handle NULL partition key here if there's a null-accepting list
 		 * partition. Else it will be routed to the default partition if one
@@ -2348,12 +2408,18 @@ 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->content = (RangeDatumContent *) palloc0(key->partnatts *
-												   sizeof(RangeDatumContent));
+	bound->datums = datums ? (Datum *) palloc0(key->partnatts * sizeof(Datum))
+		: NULL;
+	bound->content = (RangeDatumContent *) palloc0(
+				(datums ? key->partnatts : 1) * sizeof(RangeDatumContent));
 	bound->lower = lower;
 
 	i = 0;
+
+	/* If default, datums are NULL */
+	if (datums == NULL)
+		bound->content[i] = RANGE_DATUM_DEFAULT;
+
 	foreach(lc, datums)
 	{
 		PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc));
@@ -2410,9 +2476,19 @@ partition_rbound_cmp(PartitionKey key,
 	for (i = 0; i < key->partnatts; i++)
 	{
 		/*
-		 * First, handle cases involving infinity, which don't require
-		 * invoking the comparison proc.
+		 * First, handle cases involving infinity and default, which don't
+		 * require invoking the comparison proc.
 		 */
+		if (content1[i] == RANGE_DATUM_DEFAULT ||
+			content2[i] == RANGE_DATUM_DEFAULT)
+		{
+			if (content1[i] == content2[i])
+				return 0;
+			else if (content1[i] == RANGE_DATUM_DEFAULT)
+				return -1;
+			else
+				return 1;
+		}
 		if (content1[i] != RANGE_DATUM_FINITE &&
 			content2[i] != RANGE_DATUM_FINITE)
 
@@ -2463,6 +2539,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/gram.y b/src/backend/parser/gram.y
index ddb995d..e03192f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2681,7 +2681,6 @@ ForValues:
 			/*
 			 * A default partition, that can be partition of either LIST or
 			 * RANGE partitioned table.
-			 * Currently this is supported only for LIST partition.
 			 */
 			| DEFAULT
 				{
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e861195..8257c47 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3286,11 +3286,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 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 c3d39c3..441e397 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8680,6 +8680,12 @@ get_rule_expr(Node *node, deparse_context *context,
 							   list_length(spec->lowerdatums) ==
 							   list_length(spec->upperdatums));
 
+						if (spec->is_default)
+						{
+							appendStringInfoString(buf, "DEFAULT");
+							break;
+						}
+
 						appendStringInfoString(buf, "FOR VALUES FROM (");
 						sep = "";
 						foreach(cell, spec->lowerdatums)
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 75f3617..eb483ba 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3340,6 +3340,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:  a default partition "partr_def1" already exists
+-- 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:  default partition contains row(s) that would overlap with partition being created
+-- 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 3bd663e..f352662 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 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 "part3"
+-- 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:  a default partition "range2_default" already exists
+-- 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:  default partition contains row(s) that would overlap with partition being created
+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 f9eef3f..9e5307a 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -271,6 +271,15 @@ 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);
 select tableoid::regclass, * from range_parted;
  tableoid | a | b  
 ----------+---+----
@@ -280,7 +289,9 @@ select tableoid::regclass, * from range_parted;
  part3    | b |  1
  part4    | b | 10
  part4    | b | 10
-(6 rows)
+ part_def | c | 10
+ part_def |   |   
+(8 rows)
 
 -- ok
 insert into list_parted values (null, 1);
@@ -435,6 +446,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 36c56aa..f319fbb 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2169,6 +2169,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 c120713..e72891f 100644
--- a/src/test/regress/sql/insert.sql
+++ b/src/test/regress/sql/insert.sql
@@ -168,8 +168,16 @@ 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);
+
+select tableoid::regclass, * from range_parted;
 -- ok
 insert into list_parted values (null, 1);
 insert into list_parted (a) values ('aA');
@@ -277,6 +285,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
