You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2021/03/11 16:52:59 UTC

[cassandra] branch trunk updated: Fix the CQL generated for the views.where_clause column when some identifiers require quoting

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

blerer pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 92fe6a3  Fix the CQL generated for the views.where_clause column when some identifiers require quoting
92fe6a3 is described below

commit 92fe6a37fc79b9c545bccd75b93e5126fd1678e9
Author: Benjamin Lerer <b....@gmail.com>
AuthorDate: Fri Mar 5 17:44:17 2021 +0100

    Fix the CQL generated for the views.where_clause column when some identifiers require quoting
    
    patch by Benjamin Lerer; reviewed by Andrés de la Peña for CASSANDRA-16479
    
    CASSANDRA-13426 changed the way the Materialized Views WHERE clause stored
    in system_schema.views was generated. The code used to generate the WHERE
    clause text was not relying on method quoting identifiers if needed.
    The patch address this problem.
---
 CHANGES.txt                                             |  1 +
 .../org/apache/cassandra/cql3/MultiColumnRelation.java  |  4 ++--
 src/java/org/apache/cassandra/cql3/QualifiedName.java   | 12 ++++++++++++
 src/java/org/apache/cassandra/cql3/Relation.java        | 13 +++++++++++++
 .../org/apache/cassandra/cql3/SingleColumnRelation.java |  4 ++--
 src/java/org/apache/cassandra/cql3/TokenRelation.java   |  4 ++--
 src/java/org/apache/cassandra/cql3/WhereClause.java     | 14 ++++++++++++--
 .../cql3/restrictions/CustomIndexExpression.java        |  7 ++++++-
 .../org/apache/cassandra/schema/SchemaKeyspace.java     |  2 +-
 test/unit/org/apache/cassandra/cql3/ViewTest.java       | 17 +++++++++++++++++
 10 files changed, 68 insertions(+), 10 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 592c9c3..b470000 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0-beta5
+ * Fix the CQL generated for the views.where_clause column when some identifiers require quoting (CASSANDRA-16479)
  * Send FAILED_SESSION_MSG on shutdown and on in-progress repairs during startup (CASSANDRA-16425)
  * Reinstate removed ApplicationState padding (CASSANDRA-16484)
  * Expose data dirs to ColumnFamilyStoreMBean (CASSANDRA-16335)
diff --git a/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java b/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java
index b61198b..e74b962 100644
--- a/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java
+++ b/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java
@@ -227,9 +227,9 @@ public class MultiColumnRelation extends Relation
     }
 
     @Override
-    public String toString()
+    public String toCQLString()
     {
-        StringBuilder builder = new StringBuilder(Tuples.tupleToString(entities));
+        StringBuilder builder = new StringBuilder(Tuples.tupleToString(entities, ColumnIdentifier::toCQLString));
         if (isIN())
         {
             return builder.append(" IN ")
diff --git a/src/java/org/apache/cassandra/cql3/QualifiedName.java b/src/java/org/apache/cassandra/cql3/QualifiedName.java
index fb2e110..a3f70d5 100644
--- a/src/java/org/apache/cassandra/cql3/QualifiedName.java
+++ b/src/java/org/apache/cassandra/cql3/QualifiedName.java
@@ -84,6 +84,18 @@ public class QualifiedName
              : name;
     }
 
+    /**
+     * Returns a string representation of the qualified name that is safe to use directly in CQL queries.
+     * If necessary, the string will be double-quoted, and any quotes inside the string will be escaped.
+     */
+    public String toCQLString()
+    {
+        String nameQuotedIfNeeded = ColumnIdentifier.maybeQuote(name);
+        return hasKeyspace()
+             ? String.format("%s.%s", ColumnIdentifier.maybeQuote(keyspace), nameQuotedIfNeeded)
+             : nameQuotedIfNeeded;
+    }
+
     @Override
     public int hashCode()
     {
diff --git a/src/java/org/apache/cassandra/cql3/Relation.java b/src/java/org/apache/cassandra/cql3/Relation.java
index af70edd..f9f8214 100644
--- a/src/java/org/apache/cassandra/cql3/Relation.java
+++ b/src/java/org/apache/cassandra/cql3/Relation.java
@@ -257,4 +257,17 @@ public abstract class Relation
      *         a new Relation with "from" replaced by "to" is returned.
      */
     public abstract Relation renameIdentifier(ColumnIdentifier from, ColumnIdentifier to);
+
+    /**
+     * Returns a CQL representation of this relation.
+     *
+     * @return a CQL representation of this relation
+     */
+    public abstract String toCQLString();
+
+    @Override
+    public String toString()
+    {
+        return toCQLString();
+    }
 }
diff --git a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java
index ca87e10..9ff3f07 100644
--- a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java
+++ b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java
@@ -143,9 +143,9 @@ public final class SingleColumnRelation extends Relation
     }
 
     @Override
