diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d9ba87a..e85adc5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -16422,13 +16422,6 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 				 errmsg("unrecognized partitioning strategy \"%s\"",
 						partspec->strategy)));
 
-	/* Check valid number of columns for strategy */
-	if (*strategy == PARTITION_STRATEGY_LIST &&
-		list_length(partspec->partParams) != 1)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("cannot use \"list\" partition strategy with more than one column")));
-
 	/*
 	 * Create a dummy ParseState and insert the target relation as its sole
 	 * rangetable entry.  We need a ParseState for transformExpr.
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 8afddca..bbe97b1 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -1272,7 +1272,8 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 				bound_offset = partition_list_bsearch(key->partsupfunc,
 													  key->partcollation,
 													  boundinfo,
-													  values[0], &equal);
+													  key->partnatts,
+													  values, &equal);
 				if (bound_offset >= 0 && equal)
 					part_index = boundinfo->indexes[bound_offset];
 			}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b4ab401..466801a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -607,6 +607,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>		part_params
 %type <partboundspec> PartitionBoundSpec
 %type <list>		hash_partbound
+%type <list>		list_values
 %type <defelt>		hash_partbound_elem
 
 %type <str>	optColumnCompression
@@ -2880,13 +2881,13 @@ PartitionBoundSpec:
 				}
 
 			/* a LIST partition */
-			| FOR VALUES IN_P '(' expr_list ')'
+			| FOR VALUES IN_P list_values
 				{
 					PartitionBoundSpec *n = makeNode(PartitionBoundSpec);
 
 					n->strategy = PARTITION_STRATEGY_LIST;
 					n->is_default = false;
-					n->listdatums = $5;
+					n->listdatums = $4;
 					n->location = @3;
 
 					$$ = n;
@@ -2936,6 +2937,17 @@ hash_partbound:
 			}
 		;
 
