You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by mc...@apache.org on 2019/10/17 06:08:23 UTC

[cassandra] branch cassandra-3.11 updated: Fix SASI non-literal string comparisons (range operators)

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

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


The following commit(s) were added to refs/heads/cassandra-3.11 by this push:
     new b20daee  Fix SASI non-literal string comparisons (range operators)
b20daee is described below

commit b20daee68d96cca7e23f5c2e83f687ba2f3b1852
Author: Mick Semb Wever <mc...@apache.org>
AuthorDate: Mon Oct 14 21:19:46 2019 +0200

    Fix SASI non-literal string comparisons (range operators)
    
     patch by mazhenlin; reviewed by Mick Semb Wever for CASSANDRA-15169
---
 CHANGES.txt                                        |   1 +
 .../cassandra/index/sasi/plan/Expression.java      |   4 +-
 .../apache/cassandra/index/sasi/SASICQLTest.java   | 224 ++++++++++++++++++++-
 3 files changed, 226 insertions(+), 3 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index ec88756..3614071 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.11.5
+ * Fix SASI non-literal string comparisons (range operators) (CASSANDRA-15169)
  * Make sure user defined compaction transactions are always closed (CASSANDRA-15123)
  * Fix cassandra-env.sh to use $CASSANDRA_CONF to find cassandra-jaas.config (CASSANDRA-14305)
  * Fixed nodetool cfstats printing index name twice (CASSANDRA-14903)
diff --git a/src/java/org/apache/cassandra/index/sasi/plan/Expression.java b/src/java/org/apache/cassandra/index/sasi/plan/Expression.java
index 93f1938..fba7f34 100644
--- a/src/java/org/apache/cassandra/index/sasi/plan/Expression.java
+++ b/src/java/org/apache/cassandra/index/sasi/plan/Expression.java
@@ -335,7 +335,7 @@ public class Expression
         if (!hasLower())
             return true;
 
-        int cmp = term.compareTo(validator, lower.value, false);
+        int cmp = term.compareTo(validator, lower.value, operation == Op.RANGE && !isLiteral);
         return cmp > 0 || cmp == 0 && lower.inclusive;
     }
 
@@ -344,7 +344,7 @@ public class Expression
         if (!hasUpper())
             return true;
 
-        int cmp = term.compareTo(validator, upper.value, false);
+        int cmp = term.compareTo(validator, upper.value, operation == Op.RANGE && !isLiteral);
         return cmp < 0 || cmp == 0 && upper.inclusive;
     }
 
diff --git a/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java b/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java
index 17bd196..695f040 100644
--- a/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java
+++ b/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java
@@ -27,7 +27,9 @@ import org.junit.Test;
 import com.datastax.driver.core.Row;
 import com.datastax.driver.core.Session;
 import com.datastax.driver.core.SimpleStatement;
-import junit.framework.Assert;
+import com.datastax.driver.core.exceptions.InvalidQueryException;
+import org.junit.Assert;
+
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.cql3.CQLTester;
 import org.apache.cassandra.exceptions.InvalidRequestException;
@@ -126,4 +128,224 @@ public class SASICQLTest extends CQLTester
             DatabaseDescriptor.setEnableSASIIndexes(enableSASIIndexes);
         }
     }
