Fixed multiple single-node SAI query bugs relating to static columns

- Ensure MemtableIndexWriter calculates min/max properly with indexes on partition key elements
- Ensure only rows with live data are indexed
- Ensure min cannot be greater than max in intersection statistics with static keys
- Correct tracking of last key in the searcher in the presence of static keys

patch by Caleb Rackliffe; reviewed by David Capwell for CASSANDRA-20338
This commit is contained in:
Caleb Rackliffe 2025-02-20 12:25:07 -06:00
parent 944baa08da
commit 4e2e5f3c57
9 changed files with 170 additions and 13 deletions

View File

@ -1,4 +1,5 @@
5.0.4
* Fixed multiple single-node SAI query bugs relating to static columns (CASSANDRA-20338)
* Upgrade com.datastax.cassandra:cassandra-driver-core:3.11.5 to org.apache.cassandra:cassandra-driver-core:3.12.1 (CASSANDRA-17231)
* Update netty to 4.1.119.Final and netty-tcnative to 2.0.70.Final (CASSANDRA-20314)
* Serialization can lose complex deletions in a mutation with multiple collections in a row (CASSANDRA-20449)

View File

@ -177,7 +177,7 @@ public class StorageAttachedIndexGroup implements Index.Group, INotificationCons
public void insertRow(Row row)
{
// SAI does not index deletions, as these are resolved during post-filtering.
if (row.deletion().isLive())
if (row.hasLiveData(nowInSec, false))
for (Index.Indexer indexer : indexers)
indexer.insertRow(row);
}
@ -185,8 +185,10 @@ public class StorageAttachedIndexGroup implements Index.Group, INotificationCons
@Override
public void updateRow(Row oldRow, Row newRow)
{
for (Index.Indexer indexer : indexers)
indexer.updateRow(oldRow, newRow);
// SAI does not index deletions, as these are resolved during post-filtering.
if (newRow.hasLiveData(nowInSec, false))
for (Index.Indexer indexer : indexers)
indexer.updateRow(oldRow, newRow);
}
};
}

View File

