From dd51df6734d9b8d8dca9c2a22659e74f674060e2 Mon Sep 17 00:00:00 2001 From: Marten Kenbeek Date: Wed, 9 Mar 2022 16:29:56 +0100 Subject: [PATCH] Only use statically defined subcolumns when determining column definition for supercolumn cell Patch by Marten Kenbeek; review by Benjamin Lerer and Brandon Williams for CASSANDRA-14113 --- CHANGES.txt | 1 + .../org/apache/cassandra/db/LegacyLayout.java | 15 +++++- .../apache/cassandra/db/LegacyLayoutTest.java | 50 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index cebe5fa7bf..d616da2d3e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.11.14 + * Only use statically defined subcolumns when determining column definition for supercolumn cell (CASSANDRA-14113) Merged from 3.0: * Fix writetime and ttl functions forbidden for collections instead of multicell columns (CASSANDRA-17628) * Supress CVE-2020-7238 (CASSANDRA-17697) diff --git a/src/java/org/apache/cassandra/db/LegacyLayout.java b/src/java/org/apache/cassandra/db/LegacyLayout.java index 134574447f..af009254b1 100644 --- a/src/java/org/apache/cassandra/db/LegacyLayout.java +++ b/src/java/org/apache/cassandra/db/LegacyLayout.java @@ -143,10 +143,10 @@ public abstract class LegacyLayout private static LegacyCellName decodeForSuperColumn(CFMetaData metadata, Clustering clustering, ByteBuffer subcol) { - ColumnDefinition def = metadata.getColumnDefinition(subcol); + ColumnDefinition def = getStaticallyDefinedSubColumn(metadata, subcol); if (def != null) { - // it's a statically defined subcolumn + // it's a statically defined subcolumn from column_metadata return new LegacyCellName(clustering, def, null); } @@ -155,6 +155,17 @@ public abstract class LegacyLayout return new LegacyCellName(clustering, def, subcol); } + private static ColumnDefinition getStaticallyDefinedSubColumn(CFMetaData metadata, ByteBuffer subcol) { + if (metadata.isDense()) + return null; + + ColumnDefinition def = metadata.getColumnDefinition(subcol); + if (def == null || !def.isRegular() || SuperColumnCompatibility.isSuperColumnMapColumn(def)) + return null; + + return def; + } + public static LegacyCellName decodeCellName(CFMetaData metadata, ByteBuffer cellname) throws UnknownColumnException { return decodeCellName(metadata, cellname, false); diff --git a/test/unit/org/apache/cassandra/db/LegacyLayoutTest.java b/test/unit/org/apache/cassandra/db/LegacyLayoutTest.java index 65565e1d0b..d79d8a00fc 100644 --- a/test/unit/org/apache/cassandra/db/LegacyLayoutTest.java +++ b/test/unit/org/apache/cassandra/db/LegacyLayoutTest.java @@ -30,6 +30,7 @@ import org.apache.cassandra.db.LegacyLayout.LegacyBound; import org.apache.cassandra.db.LegacyLayout.LegacyCell; import org.apache.cassandra.db.LegacyLayout.LegacyRangeTombstone; import org.apache.cassandra.db.filter.ColumnFilter; +import org.apache.cassandra.db.marshal.CompositeType; import org.apache.cassandra.db.marshal.MapType; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.db.rows.BufferCell; @@ -483,4 +484,53 @@ public class LegacyLayoutTest LegacyCell cc = cell(clustering, cfm.getColumnDefinition(bytes("c")), bytes("v2"), 1); assertTrue(grouper.addAtom(cc)); } + + @Test + public void testSubColumnCellNameSparseTable() throws UnknownColumnException + { + // Sparse supercolumn table with statically defined subcolumn from column_metadata, "static_subcolumn". + CFMetaData cfm = CFMetaData.Builder.create("ks", "table", false, true, true, false) + .addPartitionKey("key", Int32Type.instance) + .addClusteringColumn("column1", Int32Type.instance) + .addRegularColumn("", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, true)) + .addRegularColumn("static_subcolumn", Int32Type.instance) + .build(); + + assertDecodedCellNameEquals(cfm, bytes("key"), cfm.compactValueColumn(), bytes("key")); + assertDecodedCellNameEquals(cfm, bytes("column1"), cfm.compactValueColumn(), bytes("column1")); + assertDecodedCellNameEquals(cfm, bytes(""), cfm.compactValueColumn(), bytes("")); + + assertDecodedCellNameEquals(cfm, bytes("static_subcolumn"), cfm.getColumnDefinition(bytes("static_subcolumn")), null); + assertDecodedCellNameEquals(cfm, bytes("regular_cellname"), cfm.compactValueColumn(), bytes("regular_cellname")); + } + + @Test + public void testSubColumnCellNameDenseTable() throws UnknownColumnException + { + // Dense supercolumn table, with no statically defined subcolumns. + CFMetaData cfm = CFMetaData.Builder.createSuper("ks", "table", false) + .addPartitionKey("key", Int32Type.instance) + .addClusteringColumn("column1", Int32Type.instance) + .addRegularColumn("", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, true)) + .build(); + + assertDecodedCellNameEquals(cfm, bytes("key"), cfm.compactValueColumn(), bytes("key")); + assertDecodedCellNameEquals(cfm, bytes("column1"), cfm.compactValueColumn(), bytes("column1")); + assertDecodedCellNameEquals(cfm, bytes(""), cfm.compactValueColumn(), bytes("")); + assertDecodedCellNameEquals(cfm, bytes("column2"), cfm.compactValueColumn(), bytes("column2")); + assertDecodedCellNameEquals(cfm, bytes("value"), cfm.compactValueColumn(), bytes("value")); + } + + private void assertDecodedCellNameEquals(CFMetaData cfm, + ByteBuffer subColumn, + ColumnDefinition columnDefinition, + ByteBuffer collectionElement) + throws UnknownColumnException + { + ByteBuffer cellNameBuffer = CompositeType.build(bytes(1), subColumn); + LegacyLayout.LegacyCellName decodedCellName = LegacyLayout.decodeCellName(cfm, cellNameBuffer, false); + assertArrayEquals(new ByteBuffer[]{bytes(1)}, decodedCellName.clustering.getRawValues()); + assertEquals(columnDefinition, decodedCellName.column); + assertEquals(collectionElement, decodedCellName.collectionElement); + } }