This is an automated email from the ASF dual-hosted git repository.

dcapwell pushed a commit to branch cassandra-4.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/cassandra-4.0 by this push:
     new 4fc8bb29fc IndexOutOfBoundsException when accessing partition where 
the column was deleted
4fc8bb29fc is described below

commit 4fc8bb29fcda935728d8863a4499fa0e9d924b82
Author: Sunil Ramchandra Pawar <[email protected]>
AuthorDate: Tue Jan 7 08:58:48 2025 -0800

    IndexOutOfBoundsException when accessing partition where the column was 
deleted
    
    patch by Sunil Ramchandra Pawar; reviewed by Caleb Rackliffe, David Capwell 
for CASSANDRA-20108
---
 CHANGES.txt                                        |  1 +
 .../org/apache/cassandra/db/filter/RowFilter.java  | 32 ++++++++++++----------
 .../cql3/validation/operations/SelectTest.java     | 30 ++++++++++++++++++++
 .../cassandra/index/sasi/plan/OperationTest.java   |  2 +-
 4 files changed, 49 insertions(+), 16 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 23dcc154f8..7eb0068d79 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 4.0.16
  * Fix gossip issue with gossip-only and bootstrapping nodes missing 
DC/Rack/Host ID endpoint state (CASSANDRA-19983)
+ * IndexOutOfBoundsException when accessing partition where the column was 
deleted (CASSANDRA-20108)
 
 
 4.0.15
diff --git a/src/java/org/apache/cassandra/db/filter/RowFilter.java 
b/src/java/org/apache/cassandra/db/filter/RowFilter.java
index 68a1d57fe1..69a57918cf 100644
--- a/src/java/org/apache/cassandra/db/filter/RowFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/RowFilter.java
@@ -175,7 +175,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
 
         for (Expression e : expressions)
         {
-            if (!e.isSatisfiedBy(metadata, partitionKey, purged))
+            if (!e.isSatisfiedBy(metadata, partitionKey, purged, nowInSec))
                 return false;
         }
         return true;
@@ -301,7 +301,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
 
                     // Short-circuit all partitions that won't match based on 
static and partition keys
                     for (Expression e : partitionLevelExpressions)
-                        if (!e.isSatisfiedBy(metadata, 
partition.partitionKey(), partition.staticRow()))
+                        if (!e.isSatisfiedBy(metadata, 
partition.partitionKey(), partition.staticRow(),  nowInSec))
                         {
                             partition.close();
                             return null;
@@ -327,7 +327,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
                         return null;
 
                     for (Expression e : rowLevelExpressions)
-                        if (!e.isSatisfiedBy(metadata, pk, purged))
+                        if (!e.isSatisfiedBy(metadata, pk, purged, nowInSec))
                             return null;
 
                     return row;
@@ -437,9 +437,9 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
          * (i.e. it should come from a RowIterator).
          * @return whether the row is satisfied by this expression.
          */
-        public abstract boolean isSatisfiedBy(TableMetadata metadata, 
DecoratedKey partitionKey, Row row);
+        public abstract boolean isSatisfiedBy(TableMetadata metadata, 
DecoratedKey partitionKey, Row row, int nowInSec);
 
-        protected ByteBuffer getValue(TableMetadata metadata, DecoratedKey 
partitionKey, Row row)
+        protected ByteBuffer getValue(TableMetadata metadata, DecoratedKey 
partitionKey, Row row, int nowInSec)
         {
             switch (column.kind)
             {
@@ -451,7 +451,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
                     return row.clustering().bufferAt(column.position());
                 default:
                     Cell<?> cell = row.getCell(column);
-                    return cell == null ? null : cell.buffer();
+                    return cell == null || cell.isTombstone() || 
!cell.isLive(nowInSec) ? null : cell.buffer();
             }
         }
 
@@ -594,7 +594,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
             super(column, operator, value);
         }
 
-        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row)
+        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row, int nowInSec)
         {
             // We support null conditions for LWT (in ColumnCondition) but not 
for RowFilter.
             // TODO: we should try to merge both code someday.
@@ -614,7 +614,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
                         // representation. See CASSANDRA-11629
                         if (column.type.isCounter())
                         {
-                            ByteBuffer foundValue = getValue(metadata, 
partitionKey, row);
+                            ByteBuffer foundValue = getValue(metadata, 
partitionKey, row, nowInSec);
                             if (foundValue == null)
                                 return false;
 
@@ -624,7 +624,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
                         else
                         {
                             // Note that CQL expression are always of the form 
'x < 4', i.e. the tested value is on the left.
-                            ByteBuffer foundValue = getValue(metadata, 
partitionKey, row);
+                            ByteBuffer foundValue = getValue(metadata, 
partitionKey, row, nowInSec);
                             return foundValue != null && 
operator.isSatisfiedBy(column.type, foundValue, value);
                         }
                     }
@@ -635,7 +635,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
                 case LIKE_MATCHES:
                     {
                         assert !column.isComplex() : "Only CONTAINS and 
CONTAINS_KEY are supported for 'complex' types";
-                        ByteBuffer foundValue = getValue(metadata, 
partitionKey, row);
+                        ByteBuffer foundValue = getValue(metadata, 
partitionKey, row, nowInSec);
                         // Note that CQL expression are always of the form 'x 
< 4', i.e. the tested value is on the left.
                         return foundValue != null && 
operator.isSatisfiedBy(column.type, foundValue, value);
                     }
@@ -665,7 +665,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
                     }
                     else
                     {
-                        ByteBuffer foundValue = getValue(metadata, 
partitionKey, row);
+                        ByteBuffer foundValue = getValue(metadata, 
partitionKey, row, nowInSec);
                         if (foundValue == null)
                             return false;
 
@@ -692,7 +692,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
                     }
                     else
                     {
-                        ByteBuffer foundValue = getValue(metadata, 
partitionKey, row);
+                        ByteBuffer foundValue = getValue(metadata, 
partitionKey, row, nowInSec);
                         return foundValue != null && 
mapType.getSerializer().getSerializedValue(foundValue, value, 
mapType.getKeysType()) != null;
                     }
 
