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

bereng 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 99e1fcc  Materialized views incorrect quoting of UDF
99e1fcc is described below

commit 99e1fcc251bd498abab17a59a9fc9593d242671b
Author: Bereng <berenguerbl...@gmail.com>
AuthorDate: Wed Aug 11 08:43:07 2021 +0200

    Materialized views incorrect quoting of UDF
    
    patch by Andres de la Peña, Jakub Zytka, Berenguer Blasi; reviewed by 
Andres de la Peña, Jakub Zytka for CASSANDRA-16836
    
    Co-authored-by: Andres de la Peña <a.penya.gar...@gmail.com>
    Co-authored-by: Jakub Zytka <jakub.zy...@datastax.com>
    Co-authored-by: Berenguer Blasi <berenguerbl...@gmail.com>
---
 .../cassandra/cql3/functions/FunctionCall.java     |   2 +-
 .../cassandra/cql3/functions/FunctionName.java     |  18 +++
 test/unit/org/apache/cassandra/cql3/ViewTest.java  | 122 ++++++++++++++++++++-
 3 files changed, 140 insertions(+), 2 deletions(-)

diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java 
b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
index 8dcb3da..c0d616e 100644
--- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
@@ -204,7 +204,7 @@ public class FunctionCall extends Term.NonTerminal
 
         public String getText()
         {
-            return name + 
terms.stream().map(Term.Raw::getText).collect(Collectors.joining(", ", "(", 
")"));
+            return name.toCQLString() + 
terms.stream().map(Term.Raw::getText).collect(Collectors.joining(", ", "(", 
")"));
         }
     }
 }
diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionName.java 
b/src/java/org/apache/cassandra/cql3/functions/FunctionName.java
index aa980e9..efa7adf 100644
--- a/src/java/org/apache/cassandra/cql3/functions/FunctionName.java
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionName.java
@@ -20,9 +20,13 @@ package org.apache.cassandra.cql3.functions;
 import com.google.common.base.Objects;
 
 import org.apache.cassandra.config.SchemaConstants;
+import org.apache.cassandra.cql3.ColumnIdentifier;
 
 public final class FunctionName
 {
+    // We special case the token function because that's the only function 
which name is a reserved keyword
+    private static final FunctionName TOKEN_FUNCTION_NAME = 
FunctionName.nativeFunction("token");
+
     public final String keyspace;
     public final String name;
 
@@ -79,4 +83,18 @@ public final class FunctionName
     {
         return keyspace == null ? name : keyspace + "." + name;
     }
+
+    /**
+     * Returns a string representation of the function name that is safe to 
use directly in CQL queries.
+     * If necessary, the name components will be double-quoted, and any quotes 
inside the names will be escaped.
+     */
+    public String toCQLString()
+    {
+        String maybeQuotedName = equalsNativeFunction(TOKEN_FUNCTION_NAME)
+               ? name
+               : ColumnIdentifier.maybeQuote(name);
+        return keyspace == null
+               ? maybeQuotedName
+               : ColumnIdentifier.maybeQuote(keyspace) + '.' + maybeQuotedName;
+    }
 }
diff --git a/test/unit/org/apache/cassandra/cql3/ViewTest.java 
b/test/unit/org/apache/cassandra/cql3/ViewTest.java
index 38b1a35..07a518b 100644
--- a/test/unit/org/apache/cassandra/cql3/ViewTest.java
+++ b/test/unit/org/apache/cassandra/cql3/ViewTest.java
@@ -24,6 +24,8 @@ import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import com.google.common.util.concurrent.Uninterruptibles;
 
 import junit.framework.Assert;
@@ -44,6 +46,7 @@ import org.apache.cassandra.concurrent.StageManager;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.config.SchemaConstants;
 import org.apache.cassandra.db.ColumnFamilyStore;
 import org.apache.cassandra.db.Keyspace;
 import org.apache.cassandra.db.SystemKeyspace;