-    public String toString()
+    public String toCQLString()
     {
-        String entityAsString = entity.toString();
+        String entityAsString = entity.toCQLString();
         if (mapKey != null)
             entityAsString = String.format("%s[%s]", entityAsString, mapKey);
 
diff --git a/src/java/org/apache/cassandra/cql3/TokenRelation.java b/src/java/org/apache/cassandra/cql3/TokenRelation.java
index be42143..139c55d 100644
--- a/src/java/org/apache/cassandra/cql3/TokenRelation.java
+++ b/src/java/org/apache/cassandra/cql3/TokenRelation.java
@@ -140,9 +140,9 @@ public final class TokenRelation extends Relation
     }
 
     @Override
-    public String toString()
+    public String toCQLString()
     {
-        return String.format("token%s %s %s", Tuples.tupleToString(entities), relationType, value);
+        return String.format("token%s %s %s", Tuples.tupleToString(entities, ColumnIdentifier::toCQLString), relationType, value);
     }
 
     @Override
diff --git a/src/java/org/apache/cassandra/cql3/WhereClause.java b/src/java/org/apache/cassandra/cql3/WhereClause.java
index 060af35..16116a2 100644
--- a/src/java/org/apache/cassandra/cql3/WhereClause.java
+++ b/src/java/org/apache/cassandra/cql3/WhereClause.java
@@ -80,9 +80,19 @@ public final class WhereClause
     @Override
     public String toString()
     {
+        return toCQLString();
+    }
+
+    /**
+     * Returns a CQL representation of this WHERE clause.
+     *
+     * @return a CQL representation of this WHERE clause
+     */
+    public String toCQLString()
+    {
         return join(" AND ",
-                    concat(transform(relations, Relation::toString),
-                           transform(expressions, CustomIndexExpression::toString)));
+                    concat(transform(relations, Relation::toCQLString),
+                           transform(expressions, CustomIndexExpression::toCQLString)));
     }
 
     @Override
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java b/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java
index 539715c..7a5fff6 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java
@@ -55,10 +55,15 @@ public class CustomIndexExpression
                                         value.bindAndGet(options));
     }
 
+    public String toCQLString()
+    {
+        return String.format("expr(%s,%s)", targetIndex.toCQLString(), valueRaw.getText());
+    }
+
     @Override
     public String toString()
     {
-        return String.format("expr(%s,%s)", targetIndex, valueRaw);
+        return toCQLString();
     }
 
     @Override
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index 7c3bc23..6d85190 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -740,7 +740,7 @@ public final class SchemaKeyspace
                                               .add("include_all_columns", view.includeAllColumns)
                                               .add("base_table_id", view.baseTableId.asUUID())
                                               .add("base_table_name", view.baseTableName)
-                                              .add("where_clause", view.whereClause.toString())
+                                              .add("where_clause", view.whereClause.toCQLString())
                                               .add("id", table.id.asUUID());
 
         addTableParamsToRowBuilder(table.params, rowBuilder);
diff --git a/test/unit/org/apache/cassandra/cql3/ViewTest.java b/test/unit/org/apache/cassandra/cql3/ViewTest.java
index 29f2be5..b8939ce 100644
--- a/test/unit/org/apache/cassandra/cql3/ViewTest.java
+++ b/test/unit/org/apache/cassandra/cql3/ViewTest.java
@@ -1442,4 +1442,21 @@ public class ViewTest extends CQLTester
             DatabaseDescriptor.setEnableMaterializedViews(enableMaterializedViews);
         }
     }
+
+    @Test
+    public void testQuotedIdentifiersInWhereClause() throws Throwable
+    {
+        createTable("CREATE TABLE %s (\"theKey\" int, \"theClustering_1\" int, \"theClustering_2\" int, \"theValue\" int, PRIMARY KEY (\"theKey\", \"theClustering_1\", \"theClustering_2\"))");
+
+        executeNet(protocolVersion, "USE " + keyspace());
+
+        createView("view1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE \"theKey\" IS NOT NULL AND \"theClustering_1\" IS NOT NULL AND \"theClustering_2\" IS NOT NULL AND \"theValue\" IS NOT NULL  PRIMARY KEY (\"theKey\", \"theClustering_1\", \"theClustering_2\");");
+        createView("view2", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE \"theKey\" IS NOT NULL AND (\"theClustering_1\", \"theClustering_2\") = (1, 2) AND \"theValue\" IS NOT NULL  PRIMARY KEY (\"theKey\", \"theClustering_1\", \"theClustering_2\");");
+        createView("view3", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE token(\"theKey\") > token(1) AND \"theClustering_1\" = 1 AND \"theClustering_2\" > 2 AND \"theValue\" IS NOT NULL  PRIMARY KEY (\"theKey\", \"theClustering_1\", \"theClustering_2\");");
+
+        assertRows(execute("SELECT where_clause FROM system_schema.views"),
+                   row("\"theKey\" IS NOT NULL AND \"theClustering_1\" IS NOT NULL AND \"theClustering_2\" IS NOT NULL AND \"theValue\" IS NOT NULL"),
+                   row("\"theKey\" IS NOT NULL AND (\"theClustering_1\", \"theClustering_2\") = (1, 2) AND \"theValue\" IS NOT NULL"),
+                   row("token(\"theKey\") > token(1) AND \"theClustering_1\" = 1 AND \"theClustering_2\" > 2 AND \"theValue\" IS NOT NULL"));
+    }
 }


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