You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by al...@apache.org on 2014/11/14 16:20:52 UTC
[2/2] cassandra git commit: Deal with conflicts between system
functions and UDFs
Deal with conflicts between system functions and UDFs
patch by Robert Stupp; reviewed by Benjamin Lerer for CASSANDRA-7813
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b4d7f3be
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b4d7f3be
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b4d7f3be
Branch: refs/heads/trunk
Commit: b4d7f3bed0687b449f6a275d9dd675e25d794aeb
Parents: 41a35ec
Author: Robert Stupp <sn...@snazy.de>
Authored: Fri Nov 14 18:18:38 2014 +0300
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Fri Nov 14 18:20:29 2014 +0300
----------------------------------------------------------------------
CHANGES.txt | 4 +-
build.xml | 2 +
pylib/cqlshlib/cql3handling.py | 2 +-
src/java/org/apache/cassandra/auth/Auth.java | 6 +-
.../org/apache/cassandra/config/KSMetaData.java | 1 +
.../org/apache/cassandra/cql3/Attributes.java | 6 +
.../org/apache/cassandra/cql3/CQLStatement.java | 2 +
.../apache/cassandra/cql3/ColumnCondition.java | 16 +-
src/java/org/apache/cassandra/cql3/Cql.g | 8 +-
.../org/apache/cassandra/cql3/Operation.java | 6 +-
.../apache/cassandra/cql3/QueryProcessor.java | 29 +-
src/java/org/apache/cassandra/cql3/Term.java | 12 +
.../org/apache/cassandra/cql3/UserTypes.java | 9 +
.../cassandra/cql3/functions/FunctionCall.java | 13 +-
.../cassandra/cql3/functions/FunctionName.java | 36 +-
.../cassandra/cql3/functions/Functions.java | 27 +-
.../cql3/functions/NativeFunction.java | 8 +-
.../cassandra/cql3/functions/UDFunction.java | 21 +-
.../selection/AbstractFunctionSelector.java | 7 +-
.../cassandra/cql3/selection/Selection.java | 10 +
.../cassandra/cql3/selection/Selector.java | 5 +
.../cql3/selection/SelectorFactories.java | 8 +
.../cql3/statements/BatchStatement.java | 10 +
.../statements/CreateFunctionStatement.java | 28 +-
.../cql3/statements/DropFunctionStatement.java | 28 +-
.../cql3/statements/ModificationStatement.java | 20 +-
.../cql3/statements/ParsedStatement.java | 5 +
.../cassandra/cql3/statements/Restriction.java | 2 +
.../cql3/statements/SelectStatement.java | 22 +-
.../statements/SingleColumnRestriction.java | 40 ++
.../org/apache/cassandra/db/DefsTables.java | 5 +-
.../org/apache/cassandra/db/SystemKeyspace.java | 7 +-
.../cassandra/service/IMigrationListener.java | 6 +-
.../cassandra/service/MigrationManager.java | 26 +-
.../org/apache/cassandra/transport/Event.java | 24 +-
.../org/apache/cassandra/transport/Server.java | 8 +-
.../apache/cassandra/cql3/AggregationTest.java | 10 +-
.../org/apache/cassandra/cql3/PgStringTest.java | 4 +-
test/unit/org/apache/cassandra/cql3/UFTest.java | 378 +++++++++++++------
39 files changed, 637 insertions(+), 224 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index f250edc..ff255d8 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,7 +1,7 @@
3.0
* Fix aggregate fn results on empty selection, result column name,
and cqlsh parsing (CASSANDRA-8229)
- * Mark sstables as repaired after full repair (CASSANDRA-7586)
+ * Mark sstables as repaired after full repair (CASSANDRA-7586)
* Extend Descriptor to include a format value and refactor reader/writer apis (CASSANDRA-7443)
* Integrate JMH for microbenchmarks (CASSANDRA-8151)
* Keep sstable levels when bootstrapping (CASSANDRA-7460)
@@ -15,7 +15,7 @@
* Remove YamlFileNetworkTopologySnitch (CASSANDRA-7917)
* Do anticompaction in groups (CASSANDRA-6851)
* Support pure user-defined functions (CASSANDRA-7395, 7526, 7562, 7740, 7781, 7929,
- 7924, 7812, 8063)
+ 7924, 7812, 8063, 7813)
* Permit configurable timestamps with cassandra-stress (CASSANDRA-7416)
* Move sstable RandomAccessReader to nio2, which allows using the
FILE_SHARE_DELETE flag on Windows (CASSANDRA-4050)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index c4e27a7..c7aa83e 100644
--- a/build.xml
+++ b/build.xml
@@ -212,6 +212,8 @@
<arg value="${build.src.java}/org/apache/cassandra/cql3/Cql.g" />
<arg value="-fo" />
<arg value="${build.src.gen-java}/org/apache/cassandra/cql3/" />
+ <arg value="-Xmaxinlinedfastates"/>
+ <arg value="10"/> <!-- default is 60 -->
</java>
</target>
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/pylib/cqlshlib/cql3handling.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index 261161c..f8a3069 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -209,7 +209,7 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
<mapLiteral> ::= "{" <term> ":" <term> ( "," <term> ":" <term> )* "}"
;
-<functionName> ::= <identifier> ( ":" ":" <identifier> )?
+<functionName> ::= <identifier> ( "." <identifier> )?
| "TOKEN"
;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/auth/Auth.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/Auth.java b/src/java/org/apache/cassandra/auth/Auth.java
index 07c9a67..8c12df6 100644
--- a/src/java/org/apache/cassandra/auth/Auth.java
+++ b/src/java/org/apache/cassandra/auth/Auth.java
@@ -337,7 +337,7 @@ public class Auth implements AuthMBean
{
}
- public void onDropFunction(String namespace, String functionName)
+ public void onDropFunction(String ksName, String functionName)
{
}
@@ -353,7 +353,7 @@ public class Auth implements AuthMBean
{
}
- public void onCreateFunction(String namespace, String functionName)
+ public void onCreateFunction(String ksName, String functionName)
{
}
@@ -369,7 +369,7 @@ public class Auth implements AuthMBean
{
}
- public void onUpdateFunction(String namespace, String functionName)
+ public void onUpdateFunction(String ksName, String functionName)
{
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/config/KSMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/KSMetaData.java b/src/java/org/apache/cassandra/config/KSMetaData.java
index d3ff62c..494f98b 100644
--- a/src/java/org/apache/cassandra/config/KSMetaData.java
+++ b/src/java/org/apache/cassandra/config/KSMetaData.java
@@ -185,6 +185,7 @@ public final class KSMetaData
mutation.delete(SystemKeyspace.SCHEMA_COLUMNS_TABLE, timestamp);
mutation.delete(SystemKeyspace.SCHEMA_TRIGGERS_TABLE, timestamp);
mutation.delete(SystemKeyspace.SCHEMA_USER_TYPES_TABLE, timestamp);
+ mutation.delete(SystemKeyspace.SCHEMA_FUNCTIONS_TABLE, timestamp);
mutation.delete(SystemKeyspace.BUILT_INDEXES_TABLE, timestamp);
return mutation;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/Attributes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Attributes.java b/src/java/org/apache/cassandra/cql3/Attributes.java
index df40b0c..851e1b4 100644
--- a/src/java/org/apache/cassandra/cql3/Attributes.java
+++ b/src/java/org/apache/cassandra/cql3/Attributes.java
@@ -45,6 +45,12 @@ public class Attributes
this.timeToLive = timeToLive;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return (timestamp != null && timestamp.usesFunction(ksName, functionName))
+ || (timeToLive != null && timeToLive.usesFunction(ksName, functionName));
+ }
+
public boolean isTimestampSet()
{
return timestamp != null;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/CQLStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CQLStatement.java b/src/java/org/apache/cassandra/cql3/CQLStatement.java
index a1642ef..d555ec3 100644
--- a/src/java/org/apache/cassandra/cql3/CQLStatement.java
+++ b/src/java/org/apache/cassandra/cql3/CQLStatement.java
@@ -58,4 +58,6 @@ public interface CQLStatement
* @param state the current query state
*/
public ResultMessage executeInternal(QueryState state, QueryOptions options) throws RequestValidationException, RequestExecutionException;
+
+ boolean usesFunction(String ksName, String functionName);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/ColumnCondition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnCondition.java b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
index fc45fdc..7daec02 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnCondition.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
@@ -34,15 +34,12 @@ import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.ByteBufferUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* A CQL3 condition on the value of a column or collection element. For example, "UPDATE .. IF a = 0".
*/
public class ColumnCondition
{
- private static final Logger logger = LoggerFactory.getLogger(ColumnCondition.class);
public final ColumnDefinition column;
@@ -96,6 +93,19 @@ public class ColumnCondition
return new ColumnCondition(column, collectionElement, inMarker, null, Operator.IN);
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ if (collectionElement != null && collectionElement.usesFunction(ksName, functionName))
+ return true;
+ if (value != null && value.usesFunction(ksName, functionName))
+ return true;
+ if (inValues != null)
+ for (Term value : inValues)
+ if (value != null && value.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
/**
* Collects the column specification for the bind variables of this operation.
*
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index 37e94b7..4c051e3 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -310,7 +310,7 @@ selectionFunctionArgs returns [List<Selectable.Raw> a]
selectCountClause returns [List<RawSelector> expr]
@init{ ColumnIdentifier alias = new ColumnIdentifier("count", false); }
- : K_COUNT '(' countArgument ')' (K_AS c=ident { alias = c; })? { $expr = new ArrayList<RawSelector>(); $expr.add( new RawSelector(new Selectable.WithFunction.Raw(new FunctionName("countRows"), Collections.<Selectable.Raw>emptyList()), alias));}
+ : K_COUNT '(' countArgument ')' (K_AS c=ident { alias = c; })? { $expr = new ArrayList<RawSelector>(); $expr.add( new RawSelector(new Selectable.WithFunction.Raw(FunctionName.nativeFunction("countRows"), Collections.<Selectable.Raw>emptyList()), alias));}
;
countArgument
@@ -977,12 +977,12 @@ intValue returns [Term.Raw value]
;
functionName returns [FunctionName s]
- : f=allowedFunctionName { $s = new FunctionName(f); }
- | b=allowedFunctionName '::' f=allowedFunctionName { $s = new FunctionName(b, f); }
+ : (ks=keyspaceName '.')? f=allowedFunctionName { $s = new FunctionName(ks, f); }
;
allowedFunctionName returns [String s]
- : f=IDENT { $s = $f.text; }
+ : f=IDENT { $s = $f.text.toLowerCase(); }
+ | f=QUOTED_NAME { $s = $f.text; }
| u=unreserved_function_keyword { $s = u; }
| K_TOKEN { $s = "token"; }
| K_COUNT { $s = "count"; }
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/Operation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Operation.java b/src/java/org/apache/cassandra/cql3/Operation.java
index 816acb2..583158b 100644
--- a/src/java/org/apache/cassandra/cql3/Operation.java
+++ b/src/java/org/apache/cassandra/cql3/Operation.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
-import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.composites.Composite;
@@ -56,6 +55,11 @@ public abstract class Operation
this.t = t;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return t != null && t.usesFunction(ksName, functionName);
+ }
+
/**
* @return whether the operation requires a read of the previous value to be executed
* (only lists setterByIdx, discard and discardByIdx requires that).
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/QueryProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
index 680f9f2..cd56075 100644
--- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
@@ -31,6 +31,7 @@ import org.antlr.runtime.*;
import org.github.jamm.MemoryMeter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.cassandra.cql3.functions.*;
import org.apache.cassandra.cql3.statements.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.composites.*;
@@ -591,11 +592,20 @@ public class QueryProcessor implements QueryHandler
public void onCreateKeyspace(String ksName) { }
public void onCreateColumnFamily(String ksName, String cfName) { }
public void onCreateUserType(String ksName, String typeName) { }
- public void onCreateFunction(String namespace, String functionName) { }
+ public void onCreateFunction(String ksName, String functionName) {
+ if (Functions.getOverloadCount(new FunctionName(ksName, functionName)) > 1)
+ {
+ // in case there are other overloads, we have to remove all overloads since argument type
+ // matching may change (due to type casting)
+ removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, functionName);
+ removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, functionName);
+ }
+ }
+
public void onUpdateKeyspace(String ksName) { }
public void onUpdateColumnFamily(String ksName, String cfName) { }
public void onUpdateUserType(String ksName, String typeName) { }
- public void onUpdateFunction(String namespace, String functionName) { }
+ public void onUpdateFunction(String ksName, String functionName) { }
public void onDropKeyspace(String ksName)
{
@@ -608,6 +618,17 @@ public class QueryProcessor implements QueryHandler
}
public void onDropUserType(String ksName, String typeName) { }
- public void onDropFunction(String namespace, String functionName) { }
- }
+ public void onDropFunction(String ksName, String functionName) {
+ removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, functionName);
+ removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, functionName);
+ }
+
+ private void removeInvalidPreparedStatementsForFunction(Iterator<ParsedStatement.Prepared> iterator,
+ String ksName, String functionName)
+ {
+ while (iterator.hasNext())
+ if (iterator.next().statement.usesFunction(ksName, functionName))
+ iterator.remove();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/Term.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Term.java b/src/java/org/apache/cassandra/cql3/Term.java
index 3f4d728..7e20df8 100644
--- a/src/java/org/apache/cassandra/cql3/Term.java
+++ b/src/java/org/apache/cassandra/cql3/Term.java
@@ -67,6 +67,8 @@ public interface Term
*/
public abstract boolean containsBindMarker();
+ boolean usesFunction(String ksName, String functionName);
+
/**
* A parsed, non prepared (thus untyped) term.
*
@@ -115,6 +117,11 @@ public interface Term
public void collectMarkerSpecification(VariableSpecifications boundNames) {}
public Terminal bind(QueryOptions options) { return this; }
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return false;
+ }
+
// While some NonTerminal may not have bind markers, no Term can be Terminal
// with a bind marker
public boolean containsBindMarker()
@@ -156,6 +163,11 @@ public interface Term
*/
public abstract class NonTerminal implements Term
{
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return false;
+ }
+
public ByteBuffer bindAndGet(QueryOptions options) throws InvalidRequestException
{
Terminal t = bind(options);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java
index 22063ff..934344c 100644
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@ -148,6 +148,15 @@ public abstract class UserTypes
this.values = values;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ if (values != null)
+ for (Term value : values)
+ if (value != null && value.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
public boolean containsBindMarker()
{
for (Term t : values)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
index 3b80fc0..efaa12a 100644
--- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
@@ -41,6 +41,11 @@ public class FunctionCall extends Term.NonTerminal
this.terms = terms;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return fun.name().keyspace.equals(ksName) && fun.name().name.equals(functionName);
+ }
+
public void collectMarkerSpecification(VariableSpecifications boundNames)
{
for (Term t : terms)
@@ -54,7 +59,7 @@ public class FunctionCall extends Term.NonTerminal
public ByteBuffer bindAndGet(QueryOptions options) throws InvalidRequestException
{
- List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(terms.size());
+ List<ByteBuffer> buffers = new ArrayList<>(terms.size());
for (Term t : terms)
{
// For now, we don't allow nulls as argument as no existing function needs it and it
@@ -110,7 +115,7 @@ public class FunctionCall extends Term.NonTerminal
public static class Raw implements Term.Raw
{
- private final FunctionName name;
+ private FunctionName name;
private final List<Term.Raw> terms;
public Raw(FunctionName name, List<Term.Raw> terms)
@@ -140,7 +145,7 @@ public class FunctionCall extends Term.NonTerminal
throw new InvalidRequestException(String.format("Incorrect number of arguments specified for function %s (expected %d, found %d)",
fun.name(), fun.argTypes().size(), terms.size()));
- List<Term> parameters = new ArrayList<Term>(terms.size());
+ List<Term> parameters = new ArrayList<>(terms.size());
boolean allTerminal = true;
for (int i = 0; i < terms.size(); i++)
{
@@ -160,7 +165,7 @@ public class FunctionCall extends Term.NonTerminal
// All parameters must be terminal
private static ByteBuffer execute(ScalarFunction fun, List<Term> parameters) throws InvalidRequestException
{
- List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(parameters.size());
+ List<ByteBuffer> buffers = new ArrayList<>(parameters.size());
for (Term t : parameters)
{
assert t instanceof Term.Terminal;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/functions/FunctionName.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionName.java b/src/java/org/apache/cassandra/cql3/functions/FunctionName.java
index 814bbbf..460e7a6 100644
--- a/src/java/org/apache/cassandra/cql3/functions/FunctionName.java
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionName.java
@@ -19,32 +19,40 @@ package org.apache.cassandra.cql3.functions;
import com.google.common.base.Objects;
-public class FunctionName
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.SystemKeyspace;
+
+public final class FunctionName
{
- public final String namespace;
+ public final String keyspace;
public final String name;
- // Use by toString rather than built from 'bundle' and 'name' so as to
- // preserve the original case.
- private final String displayName;
+ public static FunctionName nativeFunction(String name)
+ {
+ return new FunctionName(SystemKeyspace.NAME, name);
+ }
- public FunctionName(String name)
+ public FunctionName(String keyspace, String name)
{
- this("", name);
+ assert name != null : "Name parameter must not be null";
+ this.keyspace = keyspace != null ? keyspace : null;
+ this.name = name;
}
- public FunctionName(String namespace, String name)
+ public FunctionName asNativeFunction()
{
- this.namespace = namespace.toLowerCase();
- this.name = name.toLowerCase();
+ return FunctionName.nativeFunction(name);
+ }
- this.displayName = namespace.isEmpty() ? name : namespace + "::" + name;
+ public boolean hasKeyspace()
+ {
+ return keyspace != null;
}
@Override
public final int hashCode()
{
- return Objects.hashCode(namespace, name);
+ return Objects.hashCode(keyspace, name);
}
@Override
@@ -54,13 +62,13 @@ public class FunctionName
return false;
FunctionName that = (FunctionName)o;
- return Objects.equal(this.namespace, that.namespace)
+ return Objects.equal(this.keyspace, that.keyspace)
&& Objects.equal(this.name, that.name);
}
@Override
public String toString()
{
- return displayName;
+ return keyspace == null ? name : keyspace + "." + name;
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/functions/Functions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/Functions.java b/src/java/org/apache/cassandra/cql3/functions/Functions.java
index 62de2bc..7021475 100644
--- a/src/java/org/apache/cassandra/cql3/functions/Functions.java
+++ b/src/java/org/apache/cassandra/cql3/functions/Functions.java
@@ -37,7 +37,7 @@ public abstract class Functions
// We special case the token function because that's the only function whose argument types actually
// depend on the table on which the function is called. Because it's the sole exception, it's easier
// to handle it as a special case.
- private static final FunctionName TOKEN_FUNCTION_NAME = new FunctionName("token");
+ private static final FunctionName TOKEN_FUNCTION_NAME = FunctionName.nativeFunction("token");
private static final String SELECT_UDFS = "SELECT * FROM " + SystemKeyspace.NAME + '.' + SystemKeyspace.SCHEMA_FUNCTIONS_TABLE;
@@ -108,6 +108,11 @@ public abstract class Functions
fun.argTypes().get(i));
}
+ public static int getOverloadCount(FunctionName name)
+ {
+ return declared.get(name).size();
+ }
+
public static Function get(String keyspace,
FunctionName name,
List<? extends AssignmentTestable> providedArgs,
@@ -115,10 +120,25 @@ public abstract class Functions
String receiverCf)
throws InvalidRequestException
{
- if (name.equals(TOKEN_FUNCTION_NAME))
+ if (name.hasKeyspace()
+ ? name.equals(TOKEN_FUNCTION_NAME)
+ : name.name.equals(TOKEN_FUNCTION_NAME.name))
return new TokenFct(Schema.instance.getCFMetaData(receiverKs, receiverCf));
- List<Function> candidates = declared.get(name);
+ List<Function> candidates;
+ if (!name.hasKeyspace())
+ {
+ // function name not fully qualified
+ candidates = new ArrayList<>();
+ // add 'SYSTEM' (native) candidates
+ candidates.addAll(declared.get(name.asNativeFunction()));
+ // add 'current keyspace' candidates
+ candidates.addAll(declared.get(new FunctionName(keyspace, name.name)));
+ }
+ else
+ // function name is fully qualified (keyspace + name)
+ candidates = declared.get(name);
+
if (candidates.isEmpty())
return null;
@@ -165,6 +185,7 @@ public abstract class Functions
public static Function find(FunctionName name, List<AbstractType<?>> argTypes)
{
+ assert name.hasKeyspace() : "function name not fully qualified";
for (Function f : declared.get(name))
{
if (f.argTypes().equals(argTypes))
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java b/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java
index d658d9d..bff7688 100644
--- a/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java
@@ -28,12 +28,7 @@ public abstract class NativeFunction extends AbstractFunction
{
protected NativeFunction(String name, AbstractType<?> returnType, AbstractType<?>... argTypes)
{
- this(new FunctionName(name), returnType, argTypes);
- }
-
- protected NativeFunction(FunctionName name, AbstractType<?> returnType, AbstractType<?>... argTypes)
- {
- super(name, Arrays.asList(argTypes), returnType);
+ super(FunctionName.nativeFunction(name), Arrays.asList(argTypes), returnType);
}
// Most of our functions are pure, the other ones should override this
@@ -47,4 +42,3 @@ public abstract class NativeFunction extends AbstractFunction
return true;
}
}
-
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
index bf011a7..42418c6 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
@@ -30,7 +30,6 @@ import org.apache.cassandra.cql3.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.composites.Composite;
import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.CompositeType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.marshal.TypeParser;
import org.apache.cassandra.exceptions.*;
@@ -152,8 +151,8 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
private static Mutation makeSchemaMutation(FunctionName name)
{
- CompositeType kv = (CompositeType)SystemKeyspace.SchemaFunctionsTable.getKeyValidator();
- return new Mutation(SystemKeyspace.NAME, kv.decompose(name.namespace, name.name));
+ UTF8Type kv = (UTF8Type)SystemKeyspace.SchemaFunctionsTable.getKeyValidator();
+ return new Mutation(SystemKeyspace.NAME, kv.decompose(name.keyspace));
}
public Mutation toSchemaDrop(long timestamp)
@@ -161,7 +160,7 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
Mutation mutation = makeSchemaMutation(name);
ColumnFamily cf = mutation.addOrGet(SystemKeyspace.SCHEMA_FUNCTIONS_TABLE);
- Composite prefix = SystemKeyspace.SchemaFunctionsTable.comparator.make(computeSignature(argTypes));
+ Composite prefix = SystemKeyspace.SchemaFunctionsTable.comparator.make(name.name, computeSignature(argTypes));
int ldt = (int) (System.currentTimeMillis() / 1000);
cf.addAtom(new RangeTombstone(prefix, prefix.end(), timestamp, ldt));
@@ -173,7 +172,7 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
Mutation mutation = makeSchemaMutation(name);
ColumnFamily cf = mutation.addOrGet(SystemKeyspace.SCHEMA_FUNCTIONS_TABLE);
- Composite prefix = SystemKeyspace.SchemaFunctionsTable.comparator.make(computeSignature(argTypes));
+ Composite prefix = SystemKeyspace.SchemaFunctionsTable.comparator.make(name.name, computeSignature(argTypes));
CFRowAdder adder = new CFRowAdder(cf, prefix, timestamp);
adder.resetCollection("argument_names");
@@ -194,9 +193,9 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
public static UDFunction fromSchema(UntypedResultSet.Row row)
{
- String namespace = row.getString("namespace");
- String fname = row.getString("name");
- FunctionName name = new FunctionName(namespace, fname);
+ String ksName = row.getString("keyspace_name");
+ String functionName = row.getString("function_name");
+ FunctionName name = new FunctionName(ksName, functionName);
List<String> names = row.getList("argument_names", UTF8Type.instance);
List<String> types = row.getList("argument_types", UTF8Type.instance);
@@ -251,12 +250,12 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
}
}
- public static Map<ByteBuffer, UDFunction> fromSchema(Row row)
+ public static Map<Composite, UDFunction> fromSchema(Row row)
{
UntypedResultSet results = QueryProcessor.resultify("SELECT * FROM system." + SystemKeyspace.SCHEMA_FUNCTIONS_TABLE, row);
- Map<ByteBuffer, UDFunction> udfs = new HashMap<>(results.size());
+ Map<Composite, UDFunction> udfs = new HashMap<>(results.size());
for (UntypedResultSet.Row result : results)
- udfs.put(result.getBlob("signature"), fromSchema(result));
+ udfs.put(SystemKeyspace.SchemaFunctionsTable.comparator.make(result.getString("function_name"), result.getBlob("signature")), fromSchema(result));
return udfs;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
index 4660e1d..3778d41 100644
--- a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
+++ b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
@@ -49,7 +49,7 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
{
if (factories.doesAggregation() && !factories.containsOnlyAggregateFunctions())
throw new InvalidRequestException(String.format("the %s function arguments must be either all aggregates or all none aggregates",
- fun.name().name));
+ fun.name()));
}
return new Factory()
@@ -67,6 +67,11 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
return fun.returnType();
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return fun.name().keyspace.equals(ksName) && fun.name().name.equals(functionName);
+ }
+
public Selector newInstance()
{
return fun.isAggregate() ? new AggregateFunctionSelector(fun, factories.newInstances())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/selection/Selection.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java b/src/java/org/apache/cassandra/cql3/selection/Selection.java
index 7c3d34c..7c7dab7 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selection.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java
@@ -82,6 +82,11 @@ public abstract class Selection
return columns.size() - 1;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return false;
+ }
+
private static boolean isUsingFunction(List<RawSelector> rawSelectors)
{
for (RawSelector rawSelector : rawSelectors)
@@ -346,6 +351,11 @@ public abstract class Selection
throw new InvalidRequestException("the select clause must either contains only aggregates or none");
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return factories.usesFunction(ksName, functionName);
+ }
+
public boolean isAggregate()
{
return factories.containsOnlyAggregateFunctions();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/selection/Selector.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/Selector.java b/src/java/org/apache/cassandra/cql3/selection/Selector.java
index 889da70..f2c729b 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selector.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selector.java
@@ -40,6 +40,11 @@ public abstract class Selector implements AssignmentTestable
*/
public static abstract class Factory
{
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return false;
+ }
+
/**
* Returns the column specification corresponding to the output value of the selector instances created by
* this factory.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
index 6922994..4d3e974 100644
--- a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
+++ b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
@@ -89,6 +89,14 @@ final class SelectorFactories implements Iterable<Selector.Factory>
}
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ for (Factory factory : factories)
+ if (factory != null && factory.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
/**
* Checks if this <code>SelectorFactories</code> contains only factories for aggregates.
*
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
index f0874c1..2db00df 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -77,6 +77,16 @@ public class BatchStatement implements CQLStatement, MeasurableForPreparedCache
this.hasConditions = hasConditions;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ if (attrs.usesFunction(ksName, functionName))
+ return true;
+ for (ModificationStatement statement : statements)
+ if (statement.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
public long measureForPreparedCache(MemoryMeter meter)
{
long size = meter.measure(this)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
index 712a474..c41fb08 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
@@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.List;
import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.config.Schema;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.functions.*;
@@ -31,6 +32,7 @@ import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
+import org.apache.cassandra.thrift.ThriftValidation;
import org.apache.cassandra.transport.Event;
/**
@@ -40,7 +42,7 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
{
private final boolean orReplace;
private final boolean ifNotExists;
- private final FunctionName functionName;
+ private FunctionName functionName;
private final String language;
private final String body;
private final boolean deterministic;
@@ -70,17 +72,31 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
this.ifNotExists = ifNotExists;
}
- public void checkAccess(ClientState state) throws UnauthorizedException
+ public void prepareKeyspace(ClientState state) throws InvalidRequestException
+ {
+ if (!functionName.hasKeyspace() && state.getRawKeyspace() != null)
+ functionName = new FunctionName(state.getKeyspace(), functionName.name);
+
+ if (!functionName.hasKeyspace())
+ throw new InvalidRequestException("You need to be logged in a keyspace or use a fully qualified function name");
+
+ ThriftValidation.validateKeyspaceNotSystem(functionName.keyspace);
+ }
+
+ public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
{
// TODO CASSANDRA-7557 (function DDL permission)
- state.hasAllKeyspacesAccess(Permission.CREATE);
+ state.hasKeyspaceAccess(functionName.keyspace, Permission.CREATE);
}
public void validate(ClientState state) throws InvalidRequestException
{
if (ifNotExists && orReplace)
throw new InvalidRequestException("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives");
+
+ if (Schema.instance.getKSMetaData(functionName.keyspace) == null)
+ throw new InvalidRequestException(String.format("Cannot add function '%s' to non existing keyspace '%s'.", functionName.name, functionName.keyspace));
}
public Event.SchemaChange changeEvent()
@@ -98,7 +114,7 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
for (CQL3Type.Raw rawType : argRawTypes)
// We have no proper keyspace to give, which means that this will break (NPE currently)
// for UDT: #7791 is open to fix this
- argTypes.add(rawType.prepare(null).getType());
+ argTypes.add(rawType.prepare(functionName.keyspace).getType());
AbstractType<?> returnType = rawReturnType.prepare(null).getType();
@@ -110,10 +126,6 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
if (!orReplace)
throw new InvalidRequestException(String.format("Function %s already exists", old));
- // Means we're replacing the function. We still need to validate that 1) it's not a native function and 2) that the return type
- // matches (or that could break existing code badly)
- if (old.isNative())
- throw new InvalidRequestException(String.format("Cannot replace native function %s", old));
if (!old.returnType().isValueCompatibleWith(returnType))
throw new InvalidRequestException(String.format("Cannot replace function %s, the new return type %s is not compatible with the return type %s of existing function",
functionName, returnType.asCQL3Type(), old.returnType().asCQL3Type()));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
index 78c8607..5aaf9b1 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
@@ -29,6 +29,7 @@ import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
+import org.apache.cassandra.thrift.ThriftValidation;
import org.apache.cassandra.transport.Event;
/**
@@ -36,7 +37,7 @@ import org.apache.cassandra.transport.Event;
*/
public final class DropFunctionStatement extends SchemaAlteringStatement
{
- private final FunctionName functionName;
+ private FunctionName functionName;
private final boolean ifExists;
private final List<CQL3Type.Raw> argRawTypes;
private final boolean argsPresent;
@@ -52,11 +53,22 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
this.ifExists = ifExists;
}
- public void checkAccess(ClientState state) throws UnauthorizedException
+ public void prepareKeyspace(ClientState state) throws InvalidRequestException
+ {
+ if (!functionName.hasKeyspace() && state.getRawKeyspace() != null)
+ functionName = new FunctionName(state.getKeyspace(), functionName.name);
+
+ if (!functionName.hasKeyspace())
+ throw new InvalidRequestException("You need to be logged in a keyspace or use a fully qualified function name");
+
+ ThriftValidation.validateKeyspaceNotSystem(functionName.keyspace);
+ }
+
+ public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
{
// TODO CASSANDRA-7557 (function DDL permission)
- state.hasAllKeyspacesAccess(Permission.DROP);
+ state.hasKeyspaceAccess(functionName.keyspace, Permission.DROP);
}
/**
@@ -88,11 +100,7 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
List<AbstractType<?>> argTypes = new ArrayList<>(argRawTypes.size());
for (CQL3Type.Raw rawType : argRawTypes)
- {
- // We have no proper keyspace to give, which means that this will break (NPE currently)
- // for UDT: #7791 is open to fix this
- argTypes.add(rawType.prepare(null).getType());
- }
+ argTypes.add(rawType.prepare(functionName.keyspace).getType());
Function old;
if (argsPresent)
@@ -125,10 +133,6 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
old = olds.get(0);
}
- if (old.isNative())
- throw new InvalidRequestException(String.format("Cannot drop function '%s' because it is a " +
- "native (built-in) function", functionName));
-
MigrationManager.announceFunctionDrop((UDFunction)old, isLocalOnly);
return true;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index 61f6401..7dc9c66 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -22,7 +22,6 @@ import java.util.*;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.github.jamm.MemoryMeter;
import org.apache.cassandra.auth.Permission;
@@ -88,6 +87,25 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
this.attrs = attrs;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ if (attrs.usesFunction(ksName, functionName))
+ return true;
+ for (Restriction restriction : processedKeys.values())
+ if (restriction != null && restriction.usesFunction(ksName, functionName))
+ return true;
+ for (Operation operation : columnOperations)
+ if (operation != null && operation.usesFunction(ksName, functionName))
+ return true;
+ for (ColumnCondition condition : columnConditions)
+ if (condition != null && condition.usesFunction(ksName, functionName))
+ return true;
+ for (ColumnCondition condition : staticConditions)
+ if (condition != null && condition.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
public long measureForPreparedCache(MemoryMeter meter)
{
return meter.measure(this)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
index d048327..bcce9ce 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
@@ -61,4 +61,9 @@ public abstract class ParsedStatement
this(statement, Collections.<ColumnSpecification>emptyList());
}
}
+
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/Restriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/Restriction.java b/src/java/org/apache/cassandra/cql3/statements/Restriction.java
index 659ed95..b264156 100644
--- a/src/java/org/apache/cassandra/cql3/statements/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/statements/Restriction.java
@@ -49,6 +49,8 @@ public interface Restriction
// Not supported by Slice, but it's convenient to have here
public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException;
+ boolean usesFunction(String ksName, String functionName);
+
public static interface EQ extends Restriction {}
public static interface IN extends Restriction
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index f214774..621c4db 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -56,8 +56,6 @@ import org.apache.cassandra.thrift.ThriftValidation;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Encapsulates a completely parsed SELECT query, including the target
@@ -66,8 +64,6 @@ import org.slf4j.LoggerFactory;
*/
public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
{
- private static final Logger logger = LoggerFactory.getLogger(SelectStatement.class);
-
private static final int DEFAULT_COUNT_PAGE_SIZE = 10000;
private final int boundTerms;
@@ -125,6 +121,24 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
initStaticColumnsInfo();
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ if (selection.usesFunction(ksName, functionName))
+ return true;
+ if (limit != null && limit.usesFunction(ksName, functionName))
+ return true;
+ for (Restriction restriction : metadataRestrictions.values())
+ if (restriction != null && restriction.usesFunction(ksName, functionName))
+ return true;
+ for (Restriction restriction : keyRestrictions)
+ if (restriction != null && restriction.usesFunction(ksName, functionName))
+ return true;
+ for (Restriction restriction : columnRestrictions)
+ if (restriction != null && restriction.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
private void initStaticColumnsInfo()
{
if (!cfm.hasStaticColumns())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java b/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
index b1c6ccc..b6ca640 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
@@ -43,6 +43,11 @@ public abstract class SingleColumnRestriction implements Restriction
this.onToken = onToken;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return value != null && value.usesFunction(ksName, functionName);
+ }
+
public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
{
return Collections.singletonList(value.bindAndGet(options));
@@ -94,6 +99,15 @@ public abstract class SingleColumnRestriction implements Restriction
this.values = values;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ if (values != null)
+ for (Term value : values)
+ if (value != null && value.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
{
List<ByteBuffer> buffers = new ArrayList<>(values.size());
@@ -153,6 +167,11 @@ public abstract class SingleColumnRestriction implements Restriction
this.marker = marker;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ return false;
+ }
+
public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
{
Term.MultiItemTerminal lval = (Term.MultiItemTerminal)marker.bind(options);
@@ -216,6 +235,14 @@ public abstract class SingleColumnRestriction implements Restriction
this.onToken = onToken;
}
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ for (Term value : bounds)
+ if (value != null && value.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
public boolean isSlice()
{
return true;
@@ -343,6 +370,19 @@ public abstract class SingleColumnRestriction implements Restriction
private List<Term> values; // for CONTAINS
private List<Term> keys; // for CONTAINS_KEY
+ public boolean usesFunction(String ksName, String functionName)
+ {
+ if (values != null)
+ for (Term value : values)
+ if (value != null && value.usesFunction(ksName, functionName))
+ return true;
+ if (keys != null)
+ for (Term key : keys)
+ if (key != null && key.usesFunction(ksName, functionName))
+ return true;
+ return false;
+ }
+
public boolean hasContains()
{
return values != null;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/db/DefsTables.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/DefsTables.java b/src/java/org/apache/cassandra/db/DefsTables.java
index a02f65e..bcb0893 100644
--- a/src/java/org/apache/cassandra/db/DefsTables.java
+++ b/src/java/org/apache/cassandra/db/DefsTables.java
@@ -37,6 +37,7 @@ import org.apache.cassandra.cql3.functions.Functions;
import org.apache.cassandra.cql3.functions.UDFunction;
import org.apache.cassandra.db.commitlog.CommitLog;
import org.apache.cassandra.db.compaction.CompactionManager;
+import org.apache.cassandra.db.composites.Composite;
import org.apache.cassandra.db.filter.QueryFilter;
import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.UserType;
@@ -303,7 +304,7 @@ public class DefsTables
MapDifference<DecoratedKey, ColumnFamily> diff = Maps.difference(before, after);
- // New namespace with functions
+ // New keyspace with functions
for (Map.Entry<DecoratedKey, ColumnFamily> entry : diff.entriesOnlyOnRight().entrySet())
if (entry.getValue().hasColumns())
created.addAll(UDFunction.fromSchema(new Row(entry.getKey(), entry.getValue())).values());
@@ -315,7 +316,7 @@ public class DefsTables
if (pre.hasColumns() && post.hasColumns())
{
- MapDifference<ByteBuffer, UDFunction> delta =
+ MapDifference<Composite, UDFunction> delta =
Maps.difference(UDFunction.fromSchema(new Row(entry.getKey(), pre)),
UDFunction.fromSchema(new Row(entry.getKey(), post)));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/db/SystemKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java
index 49c1502..7806d5f 100644
--- a/src/java/org/apache/cassandra/db/SystemKeyspace.java
+++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java
@@ -182,11 +182,12 @@ public final class SystemKeyspace
+ "PRIMARY KEY ((keyspace_name), type_name))")
.gcGraceSeconds(WEEK);
+
public static final CFMetaData SchemaFunctionsTable =
compile(SCHEMA_FUNCTIONS_TABLE, "user defined function definitions",
"CREATE TABLE %s ("
- + "namespace text,"
- + "name text,"
+ + "keyspace_name text,"
+ + "function_name text,"
+ "signature blob,"
+ "argument_names list<text>,"
+ "argument_types list<text>,"
@@ -194,7 +195,7 @@ public final class SystemKeyspace
+ "deterministic boolean,"
+ "language text,"
+ "return_type text,"
- + "PRIMARY KEY ((namespace, name), signature))")
+ + "PRIMARY KEY ((keyspace_name), function_name, signature))")
.gcGraceSeconds(WEEK);
public static final CFMetaData BuiltIndexesTable =
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/service/IMigrationListener.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/IMigrationListener.java b/src/java/org/apache/cassandra/service/IMigrationListener.java
index b4eb392..bc67e8a 100644
--- a/src/java/org/apache/cassandra/service/IMigrationListener.java
+++ b/src/java/org/apache/cassandra/service/IMigrationListener.java
@@ -22,16 +22,16 @@ public interface IMigrationListener
public void onCreateKeyspace(String ksName);
public void onCreateColumnFamily(String ksName, String cfName);
public void onCreateUserType(String ksName, String typeName);
- public void onCreateFunction(String namespace, String functionName);
+ public void onCreateFunction(String ksName, String functionName);
public void onUpdateKeyspace(String ksName);
public void onUpdateColumnFamily(String ksName, String cfName);
public void onUpdateUserType(String ksName, String typeName);
- public void onUpdateFunction(String namespace, String functionName);
+ public void onUpdateFunction(String ksName, String functionName);
public void onDropKeyspace(String ksName);
public void onDropColumnFamily(String ksName, String cfName);
public void onDropUserType(String ksName, String typeName);
- public void onDropFunction(String namespace, String functionName);
+ public void onDropFunction(String ksName, String functionName);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/service/MigrationManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/MigrationManager.java b/src/java/org/apache/cassandra/service/MigrationManager.java
index a5d4628..8c3199f 100644
--- a/src/java/org/apache/cassandra/service/MigrationManager.java
+++ b/src/java/org/apache/cassandra/service/MigrationManager.java
@@ -38,8 +38,11 @@ import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.KSMetaData;
import org.apache.cassandra.config.UTMetaData;
import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.functions.AggregateFunction;
+import org.apache.cassandra.cql3.functions.ScalarFunction;
import org.apache.cassandra.cql3.functions.UDFunction;
import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.AlreadyExistsException;
import org.apache.cassandra.exceptions.ConfigurationException;
@@ -177,19 +180,36 @@ public class MigrationManager
public void notifyCreateFunction(UDFunction udf)
{
for (IMigrationListener listener : listeners)
- listener.onCreateFunction(udf.name().namespace, udf.name().name);
+ listener.onCreateFunction(udf.name().keyspace, udf.name().name);
}
public void notifyUpdateFunction(UDFunction udf)
{
for (IMigrationListener listener : listeners)
- listener.onUpdateFunction(udf.name().namespace, udf.name().name);
+ listener.onUpdateFunction(udf.name().keyspace, udf.name().name);
}
public void notifyDropFunction(UDFunction udf)
{
for (IMigrationListener listener : listeners)
- listener.onDropFunction(udf.name().namespace, udf.name().name);
+ listener.onDropFunction(udf.name().keyspace, udf.name().name);
+ }
+
+ private List<String> asString(List<AbstractType<?>> abstractTypes)
+ {
+ List<String> r = new ArrayList<>(abstractTypes.size());
+ for (AbstractType<?> abstractType : abstractTypes)
+ r.add(abstractType.asCQL3Type().toString());
+ return r;
+ }
+
+ private String udType(UDFunction udf)
+ {
+ if (udf instanceof ScalarFunction)
+ return "scalar";
+ if (udf instanceof AggregateFunction)
+ return "aggregate";
+ return "";
}
public void notifyUpdateKeyspace(KSMetaData ksm)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/transport/Event.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/Event.java b/src/java/org/apache/cassandra/transport/Event.java
index 85943cf..9962599 100644
--- a/src/java/org/apache/cassandra/transport/Event.java
+++ b/src/java/org/apache/cassandra/transport/Event.java
@@ -208,15 +208,15 @@ public abstract class Event
public final Change change;
public final Target target;
- public final String keyOrNamespace;
+ public final String keyspace;
public final String tableOrTypeOrFunction;
- public SchemaChange(Change change, Target target, String keyOrNamespace, String tableOrTypeOrFunction)
+ public SchemaChange(Change change, Target target, String keyspace, String tableOrTypeOrFunction)
{
super(Type.SCHEMA_CHANGE);
this.change = change;
this.target = target;
- this.keyOrNamespace = keyOrNamespace;
+ this.keyspace = keyspace;
this.tableOrTypeOrFunction = tableOrTypeOrFunction;
if (target != Target.KEYSPACE)
assert this.tableOrTypeOrFunction != null : "Table or type should be set for non-keyspace schema change events";
@@ -252,7 +252,7 @@ public abstract class Event
{
CBUtil.writeEnumValue(change, dest);
CBUtil.writeEnumValue(target, dest);
- CBUtil.writeString(keyOrNamespace, dest);
+ CBUtil.writeString(keyspace, dest);
if (target != Target.KEYSPACE)
CBUtil.writeString(tableOrTypeOrFunction, dest);
}
@@ -263,13 +263,13 @@ public abstract class Event
// For the v1/v2 protocol, we have no way to represent type changes, so we simply say the keyspace
// was updated. See CASSANDRA-7617.
CBUtil.writeEnumValue(Change.UPDATED, dest);
- CBUtil.writeString(keyOrNamespace, dest);
+ CBUtil.writeString(keyspace, dest);
CBUtil.writeString("", dest);
}
else
{
CBUtil.writeEnumValue(change, dest);
- CBUtil.writeString(keyOrNamespace, dest);
+ CBUtil.writeString(keyspace, dest);
CBUtil.writeString(target == Target.KEYSPACE ? "" : tableOrTypeOrFunction, dest);
}
}
@@ -281,7 +281,7 @@ public abstract class Event
{
int size = CBUtil.sizeOfEnumValue(change)
+ CBUtil.sizeOfEnumValue(target)
- + CBUtil.sizeOfString(keyOrNamespace);
+ + CBUtil.sizeOfString(keyspace);
if (target != Target.KEYSPACE)
size += CBUtil.sizeOfString(tableOrTypeOrFunction);
@@ -293,11 +293,11 @@ public abstract class Event
if (target == Target.TYPE)
{
return CBUtil.sizeOfEnumValue(Change.UPDATED)
- + CBUtil.sizeOfString(keyOrNamespace)
+ + CBUtil.sizeOfString(keyspace)
+ CBUtil.sizeOfString("");
}
return CBUtil.sizeOfEnumValue(change)
- + CBUtil.sizeOfString(keyOrNamespace)
+ + CBUtil.sizeOfString(keyspace)
+ CBUtil.sizeOfString(target == Target.KEYSPACE ? "" : tableOrTypeOrFunction);
}
}
@@ -305,13 +305,13 @@ public abstract class Event
@Override
public String toString()
{
- return change + " " + target + " " + keyOrNamespace + (tableOrTypeOrFunction == null ? "" : "." + tableOrTypeOrFunction);
+ return change + " " + target + " " + keyspace + (tableOrTypeOrFunction == null ? "" : "." + tableOrTypeOrFunction);
}
@Override
public int hashCode()
{
- return Objects.hashCode(change, target, keyOrNamespace, tableOrTypeOrFunction);
+ return Objects.hashCode(change, target, keyspace, tableOrTypeOrFunction);
}
@Override
@@ -323,7 +323,7 @@ public abstract class Event
SchemaChange scc = (SchemaChange)other;
return Objects.equal(change, scc.change)
&& Objects.equal(target, scc.target)
- && Objects.equal(keyOrNamespace, scc.keyOrNamespace)
+ && Objects.equal(keyspace, scc.keyspace)
&& Objects.equal(tableOrTypeOrFunction, scc.tableOrTypeOrFunction);
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/src/java/org/apache/cassandra/transport/Server.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/Server.java b/src/java/org/apache/cassandra/transport/Server.java
index f8822a5..15fad88 100644
--- a/src/java/org/apache/cassandra/transport/Server.java
+++ b/src/java/org/apache/cassandra/transport/Server.java
@@ -27,8 +27,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
-import io.netty.buffer.ByteBufAllocator;
-import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
@@ -411,7 +409,7 @@ public class Server implements CassandraDaemon.Server
server.connectionTracker.send(new Event.SchemaChange(Event.SchemaChange.Change.CREATED, Event.SchemaChange.Target.TYPE, ksName, typeName));
}
- public void onCreateFunction(String namespace, String functionName)
+ public void onCreateFunction(String ksName, String functionName)
{
}
@@ -430,7 +428,7 @@ public class Server implements CassandraDaemon.Server
server.connectionTracker.send(new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TYPE, ksName, typeName));
}
- public void onUpdateFunction(String namespace, String functionName)
+ public void onUpdateFunction(String ksName, String functionName)
{
}
@@ -449,7 +447,7 @@ public class Server implements CassandraDaemon.Server
server.connectionTracker.send(new Event.SchemaChange(Event.SchemaChange.Change.DROPPED, Event.SchemaChange.Target.TYPE, ksName, typeName));
}
- public void onDropFunction(String namespace, String functionName)
+ public void onDropFunction(String ksName, String functionName)
{
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/test/unit/org/apache/cassandra/cql3/AggregationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/AggregationTest.java b/test/unit/org/apache/cassandra/cql3/AggregationTest.java
index 99db62a..859fe65 100644
--- a/test/unit/org/apache/cassandra/cql3/AggregationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/AggregationTest.java
@@ -37,7 +37,7 @@ public class AggregationTest extends CQLTester
assertColumnNames(execute("SELECT COUNT(*) FROM %s"), "count");
assertRows(execute("SELECT COUNT(*) FROM %s"), row(0L));
assertColumnNames(execute("SELECT max(b), min(b), sum(b), avg(b) , max(c), sum(c), avg(c), sum(d), avg(d) FROM %s"),
- "max(b)", "min(b)", "sum(b)", "avg(b)" , "max(c)", "sum(c)", "avg(c)", "sum(d)", "avg(d)");
+ "system.max(b)", "system.min(b)", "system.sum(b)", "system.avg(b)" , "system.max(c)", "system.sum(c)", "system.avg(c)", "system.sum(d)", "system.avg(d)");
assertRows(execute("SELECT max(b), min(b), sum(b), avg(b) , max(c), sum(c), avg(c), sum(d), avg(d) FROM %s"),
row(null, null, 0, 0, null, 0.0, 0.0, new BigDecimal("0"), new BigDecimal("0")));
@@ -94,15 +94,15 @@ public class AggregationTest extends CQLTester
{
createTable("CREATE TABLE %s (a int primary key, b timeuuid, c double, d double)");
- execute("CREATE OR REPLACE FUNCTION copySign(magnitude double, sign double) RETURNS double LANGUAGE JAVA\n" +
+ execute("CREATE OR REPLACE FUNCTION "+KEYSPACE+".copySign(magnitude double, sign double) RETURNS double LANGUAGE JAVA\n" +
"AS 'return Double.valueOf(Math.copySign(magnitude.doubleValue(), sign.doubleValue()));';");
- assertColumnNames(execute("SELECT max(a), max(unixTimestampOf(b)) FROM %s"), "max(a)", "max(unixtimestampof(b))");
+ assertColumnNames(execute("SELECT max(a), max(unixTimestampOf(b)) FROM %s"), "system.max(a)", "system.max(system.unixtimestampof(b))");
assertRows(execute("SELECT max(a), max(unixTimestampOf(b)) FROM %s"), row(null, null));
- assertColumnNames(execute("SELECT max(a), unixTimestampOf(max(b)) FROM %s"), "max(a)", "unixtimestampof(max(b))");
+ assertColumnNames(execute("SELECT max(a), unixTimestampOf(max(b)) FROM %s"), "system.max(a)", "system.unixtimestampof(system.max(b))");
assertRows(execute("SELECT max(a), unixTimestampOf(max(b)) FROM %s"), row(null, null));
- assertColumnNames(execute("SELECT max(copySign(c, d)) FROM %s"), "max(copysign(c, d))");
+ assertColumnNames(execute("SELECT max(copySign(c, d)) FROM %s"), "system.max("+KEYSPACE+".copysign(c, d))");
assertRows(execute("SELECT max(copySign(c, d)) FROM %s"), row((Object) null));
execute("INSERT INTO %s (a, b, c, d) VALUES (1, maxTimeuuid('2011-02-03 04:05:00+0000'), -1.2, 2.1)");
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4d7f3be/test/unit/org/apache/cassandra/cql3/PgStringTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/PgStringTest.java b/test/unit/org/apache/cassandra/cql3/PgStringTest.java
index 856a255..1870a9a 100644
--- a/test/unit/org/apache/cassandra/cql3/PgStringTest.java
+++ b/test/unit/org/apache/cassandra/cql3/PgStringTest.java
@@ -26,7 +26,7 @@ public class PgStringTest extends CQLTester
@Test
public void testPgSyleFunction() throws Throwable
{
- execute("create or replace function pg::pgfun1 ( input double ) returns text language java\n" +
+ execute("create or replace function "+KEYSPACE+".pgfun1 ( input double ) returns text language java\n" +
"AS $$return \"foobar\";$$");
}
@@ -70,7 +70,7 @@ public class PgStringTest extends CQLTester
public void testMarkerPgFail() throws Throwable
{
// must throw SyntaxException - not StringIndexOutOfBoundsException or similar
- execute("create function foo::pgfun1 ( input double ) returns text language java\n" +
+ execute("create function "+KEYSPACE+".pgfun1 ( input double ) returns text language java\n" +
"AS $javasrc$return 0L;$javasrc$;");
}
}