[ASTERIXDB-3344][COMP] Support ORDER BY without PARTITION BY in COPY TO

- user model changes: yes
- storage format changes: no
- interface changes: no

Details:
Support sorting the written result of COPY TO without PARTITION BY.

Change-Id: I7f3b3e32bd5c40e18111c816b2791f5fff65bc82
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/18137
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Wail Alkowaileet <wael.y.k@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/CompiledStatements.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/CompiledStatements.java
index 54d4cfc..0ff0b72 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/CompiledStatements.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/CompiledStatements.java
@@ -600,8 +600,8 @@
         private final List<Expression> partitionExpressions;
         private final Map<Integer, VariableExpr> partitionsVariables;
         private final List<Expression> orderbyList;
-        private final List<OrderbyClause.OrderModifier> orderbyModifiers;
-        private final List<OrderbyClause.NullOrderModifier> orderbyNullModifierList;
+        private final List<OrderbyClause.OrderModifier> orderByModifiers;
+        private final List<OrderbyClause.NullOrderModifier> orderByNullModifierList;
 
         public CompiledCopyToStatement(CopyToStatement copyToStatement) {
             this.query = copyToStatement.getQuery();
@@ -612,9 +612,9 @@
             this.pathExpressions = copyToStatement.getPathExpressions();
             this.partitionExpressions = copyToStatement.getPartitionExpressions();
             this.partitionsVariables = copyToStatement.getPartitionsVariables();
-            this.orderbyList = copyToStatement.getOrderbyList();
-            this.orderbyModifiers = copyToStatement.getOrderbyModifiers();
-            this.orderbyNullModifierList = copyToStatement.getOrderbyNullModifierList();
+            this.orderbyList = copyToStatement.getOrderByList();
+            this.orderByModifiers = copyToStatement.getOrderByModifiers();
+            this.orderByNullModifierList = copyToStatement.getOrderByNullModifierList();
         }
 
         @Override
@@ -658,16 +658,16 @@
             return partitionsVariables.get(index);
         }
 
-        public List<Expression> getOrderbyExpressions() {
+        public List<Expression> getOrderByExpressions() {
             return orderbyList;
         }
 
-        public List<OrderbyClause.OrderModifier> getOrderbyModifiers() {
-            return orderbyModifiers;
+        public List<OrderbyClause.OrderModifier> getOrderByModifiers() {
+            return orderByModifiers;
         }
 
-        public List<OrderbyClause.NullOrderModifier> getOrderbyNullModifiers() {
-            return orderbyNullModifierList;
+        public List<OrderbyClause.NullOrderModifier> getOrderByNullModifiers() {
+            return orderByNullModifierList;
         }
     }
 
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
index 56719e2..7dd0217 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
@@ -394,21 +394,29 @@
             }
         }
 
-        // Set order expression(s) if any. We do order first to prevent partition variables to be used
+        // Handle order by
+        List<Expression> orderExprList = copyTo.getOrderByExpressions();
+        List<OrderbyClause.OrderModifier> orderModifierList = copyTo.getOrderByModifiers();
+        List<OrderbyClause.NullOrderModifier> nullOrderModifierList = copyTo.getOrderByNullModifiers();
         List<Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>>> orderExprListOut = new ArrayList<>();