@ -37,6 +37,7 @@ import org.apache.cassandra.index.sai.StorageAttachedIndex;
import org.apache.cassandra.index.sai.disk.format.IndexDescriptor;
import org.apache.cassandra.index.sai.utils.PrimaryKey;
import org.apache.cassandra.io.sstable.SSTableFlushObserver;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Throwables;
/**
@ -52,6 +53,8 @@ public class StorageAttachedIndexWriter implements SSTableFlushObserver
private final PerSSTableIndexWriter perSSTableWriter;
private final Stopwatch stopwatch = Stopwatch.createUnstarted();
private final RowMapping rowMapping;
private final long nowInSeconds = FBUtilities.nowInSeconds();
private DecoratedKey currentKey;
private boolean tokenOffsetWriterCompleted = false;
private boolean aborted = false;
@ -126,9 +129,14 @@ public class StorageAttachedIndexWriter implements SSTableFlushObserver
if (!unfiltered.isRow())
return;
// Ignore rows with no live data...
Row row = (Row) unfiltered;
if (!row.hasLiveData(nowInSeconds, false))
return;
try
{
addRow((Row)unfiltered);
addRow(row);
}
catch (Throwable t)
{

View File

@ -91,14 +91,16 @@ public class MemtableIndexWriter implements PerColumnIndexWriter
// keys and row IDs in the flushing SSTable. This writer, therefore, does nothing in
// response to the flushing of individual rows except for keeping index-specific statistics.
boolean isStatic = indexTermType.columnMetadata().isStatic();
boolean isPartitionKey = indexTermType.columnMetadata().isPartitionKey();
// Indexes on static columns should only track static rows, and indexes on non-static columns
// should only track non-static rows. (Within a partition, the row ID for a static row will always
// come before any non-static row.)
if (key.kind() == PrimaryKey.Kind.STATIC && isStatic || key.kind() != PrimaryKey.Kind.STATIC && !isStatic)
// come before any non-static row.) The only exception to this is indexes on partition key elements.
if ((key.kind() == PrimaryKey.Kind.STATIC && (isStatic || isPartitionKey)) || key.kind() != PrimaryKey.Kind.STATIC && !isStatic)
{
if (minKey == null)
minKey = key;
maxKey = key;
rowCount++;
maxSSTableRowId = Math.max(maxSSTableRowId, sstableRowId);

View File

@ -375,6 +375,10 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
min = nullSafeMax(min, range.getMinimum());
// maximum of the intersection is the smallest maximum of individual iterators
max = nullSafeMin(max, range.getMaximum());
// With STATIC keys, it is possible for the min to overtake the max, which must be corrected.
min = nullSafeMin(min, max);
if (empty)
{
empty = false;

View File

@ -229,7 +229,7 @@ public class StorageAttachedIndexSearcher implements Index.Searcher
if (firstKey == null)
return Collections.emptyList();
}
while (queryController.doesNotSelect(firstKey) || firstKey.equals(lastKey));
while (queryController.doesNotSelect(firstKey) || firstKey.equals(lastKey, false));
lastKey = firstKey;
threadLocalNextKeys.add(firstKey);
@ -291,7 +291,7 @@ public class StorageAttachedIndexSearcher implements Index.Searcher
if (key == null)
break;
if (queryController.doesNotSelect(key) || key.equals(lastKey))
if (queryController.doesNotSelect(key) || key.equals(lastKey, false))
continue;
nextPrimaryKeys.add(key);

View File

@ -218,10 +218,20 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
}
@Override
public boolean equals(Object obj)
public boolean equals(Object o)
{
if (obj instanceof PrimaryKey)
return compareTo((PrimaryKey) obj) == 0;
if (o instanceof PrimaryKey)
return compareTo((PrimaryKey) o) == 0;
return false;
}
@Override
public boolean equals(Object o, boolean strict)
{
if (o == null)
return false;
if (o instanceof PrimaryKey)
return compareTo((PrimaryKey) o, strict) == 0;
return false;
}
@ -494,4 +504,6 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
{
return compareTo(key);
}
boolean equals(Object obj, boolean strict);
}

View File

@ -22,19 +22,99 @@ import java.math.BigInteger;
import org.junit.Test;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.db.marshal.ByteType;
import org.apache.cassandra.db.marshal.FloatType;
import org.apache.cassandra.db.marshal.IntegerType;
import org.apache.cassandra.db.marshal.SimpleDateType;
import org.apache.cassandra.db.marshal.TimeType;
import org.apache.cassandra.db.marshal.UUIDType;
import org.apache.cassandra.index.sai.SAITester;
import org.apache.cassandra.utils.ByteBufferUtil;
public class CompositePartitionKeyIndexTest extends SAITester
{
@Test
public void testStaticAndNonStaticKeysOnFlush() throws Throwable
{
createTable("CREATE TABLE %s (pk0 tinyint, pk1 bigint, ck0 blob, s1 text static, s0 set<uuid> static, v0 smallint, PRIMARY KEY ((pk0, pk1), ck0)) WITH CLUSTERING ORDER BY (ck0 DESC)");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX tbl_pk0 ON %s(pk0) USING 'sai'");
createIndex("CREATE INDEX tbl_pk1 ON %s(pk1) USING 'sai'");
createIndex("CREATE INDEX tbl_s1 ON %s(s1) USING 'sai'");
createIndex("CREATE INDEX tbl_v0 ON %s(v0) USING 'sai'");
execute("INSERT INTO %s (pk0, pk1, ck0, s1, s0, v0) VALUES (-62, -5815950741950477880, 0x326f, '켅\uF6EB憓ᤃ\uEF32ꝃ窰ŷ', {00000000-0000-4700-aa00-000000000000}, 19310) USING TIMESTAMP 1");
execute("DELETE FROM %s USING TIMESTAMP 2 WHERE pk0 = 45 AND pk1 = 6014418364385708772 AND ck0 = 0x7c10");
execute("DELETE FROM %s USING TIMESTAMP 3 WHERE pk0 = -41 AND pk1 = -3934225888295599640");
execute("INSERT INTO %s (pk0, pk1, ck0, s1, s0, v0) " +
"VALUES (-64, 7973592261481566341, 0x0d, '\uE11B摻', {00000000-0000-4800-8900-000000000000, 00000000-0000-4900-8600-000000000000}, -23873) USING TIMESTAMP 4");
flush(KEYSPACE);
execute("UPDATE %s USING TIMESTAMP 5 SET v0=-359, s1='ل≻Ⱆ喡䮠?' WHERE pk0 = -64 AND pk1 = 7973592261481566341 AND ck0 = 0x99d570024de738f37877");
execute("INSERT INTO %s (pk0, pk1, ck0, v0, s1, s0) " +
"VALUES (-104, -4990846884898776392, 0xf7ac771298eaf1d4, -6977, '凘纖볭菮⏏↶?蜑', null) USING TIMESTAMP 6");
execute("INSERT INTO %s (pk0, pk1, ck0, s1, s0, v0) " +
"VALUES (-62, -5815950741950477880, 0x9277e744212e1c4b50, '\uF6AD瀛⛕徳倬糽ᢷ' + '雴', {00000000-0000-4700-b100-000000000000, 00000000-0000-4800-9300-000000000000}, 5423) USING TIMESTAMP 7");
execute("DELETE FROM %s USING TIMESTAMP 8 WHERE pk0 = -64 AND pk1 = 7973592261481566341");
flush(KEYSPACE);
execute("DELETE s0, s1, s0 FROM %s USING TIMESTAMP 9 WHERE pk0 = -62 AND pk1 = -5815950741950477880");
execute("DELETE FROM %s USING TIMESTAMP 10 WHERE pk0 = -41 AND pk1 = -3934225888295599640 AND ck0 = 0xd753dc3a473acaf665");
execute("INSERT INTO %s (pk0, pk1, ck0, s1, s0, v0) " +
"VALUES (-62, -5815950741950477880, 0xd1e07b568a7188, 'ᑿ鼾戆' + '篐뵡?䰫', {00000000-0000-4500-b000-000000000000}, 17933) USING TIMESTAMP 11");
execute("UPDATE %s USING TIMESTAMP 12 SET v0=null, s0={00000000-0000-4600-a000-000000000000, 00000000-0000-4d00-8200-000000000000, 00000000-0000-4f00-9200-000000000000} " +
"WHERE pk0 = -41 AND pk1 = -3934225888295599640 AND ck0 = 0x0dab3b038131efa2");
assertRowCount(execute("SELECT * FROM %s WHERE pk0 >= ? LIMIT 81", (byte) -104), 5);
execute("DELETE FROM %s USING TIMESTAMP 13 WHERE pk0 = -64 AND pk1 = 7973592261481566341");
flush(KEYSPACE);
beforeAndAfterFlush(() ->
assertRows(execute("SELECT pk0, pk1, ck0 FROM %s WHERE pk0 >= ?", (byte) -104),
row((byte) -62, -5815950741950477880L, ByteBufferUtil.hexToBytes("d1e07b568a7188")),
row((byte) -62, -5815950741950477880L, ByteBufferUtil.hexToBytes("9277e744212e1c4b50")),
row((byte) -62, -5815950741950477880L, ByteBufferUtil.hexToBytes("326f")),
row((byte) -104, -4990846884898776392L, ByteBufferUtil.hexToBytes("f7ac771298eaf1d4")),
row((byte) -41, -3934225888295599640L, null)));
}
@Test
public void testIgnoreCellDeletions() throws Throwable
{
createTable("CREATE TABLE %s (pk0 boolean, pk1 varint, ck0 tinyint, ck1 varint, s0 list<frozen<map<double, smallint>>> static, " +
" s1 map<frozen<set<uuid>>, frozen<map<inet, date>>> static, v0 frozen<map<frozen<set<text>>, uuid>>, " +
" PRIMARY KEY ((pk0, pk1), ck0, ck1)) WITH CLUSTERING ORDER BY (ck0 DESC, ck1 DESC)");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX tbl_pk0 ON %s(pk0) USING 'sai'");
execute("INSERT INTO %s (pk0, pk1, ck0, ck1, s0, s1, v0) " +
"VALUES (true, 0, 109, 0, [{2.2352903520430565E260: -29214, 2.605618737869944E274: -13041}], " +
" {{00000000-0000-4400-9f00-000000000000, 00000000-0000-4500-9b00-000000000000, 00000000-0000-4b00-bf00-000000000000}: {'18.112.79.221': '-2306623-03-19', '227.58.183.116': '-3929454-04-25'}}, " +
" {{'⭎憢?', '黣偛紑'}: 00000000-0000-4900-8600-000000000000, {'㛽ꓗ', '剢ꮱ死䰀륬ਐ喑ퟚ', '竖䝏爐뷤曀'}: 00000000-0000-4900-bc00-000000000000}) USING TIMESTAMP 1");
execute("INSERT INTO %s (pk0, pk1, ck0, ck1, s1, v0) " +
"VALUES (true, 0, 114, 742, {{00000000-0000-4000-9a00-000000000000, 00000000-0000-4700-ba00-000000000000}: {'96.31.70.25': '-912836-06-15', '185.90.18.173': '-5257542-01-31', '223.18.191.245': '-4633145-10-30'}}, " +
" {{'뫥㩎뎠ྭẒ'}: 00000000-0000-4800-8600-000000000000}) USING TIMESTAMP 2");
// This will result in the creation of erroneous postings if cell deletions are not accounted for:
execute("DELETE v0, s1, s0 FROM %s USING TIMESTAMP 6 WHERE pk0 = true AND pk1 = 0 AND ck0 = 121 AND ck1 = 1");
execute("UPDATE %s USING TIMESTAMP 8 SET s0 += [{4.3056056376102396E-169: 22551, 1.439623561042819E208: 20450}, {-2.7900719406964408E-242: 30147, 8.586565205109037E-211: 28721, 4.603864140847754E20: -12814}], " +
" s1 += {{00000000-0000-4200-b900-000000000000, 00000000-0000-4500-ab00-000000000000}: {'2.67.240.121': '-471656-04-17', '134.186.187.51': '-2056459-04-13'}}, " +
" v0={{'?', '蠥╩徰昰弳펠재', '됢簔Ὕ텇⢌យ稭澣'}: 00000000-0000-4d00-8d00-000000000000} " +
"WHERE pk0 = true AND pk1 = 0 AND ck0 = 37 AND ck1 = 0");
beforeAndAfterFlush(() ->
assertRows(execute("SELECT pk0, pk1, ck0, ck1 FROM %s WHERE pk0 = ? LIMIT 4", true),
row(true, IntegerType.instance.fromString("0"), ByteType.instance.fromString("114"), IntegerType.instance.fromString("742")),
row(true, IntegerType.instance.fromString("0"), ByteType.instance.fromString("109"), IntegerType.instance.fromString("0")),
row(true, IntegerType.instance.fromString("0"), ByteType.instance.fromString("37"), IntegerType.instance.fromString("0"))));
}
@Test
public void testIntersectionOnMixedPostingsOnDelete() throws Throwable
{
createTable("CREATE TABLE %s (pk0 boolean, pk1 uuid, ck0 date, ck1 smallint, s0 timeuuid static, v0 bigint, v1 float, PRIMARY KEY ((pk0, pk1), ck0, ck1)) WITH CLUSTERING ORDER BY (ck0 DESC, ck1 ASC)");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX tbl_pk0 ON %s(pk0) USING 'sai'");
createIndex("CREATE INDEX tbl_ck0 ON %s(ck0) USING 'sai'");
@ -54,7 +134,7 @@ public class CompositePartitionKeyIndexTest extends SAITester
public void testIntersectionOnMixedPostingsOnUpdate() throws Throwable
{
createTable("CREATE TABLE %s (pk0 boolean, pk1 uuid, ck0 date, ck1 smallint, s0 timeuuid static, v0 bigint, v1 float, PRIMARY KEY ((pk0, pk1), ck0, ck1)) WITH CLUSTERING ORDER BY (ck0 DESC, ck1 ASC)");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX tbl_pk0 ON %s(pk0) USING 'sai'");
createIndex("CREATE INDEX tbl_ck0 ON %s(ck0) USING 'sai'");
@ -74,6 +154,7 @@ public class CompositePartitionKeyIndexTest extends SAITester
public void testIntersectionWithStaticOverlap() throws Throwable
{
createTable("CREATE TABLE %s (pk0 int, pk1 int, ck0 int, s1 int static, v0 int, PRIMARY KEY((pk0, pk1), ck0))");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX ON %s(pk0) USING 'sai'");
execute("UPDATE %s USING TIMESTAMP 1 SET s1 = 0, v0 = 0 WHERE pk0 = 0 AND pk1 = 1 AND ck0 = 0");
@ -91,6 +172,7 @@ public class CompositePartitionKeyIndexTest extends SAITester
public void testIntersectionWithStaticUpdate() throws Throwable
{
createTable("CREATE TABLE %s (pk0 time, pk1 varint, ck0 date, s0 boolean static, s1 text static, v0 boolean, PRIMARY KEY ((pk0, pk1), ck0))");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX tbl_pk0 ON %s(pk0) USING 'sai'");
createIndex("CREATE INDEX tbl_s0 ON %s(s0) USING 'sai'");
@ -116,6 +198,7 @@ public class CompositePartitionKeyIndexTest extends SAITester
public void testCompositePartitionIndex() throws Throwable
{
createTable("CREATE TABLE %s (pk1 int, pk2 text, val int, PRIMARY KEY((pk1, pk2)))");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX ON %s(pk1) USING 'sai'");
createIndex("CREATE INDEX ON %s(pk2) USING 'sai'");
@ -168,6 +251,7 @@ public class CompositePartitionKeyIndexTest extends SAITester
public void testFilterWithIndexForContains() throws Throwable
{
createTable("CREATE TABLE %s (k1 int, k2 int, v set<int>, PRIMARY KEY ((k1, k2)))");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX ON %s(k2) USING 'sai'");
execute("INSERT INTO %s (k1, k2, v) VALUES (?, ?, ?)", 0, 0, set(1, 2, 3));

View File

@ -20,6 +20,9 @@ package org.apache.cassandra.index.sai.cql;
import org.junit.Test;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.TimeType;
import org.apache.cassandra.db.marshal.UUIDType;
import org.apache.cassandra.index.sai.SAITester;
public class StaticColumnIndexTest extends SAITester
@ -28,6 +31,7 @@ public class StaticColumnIndexTest extends SAITester
public void staticIndexReturnsAllRowsInPartition() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck int, val1 int static, val2 int, PRIMARY KEY(pk, ck))");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX ON %s(val1) USING 'sai'");
execute("INSERT INTO %s(pk, ck, val1, val2) VALUES(?, ?, ?, ?)", 1, 1, 2, 1);
@ -42,6 +46,7 @@ public class StaticColumnIndexTest extends SAITester
public void staticIndexAndNonStaticIndex() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck int, val1 int static, val2 int, PRIMARY KEY(pk, ck))");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX ON %s(val1) USING 'sai'");
createIndex("CREATE INDEX ON %s(val2) USING 'sai'");
@ -57,6 +62,7 @@ public class StaticColumnIndexTest extends SAITester
public void staticAndNonStaticRangeIntersection() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck int, v1 int, s1 int static, PRIMARY KEY(pk, ck))");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
createIndex("CREATE INDEX ON %s(s1) USING 'sai'");
@ -71,4 +77,42 @@ public class StaticColumnIndexTest extends SAITester
beforeAndAfterFlush(() -> assertRowCount(execute("SELECT * FROM %s WHERE pk = ? AND v1 > ? AND s1 = ?", 0, 2, 100), 3));
}
@Test
public void testTupleAndBlobFiltering() throws Throwable
{
String blobTupleType = createType("CREATE TYPE IF NOT EXISTS %s (f0 blob)");
String boolTinyTextType = createType("CREATE TYPE IF NOT EXISTS %s (f0 boolean, f1 tinyint, f2 text)");
createTable("CREATE TABLE %s (pk0 time, pk1 uuid, ck0 uuid, ck1 blob, s0 frozen<tuple<smallint, frozen<set<float>>>> static, " +
" v0 vector<vector<int, 2>, 3>, v1 frozen<map<frozen<" + blobTupleType + ">, vector<bigint, 2>>>, " +
" v2 vector<frozen<" + boolTinyTextType + ">, 2>, v3 bigint, PRIMARY KEY ((pk0, pk1), ck0, ck1)) WITH CLUSTERING ORDER BY (ck0 DESC, ck1 DESC)");
disableCompaction(KEYSPACE);
createIndex("CREATE INDEX tbl_pk1 ON %s(pk1) USING 'sai'");
createIndex("CREATE INDEX tbl_s0 ON %s(s0) USING 'sai'");
execute("INSERT INTO %s (pk0, pk1, ck0, ck1, s0, v0, v1, v2, v3) " +
"VALUES ('02:43:47.716011275', 00000000-0000-4200-b200-000000000000, 00000000-0000-4e00-8400-000000000000, 0xf2791941aea8e469, " +
" (12129, {-2.58545975E14}), [[-1781797567, 330686172], [103364202, 2031130152], [-550709009, 492544493]], " +
" {{f0: 0x34839b8bae653b2bdee8}: [-8431172225521461427, 8894719445990427242]}, [{f0: false, f1: 53, f2: '嵆왛孷쏆䊖恣'}, {f0: true, f1: 21, f2: 'ᨚ?榥쯢?ɚ챛ퟡ'}], 9167463065336786821) USING TIMESTAMP 3");
execute("UPDATE %s USING TIMESTAMP 4 " +
"SET s0=(23307, {-8.214548E-18}), v0=[[672139924, -1253475201], [353181149, -1829076723], [179355765, 379303855]], " +
" v1={{f0: 0x64850696464d}: [-7485547085069825418, 7795885370802556756], {f0: 0x67633db6f091}: [-8484578637223040646, 8216210044102487771]}, " +
" v2=[{f0: true, f1: 68, f2: '䝿ᝧ䶨푥펟겭매郂쀌'}, {f0: true, f1: 98, f2: '髃爫삿챥卛☓읂ີ?'}], v3=-4626482462417652499 * -7377486305688263453 " +
"WHERE pk0 = '03:36:30.876439626' AND pk1 = 00000000-0000-4000-ad00-000000000000 AND ck0 = 00000000-0000-4000-9f00-000000000000 AND ck1 = 0xa06bb301");
execute("INSERT INTO %s (pk0, pk1, ck0, ck1, s0, v0, v1, v2, v3) " +
"VALUES ('07:08:47.775161332', 00000000-0000-4800-ad00-000000000000, 00000000-0000-4a00-a500-000000000000, 0xfef0d63ff7, (-15283, {-1.132058E24, 2.9319742E-31}), " +
" [[-335960956, 678086816], [-2139882146, 1011627708], [-55338955, -2094185756]], {{f0: 0xd9c3ab}: [-9002034104664383537, -8074261670215737032]}, " +
" [{f0: true, f1: -79, f2: '霠♘칳⦵ঋ幗䶐'}, {f0: true, f1: 7, f2: '䉻ݹ鞞텔㙠'}], 1885613374025825905) USING TIMESTAMP 5");
execute("DELETE FROM %s USING TIMESTAMP 6 WHERE pk0 = '14:02:14.975449434' AND pk1 = 00000000-0000-4900-9900-000000000000");
execute("DELETE FROM %s USING TIMESTAMP 7 WHERE pk0 = '12:15:35.151327231' AND pk1 = 00000000-0000-4500-ac00-000000000000");
execute("DELETE FROM %s USING TIMESTAMP 8 WHERE pk0 = '07:08:47.775161332' AND pk1 = 00000000-0000-4800-ad00-000000000000 AND ck0 = 00000000-0000-4b00-b000-000000000000 AND ck1 = 0xa4121adb08");
execute("INSERT INTO %s (pk0, pk1, ck0, ck1, s0, v0, v1, v2, v3) " +
"VALUES ('03:36:30.876439626', 00000000-0000-4000-ad00-000000000000, 00000000-0000-4600-b400-000000000000, 0x63f5, (28387, {-1.18764904E-20}), " +
" [[-441895935, 313114446], [-740629531, -678512740], [1429899934, -1259907921]], {{f0: 0x5df1}: [414225888834712632, -5730196176171247108], " +
" {f0: 0x92c1497d7072b81c91}: [-7587541014989351350, -2813091340484612608]}, [{f0: true, f1: 41, f2: '쎺╇⒀왶'}, {f0: true, f1: -84, f2: '턺䋏篷'}], -1473884563651667176 + 128345915915881356) USING TIMESTAMP 9");
beforeAndAfterFlush(() -> assertRows(execute("SELECT pk0, pk1, ck0, ck1 FROM %s WHERE s0 = (28387, {-1.18764904E-20}) AND pk1 = 00000000-0000-4000-ad00-000000000000 AND ck1 = 0xa06bb301 LIMIT 307 ALLOW FILTERING"),
row(TimeType.instance.fromString("03:36:30.876439626"), UUIDType.instance.fromString("00000000-0000-4000-ad00-000000000000"),
UUIDType.instance.fromString("00000000-0000-4000-9f00-000000000000"), BytesType.instance.fromString("a06bb301"))));
}
}