+
+    /**
+     * Tests query condition '>' on string columns with is_literal=false.
+     */
+    @Test
+    public void testNonLiteralStringCompare() throws Throwable
+    {
+        for (String mode : new String[]{ "PREFIX", "CONTAINS", "SPARSE"})
+        {
+            for (boolean forceFlush : new boolean[]{ false, true })
+            {
+                try
+                {
+                    createTable("CREATE TABLE %s (pk int primary key, v text);");
+                    createIndex(String.format("CREATE CUSTOM INDEX ON %%s (v) USING 'org.apache.cassandra.index.sasi.SASIIndex' WITH OPTIONS = {'is_literal': 'false', 'mode': '%s'};", mode));
+
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 0, "a");
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 1, "abc");
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 2, "ac");
+
+                    flush(forceFlush);
+
+                    Session session = sessionNet();
+                    SimpleStatement stmt = new SimpleStatement("SELECT * FROM " + KEYSPACE + '.' + currentTable() + " WHERE v = 'ab'");
+                    stmt.setFetchSize(5);
+                    List<Row> rs = session.execute(stmt).all();
+                    Assert.assertEquals(0, rs.size());
+
+                    try
+                        {
+                        sessionNet();
+                        stmt = new SimpleStatement("SELECT * FROM " + KEYSPACE + '.' + currentTable() + " WHERE v > 'ab'");
+                        stmt.setFetchSize(5);
+                        rs = session.execute(stmt).all();
+                        Assert.assertFalse("CONTAINS mode on non-literal string type should not support RANGE operators", "CONTAINS".equals(mode));
+                        Assert.assertEquals(2, rs.size());
+                        Assert.assertEquals(1, rs.get(0).getInt("pk"));
+                    }
+                    catch (InvalidQueryException ex)
+                    {
+                        if (!"CONTAINS".equals(mode))
+                            throw ex;
+                    }
+                }
+                catch (Throwable th)
+                {
+                    throw new AssertionError(String.format("Failure with mode:%s and flush:%s ", mode, forceFlush), th);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Tests query condition '>' on string columns with is_literal=true (default).
+     */
+    @Test
+    public void testStringCompare() throws Throwable
+    {
+        for (String mode : new String[]{ "PREFIX", "CONTAINS"})
+        {
+            for (boolean forceFlush : new boolean[]{ false, true })
+            {
+                try
+                {
+                    createTable("CREATE TABLE %s (pk int primary key, v text);");
+                    createIndex(String.format("CREATE CUSTOM INDEX ON %%s (v) USING 'org.apache.cassandra.index.sasi.SASIIndex' WITH OPTIONS = {'mode': '%s'};", mode));
+
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 0, "a");
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 1, "abc");
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 2, "ac");
+
+                    flush(forceFlush);
+
+                    Session session = sessionNet();
+                    SimpleStatement stmt = new SimpleStatement("SELECT * FROM " + KEYSPACE + '.' + currentTable() + " WHERE v = 'ab'");
+                    stmt.setFetchSize(5);
+                    List<Row> rs = session.execute(stmt).all();
+                    Assert.assertEquals(0, rs.size());
+
+                    try
+                    {
+                        session = sessionNet();
+                        stmt = new SimpleStatement("SELECT * FROM " + KEYSPACE + '.' + currentTable() + " WHERE v > 'ab'");
+                        stmt.setFetchSize(5);
+                        rs = session.execute(stmt).all();
+                        throw new AssertionError("literal string type should not support RANGE operators");
+                    }
+                    catch (InvalidQueryException ex)
+                    {}
+                }
+                catch (Throwable th)
+                {
+                    throw new AssertionError(String.format("Failure with mode:%s and flush:%s ", mode, forceFlush), th);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Tests query condition like_prefix on string columns.
+     */
+    @Test
+    public void testStringLikePrefix() throws Throwable
+    {
+        for (String mode : new String[]{ "PREFIX", "CONTAINS"})
+        {
+            for (boolean forceFlush : new boolean[]{ false, true })
+            {
+                try
+                {
+                    createTable("CREATE TABLE %s (pk int primary key, v text);");
+                    createIndex(String.format("CREATE CUSTOM INDEX ON %%s (v) USING 'org.apache.cassandra.index.sasi.SASIIndex' WITH OPTIONS = {'mode': '%s'};", mode));
+
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 0, "a");
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 1, "abc");
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 2, "ac");
+
+                    flush(forceFlush);
+
+                    Session session = sessionNet();
+                    SimpleStatement stmt = new SimpleStatement("SELECT * FROM " + KEYSPACE + '.' + currentTable() + " WHERE v LIKE 'ab%'");
+                    stmt.setFetchSize(5);
+                    List<Row> rs = session.execute(stmt).all();
+                    Assert.assertEquals(1, rs.size());
+                    Assert.assertEquals(1, rs.get(0).getInt("pk"));
+                }
+                catch (Throwable th)
+                {
+                    throw new AssertionError(String.format("Failure with mode:%s and flush:%s ", mode, forceFlush), th);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Tests query condition '>' on blob columns.
+     */
+    @Test
+    public void testBlobCompare() throws Throwable
+    {
+        for (String mode : new String[]{ "PREFIX", "CONTAINS", "SPARSE"})
+        {
+            for (boolean forceFlush : new boolean[]{ false, true })
+            {
+                try
+                {
+                    createTable("CREATE TABLE %s (pk int primary key, v blob);");
+                    createIndex(String.format("CREATE CUSTOM INDEX ON %%s (v) USING 'org.apache.cassandra.index.sasi.SASIIndex' WITH OPTIONS = {'mode': '%s'};", mode));
+
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 0, 0x1234);
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 1, 0x12345678);
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 2, 0x12350000);
+
+                    flush(forceFlush);
+
+                    Session session = sessionNet();
+                    SimpleStatement stmt = new SimpleStatement("SELECT * FROM " + KEYSPACE + '.' + currentTable() + " WHERE v > 0x1234");
+                    stmt.setFetchSize(5);
+                    List<Row> rs = session.execute(stmt).all();
+                    Assert.assertFalse("CONTAINS mode on non-literal blob type should not support RANGE operators", "CONTAINS".equals(mode));
+                    Assert.assertEquals(2, rs.size());
+                    Assert.assertEquals(1, rs.get(0).getInt("pk"));
+                }
+                catch (InvalidQueryException ex)
+                {
+                    if (!"CONTAINS".equals(mode))
+                        throw ex;
+                }
+                catch (Throwable th)
+                {
+                    throw new AssertionError(String.format("Failure with mode:%s and flush:%s ", mode, forceFlush), th);
+                }
+
+            }
+        }
+    }
+
+    @Test
+    public void testIntCompare() throws Throwable
+    {
+        for (String mode : new String[]{ "PREFIX", "CONTAINS", "SPARSE"})
+        {
+            for (boolean forceFlush : new boolean[]{ false, true })
+            {
+                try
+                {
+                    createTable("CREATE TABLE %s (pk int primary key, v int);");
+
+                    createIndex(String.format("CREATE CUSTOM INDEX ON %%s (v) USING 'org.apache.cassandra.index.sasi.SASIIndex' WITH OPTIONS = {'mode': '%s'};", mode));
+
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 0, 100);
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 1, 200);
+                    execute("INSERT INTO %s (pk, v) VALUES (?, ?);", 2, 300);
+
+                    flush(forceFlush);
+
+                    Session session = sessionNet();
+                    SimpleStatement stmt = new SimpleStatement("SELECT * FROM " + KEYSPACE + '.' + currentTable() + " WHERE v > 200");
+                    stmt.setFetchSize(5);
+                    List<Row> rs = session.execute(stmt).all();
+                    Assert.assertFalse("CONTAINS mode on non-literal int type should not support RANGE operators", "CONTAINS".equals(mode));
+                    Assert.assertEquals(1, rs.size());
+                    Assert.assertEquals(2, rs.get(0).getInt("pk"));
+                }
+                catch (InvalidQueryException ex)
+                {
+                    if (!"CONTAINS".equals(mode))
+                        throw ex;
+                }
+                catch (Throwable th)
+                {
+                    throw new AssertionError(String.format("Failure with mode:%s and flush:%s ", mode, forceFlush), th);
+                }
+
+            }
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org