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.le...@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-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org