-        List<Expression> orderExprList = copyTo.getOrderbyExpressions();
-        List<OrderbyClause.OrderModifier> orderModifierList = copyTo.getOrderbyModifiers();
-        List<OrderbyClause.NullOrderModifier> nullOrderModifierList = copyTo.getOrderbyNullModifiers();
-        for (int i = 0; i < orderExprList.size(); i++) {
-            Expression orderExpr = orderExprList.get(i);
-            OrderbyClause.OrderModifier orderModifier = orderModifierList.get(i);
-            OrderbyClause.NullOrderModifier nullOrderModifier = nullOrderModifierList.get(i);
-            Pair<ILogicalExpression, Mutable<ILogicalOperator>> orderExprResult =
-                    langExprToAlgExpression(orderExpr, topOpRef);
-            Pair<Mutable<ILogicalExpression>, Mutable<ILogicalOperator>> wrappedPair =
-                    wrapInAssign(context.newVar(), orderExprResult.first, orderExprResult.second);
-            addOrderByExpression(orderExprListOut, wrappedPair.first.getValue(), orderModifier, nullOrderModifier);
-            topOpRef = wrappedPair.second;
+        if (!copyTo.isPartitioned() && copyTo.isOrdered()) {
+            // The output must be ordered (sorted) entirely, create an implicit orderBy-clause
+            OrderbyClause orderbyClause = new OrderbyClause(orderExprList, orderModifierList, nullOrderModifierList);
+            Pair<ILogicalOperator, LogicalVariable> order = orderbyClause.accept(this, topOpRef);
+            topOpRef = new MutableObject<>(order.first);
+        } else {
+            // Potentially an ordered partitions. Set order expression(s) if any.
+            for (int i = 0; i < orderExprList.size(); i++) {
+                Expression orderExpr = orderExprList.get(i);
+                OrderbyClause.OrderModifier orderModifier = orderModifierList.get(i);
+                OrderbyClause.NullOrderModifier nullOrderModifier = nullOrderModifierList.get(i);
+                Pair<ILogicalExpression, Mutable<ILogicalOperator>> orderExprResult =
+                        langExprToAlgExpression(orderExpr, topOpRef);
+                Pair<Mutable<ILogicalExpression>, Mutable<ILogicalOperator>> wrappedPair =
+                        wrapInAssign(context.newVar(), orderExprResult.first, orderExprResult.second);
+                addOrderByExpression(orderExprListOut, wrappedPair.first.getValue(), orderModifier, nullOrderModifier);
+                topOpRef = wrappedPair.second;
+            }
         }
 
         // Set Path
@@ -445,12 +453,14 @@
             fullPathExpr = concat;
         }
 
+        // Write adapter configuration
         writeDataSink = new WriteDataSink(copyTo.getAdapter(), copyTo.getProperties());
         // writeOperator
         WriteOperator writeOperator = new WriteOperator(sourceExprRef, new MutableObject<>(fullPathExpr),
                 partitionExpressionRefs, orderExprListOut, writeDataSink);
         writeOperator.getInputs().add(topOpRef);
 