+list_values:
+		'(' expr_list ')'
+			{
+				$$ = list_make1($2);
+			}
+		| list_values ',' '(' expr_list ')'
+			{
+				$$ = lappend($1, $4);
+			}
+		;
+
 /*****************************************************************************
  *
  *	ALTER TYPE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 9dd3037..14956bc 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -146,6 +146,11 @@ static void validateInfiniteBounds(ParseState *pstate, List *blist);
 static Const *transformPartitionBoundValue(ParseState *pstate, Node *con,
 										   const char *colName, Oid colType, int32 colTypmod,
 										   Oid partCollation);
+static List *transformPartitionListBounds(ParseState *pstate,
+										  PartitionBoundSpec *spec,
+										  char *colname, Oid coltype,
+										  int32 coltypmod, Oid partcollation,
+										  int partnatts);
 
 
 /*
@@ -4015,6 +4020,41 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
 }
 
 /*
+ * checkForDuplicates
+ *
+ * Return TRUE if the element is already present in the list.
+ * FALSE otherwise.
+ */
+static bool
+checkForDuplicates(List *source, List *searchElem)
+{
+	ListCell   *cell;
+
+	foreach(cell, source)
+	{
+		int		i;
+		List   *elem = lfirst(cell);
+		bool	isDuplicate	= true;
+
+		for (i = 0; i < list_length(elem); i++)
+		{
+			Const   *value1 = castNode(Const, list_nth(elem, i));
+			Const   *value2 = castNode(Const, list_nth(searchElem, i));
+
+			if (!equal(value1, value2)){
+				isDuplicate = false;
+				break;
+			}
+		}
+
+		if (isDuplicate)
+			return true;
+	}
+
+	return false;
+}
+
+/*
  * transformPartitionBound
  *
  * Transform a partition bound specification
@@ -4077,7 +4117,6 @@ transformPartitionBound(ParseState *pstate, Relation parent,
 	}
 	else if (strategy == PARTITION_STRATEGY_LIST)
 	{
-		ListCell   *cell;
 		char	   *colname;
 		Oid			coltype;
 		int32		coltypmod;
@@ -4103,36 +4142,10 @@ transformPartitionBound(ParseState *pstate, Relation parent,
 		coltypmod = get_partition_col_typmod(key, 0);
 		partcollation = get_partition_col_collation(key, 0);
 
-		result_spec->listdatums = NIL;
-		foreach(cell, spec->listdatums)
-		{
-			Node	   *expr = lfirst(cell);
-			Const	   *value;
-			ListCell   *cell2;
-			bool		duplicate;
-
-			value = transformPartitionBoundValue(pstate, expr,
-												 colname, coltype, coltypmod,
-												 partcollation);
-
-			/* Don't add to the result if the value is a duplicate */
-			duplicate = false;
-			foreach(cell2, result_spec->listdatums)
-			{
-				Const	   *value2 = castNode(Const, lfirst(cell2));
-
-				if (equal(value, value2))
-				{
-					duplicate = true;
-					break;
-				}
-			}
-			if (duplicate)
-				continue;
-
-			result_spec->listdatums = lappend(result_spec->listdatums,
-											  value);
-		}
+		result_spec->listdatums =
+			transformPartitionListBounds(pstate, spec, colname, coltype,
+										 coltypmod, partcollation,
+										 key->partnatts);
 	}
 	else if (strategy == PARTITION_STRATEGY_RANGE)
 	{
@@ -4168,6 +4181,72 @@ transformPartitionBound(ParseState *pstate, Relation parent,
 	return result_spec;
 }
 
+static List *
+transformPartitionListBounds(ParseState *pstate, PartitionBoundSpec *spec,
+							 char *colname, Oid coltype, int32 coltypmod,
+							 Oid partcollation, int partnatts)
+{
+	ListCell   *cell;
+	List	   *result = NIL;
+
+	foreach(cell, spec->listdatums)
+	{
+		List	   *elem = lfirst(cell);
+		List	   *value = NIL;
+		ListCell   *cell2;
+		bool		isDuplicate;
+
+		if (partnatts == 1 && list_length(spec->listdatums) == 1)
+		{
+			foreach(cell2, elem)
+			{
+				List	   *value = NIL;
+				Node	   *expr = lfirst(cell2);
+				Const	   *val;
+
+				val = transformPartitionBoundValue(pstate, expr, colname,
+												   coltype, coltypmod,
+												   partcollation);
+
+				value = lappend(value, val);
+
+				isDuplicate = checkForDuplicates(result, value);
+				if (isDuplicate)
+					continue;
+
+				result = lappend(result, value);
+			}
+		}
+		else
+		{
+			if (partnatts !=  list_length(elem))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("Must specify exactly one value per partitioning column"),
+						 parser_errposition(pstate, exprLocation((Node *) spec))));
+
+			foreach(cell2, elem)
+			{
+				Node       *expr = lfirst(cell2);
+				Const      *val;
+
+				val = transformPartitionBoundValue(pstate, expr, colname,
+												   coltype, coltypmod,
+												   partcollation);
+				value = lappend(value, val);
+			}
+
+			isDuplicate = checkForDuplicates(result, value);
+			if (isDuplicate)
+				continue;
+
+			result = lappend(result, value);
+		}
+	}
+
+	return result;
+}
+
 /*
  * transformPartitionRangeBounds
  *		This converts the expressions for range partition bounds from the raw
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index c9c7892..2056cc3 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -57,7 +57,7 @@ typedef struct PartitionHashBound
 typedef struct PartitionListValue
 {
 	int			index;
-	Datum		value;
+	Datum	   *values;
 } PartitionListValue;
 
 /* One bound of a range partition */
@@ -239,6 +239,12 @@ static void get_range_key_properties(PartitionKey key, int keynum,
 									 Expr **keyCol,
 									 Const **lower_val, Const **upper_val);
 static List *get_range_nulltest(PartitionKey key);
