dcapwell commented on code in PR #4024:
URL: https://github.com/apache/cassandra/pull/4024#discussion_r2025165752


##########
test/harry/main/org/apache/cassandra/harry/model/ASTSingleTableModel.java:
##########
@@ -341,61 +380,142 @@ else if (!clusterings.isEmpty())
         }
     }
 
+    private static void maybeUpdateColumns(Set<Symbol> columns,
+                                           @Nullable BytesPartitionState.Row 
row,
+                                           long nowTs, Map<Symbol, Expression> 
set,
+                                           ColumnUpdate update)
+    {
+        if (columns.isEmpty())
+        {
+            update.update(nowTs, Collections.emptyMap());
+            return;
+        }
+        // static columns to add in.  If we are doing something like += to a 
row that doesn't exist, we still update statics...
+        Map<Symbol, Update> write = new HashMap<>();
+        for (Symbol col : columns)
+        {
+            ByteBuffer current = row == null ? null : row.get(col);
+            Update result = eval(col, current, set.get(col));
+            if (result.kind == Update.Kind.SKIP) continue;
+            write.put(col, result);
+        }
+        if (!write.isEmpty())
+            update.update(nowTs, write);
+    }
+
+    public boolean shouldApply(Batch batch)
+    {
+        if (!batch.isCas()) return true;
+        //TODO (correctness): what happens with CAS on multiple partitions?
+        boolean shouldApply = 
batch.mutations.stream().allMatch(this::shouldApply);
+        if (shouldApply
+            && 
ignoredIssues.contains(KnownIssue.BATCH_CAS_DELETE_STATIC_WITH_UPDATE_IF_NOT_EXISTS)
+            && !factory.staticColumns.isEmpty())
+        {
+            // No really, should it?
+            // Let's say you have 2 mutations
+            // DELETE s0 FROM tbl WHERE pk=0 IF EXISTS
+            // INSERT INTO tbl (pk, ck, r) VALUES (0, 0, 0) IF NOT EXISTS
+            // There are 2 cases this query can be in: static columns have 
values, static columns are null
+            // When the static columns are not null, then this applies as 
expected, but when the static columns are null then the CAS read query returns 
empty!
+            // CAS does "SELECT s0, r FROM tbl WHERE pk=0 AND ck=0" and since 
the row and the static columns are null, the empty result is generated; so the 
DELETE's condition of "IF EXISTS" can not apply!
+            // Now, it doesn't mean that the partition doesn't exist, the 
DELETE outside a batch would do "SELECT s0 FROM tbl WHERE pk=0 LIMIT 1"
+            boolean partitionExistsButCasConditionWillFail = false;
+            boolean rowDoesNotExist = false;
+            for (var m : batch.mutations)
+            {
+                if (m.casCondition().isEmpty()) continue;
+                var condition = m.casCondition().get();
+
+                var row = referenceRow(m);
+                if (m.kind == Mutation.Kind.DELETE)
+                {
+                    var delete = (Mutation.Delete) m;
+                    if (!delete.columns.isEmpty()
+                        && row.clustering == null // delete is partition 
level, so deletes static columns
+                        && existsOrColumnConditionsEqNull(condition))
+                    {
+                        // are the columns deleted currently null?
+                        var partition = partitions.get(row.partition);
+                        if (partition != null)
+                        {
+                            if (partition.staticRow().isEmpty())
+                                partitionExistsButCasConditionWillFail = true;
+                        }
+                    }
+                }
+                if (row.clustering != null && condition == 
CasCondition.Simple.NotExists)
+                    rowDoesNotExist = true;
+            }
+            if (rowDoesNotExist && partitionExistsButCasConditionWillFail)
+                return false;
+        }
+        return shouldApply;
+    }
+
+    private static boolean existsOrColumnConditionsEqNull(CasCondition 
condition)
+    {
+        if (condition == CasCondition.Simple.Exists) return true;
+        var conditions = ((CasCondition.IfCondition) 
condition).conditional.simplify();
+        return conditions.stream().allMatch(c -> {
+            if (!(c instanceof Conditional.Where)) return false;
+            var where = (Conditional.Where) c;
+            if (where.kind != Inequality.EQUAL) return false;
+            return ExpressionEvaluator.eval(where.rhs) == null;
+        });
+    }
+
     public boolean shouldApply(Mutation mutation)
     {
         if (!mutation.isCas()) return true;
         return shouldApply(mutation, selectPartitionForCAS(mutation));
     }
 