+        // We need DistributeResultOperator to ensure all warnings to be delivered to the user
         ResultSetSinkId rssId = new ResultSetSinkId(metadataProvider.getResultSetId());
         ResultSetDataSink sink = new ResultSetDataSink(rssId, null);
         DistributeResultOperator newTop = new DistributeResultOperator(new ArrayList<>(), sink, resultMetadata);
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.0.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.0.sqlpp
new file mode 100644
index 0000000..a742c46
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.0.sqlpp
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+DROP DATAVERSE test IF EXISTS;
+CREATE DATAVERSE test;
+
+USE test;
+
+CREATE TYPE OpenType AS {
+    uid: uuid
+};
+
+CREATE DATASET OpenDataset(OpenType)
+PRIMARY KEY uid AUTOGENERATED;
+
+SET `compiler.sort.parallel` "true";
+COPY OpenDataset x
+TO localfs
+PATH("myPath")
+OVER(ORDER BY x.id DESC)
+WITH {
+   "format": "json"
+};
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.1.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.1.sqlpp
new file mode 100644
index 0000000..ba01aee
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.1.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+SET `compiler.sort.parallel` "true";
+COPY (
+   SELECT od.name, od.id
+   FROM OpenDataset od
+) x
+TO localfs
+PATH("myPath")
+OVER(ORDER BY x.id DESC, x.name ASC)
+WITH {
+   "format": "json"
+};
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.2.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.2.sqlpp
new file mode 100644
index 0000000..b76ae22
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.2.sqlpp
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+DROP DATAVERSE test IF EXISTS;
+CREATE DATAVERSE test;
+
+USE test;
+
+CREATE TYPE OpenType AS {
+    uid: uuid
+};
+
+CREATE DATASET OpenDataset(OpenType)
+PRIMARY KEY uid AUTOGENERATED;
+
+SET `compiler.sort.parallel` "false";
+COPY OpenDataset x
+TO localfs
+PATH("myPath")
+OVER(ORDER BY x.id DESC)
+WITH {
+   "format": "json"
+};
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.3.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.3.sqlpp
new file mode 100644
index 0000000..38133b3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/copy-to/copy-to.3.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+SET `compiler.sort.parallel` "false";
+COPY (
+   SELECT od.name, od.id
+   FROM OpenDataset od
+) x
+TO localfs
+PATH("myPath")
+OVER(ORDER BY x.id DESC, x.name ASC)
+WITH {
+   "format": "json"
+};
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.0.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.0.plan
new file mode 100644
index 0000000..bd47279
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.0.plan
@@ -0,0 +1,29 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- SINK_WRITE  |PARTITIONED|
+      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+        -- STABLE_SORT [$$25(DESC)]  |PARTITIONED|
+          -- RANGE_PARTITION_EXCHANGE [$$25(DESC)]  |PARTITIONED|
+            -- FORWARD  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- REPLICATE  |PARTITIONED|
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- ASSIGN  |PARTITIONED|
+                      -- STREAM_PROJECT  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- DATASOURCE_SCAN (test.OpenDataset)  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              -- BROADCAST_EXCHANGE  |PARTITIONED|
+                -- AGGREGATE  |UNPARTITIONED|
+                  -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                    -- AGGREGATE  |PARTITIONED|
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        -- REPLICATE  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN (test.OpenDataset)  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.1.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.1.plan
new file mode 100644
index 0000000..cc2cf3b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.1.plan
@@ -0,0 +1,33 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- SINK_WRITE  |PARTITIONED|
+      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+        -- STABLE_SORT [$$32(DESC), $$33(ASC)]  |PARTITIONED|
+          -- RANGE_PARTITION_EXCHANGE [$$32(DESC), $$33(ASC)]  |PARTITIONED|
+            -- FORWARD  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- REPLICATE  |PARTITIONED|
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- ASSIGN  |PARTITIONED|
+                      -- STREAM_PROJECT  |PARTITIONED|
+                        -- ASSIGN  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- DATASOURCE_SCAN (test.OpenDataset)  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              -- BROADCAST_EXCHANGE  |PARTITIONED|
+                -- AGGREGATE  |UNPARTITIONED|
+                  -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                    -- AGGREGATE  |PARTITIONED|
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        -- REPLICATE  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- DATASOURCE_SCAN (test.OpenDataset)  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.2.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.2.plan
new file mode 100644
index 0000000..d6efffc
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.2.plan
@@ -0,0 +1,12 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- SINK_WRITE  |PARTITIONED|
+      -- SORT_MERGE_EXCHANGE [$$25(DESC) ]  |PARTITIONED|
+        -- STABLE_SORT [$$25(DESC)]  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- ASSIGN  |PARTITIONED|
+              -- STREAM_PROJECT  |PARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                  -- DATASOURCE_SCAN (test.OpenDataset)  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.3.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.3.plan
new file mode 100644
index 0000000..070698a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/copy-to/copy-to.3.plan
@@ -0,0 +1,14 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- SINK_WRITE  |PARTITIONED|
+      -- SORT_MERGE_EXCHANGE [$$32(DESC), $$33(ASC) ]  |PARTITIONED|
+        -- STABLE_SORT [$$32(DESC), $$33(ASC)]  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- ASSIGN  |PARTITIONED|
+              -- STREAM_PROJECT  |PARTITIONED|
+                -- ASSIGN  |PARTITIONED|
+                  -- STREAM_PROJECT  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- DATASOURCE_SCAN (test.OpenDataset)  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/negative/empty-over/empty-over.01.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/negative/empty-over/empty-over.01.ddl.sqlpp
new file mode 100644
index 0000000..b68c38b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/negative/empty-over/empty-over.01.ddl.sqlpp
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+DROP DATAVERSE test IF EXISTS;
+CREATE DATAVERSE test;
+
+USE test;
+
+CREATE TYPE OpenType AS {
+};
+
+CREATE EXTERNAL DATASET Customer(OpenType) USING S3 (
+    ("accessKeyId"="dummyAccessKey"),
+    ("secretAccessKey"="dummySecretKey"),
+    ("region"="us-west-2"),
+    ("serviceEndpoint"="http://127.0.0.1:8001"),
+    ("container"="playground"),
+    ("definition"="external-filter/car/{company:string}/customer/{customer_id:int}"),
+    ("embed-filter-values" = "false"),
+    ("format"="json")
+);
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/negative/empty-over/empty-over.02.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/negative/empty-over/empty-over.02.update.sqlpp
new file mode 100644
index 0000000..cc0c4b4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/negative/empty-over/empty-over.02.update.sqlpp
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+COPY Customer c
+TO S3
+PATH ("copy-to-result-with-over")
+OVER()
+WITH {
+    "accessKeyId":"dummyAccessKey",
+    "secretAccessKey":"dummySecretKey",
+    "region":"us-west-2",
+    "serviceEndpoint":"http://127.0.0.1:8001",
+    "container":"playground",
+    "format":"json"
+}
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.01.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.01.ddl.sqlpp
new file mode 100644
index 0000000..b68c38b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.01.ddl.sqlpp
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+DROP DATAVERSE test IF EXISTS;
+CREATE DATAVERSE test;
+
+USE test;
+
+CREATE TYPE OpenType AS {
+};
+
+CREATE EXTERNAL DATASET Customer(OpenType) USING S3 (
+    ("accessKeyId"="dummyAccessKey"),
+    ("secretAccessKey"="dummySecretKey"),
+    ("region"="us-west-2"),
+    ("serviceEndpoint"="http://127.0.0.1:8001"),
+    ("container"="playground"),
+    ("definition"="external-filter/car/{company:string}/customer/{customer_id:int}"),
+    ("embed-filter-values" = "false"),
+    ("format"="json")
+);
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.02.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.02.update.sqlpp
new file mode 100644
index 0000000..66bb709
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.02.update.sqlpp
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+COPY Customer c
+TO S3
+PATH ("copy-to-result", "order-by1")
+-- We do not have a way to determine the output is sorted
+-- However, this test ensures that there's no regressions when having ORDER BY only in the OVER-clause
+OVER (ORDER BY c.company)
+WITH {
+    "accessKeyId":"dummyAccessKey",
+    "secretAccessKey":"dummySecretKey",
+    "region":"us-west-2",
+    "serviceEndpoint":"http://127.0.0.1:8001",
+    "container":"playground",
+    "format":"json"
+}
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.03.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.03.ddl.sqlpp
new file mode 100644
index 0000000..0380332
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.03.ddl.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+CREATE EXTERNAL DATASET CustomerCopy(OpenType) USING S3 (
+    ("accessKeyId"="dummyAccessKey"),
+    ("secretAccessKey"="dummySecretKey"),
+    ("region"="us-west-2"),
+    ("serviceEndpoint"="http://127.0.0.1:8001"),
+    ("container"="playground"),
+    ("definition"="copy-to-result/order-by1"),
+    ("embed-filter-values" = "false"),
+    ("format"="json")
+);
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.04.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.04.query.sqlpp
new file mode 100644
index 0000000..eace40c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.04.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c
+FROM Customer c
+ORDER BY c.customer_id,
+         c.company,
+         c.year,
+         c.month,
+         c.day;
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.05.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.05.query.sqlpp
new file mode 100644
index 0000000..7c3dab0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.05.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c
+FROM CustomerCopy c
+ORDER BY c.customer_id,
+         c.company,
+         c.year,
+         c.month,
+         c.day;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.20.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.20.update.sqlpp
new file mode 100644
index 0000000..1fae58f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.20.update.sqlpp
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+SET `compiler.sort.parallel` "true";
+COPY (
+   SELECT c.company make
+   FROM Customer c
+) x
+TO S3
+PATH ("copy-to-result", "order-by2")
+-- We do not have a way to determine the output is sorted
+-- However, this test ensures that there's no regressions when having ORDER BY only in the OVER-clause
+OVER (ORDER BY x.make)
+WITH {
+    "accessKeyId":"dummyAccessKey",
+    "secretAccessKey":"dummySecretKey",
+    "region":"us-west-2",
+    "serviceEndpoint":"http://127.0.0.1:8001",
+    "container":"playground",
+    "format":"json"
+}
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.21.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.21.ddl.sqlpp
new file mode 100644
index 0000000..fab6964
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.21.ddl.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+CREATE EXTERNAL DATASET CustomerCarMake1(OpenType) USING S3 (
+    ("accessKeyId"="dummyAccessKey"),
+    ("secretAccessKey"="dummySecretKey"),
+    ("region"="us-west-2"),
+    ("serviceEndpoint"="http://127.0.0.1:8001"),
+    ("container"="playground"),
+    ("definition"="copy-to-result/order-by2"),
+    ("embed-filter-values" = "false"),
+    ("format"="json")
+);
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.22.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.22.query.sqlpp
new file mode 100644
index 0000000..321f545
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.22.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT DISTINCT VALUE c
+FROM CustomerCarMake1 c
+ORDER BY c.make
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.30.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.30.update.sqlpp
new file mode 100644
index 0000000..dc0a631
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.30.update.sqlpp
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+SET `compiler.sort.parallel` "false";
+COPY (
+   SELECT c.company make
+   FROM Customer c
+) x
+TO S3
+PATH ("copy-to-result", "order-by3")
+-- We do not have a way to determine the output is sorted
+-- However, this test ensures that there's no regressions when having ORDER BY only in the OVER-clause
+OVER (ORDER BY x.make)
+WITH {
+    "accessKeyId":"dummyAccessKey",
+    "secretAccessKey":"dummySecretKey",
+    "region":"us-west-2",
+    "serviceEndpoint":"http://127.0.0.1:8001",
+    "container":"playground",
+    "format":"json"
+}
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.31.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.31.ddl.sqlpp
new file mode 100644
index 0000000..e3369be
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.31.ddl.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+CREATE EXTERNAL DATASET CustomerCarMake2(OpenType) USING S3 (
+    ("accessKeyId"="dummyAccessKey"),
+    ("secretAccessKey"="dummySecretKey"),
+    ("region"="us-west-2"),
+    ("serviceEndpoint"="http://127.0.0.1:8001"),
+    ("container"="playground"),
+    ("definition"="copy-to-result/order-by2"),
+    ("embed-filter-values" = "false"),
+    ("format"="json")
+);
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.32.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.32.query.sqlpp
new file mode 100644
index 0000000..42b0e32
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/copy-to/order-by/order-by.32.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT DISTINCT VALUE c
+FROM CustomerCarMake2 c
+ORDER BY c.make
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.04.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.04.adm
new file mode 100644
index 0000000..4e13836
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.04.adm
@@ -0,0 +1,81 @@
+{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.05.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.05.adm
new file mode 100644
index 0000000..4e13836
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.05.adm
@@ -0,0 +1,81 @@
+{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 1, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 1, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 1, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 2, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 2, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 2, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "lexus", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "lexus", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "lexus", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "toyota", "year": 2001, "month": 1, "day": 1 }
+{ "customer_id": 3, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "toyota", "year": 2002, "month": 2, "day": 2 }
+{ "customer_id": 3, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
+{ "customer_id": 3, "company": "toyota", "year": 2003, "month": 3, "day": 3 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.22.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.22.adm
new file mode 100644
index 0000000..d7ebf64
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.22.adm
@@ -0,0 +1,3 @@
+{ "make": "ford" }
+{ "make": "lexus" }
+{ "make": "toyota" }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.32.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.32.adm
new file mode 100644
index 0000000..d7ebf64
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/order-by/order-by.32.adm
@@ -0,0 +1,3 @@
+{ "make": "ford" }
+{ "make": "lexus" }
+{ "make": "toyota" }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/simple-write/simple-write.06.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/simple-write/simple-write.06.adm
deleted file mode 100644
index 330cafe..0000000
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/copy-to/simple-write/simple-write.06.adm
+++ /dev/null
@@ -1,27 +0,0 @@
-{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 1, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 1, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 1, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 2, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 2, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 2, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 3, "company": "ford", "year": 2001, "month": 1, "day": 1 }
-{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 3, "company": "ford", "year": 2002, "month": 2, "day": 2 }
-{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
-{ "customer_id": 3, "company": "ford", "year": 2003, "month": 3, "day": 3 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_external_dataset_s3.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_external_dataset_s3.xml
index 421aacd..db4b5a8 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_external_dataset_s3.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_external_dataset_s3.xml
@@ -44,6 +44,11 @@
         <output-dir compare="Text">empty-path</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="copy-to">
+      <compilation-unit name="order-by">
+        <output-dir compare="Text">order-by</output-dir>
+      </compilation-unit>
+    </test-case>
     <test-case FilePath="copy-to/negative">
       <compilation-unit name="early-missing">
         <output-dir compare="Text">early-missing</output-dir>
@@ -79,6 +84,12 @@
         <expected-error>ASX1096: Unknown compression scheme rar. Supported schemes are [gzip]</expected-error>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="copy-to/negative">
+      <compilation-unit name="empty-over">
+        <output-dir compare="Text">empty-over</output-dir>
+        <expected-error>ASX1001: Syntax error: OVER-clause cannot be empty</expected-error>
+      </compilation-unit>
+    </test-case>
   </test-group>
   <test-group name="aws-s3-external-dataset">
     <test-case FilePath="external-dataset">
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CopyToStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CopyToStatement.java
index c6ca349..dbe7b35 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CopyToStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CopyToStatement.java
@@ -40,22 +40,22 @@
     private final VariableExpr sourceVariable;
     private final ExternalDetailsDecl externalDetailsDecl;
     private final Map<Integer, VariableExpr> partitionsVariables;
-    private final List<OrderbyClause.OrderModifier> orderbyModifiers;
-    private final List<OrderbyClause.NullOrderModifier> orderbyNullModifierList;
+    private final List<OrderbyClause.OrderModifier> orderByModifiers;
+    private final List<OrderbyClause.NullOrderModifier> orderByNullModifierList;
 
     private Namespace namespace;
     private Query query;
     private List<Expression> pathExpressions;
 
     private List<Expression> partitionExpressions;
-    private List<Expression> orderbyList;
+    private List<Expression> orderByList;
     private int varCounter;
 
     public CopyToStatement(Namespace namespace, String datasetName, Query query, VariableExpr sourceVariable,
             ExternalDetailsDecl externalDetailsDecl, List<Expression> pathExpressions,
             List<Expression> partitionExpressions, Map<Integer, VariableExpr> partitionsVariables,
-            List<Expression> orderbyList, List<OrderbyClause.OrderModifier> orderbyModifiers,
-            List<OrderbyClause.NullOrderModifier> orderbyNullModifierList, int varCounter) {
+            List<Expression> orderbyList, List<OrderbyClause.OrderModifier> orderByModifiers,
+            List<OrderbyClause.NullOrderModifier> orderByNullModifierList, int varCounter) {
         this.namespace = namespace;
         this.datasetName = datasetName;
         this.query = query;
@@ -64,9 +64,9 @@
         this.pathExpressions = pathExpressions;
         this.partitionExpressions = partitionExpressions;
         this.partitionsVariables = partitionsVariables;
-        this.orderbyList = orderbyList;
-        this.orderbyModifiers = orderbyModifiers;
-        this.orderbyNullModifierList = orderbyNullModifierList;
+        this.orderByList = orderbyList;
+        this.orderByModifiers = orderByModifiers;
+        this.orderByNullModifierList = orderByNullModifierList;
         this.varCounter = varCounter;
 
         if (pathExpressions.isEmpty()) {
@@ -141,28 +141,32 @@
         return partitionsVariables;
     }
 
-    public List<Expression> getOrderbyList() {
-        return orderbyList;
+    public List<Expression> getOrderByList() {
+        return orderByList;
     }
 
-    public void setOrderbyList(List<Expression> orderbyList) {
-        this.orderbyList = orderbyList;
+    public void setOrderByList(List<Expression> orderbyList) {
+        this.orderByList = orderbyList;
     }
 
-    public List<OrderbyClause.OrderModifier> getOrderbyModifiers() {
-        return orderbyModifiers;
+    public List<OrderbyClause.OrderModifier> getOrderByModifiers() {
+        return orderByModifiers;
     }
 
-    public List<OrderbyClause.NullOrderModifier> getOrderbyNullModifierList() {
-        return orderbyNullModifierList;
+    public List<OrderbyClause.NullOrderModifier> getOrderByNullModifierList() {
+        return orderByNullModifierList;
     }
 
     public boolean hasOverClause() {
+        return hasPartitionClause() || hasOrderClause();
+    }
+
+    public boolean hasPartitionClause() {
         return !partitionExpressions.isEmpty();
     }
 
     public boolean hasOrderClause() {
-        return !orderbyList.isEmpty();
+        return !orderByList.isEmpty();
     }
 
     @Override
@@ -186,7 +190,7 @@
         topLevelExpressions.add(query.getBody());
         topLevelExpressions.addAll(pathExpressions);
         topLevelExpressions.addAll(partitionExpressions);
-        topLevelExpressions.addAll(orderbyList);
+        topLevelExpressions.addAll(orderByList);
         return topLevelExpressions;
     }
 
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
index 22c011a..8c7b915 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
@@ -298,9 +298,9 @@
         changed |= part.first;
         stmtCopy.setPartitionExpressions(part.second);
 
-        Pair<Boolean, List<Expression>> order = inlineUdfsInExprList(stmtCopy.getOrderbyList());
+        Pair<Boolean, List<Expression>> order = inlineUdfsInExprList(stmtCopy.getOrderByList());
         changed |= order.first;
-        stmtCopy.setOrderbyList(order.second);
+        stmtCopy.setOrderByList(order.second);
 
         return changed;
     }
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
index 9c904da..3091b30 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
@@ -581,27 +581,30 @@
 
         if (cto.hasOverClause()) {
             out.print("over (");
-            List<Expression> partitionExprs = cto.getPartitionExpressions();
-            Map<Integer, VariableExpr> partitionVars = cto.getPartitionsVariables();
-            out.print(skip(step + 1) + "partition ");
-            for (int i = 0; i < partitionExprs.size(); i++) {
-                if (i > 0) {
-                    out.print(COMMA + " ");
-                }
-                partitionExprs.get(i).accept(this, step + 2);
-                VariableExpr partVar = partitionVars.get(i);
-                if (partVar != null) {
-                    out.print(" as ");
-                    partVar.accept(this, step);
+            if (cto.hasPartitionClause()) {
+                List<Expression> partitionExprs = cto.getPartitionExpressions();
+                Map<Integer, VariableExpr> partitionVars = cto.getPartitionsVariables();
+                out.print(skip(step + 1) + "partition ");
+                for (int i = 0; i < partitionExprs.size(); i++) {
+                    if (i > 0) {
+                        out.print(COMMA + " ");
+                    }
+                    partitionExprs.get(i).accept(this, step + 2);
+                    VariableExpr partVar = partitionVars.get(i);
+                    if (partVar != null) {
+                        out.print(" as ");
+                        partVar.accept(this, step);
+                    }
                 }
             }
             out.println();
             if (cto.hasOrderClause()) {
                 out.print(skip(step + 1) + "order ");
-                printDelimitedObyExpressions(cto.getOrderbyList(), cto.getOrderbyModifiers(),
-                        cto.getOrderbyNullModifierList(), step + 1);
+                printDelimitedObyExpressions(cto.getOrderByList(), cto.getOrderByModifiers(),
+                        cto.getOrderByNullModifierList(), step + 1);
                 out.println();
             }
+            out.println(')');
         }
 
         out.println("with ");
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
index 3373495..febc9d8 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
@@ -254,7 +254,7 @@
         stmtCopy.getQuery().accept(this, arg);
         acceptList(stmtCopy.getPathExpressions(), arg);
         acceptList(stmtCopy.getPartitionExpressions(), arg);
-        acceptList(stmtCopy.getOrderbyList(), arg);
+        acceptList(stmtCopy.getOrderByList(), arg);
         return null;
     }
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
index abae3ee..1863956 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
@@ -317,7 +317,7 @@
     public Boolean visit(CopyToStatement stmtCopy, ILangExpression arg) throws CompilationException {
         return stmtCopy.getQuery().accept(this, arg) || visitExprList(stmtCopy.getPathExpressions(), arg)
                 || visitExprList(stmtCopy.getPartitionExpressions(), arg)
-                || visitExprList(stmtCopy.getOrderbyList(), arg);
+                || visitExprList(stmtCopy.getOrderByList(), arg);
     }
 
     @Override
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
index 37dd1f1..bfa5c91 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
@@ -523,7 +523,7 @@
         stmtCopy.getBody().accept(this, freeVars);
         visit(stmtCopy.getPathExpressions(), freeVars);
         visit(stmtCopy.getPartitionExpressions(), freeVars);
-        visit(stmtCopy.getOrderbyList(), freeVars);
+        visit(stmtCopy.getOrderByList(), freeVars);
         return null;
     }
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
index 39fe905a..e4117f0 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
@@ -312,7 +312,7 @@
     public Boolean visit(CopyToStatement stmtCopy, T arg) throws CompilationException {
         return stmtCopy.accept(this, arg) || visitExprList(stmtCopy.getPathExpressions(), arg)
                 || visitExprList(stmtCopy.getPartitionExpressions(), arg)
-                || visitExprList(stmtCopy.getOrderbyList(), arg);
+                || visitExprList(stmtCopy.getOrderByList(), arg);
     }
 
     private boolean visit(ILangExpression expr, T arg) throws CompilationException {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
index 1ecc3a2..e3e2484 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
@@ -432,7 +432,7 @@
         }
 
         // Visit order by
-        stmtCopy.setOrderbyList(visit(stmtCopy.getOrderbyList(), stmtCopy));
+        stmtCopy.setOrderByList(visit(stmtCopy.getOrderByList(), stmtCopy));
 
         // Visit path exprs
         stmtCopy.setPathExpressions(visit(stmtCopy.getPathExpressions(), stmtCopy));
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
index 9b2501d..f3d2675 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
@@ -363,7 +363,7 @@
         stmtCopy.setBody(stmtCopy.getBody().accept(this, arg));
         stmtCopy.setPathExpressions(visit(stmtCopy.getPathExpressions(), arg));
         stmtCopy.setPartitionExpressions(visit(stmtCopy.getPartitionExpressions(), arg));
-        stmtCopy.setOrderbyList(visit(stmtCopy.getOrderbyList(), arg));
+        stmtCopy.setOrderByList(visit(stmtCopy.getOrderByList(), arg));
         return null;
     }
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 73f6da6..a81807c 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -2959,13 +2959,15 @@
   OrderbyClause orderByClause = null;
 }
 {
-   <OVER> <LEFTPAREN> <IDENTIFIER> {expectToken(PARTITION);} <BY>
+   <OVER> <LEFTPAREN>
+
+   (<IDENTIFIER> {expectToken(PARTITION);} <BY>
    partitionExpr = Expression() { partitionExprs.add(partitionExpr);}
    ((<AS>)? partitionVar = Variable() { partitionVarExprs.put(0, partitionVar); })?
    (
      <COMMA> partitionExpr = Expression() { partitionExprs.add(partitionExpr); }
      ((<AS>)? partitionVar = Variable() { partitionVarExprs.put(partitionExprs.size() - 1, partitionVar); })?
-   )*
+   )*)?
    (
       orderByClause = OrderbyClause()
       {
@@ -2975,6 +2977,11 @@
       }
    )?
    <RIGHTPAREN>
+   {
+     if(partitionExprs.isEmpty() && orderbyList.isEmpty()) {
+       throw new SqlppParseException(getSourceLocation(token), "OVER-clause cannot be empty");
+     }
+   }
 }