com.datastax.driver.core.FunctionMetadata Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dse-java-driver-core Show documentation
Show all versions of dse-java-driver-core Show documentation
A driver for DataStax Enterprise (DSE)
and Apache Cassandra 1.2+ clusters that works exclusively with the
Cassandra Query Language version 3 (CQL3) and Cassandra's binary protocol,
supporting DSE-specific features such as geospatial types, DSE Graph and DSE authentication.
/*
* Copyright (C) 2012-2017 DataStax Inc.
*
* This software can be used solely with DataStax Enterprise. Please consult the license at
* http://www.datastax.com/terms/datastax-dse-driver-license-terms
*/
package com.datastax.driver.core;
import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Describes a CQL function (created with {@code CREATE FUNCTION...}).
*/
public class FunctionMetadata {
private static final Logger logger = LoggerFactory.getLogger(FunctionMetadata.class);
private final KeyspaceMetadata keyspace;
private final String simpleName;
private final Map arguments;
private final String body;
private final boolean calledOnNullInput;
private final String language;
private final DataType returnType;
private FunctionMetadata(KeyspaceMetadata keyspace,
String simpleName,
Map arguments,
String body,
boolean calledOnNullInput,
String language,
DataType returnType) {
this.keyspace = keyspace;
this.simpleName = simpleName;
this.arguments = arguments;
this.body = body;
this.calledOnNullInput = calledOnNullInput;
this.language = language;
this.returnType = returnType;
}
// Cassandra < 3.0:
// CREATE TABLE system.schema_functions (
// keyspace_name text,
// function_name text,
// signature frozen>,
// argument_names list,
// argument_types list,
// body text,
// called_on_null_input boolean,
// language text,
// return_type text,
// PRIMARY KEY (keyspace_name, function_name, signature)
// ) WITH CLUSTERING ORDER BY (function_name ASC, signature ASC)
//
// Cassandra >= 3.0:
// CREATE TABLE system_schema.functions (
// keyspace_name text,
// function_name text,
// argument_names frozen>,
// argument_types frozen>,
// body text,
// called_on_null_input boolean,
// language text,
// return_type text,
// PRIMARY KEY (keyspace_name, function_name, argument_types)
// ) WITH CLUSTERING ORDER BY (function_name ASC, argument_types ASC)
//
static FunctionMetadata build(KeyspaceMetadata ksm, Row row, VersionNumber version, Cluster cluster) {
CodecRegistry codecRegistry = cluster.getConfiguration().getCodecRegistry();
ProtocolVersion protocolVersion = cluster.getConfiguration().getProtocolOptions().getProtocolVersion();
String simpleName = row.getString("function_name");
List argumentNames = row.getList("argument_names", String.class);
// this will be a list of C* types in 2.2 and a list of CQL types in 3.0
List argumentTypes = row.getList("argument_types", String.class);
Map arguments = buildArguments(ksm, argumentNames, argumentTypes, version, cluster);
if (argumentNames.size() != argumentTypes.size()) {
String fullName = Metadata.fullFunctionName(simpleName, arguments.values());
logger.error(String.format("Error parsing definition of function %1$s.%2$s: the number of argument names and types don't match."
+ "Cluster.getMetadata().getKeyspace(\"%1$s\").getFunction(\"%2$s\") will be missing.",
ksm.getName(), fullName));
return null;
}
String body = row.getString("body");
boolean calledOnNullInput = row.getBool("called_on_null_input");
String language = row.getString("language");
DataType returnType;
if (version.getMajor() >= 3.0) {
returnType = DataTypeCqlNameParser.parse(row.getString("return_type"), cluster, ksm.getName(), ksm.userTypes, null, false, false);
} else {
returnType = DataTypeClassNameParser.parseOne(row.getString("return_type"), protocolVersion, codecRegistry);
}
return new FunctionMetadata(ksm, simpleName, arguments, body, calledOnNullInput, language, returnType);
}
// Note: the caller ensures that names and types have the same size
private static Map buildArguments(KeyspaceMetadata ksm, List names, List types, VersionNumber version, Cluster cluster) {
if (names.isEmpty())
return Collections.emptyMap();
ImmutableMap.Builder builder = ImmutableMap.builder();
CodecRegistry codecRegistry = cluster.getConfiguration().getCodecRegistry();
ProtocolVersion protocolVersion = cluster.getConfiguration().getProtocolOptions().getProtocolVersion();
Iterator iterTypes = types.iterator();
for (String name : names) {
DataType type;
if (version.getMajor() >= 3) {
type = DataTypeCqlNameParser.parse(iterTypes.next(), cluster, ksm.getName(), ksm.userTypes, null, false, false);
} else {
type = DataTypeClassNameParser.parseOne(iterTypes.next(), protocolVersion, codecRegistry);
}
builder.put(name, type);
}
return builder.build();
}
/**
* Returns a CQL query representing this function in human readable form.
*
* This method is equivalent to {@link #asCQLQuery} but the output is formatted.
*
* @return the CQL query representing this function.
*/
public String exportAsString() {
return asCQLQuery(true);
}
/**
* Returns a CQL query representing this function.
*
* This method returns a single 'CREATE FUNCTION' query corresponding to
* this function definition.
*
* @return the 'CREATE FUNCTION' query corresponding to this function.
*/
public String asCQLQuery() {
return asCQLQuery(false);
}
@Override
public String toString() {
return asCQLQuery(false);
}
private String asCQLQuery(boolean formatted) {
StringBuilder sb = new StringBuilder("CREATE FUNCTION ");
sb
.append(Metadata.quoteIfNecessary(keyspace.getName()))
.append('.')
.append(Metadata.quoteIfNecessary(simpleName))
.append('(');
boolean first = true;
for (Map.Entry entry : arguments.entrySet()) {
if (first)
first = false;
else
sb.append(',');
TableMetadata.newLine(sb, formatted);
String name = entry.getKey();
DataType type = entry.getValue();
sb
.append(TableMetadata.spaces(4, formatted))
.append(Metadata.quoteIfNecessary(name))
.append(' ')
.append(type.asFunctionParameterString());
}
sb.append(')');
TableMetadata.spaceOrNewLine(sb, formatted)
.append(calledOnNullInput ? "CALLED ON NULL INPUT" : "RETURNS NULL ON NULL INPUT");
TableMetadata.spaceOrNewLine(sb, formatted)
.append("RETURNS ")
.append(returnType);
TableMetadata.spaceOrNewLine(sb, formatted)
.append("LANGUAGE ")
.append(language);
TableMetadata.spaceOrNewLine(sb, formatted)
.append("AS '")
.append(body)
.append("';");
return sb.toString();
}
/**
* Returns the keyspace this function belongs to.
*
* @return the keyspace metadata of the keyspace this function belongs to.
*/
public KeyspaceMetadata getKeyspace() {
return keyspace;
}
/**
* Returns the CQL signature of this function.
*
* This is the name of the function, followed by the names of the argument types between parentheses,
* for example {@code sum(int,int)}.
*
* Note that the returned signature is not qualified with the keyspace name.
*
* @return the signature of this function.
*/
public String getSignature() {
StringBuilder sb = new StringBuilder();
sb
.append(Metadata.quoteIfNecessary(simpleName))
.append('(');
boolean first = true;
for (DataType type : arguments.values()) {
if (first)
first = false;
else
sb.append(',');
sb.append(type.asFunctionParameterString());
}
sb.append(')');
return sb.toString();
}
/**
* Returns the simple name of this function.
*
* This is the name of the function, without arguments. Note that functions can be overloaded with
* different argument lists, therefore the simple name may not be unique. For example,
* {@code sum(int,int)} and {@code sum(int,int,int)} both have the simple name {@code sum}.
*
* @return the simple name of this function.
* @see #getSignature()
*/
public String getSimpleName() {
return simpleName;
}
/**
* Returns the names and types of this function's arguments.
*
* @return a map from argument name to argument type.
*/
public Map getArguments() {
return arguments;
}
/**
* Returns the body of this function.
*
* @return the body.
*/
public String getBody() {
return body;
}
/**
* Indicates whether this function's body gets called on null input.
*
* This is {@code true} if the function was created with {@code CALLED ON NULL INPUT},
* and {@code false} if it was created with {@code RETURNS NULL ON NULL INPUT}.
*
* @return whether this function's body gets called on null input.
*/
public boolean isCalledOnNullInput() {
return calledOnNullInput;
}
/**
* Returns the programming language in which this function's body is written.
*
* @return the language.
*/
public String getLanguage() {
return language;
}
/**
* Returns the return type of this function.
*
* @return the return type.
*/
public DataType getReturnType() {
return returnType;
}
@Override
public boolean equals(Object other) {
if (other == this)
return true;
if (other instanceof FunctionMetadata) {
FunctionMetadata that = (FunctionMetadata) other;
return this.keyspace.getName().equals(that.keyspace.getName()) &&
this.arguments.equals(that.arguments) &&
this.body.equals(that.body) &&
this.calledOnNullInput == that.calledOnNullInput &&
this.language.equals(that.language) &&
this.returnType.equals(that.returnType);
}
return false;
}
@Override
public int hashCode() {
return MoreObjects.hashCode(keyspace.getName(), arguments, body, calledOnNullInput, language, returnType);
}
}