Relax the sort requirement in columnstore.
diff --git a/parser/CMakeLists.txt b/parser/CMakeLists.txt
index b3ddf30..d4aaab4 100644
--- a/parser/CMakeLists.txt
+++ b/parser/CMakeLists.txt
@@ -150,6 +150,7 @@
                       quickstep_parser_ParseTreeNode
                       quickstep_utility_Macros
                       quickstep_utility_PtrList
+                      quickstep_utility_SqlError
                       quickstep_utility_StringUtil)
 target_link_libraries(quickstep_parser_ParseCaseExpressions
                       quickstep_parser_ParseExpression
diff --git a/parser/ParseBlockProperties.hpp b/parser/ParseBlockProperties.hpp
index ce0cd92..fa176b1 100644
--- a/parser/ParseBlockProperties.hpp
+++ b/parser/ParseBlockProperties.hpp
@@ -31,6 +31,7 @@
 #include "parser/ParseTreeNode.hpp"
 #include "utility/Macros.hpp"
 #include "utility/PtrList.hpp"
+#include "utility/SqlError.hpp"
 #include "utility/StringUtil.hpp"
 
 #include "glog/logging.h"
@@ -143,10 +144,13 @@
     if (sort_key_value == nullptr) {
       return nullptr;
     }
-    if (sort_key_value->getKeyValueType() !=
-        ParseKeyValue::KeyValueType::kStringString) {
-      return nullptr;
+    if (sort_key_value->getKeyValueType() ==
+        ParseKeyValue::KeyValueType::kStringStringList) {
+      THROW_SQL_ERROR_AT(sort_key_value)
+          << "The SORT property must be a string, not a string list.";
     }
+
+    DCHECK(sort_key_value->getKeyValueType() == ParseKeyValue::KeyValueType::kStringString);
     return static_cast<const ParseKeyStringValue*>(sort_key_value)->value();
   }
 
diff --git a/parser/tests/Create.test b/parser/tests/Create.test
index 8c11054..3923c13 100644
--- a/parser/tests/Create.test
+++ b/parser/tests/Create.test
@@ -259,6 +259,51 @@
       +-value=String[value=rowstore]
 ==
 
+CREATE TABLE test (attr INT, attr2 INT) WITH BLOCKPROPERTIES
+(TYPE columnstore)
+--
+CreateTableStatement[relation_name=test]
++-attribute_list=
+| +-AttributeDefinition[name=attr,type=Int]
+| +-AttributeDefinition[name=attr2,type=Int]
++-block_properties=
+  +-BlockProperties
+    +-block_property=KeyStringValue[key=TYPE]
+      +-value=String[value=columnstore]
+==
+
+CREATE TABLE test (attr INT, attr2 INT) WITH BLOCKPROPERTIES
+(TYPE columnstore, SORT attr)
+--
+CreateTableStatement[relation_name=test]
++-attribute_list=
+| +-AttributeDefinition[name=attr,type=Int]
+| +-AttributeDefinition[name=attr2,type=Int]
++-block_properties=
+  +-BlockProperties
+    +-block_property=KeyStringValue[key=TYPE]
+    | +-value=String[value=columnstore]
+    +-block_property=KeyStringValue[key=SORT]
+      +-value=String[value=attr]
+==
+
+CREATE TABLE test (attr INT, attr2 INT) WITH BLOCKPROPERTIES
+(TYPE columnstore, SORT (attr, attr2))
+--
+CreateTableStatement[relation_name=test]
++-attribute_list=
+| +-AttributeDefinition[name=attr,type=Int]
+| +-AttributeDefinition[name=attr2,type=Int]
++-block_properties=
+  +-BlockProperties
+    +-block_property=KeyStringValue[key=TYPE]
+    | +-value=String[value=columnstore]
+    +-block_property=KeyStringList[key=SORT]
+      +-value_list=
+        +-String[value=attr]
+        +-String[value=attr2]
+==
+
 CREATE TABLE test (attr INT) WITH BLOCKPROPERTIES
 (TYPE compressed_columnstore, SORT attr, COMPRESS ALL)
 --
diff --git a/query_optimizer/OptimizerTree.hpp b/query_optimizer/OptimizerTree.hpp
index c54ce20..0f4713e 100644
--- a/query_optimizer/OptimizerTree.hpp
+++ b/query_optimizer/OptimizerTree.hpp
@@ -240,9 +240,12 @@
     }
     case TupleStorageSubBlockDescription::BASIC_COLUMN_STORE: {
       node->addProperty("blocktype", "columnstore");
-      node->addProperty("sort",
-          storage_block_description.GetExtension(
-              quickstep::BasicColumnStoreTupleStorageSubBlockDescription::sort_attribute_id));
+      if (storage_block_description.HasExtension(
+              quickstep::BasicColumnStoreTupleStorageSubBlockDescription::sort_attribute_id)) {
+        node->addProperty("sort",
+            storage_block_description.GetExtension(
+                quickstep::BasicColumnStoreTupleStorageSubBlockDescription::sort_attribute_id));
+      }
       break;
     }
     case TupleStorageSubBlockDescription::COMPRESSED_COLUMN_STORE: {
diff --git a/query_optimizer/resolver/Resolver.cpp b/query_optimizer/resolver/Resolver.cpp
index 935e235..2991568 100644
--- a/query_optimizer/resolver/Resolver.cpp
+++ b/query_optimizer/resolver/Resolver.cpp
@@ -687,7 +687,7 @@
 
   // Resolve TYPE property.
   // The type of the block will determine these:
-  bool block_requires_sort = false;
+  bool block_allows_sort = false;
   bool block_requires_compress = false;
 
   const ParseString *type_parse_string = block_properties->getType();
@@ -702,7 +702,8 @@
   } else if (type_string.compare("columnstore") == 0) {
     description->set_sub_block_type(
         quickstep::TupleStorageSubBlockDescription::BASIC_COLUMN_STORE);
-    block_requires_sort = true;
+    // NOTE(zuyu): sort is optional.
+    block_allows_sort = true;
   } else if (type_string.compare("compressed_rowstore") == 0) {
     description->set_sub_block_type(
         quickstep::TupleStorageSubBlockDescription::COMPRESSED_PACKED_ROW_STORE);
@@ -710,7 +711,7 @@
   } else if (type_string.compare("compressed_columnstore") == 0) {
     description->set_sub_block_type(
         quickstep::TupleStorageSubBlockDescription::COMPRESSED_COLUMN_STORE);
-    block_requires_sort = true;
+    block_allows_sort = true;
     block_requires_compress = true;
   } else {
     THROW_SQL_ERROR_AT(type_parse_string) << "Unrecognized storage type.";
@@ -718,10 +719,12 @@
 
   // Resolve the SORT property.
   const ParseString *sort_parse_string = block_properties->getSort();
-  if (block_requires_sort) {
+  if (block_allows_sort) {
     if (sort_parse_string == nullptr) {
-      THROW_SQL_ERROR_AT(type_parse_string)
-          << "The SORT property must be specified as an attribute name.";
+      if (description->sub_block_type() != TupleStorageSubBlockDescription::BASIC_COLUMN_STORE) {
+        THROW_SQL_ERROR_AT(type_parse_string)
+            << "The SORT property must be specified as an attribute name.";
+      }
     } else {
       // Lookup the name and map to a column id.
       const attribute_id sort_id = GetAttributeIdFromName(create_table_statement.attribute_definition_list(),
diff --git a/query_optimizer/tests/resolver/Create.test b/query_optimizer/tests/resolver/Create.test
index c216c85..ed9158a 100644
--- a/query_optimizer/tests/resolver/Create.test
+++ b/query_optimizer/tests/resolver/Create.test
@@ -133,13 +133,37 @@
                            ^
 ==
 
-# Columnstores require a sort attribute.
+# Columnstores do not require a sort attribute.
 CREATE TABLE foo (attr INT) WITH BLOCKPROPERTIES
 (TYPE columnstore);
 --
-ERROR: The SORT property must be specified as an attribute name. (2 : 7)
-(TYPE columnstore);
-      ^
+TopLevelPlan
++-plan=CreateTable[relation=foo]
+| +-block_properties=ProtoDescription
+| | +-Property=ProtoProperty[Property=blocktype,Value=columnstore]
+| | +-Property=ProtoProperty[Property=slots,Value=1]
+| +-attributes=
+|   +-AttributeReference[id=0,name=attr,relation=foo,type=Int]
++-output_attributes=
+  +-AttributeReference[id=0,name=attr,relation=foo,type=Int]
+==
+
+# Columnstores have a optional sort attribute.
+CREATE TABLE foo (attr INT, attr2 INT) WITH BLOCKPROPERTIES
+(TYPE columnstore, SORT attr2);
+--
+TopLevelPlan
++-plan=CreateTable[relation=foo]
+| +-block_properties=ProtoDescription
+| | +-Property=ProtoProperty[Property=blocktype,Value=columnstore]
+| | +-Property=ProtoProperty[Property=sort,Value=1]
+| | +-Property=ProtoProperty[Property=slots,Value=1]
+| +-attributes=
+|   +-AttributeReference[id=0,name=attr,relation=foo,type=Int]
+|   +-AttributeReference[id=1,name=attr2,relation=foo,type=Int]
++-output_attributes=
+  +-AttributeReference[id=0,name=attr,relation=foo,type=Int]
+  +-AttributeReference[id=1,name=attr2,relation=foo,type=Int]
 ==
 
 # Non-existant columns should be caught.
@@ -155,9 +179,9 @@
 CREATE TABLE foo (attr INT, attr2 INT) WITH BLOCKPROPERTIES
 (TYPE columnstore, SORT (attr, attr2));
 --
-ERROR: The SORT property must be specified as an attribute name. (2 : 7)
-(TYPE columnstore, SORT (attr, attr2...
-      ^
+ERROR: The SORT property must be a string, not a string list. (2 : 20)
+(TYPE columnstore, SORT (attr, attr2));
+                   ^
 ==
 # Compress should only be specified on compressed blocks.
 CREATE TABLE foo (attr INT) WITH BLOCKPROPERTIES