-    private SelectResult selectPartitionForCAS(Mutation mutation)
+    private CasContext selectPartitionForCAS(Mutation mutation)
     {
-        var partition = partitions.get(factory.createRef(pd(mutation)));
-        if (partition == null) return 
SelectResult.ordered(factory.selectionOrder, NO_ROWS);
-
-        var cd = cdOrNull(mutation);
-        var row = cd == null ? null : partition.get(cd);
-        ImmutableUniqueList<Symbol> columns = cd != null ? 
factory.selectionOrder : factory.partitionAndStaticColumns;
-        return SelectResult.ordered(columns, new ByteBuffer[][] { 
getRowAsByteBuffer(columns, partition, row)});
+        BytesPartitionState.Ref ref = referencePartition(mutation);
+        Clustering<ByteBuffer> cd = cdOrNull(mutation);
+        BytesPartitionState partition = partitions.get(ref);
+        return new CasContext(ref, cd, partition);
     }
 
-    private boolean shouldApply(Mutation mutation, SelectResult current)
+    private boolean shouldApply(Mutation mutation, CasContext ctx)
     {
         Preconditions.checkArgument(mutation.isCas());
         // process condition
-        CasCondition condition;
-        switch (mutation.kind)
-        {
-            case INSERT:
-                condition = CasCondition.Simple.NotExists;
-                break;
-            case UPDATE:
-                condition = ((Mutation.Update) mutation).casCondition.get();
-                break;
-            case DELETE:
-                condition = ((Mutation.Delete) mutation).casCondition.get();
-                break;
-            default:
-                throw new UnsupportedOperationException(mutation.kind.name());
-        }
+        CasCondition condition = mutation.casCondition().get();
+        boolean partitionOrRow = ctx.clustering == null;
+        boolean partitionKnown = ctx.partition != null;
+        BytesPartitionState.Row row = partitionKnown && !partitionOrRow
+                                      ? ctx.partition.get(ctx.clustering)
+                                      : null;
         if (condition instanceof CasCondition.Simple)
         {
-            boolean hasPartition = current.rows.length > 0;
-            boolean partitionOrRow = 
current.columns.equals(factory.partitionAndStaticColumns);
-            boolean hasRow = partitionOrRow ? hasPartition : 
current.isAllDefined(factory.clusteringColumns);
+            if (partitionOrRow && factory.staticColumns.isEmpty())
+                throw new AssertionError("Attempted to create a EXISTS 
condition on partition without static columns; " + mutation.toCQL());
+            // CAS's definition of partition EXISTS isn't based off the 
partition existing, its based off the static row
+            // existing (aka at least 1 static column exists and is not null).
+            boolean hasPartition = partitionKnown && 
!ctx.partition.staticRow().isEmpty();
+            boolean hasRow = row != null && !row.isEmpty();
             var simple = (CasCondition.Simple) condition;
             switch (simple)
             {
                 case Exists:
-                    return hasRow;
+                    return partitionOrRow ? hasPartition : hasRow;

Review Comment:
   turns out that CAS defines partition `EXISTS` as 
   
   1) partition is defined
   2) static row is not empty
   
   The previous logic defined it only as #1 so had to make this change to 
support the empty static row check



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: pr-unsubscr...@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscr...@cassandra.apache.org
For additional commands, e-mail: pr-h...@cassandra.apache.org

Reply via email to