>From Shahrzad Shirazi <[email protected]>:

Shahrzad Shirazi has uploaded this change for review. ( 
https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21205?usp=email )


Change subject: WIP: replace an entire object or field with "UPDATE"
......................................................................

WIP: replace an entire object or field with "UPDATE"

Change-Id: I07a2fc3c76afe1f26d95225e4010b57f662f5ef0
---
A 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.12.ddl.sqlpp
A 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.13.query.sqlpp
A 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.14.ddl.sqlpp
A 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.15.query.sqlpp
A 
asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.13.adm
A 
asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.15.adm
M 
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/ChangeExpression.java
M 
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SetExpressionTree.java
M 
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppChangeExprToSelectExprVisitor.java
9 files changed, 299 insertions(+), 4 deletions(-)



  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb 
refs/changes/05/21205/1

diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.12.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.12.ddl.sqlpp
new file mode 100644
index 0000000..d92587e
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.12.ddl.sqlpp
@@ -0,0 +1,80 @@
+
+/*
+ * 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 UserType2 AS {
+  userId: int
+};
+
+CREATE DATASET UserTypes2(UserType2) PRIMARY KEY userId;
+
+
+INSERT INTO UserTypes2([
+  {
+    "userId": 3,
+    "email": "[email protected]",
+    "username": "carol",
+    "isActive": true,
+    "roles": [
+      { "roleId": "role1", "roleName": "moderator" }
+    ],
+    "c_purchase_history": {
+      "c_recent_orders": [
+        { "orderId": "101"} ,
+        { "orderId": "102" } ,
+        { "orderId": "103"}
+      ]
+    }
+  },
+  {
+    "userId": 4,
+    "email": "[email protected]",
+    "username": "carol",
+    "isActive": true,
+    "roles": [
+      { "roleId": "role1", "roleName": "moderator" }
+    ],
+    "c_purchase_history": {
+      "c_recent_orders": [
+        { "orderId": "101"} ,
+        { "orderId": "102" } ,
+        { "orderId": "103"}
+      ]
+    }
+  },{
+    "userId": 5,
+    "email": "[email protected]",
+    "username": "carol",
+    "isActive": true,
+    "roles": [
+      { "roleId": "role1", "roleName": "moderator" }
+    ],
+    "c_purchase_history": {
+      "c_recent_orders": [
+        { "orderId": "101"} ,
+        { "orderId": "102" } ,
+        { "orderId": "103"}
+      ]
+    }
+  }
+]);
+
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.13.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.13.query.sqlpp
new file mode 100644
index 0000000..f5e8586
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.13.query.sqlpp
@@ -0,0 +1,40 @@
+
+/*
+ * 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;
+
+UPDATE UserTypes2 AS u
+set u. c_purchase_history={"a1":33}
+set u. c_purchase_history2={"a2":33}
+WHERE u.userId= 3;
+
+UPDATE UserTypes2 AS u
+set u. c_purchase_history.c_recent_orders=["a","5"]
+set u.a="p"
+set u.isActive=Missing
+WHERE u.userId= 4;
+
+
+UPDATE UserTypes2 AS u
+set u={"userId":5}
+WHERE u.userId= 5;
+
+
+select * from UserTypes2
+order by userId;
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.14.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.14.ddl.sqlpp
new file mode 100644
index 0000000..1e7bf85
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.14.ddl.sqlpp
@@ -0,0 +1,82 @@
+/*
+ * 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 UserType2 AS {
+  userId: int
+};
+
+CREATE DATASET UserTypes2(UserType2) PRIMARY KEY userId;
+
+
+INSERT INTO UserTypes2([
+  {
+    "userId": 3,
+    "email": "[email protected]",
+    "username": "carol",
+    "isActive": true,
+    "roles": [
+      { "roleId": "role1", "roleName": "moderator" },
+      { "roleId": "role2", "roleName": "moderator" }
+
+
+    ],
+    "c_purchase_history": {
+      "c_recent_orders": [
+        { "orderId": "101"} ,
+        { "orderId": "102" } ,
+        { "orderId": "103"}
+      ]
+    }
+  },
+  {
+    "userId": 4,
+    "email": "[email protected]",
+    "username": "carol",
+    "isActive": true,
+    "roles": [
+      { "roleId": "role1", "roleName": "moderator" }
+    ],
+    "c_purchase_history": {
+      "c_recent_orders": [
+        { "orderId": "101"} ,
+        { "orderId": "102" } ,
+        { "orderId": "103"}
+      ]
+    }
+  },{
+    "userId": 5,
+    "email": "[email protected]",
+    "username": "carol",
+    "isActive": true,
+    "roles": [
+      { "roleId": "role1", "roleName": "moderator" }
+    ],
+    "c_purchase_history": {
+      "c_recent_orders": [
+        { "orderId": "101"} ,
+        { "orderId": "102" } ,
+        { "orderId": "103"}
+      ]
+    }
+  }
+]);
+
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.15.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.15.query.sqlpp
new file mode 100644
index 0000000..7947916
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/update-statements/complex-case/complex-case.15.query.sqlpp
@@ -0,0 +1,49 @@
+
+/*
+ * 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;
+
+UPDATE UserTypes2 AS u
+(UPDATE roles AS r at o
+SET r.roleName = {"a":"manager"}
+where o=0)
+set u.c_purchase_history.c_recent_orders={"a":33}
+WHERE u.userId= 3;
+
+
+UPDATE UserTypes2 AS u
+(UPDATE roles AS r
+SET r.roleName = {"a":"manager"})
+set u.c_purchase_history.c_recent_orders={"a":33}
+set u.isActive=missing
+set u.username=null
+WHERE u.userId= 4;
+
+
+
+UPDATE UserTypes2 AS u
+set u.c_purchase_history.c_recent_orders={"a":null}
+set u.isActive=missing
+set u.username=null
+WHERE u.userId= 5;
+
+
+
+select * from UserTypes2
+order by userId;
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.13.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.13.adm
new file mode 100644
index 0000000..3f72b2d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.13.adm
@@ -0,0 +1,3 @@
+{ "UserTypes2": { "userId": 3, "c_purchase_history2": { "a2": 33 }, 
"c_purchase_history": { "a1": 33 }, "email": "[email protected]", "username": 
"carol", "isActive": true, "roles": [ { "roleId": "role1", "roleName": 
"moderator" } ] } }
+{ "UserTypes2": { "userId": 4, "a": "p", "c_purchase_history": { 
"c_recent_orders": [ "a", "5" ] }, "email": "[email protected]", "username": 
"carol", "roles": [ { "roleId": "role1", "roleName": "moderator" } ] } }
+{ "UserTypes2": { "userId": 5 } }
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.15.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.15.adm
new file mode 100644
index 0000000..8356589
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/update-statements/complex-case/complex-case.15.adm
@@ -0,0 +1,3 @@
+{ "UserTypes2": { "userId": 3, "c_purchase_history": { "c_recent_orders": { 
"a": 33 } }, "roles": [ { "roleId": "role1", "roleName": "moderator" }, { 
"roleId": "role2", "roleName": "moderator" } ], "email": "[email protected]", 
"username": "carol", "isActive": true } }
+{ "UserTypes2": { "userId": 4, "username": null, "c_purchase_history": { 
"c_recent_orders": { "a": 33 } }, "roles": [ { "roleName": { "a": "manager" }, 
"roleId": "role1" } ], "email": "[email protected]" } }
+{ "UserTypes2": { "userId": 5, "username": null, "c_purchase_history": { 
"c_recent_orders": { "a": null } }, "email": "[email protected]", "roles": [ { 
"roleId": "role1", "roleName": "moderator" } ] } }
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/ChangeExpression.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/ChangeExpression.java
index 39b6a44..cfc6984 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/ChangeExpression.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/ChangeExpression.java
@@ -244,6 +244,10 @@
         dataRemovalRecord = result.second;
     }

+    public boolean assignWholeRecord() {
+        return exprTree.isWholeRowAssignment();
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(pathExprs, valueExprs, dataTransformRecord, 
dataRemovalRecord, exprTree, priorExpr,
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SetExpressionTree.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SetExpressionTree.java
index fe2031f..e1e670a 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SetExpressionTree.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SetExpressionTree.java
@@ -1,3 +1,4 @@
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -50,7 +51,11 @@
  *       by walking the tree:
  *       <ul>
  *         <li><b>Transformation record:</b> contains all fields with normal 
values (for updates/adds)</li>
- *         <li><b>Deletion record:</b> contains fields set to {@code MISSING} 
(for removal)</li>
+ *         <li><b>Deletion record:</b> contains fields set to {@code MISSING} 
(for removal), plus when a path is
+ *         assigned a <em>record constructor</em> {@code { ... }} directly 
(e.g. {@code SET u.s = {"rating": 675}}),
+ *         a {@code null} marker on that path so {@code 
object-remove-recursive} clears the previous value before
+ *         {@code object-transform}. That replaces the whole field object; 
assigning a scalar or a longer path
+ *         (e.g. {@code SET u.s.rating = 675}) uses merge only and keeps 
sibling fields under {@code u.s}.</li>
  *       </ul>
  *   </li>
  * </ul>
@@ -102,6 +107,14 @@
         return createRecordConstructorInner(root);
     }

+    /**
+     * {@code SET alias = expr} alone (no {@code SET alias.field = ...}): the 
result row is exactly {@code expr},
+     * not a merge of {@code expr} into the previous row.
+     */
+    public boolean isWholeRowAssignment() {
+        return root.hasExpression() && !root.hasChildren();
+    }
+
     private Node accessOrCreatePath(FieldAccessor path, Node node) throws 