@@ -52,6 +55,7 @@ import org.apache.cassandra.db.marshal.AsciiType;
 import org.apache.cassandra.db.view.View;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.schema.KeyspaceParams;
+import org.apache.cassandra.schema.SchemaKeyspace;
 import org.apache.cassandra.service.ClientWarn;
 import org.apache.cassandra.transport.ProtocolVersion;
 import org.apache.cassandra.utils.FBUtilities;
@@ -71,6 +75,7 @@ public class ViewTest extends CQLTester
     /** Latch used by {@link #testTruncateWhileBuilding()} Byteman injections. 
*/
     @SuppressWarnings("unused")
     private static final CountDownLatch blockViewBuild = new CountDownLatch(1);
+    private static final AtomicInteger viewNameSeqNumber = new AtomicInteger();
 
     private static final ProtocolVersion protocolVersion = 
ProtocolVersion.CURRENT;
     private final List<String> views = new ArrayList<>();
@@ -1503,6 +1508,88 @@ public class ViewTest extends CQLTester
         }
     }
 
+    @Test
+    public void testFunctionInWhereClause() throws Throwable
+    {
+        // Native token function with lowercase, should be unquoted in the 
schema where clause
+        assertEmpty(testFunctionInWhereClause("CREATE TABLE %s (k bigint 
PRIMARY KEY, v int)",
+                                              null,
+                                              "CREATE MATERIALIZED VIEW %s AS" 
+
+                                              "   SELECT * FROM %%s WHERE k = 
token(1) AND v IS NOT NULL " +
+                                              "   PRIMARY KEY (v, k)",
+                                              "k = token(1) AND v IS NOT NULL",
+                                              "INSERT INTO %s(k, v) VALUES (0, 
1)",
+                                              "INSERT INTO %s(k, v) VALUES (2, 
3)"));
+
+        // Native token function with uppercase, should be unquoted and 
lowercased in the schema where clause
+        assertEmpty(testFunctionInWhereClause("CREATE TABLE %s (k bigint 
PRIMARY KEY, v int)",
+                                              null,
+                                              "CREATE MATERIALIZED VIEW %s AS" 
+
+                                              "   SELECT * FROM %%s WHERE k = 
TOKEN(1) AND v IS NOT NULL" +
+                                              "   PRIMARY KEY (v, k)",
+                                              "k = token(1) AND v IS NOT NULL",
+                                              "INSERT INTO %s(k, v) VALUES (0, 
1)",
+                                              "INSERT INTO %s(k, v) VALUES (2, 
3)"));
+
+        // UDF with lowercase name, shouldn't be quoted in the schema where 
clause
+        assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY 
KEY, v int)",
+                                             "CREATE FUNCTION fun()" +
+                                             "   CALLED ON NULL INPUT" +
+                                             "   RETURNS int LANGUAGE java" +
+                                             "   AS 'return 2;'",
+                                             "CREATE MATERIALIZED VIEW %s AS " 
+
+                                             "   SELECT * FROM %%s WHERE k = 
fun() AND v IS NOT NULL" +
+                                             "   PRIMARY KEY (v, k)",
+                                             "k = fun() AND v IS NOT NULL",
+                                             "INSERT INTO %s(k, v) VALUES (0, 
1)",
+                                             "INSERT INTO %s(k, v) VALUES (2, 
3)"), row(3, 2));
+
+        // UDF with uppercase name, should be quoted in the schema where clause
+        assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY 
KEY, v int)",
+                                             "CREATE FUNCTION \"FUN\"()" +
+                                             "   CALLED ON NULL INPUT" +
+                                             "   RETURNS int" +
+                                             "   LANGUAGE java" +
+                                             "   AS 'return 2;'",
+                                             "CREATE MATERIALIZED VIEW %s AS " 
+
+                                             "   SELECT * FROM %%s WHERE k = 
\"FUN\"() AND v IS NOT NULL" +
+                                             "   PRIMARY KEY (v, k)",
+                                             "k = \"FUN\"() AND v IS NOT NULL",
+                                             "INSERT INTO %s(k, v) VALUES (0, 
1)",
+                                             "INSERT INTO %s(k, v) VALUES (2, 
3)"), row(3, 2));
+
+        // UDF with uppercase name conflicting with TOKEN keyword but not with 
native token function name,
+        // should be quoted in the schema where clause
+        assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY 
KEY, v int)",
+                                             "CREATE FUNCTION \"TOKEN\"(x 
int)" +
+                                             "   CALLED ON NULL INPUT" +
+                                             "   RETURNS int" +
+                                             "   LANGUAGE java" +
+                                             "   AS 'return x;'",
+                                             "CREATE MATERIALIZED VIEW %s AS" +
+                                             "   SELECT * FROM %%s WHERE k = 
\"TOKEN\"(2) AND v IS NOT NULL" +
+                                             "   PRIMARY KEY (v, k)",
+                                             "k = \"TOKEN\"(2) AND v IS NOT 
NULL",
+                                             "INSERT INTO %s(k, v) VALUES (0, 
1)",
+                                             "INSERT INTO %s(k, v) VALUES (2, 
3)"), row(3, 2));
+
+        // UDF with lowercase name conflicting with both TOKEN keyword and 
native token function name,
+        // requires specifying the keyspace and should be quoted in the schema 
where clause
+        assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY 
KEY, v int)",
+                                             "CREATE FUNCTION \"token\"(x 
int)" +
+                                             "   CALLED ON NULL INPUT" +
+                                             "   RETURNS int" +
+                                             "   LANGUAGE java" +
+                                             "   AS 'return x;'",
+                                             "CREATE MATERIALIZED VIEW %s AS" +
+                                             "   SELECT * FROM %%s " +
+                                             "   WHERE k = " + keyspace() + 
".\"token\"(2) AND v IS NOT NULL" +
+                                             "   PRIMARY KEY (v, k)",
+                                             "k = " + keyspace() + 
".\"token\"(2) AND v IS NOT NULL",
+                                             "INSERT INTO %s(k, v) VALUES (0, 
1)",
+                                             "INSERT INTO %s(k, v) VALUES (2, 
3)"), row(3, 2));
+    }
+
     /**
      * Tests that truncating a table stops the ongoing builds of its 
materialized views,
      * so they don't write into the MV data that has been truncated in the 
base table.
@@ -1557,4 +1644,37 @@ public class ViewTest extends CQLTester
     {
         return CompactionManager.instance.getPendingTasks() + 
CompactionManager.instance.getActiveCompactions();
     }
-}
\ No newline at end of file
+
+    private UntypedResultSet testFunctionInWhereClause(String createTableQuery,
+                                                       String 
createFunctionQuery,
+                                                       String createViewQuery,
+                                                       String 
expectedSchemaWhereClause,
+                                                       String... 
insertQueries) throws Throwable
+    {
+        createTable(createTableQuery);
+
+        execute("USE " + keyspace());
+        executeNet(protocolVersion, "USE " + keyspace());
+
+        if (createFunctionQuery != null)
+        {
+            execute(createFunctionQuery);
+        }
+
+        String viewName = "view_" + viewNameSeqNumber.getAndIncrement();
+        createView(viewName, createViewQuery);
+
+        // Test the where clause stored in system_schema.views
+        String schemaQuery = String.format("SELECT where_clause FROM %s.%s 
WHERE keyspace_name = ? AND view_name = ?",
+                                           
SchemaConstants.SCHEMA_KEYSPACE_NAME,
+                                           SchemaKeyspace.VIEWS);
+        assertRows(execute(schemaQuery, keyspace(), viewName), 
row(expectedSchemaWhereClause));
+
+        for (String insert : insertQueries)
+        {
+            execute(insert);
+        }
+
+        return execute("SELECT * FROM " + viewName);
+    }
+}

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

Reply via email to