+static int partition_list_bserach_complete(FmgrInfo *partsupfunc,
+										   Oid *partcollation,
+										   PartitionBoundInfo boundinfo,
+										   int nvalues,
+										   Datum *values, bool *is_equal,
+										   int mid);
 
 /*
  * get_qual_from_partbound
@@ -444,6 +450,7 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
 	PartitionListValue **all_values = NULL;
 	ListCell   *cell;
 	int			i = 0;
+	int			j = 0;
 	int			ndatums = 0;
 	int			next_index = 0;
 	int			default_index = -1;
@@ -479,25 +486,22 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
 
 		foreach(c, spec->listdatums)
 		{
-			Const	   *val = castNode(Const, lfirst(c));
+			List	   *elem = lfirst(c);
+			ListCell   *cell;
 			PartitionListValue *list_value = NULL;
 
-			if (!val->constisnull)
-			{
-				list_value = (PartitionListValue *)
-					palloc0(sizeof(PartitionListValue));
-				list_value->index = i;
-				list_value->value = val->constvalue;
-			}
-			else
+			list_value = (PartitionListValue *)
+				palloc0(sizeof(PartitionListValue));
+			list_value->index = i;
+			list_value->values = (Datum *) palloc0(key->partnatts * sizeof(Datum));
+
+			j = 0;
+			foreach(cell, elem)
 			{
-				/*
-				 * Never put a null into the values array; save the index of
-				 * the partition that stores nulls, instead.
-				 */
-				if (null_index != -1)
-					elog(ERROR, "found null more than once");
-				null_index = i;
+				Const	   *val = castNode(Const, lfirst(cell));
+
+				list_value->values[j] = val->constvalue;
+				j++;
 			}
 
 			if (list_value)
@@ -520,7 +524,7 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
 
 		all_values[i] = (PartitionListValue *)
 			palloc(sizeof(PartitionListValue));
-		all_values[i]->value = src->value;
+		all_values[i]->values = src->values;
 		all_values[i]->index = src->index;
 		i++;
 	}
@@ -542,11 +546,12 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
 	for (i = 0; i < ndatums; i++)
 	{
 		int			orig_index = all_values[i]->index;
+		boundinfo->datums[i] = (Datum *) palloc(key->partnatts * sizeof(Datum));
 
-		boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
-		boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
-											key->parttypbyval[0],
-											key->parttyplen[0]);
+		for (j = 0; j < key->partnatts; j++)
+			boundinfo->datums[i][j] = datumCopy(all_values[i]->values[j],
+												key->parttypbyval[0],
+												key->parttyplen[0]);
 
 		/* If the old index has no mapping, assign one */
 		if ((*mapping)[orig_index] == -1)