CompilationException {
         Expression leadingExpr = path.getExpr();

@@ -118,6 +131,12 @@
         if (node.hasExpression()) {
             Expression expr = node.getExpression();
             if (expr.getKind() != Expression.Kind.LITERAL_EXPRESSION) {
+                //if we are assigning a record to a field, we need a 
deletionRecord
+                // for that field and also a setRecord to make sure that the 
new
+                // value for the field only includes the new sub.fields.
+                if (useRemovalBeforeAssignRecordValue(node, expr)) {
+                    return new Pair<>(expr, new 
LiteralExpr(NullLiteral.INSTANCE));
+                }
                 return new Pair<>(expr, null);
             }
             LiteralExpr literalExpr = (LiteralExpr) expr;
@@ -146,6 +165,19 @@
         return new Pair<>(setRecord, deletionRecord);
     }

+    /**
+     * if the Node name is init that means we are at the root of the tree and 
we are replacing the whole record
+     * Note that this is only possible if the primary key is not changes in 
the new record
+     */
+    private static boolean isSyntheticTreeRoot(Node node) {
+        return "init".equals(node.name);
+    }
+
+    private static boolean useRemovalBeforeAssignRecordValue(Node node, 
Expression expr) {
+        return !node.hasChildren() && !isSyntheticTreeRoot(node)
+                && expr.getKind() == 
Expression.Kind.RECORD_CONSTRUCTOR_EXPRESSION;
+    }
+
     private static class Node {
         private final String name;
         private Expression expr;
@@ -180,10 +212,10 @@
                 }
             }
             // If not found and createIfEmpty is true, create and add the 