@@ -765,7 +765,8 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
             return CompositeType.build(ByteBufferAccessor.instance, key, 
value);
         }
 
-        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row)
+        @Override
+        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row, int nowInSec)
         {
             assert key != null;
             // We support null conditions for LWT (in ColumnCondition) but not 
for RowFilter.
@@ -783,7 +784,7 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
             }
             else
             {
-                ByteBuffer serializedMap = getValue(metadata, partitionKey, 
row);
+                ByteBuffer serializedMap = getValue(metadata, partitionKey, 
row, nowInSec);
                 if (serializedMap == null)
                     return false;
 
@@ -879,7 +880,8 @@ public abstract class RowFilter implements 
Iterable<RowFilter.Expression>
         }
 
         // Filtering by custom expressions isn't supported yet, so just accept 
any row
-        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row)
+        @Override
+        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row, int nowInSec)
         {
             return true;
         }
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index d0493cf8fc..0a7b507df1 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@ -2301,6 +2301,36 @@ public class SelectTest extends CQLTester
         });
     }
 
+    @Test
+    public void filteringOnDeletedStaticColumnValue() throws Throwable
+    {
+        // Create table with int-only columns
+        createTable("CREATE TABLE %s (pk0 int, pk1 int, ck0 int, ck1 int, s0 
tinyint static, v0 int, v1 int, PRIMARY KEY ((pk0, pk1), ck0, ck1))");
+
+        // Insert rows
+        execute("INSERT INTO %s (pk0, pk1, s0, ck0, ck1, v0, v1) VALUES (?, ?, 
?, ?, ?, ?, ?)", 1000, 2000, (byte) 126, 100, 1, 20, 30);
+        execute("INSERT INTO %s (pk0, pk1, s0, ck0, ck1, v0, v1) VALUES (?, ?, 
?, ?, ?, ?, ?)", 1000, 2000, (byte) 125, 200, 2, 40, 50);
+        execute("INSERT INTO %s (pk0, pk1, s0, ck0, ck1, v0, v1) VALUES (?, ?, 
?, ?, ?, ?, ?)", 1000, 3000, (byte) 122, 300, 3, 60, 70);
+        execute("DELETE s0,v0,v1 FROM %s WHERE pk0=1000 AND pk1=2000 and 
ck0=100 and ck1=1");
+
+        beforeAndAfterFlush(() -> {
+            // Verify the columns are deleted
+            assertRows(execute("SELECT pk0, pk1, s0, ck0, ck1, v0, v1 FROM %s 
WHERE s0=? ALLOW FILTERING", (byte) 122),
+                       row(1000, 3000, (byte) 122, 300, 3, 60, 70));
+        });
+
+        execute("DELETE v0 FROM %s WHERE pk0=1000 AND pk1=3000 AND ck0=300 AND 
ck1=3");
+
+        beforeAndAfterFlush(() -> {
+            assertRows(execute("SELECT pk0, pk1, s0, ck0, ck1, v0, v1 FROM %s 
WHERE s0=? ALLOW FILTERING", (byte) 122),
+                       row(1000, 3000, (byte) 122, 300, 3, null, 70));
+
+            assertRows(execute("SELECT pk0, pk1, s0, ck0, ck1, v0, v1 FROM %s 
WHERE pk0=1000 AND pk1=3000 AND ck0=300 AND ck1=3"),
+                       row(1000, 3000, (byte) 122, 300, 3, null, 70));
+        });
+
+    }
+
     @Test
     public void containsFilteringOnNonClusteringColumn() throws Throwable {
         createTable("CREATE TABLE %s (a int, b int, c int, d list<int>, 
PRIMARY KEY (a, b, c))");
diff --git a/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java 
b/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
index 4468f2c219..e80e68fb76 100644
--- a/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
+++ b/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
@@ -647,7 +647,7 @@ public class OperationTest extends SchemaLoader
         }
 
         @Override
-        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row)
+        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey 
partitionKey, Row row, int nowInSec)
         {
             throw new UnsupportedOperationException();
         }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to