@@ -922,9 +927,6 @@ partition_bounds_copy(PartitionBoundInfo src,
 	nindexes = dest->nindexes = src->nindexes;
 	partnatts = key->partnatts;
 
-	/* List partitioned tables have only a single partition key. */
-	Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
-
 	dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
 
 	if (src->kind != NULL)
@@ -2950,32 +2952,31 @@ check_new_partition_bound(char *relname, Relation parent,
 
 					foreach(cell, spec->listdatums)
 					{
-						Const	   *val = castNode(Const, lfirst(cell));
+						int			i;
+						int         offset;
+						bool        equal;
+						List	   *elem = lfirst(cell);
+						Datum	   *values = (Datum *) palloc0(key->partnatts * sizeof(Datum));;
 
-						overlap_location = val->location;
-						if (!val->constisnull)
+						for (i = 0; i < key->partnatts; i++)
 						{
-							int			offset;
-							bool		equal;
-
-							offset = partition_list_bsearch(&key->partsupfunc[0],
-															key->partcollation,
-															boundinfo,
-															val->constvalue,
-															&equal);
-							if (offset >= 0 && equal)
-							{
-								overlap = true;
-								with = boundinfo->indexes[offset];
-								break;
-							}
+							Const      *val = castNode(Const, list_nth(elem, i));
+							values[i] = val->constvalue;
 						}
-						else if (partition_bound_accepts_nulls(boundinfo))
+
+						offset = partition_list_bsearch(&key->partsupfunc[0],
+														key->partcollation,
+														boundinfo,
+														key->partnatts,
+														values,
+														&equal);
+						if (offset >= 0 && equal)
 						{
 							overlap = true;
-							with = boundinfo->null_index;
+							with = boundinfo->indexes[offset];
 							break;
 						}
+						pfree(values);
 					}
 				}
 
@@ -3488,6 +3489,109 @@ partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
 }
 
 /*
+ * partition_lbound_datum_cmp
+ *
+ * This function compares the list bound values of all the partition key
+ * columns. Returns TRUE if all the values are matching. Returns FALSE if any
+ * of the value is not matching.
+ *
+ */
+static bool
+partition_lbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
+						   Datum *lb_datums, int nvalues, Datum *values)
+{
+	int i = 0;
+	int32 cmpval = 0;
+
+	for (i = 1; i < nvalues; i++)
+	{
+		cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+												 partcollation[0],
+												 lb_datums[i],
+												 values[i]));
+		if (cmpval != 0)
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * partition_list_bserach_complete
+ *
+ * This function is called once it finds the first match for first value of the
+ * list. Then this function will continue the serach and return the index of
+ * matching element if found.
+ *
+ */
+static int
+partition_list_bserach_complete(FmgrInfo *partsupfunc, Oid *partcollation,
+								PartitionBoundInfo boundinfo, int nvalues,
+								Datum *values, bool *is_equal, int mid)
+{
+
+	int32 res = partition_lbound_datum_cmp(partsupfunc, partcollation,
+										   boundinfo->datums[mid],
+										   nvalues, values);
+	if (res == true)
+	{
+		*is_equal = true;
+		return mid;
+	}
+	else
+	{
+		int i;
+		int idx = mid;
+
+		for (i = mid-1; i >= 0; i--)
+		{
+			idx--;
+			res = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+												  partcollation[0],
+												  boundinfo->datums[i][0],
+												  values[0]));
+			if (res != 0)
+				break;
+
+			res = partition_lbound_datum_cmp(partsupfunc, partcollation,
+											 boundinfo->datums[i],
+											 nvalues, values);
+			if (res == true)
+			{
+				*is_equal = true;
+				return idx;
+			}
+		}
+
+		idx = mid;
+		for (i = mid+1; i < boundinfo->ndatums; i++)
+		{
+			idx++;
+			res = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+												  partcollation[0],
+												  boundinfo->datums[i][0],
+												  values[0]));
+
+			if (res != 0)
+				break;
+
+			res = partition_lbound_datum_cmp(partsupfunc, partcollation,
+											 boundinfo->datums[i],
+											 nvalues, values);
+
+			if (res == true)
+			{
+				*is_equal = true;
+				return idx;
+			}
+		}
+
+		*is_equal = false;
+		return -1;
+	}
+}
+
+/*
  * partition_list_bsearch
  *		Returns the index of the greatest bound datum that is less than equal
  * 		to the given value or -1 if all of the bound datums are greater
@@ -3497,8 +3601,8 @@ partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
  */
 int
 partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