child node
-            Node newChild = new Node(childName, null); // New child node with 
no expression
+            Node newChild = new Node(childName, null);// New child node with 
no expression
             children.add(newChild);
             return newChild;
         }
     }

-}
+}
\ No newline at end of file
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppChangeExprToSelectExprVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppChangeExprToSelectExprVisitor.java
index 0d1527c..dac8e5e 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppChangeExprToSelectExprVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppChangeExprToSelectExprVisitor.java
@@ -198,7 +198,9 @@
             dataTransformRecord = 
substituteVariableInExpression(dataTransformRecord, originalVarName,
                     currentContextVariable, changeExpr);
             changeExpr.setPriorExpr(projectExpr);
-            if (dataTransformRecord.getKind() == 
Expression.Kind.RECORD_CONSTRUCTOR_EXPRESSION) {
+            if (changeExpr.assignWholeRecord()) {
+                projectExpr = dataTransformRecord;
+            } else if (dataTransformRecord.getKind() == 
Expression.Kind.RECORD_CONSTRUCTOR_EXPRESSION) {
                 projectExpr = new CallExpr(new 
FunctionSignature(BuiltinFunctions.RECORD_TRANSFORM),
                         new ArrayList<>(Arrays.asList(dataTransformRecord, 
projectExpr)));
                 ((CallExpr) 
projectExpr).setSourceLocation(rewrittenFirstExpr.getSourceLocation());

--
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21205?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://asterix-gerrit.ics.uci.edu/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: asterixdb
Gerrit-Branch: lumina
Gerrit-Change-Id: I07a2fc3c76afe1f26d95225e4010b57f662f5ef0
Gerrit-Change-Number: 21205
Gerrit-PatchSet: 1
Gerrit-Owner: Shahrzad Shirazi <[email protected]>

Reply via email to