-					   PartitionBoundInfo boundinfo,
-					   Datum value, bool *is_equal)
+					   PartitionBoundInfo boundinfo, int nvalues,
+					   Datum *values, bool *is_equal)
 {
 	int			lo,
 				hi,
@@ -3514,7 +3618,16 @@ partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
 		cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
 												 partcollation[0],
 												 boundinfo->datums[mid][0],
-												 value));
+												 values[0]));
+
+		if (cmpval == 0)
+		{
+			return partition_list_bserach_complete(partsupfunc, partcollation,
+												   boundinfo, nvalues, values,
+												   is_equal, mid);
+		}
+
+
 		if (cmpval <= 0)
 		{
 			lo = mid;
@@ -3684,13 +3797,14 @@ qsort_partition_hbound_cmp(const void *a, const void *b)
 static int32
 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
 {
-	Datum		val1 = (*(PartitionListValue *const *) a)->value,
-				val2 = (*(PartitionListValue *const *) b)->value;
+	Datum	   *val1 = (*(PartitionListValue *const *) a)->values;
+	Datum	   *val2 = (*(PartitionListValue *const *) b)->values;
+
 	PartitionKey key = (PartitionKey) arg;
 
 	return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
 										   key->partcollation[0],
-										   val1, val2));
+										   val1[0], val2[0]));
 }
 
 /*
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index c793742..fbc9e29 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -2728,7 +2728,7 @@ get_matching_list_bounds(PartitionPruneContext *context,
 											  boundinfo->ndatums - 1);
 
 		off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
-									 value, &is_equal);
+									 nvalues, value, &is_equal);
 		if (off >= 0 && is_equal)
 		{
 
@@ -2760,8 +2760,8 @@ get_matching_list_bounds(PartitionPruneContext *context,
 		case BTEqualStrategyNumber:
 			off = partition_list_bsearch(partsupfunc,
 										 partcollation,
-										 boundinfo, value,
-										 &is_equal);
+										 boundinfo, nvalues,
+										 value, &is_equal);
 			if (off >= 0 && is_equal)
 			{
 				Assert(boundinfo->indexes[off] >= 0);
@@ -2777,8 +2777,8 @@ get_matching_list_bounds(PartitionPruneContext *context,
 		case BTGreaterStrategyNumber:
 			off = partition_list_bsearch(partsupfunc,
 										 partcollation,
-										 boundinfo, value,
-										 &is_equal);
+										 boundinfo, nvalues,
+										 value, &is_equal);
 			if (off >= 0)
 			{
 				/* We don't want the matched datum to be in the result. */
@@ -2812,8 +2812,8 @@ get_matching_list_bounds(PartitionPruneContext *context,
 		case BTLessStrategyNumber:
 			off = partition_list_bsearch(partsupfunc,
 										 partcollation,
-										 boundinfo, value,
-										 &is_equal);
+										 boundinfo, nvalues,
+										 value, &is_equal);
 			if (off >= 0 && is_equal && !inclusive)
 				off--;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 0a4fa93..b22d39d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9394,18 +9394,27 @@ get_rule_expr(Node *node, deparse_context *context,
 					case PARTITION_STRATEGY_LIST:
 						Assert(spec->listdatums != NIL);
 
-						appendStringInfoString(buf, "FOR VALUES IN (");
+						appendStringInfoString(buf, "FOR VALUES IN ");
 						sep = "";
 						foreach(cell, spec->listdatums)
 						{
-							Const	   *val = castNode(Const, lfirst(cell));
+							ListCell   *cell2;
+							List	   *elem = lfirst(cell);
 
 							appendStringInfoString(buf, sep);
-							get_const_expr(val, context, -1);
+							appendStringInfoChar(buf, '(');
+							sep = "";
+							foreach(cell2, elem)
+							{
+								Const	   *val = castNode(Const, lfirst(cell2));
+
+								appendStringInfoString(buf, sep);
+								get_const_expr(val, context, -1);
+								sep = ", ";
+							}
+							appendStringInfoChar(buf, ')');
 							sep = ", ";
 						}
-
-						appendStringInfoChar(buf, ')');
 						break;
 
 					case PARTITION_STRATEGY_RANGE:
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index ebf3ff1..7bf20ff 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -117,7 +117,7 @@ extern int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc,
 extern int	partition_list_bsearch(FmgrInfo *partsupfunc,
 								   Oid *partcollation,
 								   PartitionBoundInfo boundinfo,
-								   Datum value, bool *is_equal);
+								   int nvalues, Datum *value, bool *is_equal);
 extern int	partition_range_datum_bsearch(FmgrInfo *partsupfunc,
 										  Oid *partcollation,
 										  PartitionBoundInfo boundinfo,
