You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ra...@apache.org on 2015/04/27 10:33:47 UTC
[3/7] phoenix git commit: PHOENIX-538 Support UDFs(Rajeshbabu
Chintaguntla)
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
index 9c38348..cf72384 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
@@ -23,12 +23,15 @@ import java.util.Map;
import org.apache.phoenix.hbase.index.util.IndexManagementUtil;
import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.query.QueryServices;
import org.apache.phoenix.schema.AmbiguousColumnException;
import org.apache.phoenix.schema.AmbiguousTableException;
import org.apache.phoenix.schema.ColumnAlreadyExistsException;
import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.ConcurrentTableMutationException;
+import org.apache.phoenix.schema.FunctionAlreadyExistsException;
+import org.apache.phoenix.schema.FunctionNotFoundException;
import org.apache.phoenix.schema.ReadOnlyTableException;
import org.apache.phoenix.schema.SequenceAlreadyExistsException;
import org.apache.phoenix.schema.SequenceNotFoundException;
@@ -320,7 +323,22 @@ public enum SQLExceptionCode {
return new SQLTimeoutException(OPERATION_TIMED_OUT.getMessage(),
OPERATION_TIMED_OUT.getSQLState(), OPERATION_TIMED_OUT.getErrorCode());
}
- }),
+ }),
+ FUNCTION_UNDEFINED(6001, "42F01", "Function undefined.", new Factory() {
+ @Override
+ public SQLException newException(SQLExceptionInfo info) {
+ return new FunctionNotFoundException(info.getFunctionName());
+ }
+ }),
+ FUNCTION_ALREADY_EXIST(6002, "42F02", "Function already exists.", new Factory() {
+ @Override
+ public SQLException newException(SQLExceptionInfo info) {
+ return new FunctionAlreadyExistsException(info.getSchemaName(), info.getTableName());
+ }
+ }),
+ UNALLOWED_USER_DEFINED_FUNCTIONS(6003, "42F03",
+ "User defined functions are configured to not be allowed. To allow configure "
+ + QueryServices.ALLOW_USER_DEFINED_FUNCTIONS_ATTRIB + " to true."),
;
private final int errorCode;
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionInfo.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionInfo.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionInfo.java
index fa31190..50dffde 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionInfo.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionInfo.java
@@ -37,6 +37,7 @@ public class SQLExceptionInfo {
public static final String TABLE_NAME = "tableName";
public static final String FAMILY_NAME = "familyName";
public static final String COLUMN_NAME = "columnName";
+ public static final String FUNCTION_NAME = "functionName";
private final Throwable rootCause;
private final SQLExceptionCode code; // Should always have one.
@@ -45,6 +46,7 @@ public class SQLExceptionInfo {
private final String tableName;
private final String familyName;
private final String columnName;
+ private final String functionName;
public static class Builder {
@@ -55,6 +57,7 @@ public class SQLExceptionInfo {
private String tableName;
private String familyName;
private String columnName;
+ private String functionName;
public Builder(SQLExceptionCode code) {
this.code = code;
@@ -90,6 +93,10 @@ public class SQLExceptionInfo {
return this;
}
+ public Builder setFunctionName(String functionName) {
+ this.functionName = functionName;
+ return this;
+ }
public SQLExceptionInfo build() {
return new SQLExceptionInfo(this);
}
@@ -108,6 +115,7 @@ public class SQLExceptionInfo {
tableName = builder.tableName;
familyName = builder.familyName;
columnName = builder.columnName;
+ functionName = builder.functionName;
}
@Override
@@ -116,6 +124,10 @@ public class SQLExceptionInfo {
if (message != null) {
builder.append(" ").append(message);
}
+ if (functionName != null) {
+ builder.append(" ").append(FUNCTION_NAME).append("=").append(functionName);
+ return builder.toString();
+ }
String columnDisplayName = SchemaUtil.getMetaDataEntityName(schemaName, tableName, familyName, columnName);
if (columnName != null) {
builder.append(" ").append(COLUMN_NAME).append("=").append(columnDisplayName);
@@ -153,6 +165,10 @@ public class SQLExceptionInfo {
return columnName;
}
+ public String getFunctionName() {
+ return functionName;
+ }
+
public SQLExceptionCode getCode() {
return code;
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
index 5f598b9..69a2049 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
@@ -95,6 +95,7 @@ import org.apache.phoenix.expression.function.ToTimeFunction;
import org.apache.phoenix.expression.function.ToTimestampFunction;
import org.apache.phoenix.expression.function.TrimFunction;
import org.apache.phoenix.expression.function.TruncFunction;
+import org.apache.phoenix.expression.function.UDFExpression;
import org.apache.phoenix.expression.function.UpperFunction;
import org.apache.phoenix.expression.function.WeekFunction;
import org.apache.phoenix.expression.function.YearFunction;
@@ -227,7 +228,8 @@ public enum ExpressionType {
InstrFunction(InstrFunction.class),
MinuteFunction(MinuteFunction.class),
DayOfMonthFunction(DayOfMonthFunction.class),
- ArrayAppendFunction(ArrayAppendFunction.class)
+ ArrayAppendFunction(ArrayAppendFunction.class),
+ UDFExpression(UDFExpression.class)
;
ExpressionType(Class<? extends Expression> clazz) {
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java
index e694680..014bda4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java
@@ -53,7 +53,7 @@ public abstract class ScalarFunction extends FunctionExpression {
}
@Override
- public final <T> T accept(ExpressionVisitor<T> visitor) {
+ public <T> T accept(ExpressionVisitor<T> visitor) {
List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
T t = visitor.visitLeave(this, l);
if (t == null) {
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/expression/function/UDFExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/UDFExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/UDFExpression.java
new file mode 100644
index 0000000..4e12b17
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/UDFExpression.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.expression.function;
+
+import static org.apache.phoenix.query.QueryServices.DYNAMIC_JARS_DIR_KEY;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.DynamicClassLoader;
+import org.apache.hadoop.io.WritableUtils;
+import org.apache.phoenix.compile.KeyPart;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.parse.PFunction;
+import org.apache.phoenix.schema.PName;
+import org.apache.phoenix.schema.PNameFactory;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.schema.types.PDataType;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.MapMaker;
+
+public class UDFExpression extends ScalarFunction {
+
+ private static Configuration config = HBaseConfiguration.create();
+
+ private static final ConcurrentMap<PName, DynamicClassLoader> tenantIdSpecificCls =
+ new MapMaker().concurrencyLevel(3).weakValues().makeMap();
+
+ private static final ConcurrentMap<String, DynamicClassLoader> pathSpecificCls =
+ new MapMaker().concurrencyLevel(3).weakValues().makeMap();
+
+ private PName tenantId;
+ private String functionClassName;
+ private String jarPath;
+ private ScalarFunction udfFunction;
+
+ public UDFExpression() {
+ }
+
+ public UDFExpression(List<Expression> children,PFunction functionInfo) {
+ super(children);
+ this.tenantId =
+ functionInfo.getTenantId() == null ? PName.EMPTY_NAME : functionInfo.getTenantId();
+ this.functionClassName = functionInfo.getClassName();
+ this.jarPath = functionInfo.getJarPath();
+ constructUDFFunction();
+ }
+
+ public UDFExpression(List<Expression> children, PName tenantId, String functionClassName,
+ String jarPath, ScalarFunction udfFunction) {
+ super(children);
+ this.tenantId = tenantId;
+ this.functionClassName = functionClassName;
+ this.jarPath = jarPath;
+ if(udfFunction != null) {
+ this.udfFunction = udfFunction;
+ } else {
+ constructUDFFunction();
+ }
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ return udfFunction.evaluate(tuple, ptr);
+ }
+
+ @Override
+ public <T> T accept(ExpressionVisitor<T> visitor) {
+ return udfFunction.accept(visitor);
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return udfFunction.getDataType();
+ }
+
+ @Override
+ public String getName() {
+ return udfFunction.getName();
+ }
+
+ @Override
+ public OrderPreserving preservesOrder() {
+ return udfFunction.preservesOrder();
+ }
+
+ @Override
+ public KeyPart newKeyPart(KeyPart childPart) {
+ return udfFunction.newKeyPart(childPart);
+ }
+
+ @Override
+ public int getKeyFormationTraversalIndex() {
+ return udfFunction.getKeyFormationTraversalIndex();
+ }
+
+ public PName getTenantId() {
+ return tenantId;
+ }
+
+ public String getFunctionClassName() {
+ return functionClassName;
+ }
+
+ public String getJarPath() {
+ return jarPath;
+ }
+
+ public ScalarFunction getUdfFunction() {
+ return udfFunction;
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ super.write(output);
+ WritableUtils.writeString(output, tenantId.getString());
+ WritableUtils.writeString(output, this.functionClassName);
+ if(this.jarPath == null) {
+ WritableUtils.writeString(output, "");
+ } else {
+ WritableUtils.writeString(output, this.jarPath);
+ }
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ this.tenantId = PNameFactory.newName(WritableUtils.readString(input));
+ this.functionClassName = WritableUtils.readString(input);
+ String str = WritableUtils.readString(input);
+ this.jarPath = str.length() == 0 ? null: str;
+ constructUDFFunction();
+ }
+
+ private void constructUDFFunction() {
+ try {
+ DynamicClassLoader classLoader = getClassLoader(this.tenantId, this.jarPath);
+ Class<?> clazz = classLoader.loadClass(this.functionClassName);
+ Constructor<?> constructor = clazz.getConstructor(List.class);
+ udfFunction = (ScalarFunction)constructor.newInstance(this.children);
+ } catch (ClassNotFoundException | NoSuchMethodException | SecurityException
+ | InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static DynamicClassLoader getClassLoader(final PName tenantId, final String jarPath) {
+ DynamicClassLoader cl = tenantIdSpecificCls.get(tenantId);
+ String parent = null;
+ if (cl != null) return cl;
+ if(jarPath != null && !jarPath.isEmpty()) {
+ cl = pathSpecificCls.get(jarPath);
+ if (cl != null) return cl;
+ Path path = new Path(jarPath);
+ if(jarPath.endsWith(".jar")) {
+ parent = path.getParent().toString();
+ } else {
+ parent = path.toString();
+ }
+ }
+ if (jarPath == null || jarPath.isEmpty() || config.get(DYNAMIC_JARS_DIR_KEY) != null
+ && (parent != null && parent.equals(config.get(DYNAMIC_JARS_DIR_KEY)))) {
+ cl = tenantIdSpecificCls.get(tenantId);
+ if (cl == null) {
+ cl = new DynamicClassLoader(config, UDFExpression.class.getClassLoader());
+ }
+ // Cache class loader as a weak value, will be GC'ed when no reference left
+ DynamicClassLoader prev = tenantIdSpecificCls.putIfAbsent(tenantId, cl);
+ if (prev != null) {
+ cl = prev;
+ }
+ return cl;
+ } else {
+ cl = pathSpecificCls.get(jarPath);
+ if (cl == null) {
+ Configuration conf = HBaseConfiguration.create(config);
+ conf.set(DYNAMIC_JARS_DIR_KEY, parent);
+ cl = new DynamicClassLoader(conf, UDFExpression.class.getClassLoader());
+ }
+ // Cache class loader as a weak value, will be GC'ed when no reference left
+ DynamicClassLoader prev = pathSpecificCls.putIfAbsent(jarPath, cl);
+ if (prev != null) {
+ cl = prev;
+ }
+ return cl;
+ }
+ }
+
+ @VisibleForTesting
+ public static void setConfig(Configuration conf) {
+ config = conf;
+ }
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
index e6ede7c..b252358 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
@@ -47,6 +47,7 @@ import org.apache.phoenix.expression.function.ArrayAnyComparisonExpression;
import org.apache.phoenix.expression.function.ArrayElemRefExpression;
import org.apache.phoenix.expression.function.ScalarFunction;
import org.apache.phoenix.expression.function.SingleAggregateFunction;
+import org.apache.phoenix.expression.function.UDFExpression;
public class CloneExpressionVisitor extends TraverseAllExpressionVisitor<Expression> {
@@ -100,6 +101,11 @@ public class CloneExpressionVisitor extends TraverseAllExpressionVisitor<Express
return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l);
}
+ public Expression visitLeave(UDFExpression node, List<Expression> l) {
+ return new UDFExpression(l, node.getTenantId(), node.getFunctionClassName(),
+ node.getJarPath(), node.getUdfFunction());
+ }
+
@Override
public Expression visitLeave(ComparisonExpression node, List<Expression> l) {
return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l);
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
index 4f785eb..4565f39 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
@@ -24,9 +24,11 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -57,8 +59,15 @@ import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.parse.AndParseNode;
+import org.apache.phoenix.parse.BaseParseNodeVisitor;
+import org.apache.phoenix.parse.BooleanParseNodeVisitor;
+import org.apache.phoenix.parse.FunctionParseNode;
import org.apache.phoenix.parse.ParseNode;
import org.apache.phoenix.parse.SQLParser;
+import org.apache.phoenix.parse.StatelessTraverseAllParseNodeVisitor;
+import org.apache.phoenix.parse.TraverseAllParseNodeVisitor;
+import org.apache.phoenix.parse.UDFParseNode;
import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.PColumn;
@@ -328,8 +337,21 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
this.immutableRows = dataTable.isImmutableRows();
int indexColByteSize = 0;
ColumnResolver resolver = null;
+ List<ParseNode> parseNodes = new ArrayList<ParseNode>(1);
+ UDFParseNodeVisitor visitor = new UDFParseNodeVisitor();
+ for (int i = indexPosOffset; i < index.getPKColumns().size(); i++) {
+ PColumn indexColumn = index.getPKColumns().get(i);
+ String expressionStr = IndexUtil.getIndexColumnExpressionStr(indexColumn);
+ try {
+ ParseNode parseNode = SQLParser.parseCondition(expressionStr);
+ parseNode.accept(visitor);
+ parseNodes.add(parseNode);
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
try {
- resolver = FromCompiler.getResolver(new TableRef(dataTable));
+ resolver = FromCompiler.getResolver(connection, new TableRef(dataTable), visitor.getUdfParseNodes());
} catch (SQLException e) {
throw new RuntimeException(e); // Impossible
}
@@ -341,9 +363,7 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
Expression expression = null;
try {
expressionIndexCompiler.reset();
- String expressionStr = IndexUtil.getIndexColumnExpressionStr(indexColumn);
- ParseNode parseNode = SQLParser.parseCondition(expressionStr);
- expression = parseNode.accept(expressionIndexCompiler);
+ expression = parseNodes.get(indexPos).accept(expressionIndexCompiler);
} catch (SQLException e) {
throw new RuntimeException(e); // Impossible
}
@@ -861,7 +881,7 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
}
}
return delete;
- }
+ }
public byte[] getIndexTableName() {
return indexTableName;
@@ -1337,4 +1357,24 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
public Set<ColumnReference> getIndexedColumns() {
return indexedColumns;
}
+
+ public static class UDFParseNodeVisitor extends StatelessTraverseAllParseNodeVisitor {
+
+ private Map<String, UDFParseNode> udfParseNodes;
+ public UDFParseNodeVisitor() {
+ udfParseNodes = new HashMap<String, UDFParseNode>(1);
+ }
+
+ @Override
+ public boolean visitEnter(FunctionParseNode node) throws SQLException {
+ if(node instanceof UDFParseNode) {
+ udfParseNodes.put(node.getName(), (UDFParseNode)node);
+ }
+ return super.visitEnter(node);
+ }
+
+ public Map<String, UDFParseNode> getUdfParseNodes() {
+ return udfParseNodes;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
index b4a5c53..c22a7fa 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
@@ -64,6 +64,7 @@ import org.apache.phoenix.execute.MutationState;
import org.apache.phoenix.expression.function.FunctionArgumentType;
import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
import org.apache.phoenix.jdbc.PhoenixStatement.PhoenixStatementParser;
+import org.apache.phoenix.parse.PFunction;
import org.apache.phoenix.query.ConnectionQueryServices;
import org.apache.phoenix.query.DelegateConnectionQueryServices;
import org.apache.phoenix.query.MetaDataMutated;
@@ -118,7 +119,7 @@ import com.google.common.collect.Maps;
*
* @since 0.1
*/
-public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jdbc7Shim.Connection, MetaDataMutated {
+public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jdbc7Shim.Connection, MetaDataMutated{
private final String url;
private final ConnectionQueryServices services;
private final Properties info;
@@ -233,7 +234,7 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
formatters.put(PDecimal.INSTANCE, FunctionArgumentType.NUMERIC.getFormatter(numberPattern));
// We do not limit the metaData on a connection less than the global one,
// as there's not much that will be cached here.
- this.metaData = metaData.pruneTables(new Pruner() {
+ Pruner pruner = new Pruner() {
@Override
public boolean prune(PTable table) {
@@ -243,8 +244,16 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
! Objects.equal(tenantId, table.getTenantId())) );
}
- });
+ @Override
+ public boolean prune(PFunction function) {
+ long maxTimestamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn;
+ return ( function.getTimeStamp() >= maxTimestamp ||
+ ! Objects.equal(tenantId, function.getTenantId()));
+ }
+ };
this.mutationState = newMutationState(maxSize);
+ this.metaData = metaData.pruneTables(pruner);
+ this.metaData = metaData.pruneFunctions(pruner);
this.services.addConnection(this);
// setup tracing, if its enabled
@@ -777,6 +786,18 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
getQueryServices().addTable(table);
return metaData;
}
+
+ @Override
+ public PMetaData addFunction(PFunction function) throws SQLException {
+ // TODO: since a connection is only used by one thread at a time,
+ // we could modify this metadata in place since it's not shared.
+ if (scn == null || scn > function.getTimeStamp()) {
+ metaData = metaData.addFunction(function);
+ }
+ //Cascade through to connectionQueryServices too
+ getQueryServices().addFunction(function);
+ return metaData;
+ }
@Override
public PMetaData addColumn(PName tenantId, String tableName, List<PColumn> columns, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows, boolean isWalDisabled, boolean isMultitenant, boolean storeNulls)
@@ -796,6 +817,14 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
}
@Override
+ public PMetaData removeFunction(PName tenantId, String functionName, long tableTimeStamp) throws SQLException {
+ metaData = metaData.removeFunction(tenantId, functionName, tableTimeStamp);
+ //Cascade through to connectionQueryServices too
+ getQueryServices().removeFunction(tenantId, functionName, tableTimeStamp);
+ return metaData;
+ }
+
+ @Override
public PMetaData removeColumn(PName tenantId, String tableName, List<PColumn> columnsToRemove, long tableTimeStamp,
long tableSeqNum) throws SQLException {
metaData = metaData.removeColumn(tenantId, tableName, columnsToRemove, tableTimeStamp, tableSeqNum);
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
index 1b8b57d..3a0b03b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
@@ -106,6 +106,11 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData, org.apache.pho
public static final int SCHEMA_NAME_INDEX = 1;
public static final int TENANT_ID_INDEX = 0;
+
+ public static final int TYPE_INDEX = 2;
+ public static final int FUNTION_NAME_INDEX = 1;
+
+
public static final String SYSTEM_CATALOG_SCHEMA = QueryConstants.SYSTEM_SCHEMA_NAME;
public static final byte[] SYSTEM_CATALOG_SCHEMA_BYTES = QueryConstants.SYSTEM_SCHEMA_NAME_BYTES;
public static final String SYSTEM_CATALOG_TABLE = "CATALOG";
@@ -209,6 +214,31 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData, org.apache.pho
public static final byte[] TABLE_FAMILY_BYTES = QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES;
public static final String TYPE_SEQUENCE = "SEQUENCE";
+ public static final String SYSTEM_FUNCTION_TABLE = "FUNCTION";
+ public static final String SYSTEM_FUNCTION_NAME = SchemaUtil.getTableName(SYSTEM_CATALOG_SCHEMA, SYSTEM_FUNCTION_TABLE);
+ public static final byte[] SYSTEM_FUNCTION_NAME_BYTES = Bytes.toBytes(SYSTEM_FUNCTION_NAME);
+
+ public static final String FUNCTION_NAME = "FUNCTION_NAME";
+ public static final byte[] FUNCTION_NAME_BYTES = Bytes.toBytes(FUNCTION_NAME);
+ public static final String CLASS_NAME = "CLASS_NAME";
+ public static final byte[] CLASS_NAME_BYTES = Bytes.toBytes(CLASS_NAME);
+ public static final String JAR_PATH = "JAR_PATH";
+ public static final byte[] JAR_PATH_BYTES = Bytes.toBytes(JAR_PATH);
+ public static final String TYPE = "TYPE";
+ public static final byte[] TYPE_BYTES = Bytes.toBytes(TYPE);
+ public static final String ARG_POSITION = "ARG_POSITION";
+ public static final byte[] ARG_POSITION_TYPE = Bytes.toBytes(ARG_POSITION);
+ public static final String RETURN_TYPE = "RETURN_TYPE";
+ public static final byte[] RETURN_TYPE_BYTES = Bytes.toBytes(RETURN_TYPE);
+ public static final String IS_ARRAY = "IS_ARRAY";
+ public static final byte[] IS_ARRAY_BYTES = Bytes.toBytes(IS_ARRAY);
+ public static final String IS_CONSTANT = "IS_CONSTANT";
+ public static final byte[] IS_CONSTANT_BYTES = Bytes.toBytes(IS_CONSTANT);
+ public static final String DEFAULT_VALUE = "DEFAULT_VALUE";
+ public static final byte[] DEFAULT_VALUE_BYTES = Bytes.toBytes(DEFAULT_VALUE);
+ public static final String NUM_ARGS = "NUM_ARGS";
+ public static final byte[] NUM_ARGS_BYTES = Bytes.toBytes(NUM_ARGS);
+
public static final byte[] SEQUENCE_FAMILY_BYTES = QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES;
public static final String SEQUENCE_SCHEMA_NAME = SYSTEM_CATALOG_SCHEMA;
public static final byte[] SEQUENCE_SCHEMA_NAME_BYTES = Bytes.toBytes(SEQUENCE_SCHEMA_NAME);
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index a70a36e..7c94d62 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -42,6 +42,7 @@ import org.apache.hadoop.hbase.util.Pair;
import org.apache.phoenix.call.CallRunner;
import org.apache.phoenix.compile.ColumnProjector;
import org.apache.phoenix.compile.ColumnResolver;
+import org.apache.phoenix.compile.CreateFunctionCompiler;
import org.apache.phoenix.compile.CreateIndexCompiler;
import org.apache.phoenix.compile.CreateSequenceCompiler;
import org.apache.phoenix.compile.CreateTableCompiler;
@@ -78,11 +79,13 @@ import org.apache.phoenix.parse.AlterSessionStatement;
import org.apache.phoenix.parse.BindableStatement;
import org.apache.phoenix.parse.ColumnDef;
import org.apache.phoenix.parse.ColumnName;
+import org.apache.phoenix.parse.CreateFunctionStatement;
import org.apache.phoenix.parse.CreateIndexStatement;
import org.apache.phoenix.parse.CreateSequenceStatement;
import org.apache.phoenix.parse.CreateTableStatement;
import org.apache.phoenix.parse.DeleteStatement;
import org.apache.phoenix.parse.DropColumnStatement;
+import org.apache.phoenix.parse.DropFunctionStatement;
import org.apache.phoenix.parse.DropIndexStatement;
import org.apache.phoenix.parse.DropSequenceStatement;
import org.apache.phoenix.parse.DropTableStatement;
@@ -94,6 +97,7 @@ import org.apache.phoenix.parse.LimitNode;
import org.apache.phoenix.parse.NamedNode;
import org.apache.phoenix.parse.NamedTableNode;
import org.apache.phoenix.parse.OrderByNode;
+import org.apache.phoenix.parse.PFunction;
import org.apache.phoenix.parse.ParseNode;
import org.apache.phoenix.parse.ParseNodeFactory;
import org.apache.phoenix.parse.PrimaryKeyConstraint;
@@ -102,6 +106,7 @@ import org.apache.phoenix.parse.SelectStatement;
import org.apache.phoenix.parse.TableName;
import org.apache.phoenix.parse.TableNode;
import org.apache.phoenix.parse.TraceStatement;
+import org.apache.phoenix.parse.UDFParseNode;
import org.apache.phoenix.parse.UpdateStatisticsStatement;
import org.apache.phoenix.parse.UpsertStatement;
import org.apache.phoenix.query.KeyRange;
@@ -332,19 +337,22 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
private static class ExecutableSelectStatement extends SelectStatement implements CompilableStatement {
private ExecutableSelectStatement(TableNode from, HintNode hint, boolean isDistinct, List<AliasedNode> select, ParseNode where,
- List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate, boolean hasSequence) {
- this(from, hint, isDistinct, select, where, groupBy, having, orderBy, limit, bindCount, isAggregate, hasSequence, Collections.<SelectStatement>emptyList());
+ List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate, boolean hasSequence, Map<String, UDFParseNode> udfParseNodes) {
+ this(from, hint, isDistinct, select, where, groupBy, having, orderBy, limit, bindCount, isAggregate, hasSequence, Collections.<SelectStatement>emptyList(), udfParseNodes);
}
private ExecutableSelectStatement(TableNode from, HintNode hint, boolean isDistinct, List<AliasedNode> select, ParseNode where,
List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate,
- boolean hasSequence, List<SelectStatement> selects) {
- super(from, hint, isDistinct, select, where, groupBy, having, orderBy, limit, bindCount, isAggregate, hasSequence, selects);
+ boolean hasSequence, List<SelectStatement> selects, Map<String, UDFParseNode> udfParseNodes) {
+ super(from, hint, isDistinct, select, where, groupBy, having, orderBy, limit, bindCount, isAggregate, hasSequence, selects, udfParseNodes);
}
@SuppressWarnings("unchecked")
@Override
public QueryPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+ if(!getUdfParseNodes().isEmpty()) {
+ stmt.throwIfUnallowedUserDefinedFunctions();
+ }
SelectStatement select = SubselectRewriter.flatten(this, stmt.getConnection());
ColumnResolver resolver = FromCompiler.getResolverForQuery(select, stmt.getConnection());
select = StatementNormalizer.normalize(select, resolver);
@@ -357,6 +365,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
plan.getContext().getSequenceManager().validateSequences(seqAction);
return plan;
}
+
}
private static final byte[] EXPLAIN_PLAN_FAMILY = QueryConstants.SINGLE_COLUMN_FAMILY;
@@ -505,13 +514,16 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
}
private static class ExecutableUpsertStatement extends UpsertStatement implements CompilableStatement {
- private ExecutableUpsertStatement(NamedTableNode table, HintNode hintNode, List<ColumnName> columns, List<ParseNode> values, SelectStatement select, int bindCount) {
- super(table, hintNode, columns, values, select, bindCount);
+ private ExecutableUpsertStatement(NamedTableNode table, HintNode hintNode, List<ColumnName> columns, List<ParseNode> values, SelectStatement select, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
+ super(table, hintNode, columns, values, select, bindCount, udfParseNodes);
}
@SuppressWarnings("unchecked")
@Override
public MutationPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+ if(!getUdfParseNodes().isEmpty()) {
+ stmt.throwIfUnallowedUserDefinedFunctions();
+ }
UpsertCompiler compiler = new UpsertCompiler(stmt);
MutationPlan plan = compiler.compile(this);
plan.getContext().getSequenceManager().validateSequences(seqAction);
@@ -520,13 +532,16 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
}
private static class ExecutableDeleteStatement extends DeleteStatement implements CompilableStatement {
- private ExecutableDeleteStatement(NamedTableNode table, HintNode hint, ParseNode whereNode, List<OrderByNode> orderBy, LimitNode limit, int bindCount) {
- super(table, hint, whereNode, orderBy, limit, bindCount);
+ private ExecutableDeleteStatement(NamedTableNode table, HintNode hint, ParseNode whereNode, List<OrderByNode> orderBy, LimitNode limit, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
+ super(table, hint, whereNode, orderBy, limit, bindCount, udfParseNodes);
}
@SuppressWarnings("unchecked")
@Override
public MutationPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+ if(!getUdfParseNodes().isEmpty()) {
+ stmt.throwIfUnallowedUserDefinedFunctions();
+ }
DeleteCompiler compiler = new DeleteCompiler(stmt);
MutationPlan plan = compiler.compile(this);
plan.getContext().getSequenceManager().validateSequences(seqAction);
@@ -549,16 +564,76 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
}
}
+ private static class ExecutableCreateFunctionStatement extends CreateFunctionStatement implements CompilableStatement {
+
+ public ExecutableCreateFunctionStatement(PFunction functionInfo, boolean temporary) {
+ super(functionInfo, temporary);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public MutationPlan compilePlan(final PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+ stmt.throwIfUnallowedUserDefinedFunctions();
+ CreateFunctionCompiler compiler = new CreateFunctionCompiler(stmt);
+ return compiler.compile(this);
+ }
+ }
+
+ private static class ExecutableDropFunctionStatement extends DropFunctionStatement implements CompilableStatement {
+
+ public ExecutableDropFunctionStatement(String functionName, boolean ifNotExists) {
+ super(functionName, ifNotExists);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public MutationPlan compilePlan(final PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+ final StatementContext context = new StatementContext(stmt);
+ return new MutationPlan() {
+
+ @Override
+ public StatementContext getContext() {
+ return context;
+ }
+
+ @Override
+ public ParameterMetaData getParameterMetaData() {
+ return PhoenixParameterMetaData.EMPTY_PARAMETER_META_DATA;
+ }
+
+ @Override
+ public ExplainPlan getExplainPlan() throws SQLException {
+ return new ExplainPlan(Collections.singletonList("DROP TABLE"));
+ }
+
+ @Override
+ public PhoenixConnection getConnection() {
+ return stmt.getConnection();
+ }
+
+ @Override
+ public MutationState execute() throws SQLException {
+ MetaDataClient client = new MetaDataClient(getConnection());
+ return client.dropFunction(ExecutableDropFunctionStatement.this);
+ }
+ };
+ }
+ }
+
private static class ExecutableCreateIndexStatement extends CreateIndexStatement implements CompilableStatement {
public ExecutableCreateIndexStatement(NamedNode indexName, NamedTableNode dataTable, IndexKeyConstraint ikConstraint, List<ColumnName> includeColumns, List<ParseNode> splits,
- ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, IndexType indexType, boolean async, int bindCount) {
- super(indexName, dataTable, ikConstraint, includeColumns, splits, props, ifNotExists, indexType, async , bindCount);
+ ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, IndexType indexType, boolean async, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
+ super(indexName, dataTable, ikConstraint, includeColumns, splits, props, ifNotExists, indexType, async , bindCount, udfParseNodes);
}
@SuppressWarnings("unchecked")
@Override
public MutationPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+ if(!getUdfParseNodes().isEmpty()) {
+ stmt.throwIfUnallowedUserDefinedFunctions();
+ }
CreateIndexCompiler compiler = new CreateIndexCompiler(stmt);
return compiler.compile(this);
}
@@ -908,19 +983,19 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
@Override
public ExecutableSelectStatement select(TableNode from, HintNode hint, boolean isDistinct, List<AliasedNode> select, ParseNode where,
List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate,
- boolean hasSequence, List<SelectStatement> selects) {
+ boolean hasSequence, List<SelectStatement> selects, Map<String, UDFParseNode> udfParseNodes) {
return new ExecutableSelectStatement(from, hint, isDistinct, select, where, groupBy == null ? Collections.<ParseNode>emptyList() : groupBy,
- having, orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate, hasSequence, selects == null ? Collections.<SelectStatement>emptyList() : selects);
+ having, orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate, hasSequence, selects == null ? Collections.<SelectStatement>emptyList() : selects, udfParseNodes);
}
@Override
- public ExecutableUpsertStatement upsert(NamedTableNode table, HintNode hintNode, List<ColumnName> columns, List<ParseNode> values, SelectStatement select, int bindCount) {
- return new ExecutableUpsertStatement(table, hintNode, columns, values, select, bindCount);
+ public ExecutableUpsertStatement upsert(NamedTableNode table, HintNode hintNode, List<ColumnName> columns, List<ParseNode> values, SelectStatement select, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
+ return new ExecutableUpsertStatement(table, hintNode, columns, values, select, bindCount, udfParseNodes);
}
@Override
- public ExecutableDeleteStatement delete(NamedTableNode table, HintNode hint, ParseNode whereNode, List<OrderByNode> orderBy, LimitNode limit, int bindCount) {
- return new ExecutableDeleteStatement(table, hint, whereNode, orderBy, limit, bindCount);
+ public ExecutableDeleteStatement delete(NamedTableNode table, HintNode hint, ParseNode whereNode, List<OrderByNode> orderBy, LimitNode limit, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
+ return new ExecutableDeleteStatement(table, hint, whereNode, orderBy, limit, bindCount, udfParseNodes);
}
@Override
@@ -938,14 +1013,18 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
}
@Override
+ public CreateFunctionStatement createFunction(PFunction functionInfo, boolean temporary) {
+ return new ExecutableCreateFunctionStatement(functionInfo, temporary);
+ }
+ @Override
public DropSequenceStatement dropSequence(TableName tableName, boolean ifExists, int bindCount){
return new ExecutableDropSequenceStatement(tableName, ifExists, bindCount);
}
@Override
public CreateIndexStatement createIndex(NamedNode indexName, NamedTableNode dataTable, IndexKeyConstraint ikConstraint, List<ColumnName> includeColumns, List<ParseNode> splits,
- ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, IndexType indexType, boolean async, int bindCount) {
- return new ExecutableCreateIndexStatement(indexName, dataTable, ikConstraint, includeColumns, splits, props, ifNotExists, indexType, async, bindCount);
+ ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, IndexType indexType, boolean async, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
+ return new ExecutableCreateIndexStatement(indexName, dataTable, ikConstraint, includeColumns, splits, props, ifNotExists, indexType, async, bindCount, udfParseNodes);
}
@Override
@@ -962,6 +1041,11 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
public DropTableStatement dropTable(TableName tableName, PTableType tableType, boolean ifExists, boolean cascade) {
return new ExecutableDropTableStatement(tableName, tableType, ifExists, cascade);
}
+
+ @Override
+ public DropFunctionStatement dropFunction(String functionName, boolean ifExists) {
+ return new ExecutableDropFunctionStatement(functionName, ifExists);
+ }
@Override
public DropIndexStatement dropIndex(NamedNode indexName, TableName tableName, boolean ifExists) {
@@ -1410,4 +1494,16 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
private void setLastQueryPlan(QueryPlan lastQueryPlan) {
this.lastQueryPlan = lastQueryPlan;
}
+
+ private void throwIfUnallowedUserDefinedFunctions() throws SQLException {
+ if (!connection
+ .getQueryServices()
+ .getProps()
+ .getBoolean(QueryServices.ALLOW_USER_DEFINED_FUNCTIONS_ATTRIB,
+ QueryServicesOptions.DEFAULT_ALLOW_USER_DEFINED_FUNCTIONS)) {
+ throw new SQLExceptionInfo.Builder(SQLExceptionCode.UNALLOWED_USER_DEFINED_FUNCTIONS)
+ .build().buildException();
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
index 7b3a63a..99ca46e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
@@ -235,7 +235,7 @@ public class QueryOptimizer {
if (PIndexState.ACTIVE.equals(resolver.getTables().get(0).getTable().getIndexState())) {
try {
// translate nodes that match expressions that are indexed to the associated column parse node
- indexSelect = ParseNodeRewriter.rewrite(indexSelect, new IndexExpressionParseNodeRewriter(index, statement.getConnection()));
+ indexSelect = ParseNodeRewriter.rewrite(indexSelect, new IndexExpressionParseNodeRewriter(index, statement.getConnection(), indexSelect.getUdfParseNodes()));
QueryCompiler compiler = new QueryCompiler(statement, indexSelect, resolver, targetColumns, parallelIteratorFactory, dataPlan.getContext().getSequenceManager(), isProjected);
QueryPlan plan = compiler.compile();
@@ -290,7 +290,7 @@ public class QueryOptimizer {
aliasedNodes.add(FACTORY.aliasedNode(null, indexColNode));
nodes.add(new ColumnParseNode(null, '"' + column.getName().getString() + '"'));
}
- SelectStatement innerSelect = FACTORY.select(indexSelect.getFrom(), indexSelect.getHint(), false, aliasedNodes, where, null, null, null, indexSelect.getLimit(), indexSelect.getBindCount(), false, indexSelect.hasSequence(), Collections.<SelectStatement>emptyList());
+ SelectStatement innerSelect = FACTORY.select(indexSelect.getFrom(), indexSelect.getHint(), false, aliasedNodes, where, null, null, null, indexSelect.getLimit(), indexSelect.getBindCount(), false, indexSelect.hasSequence(), Collections.<SelectStatement>emptyList(), indexSelect.getUdfParseNodes());
ParseNode outerWhere = FACTORY.in(nodes.size() == 1 ? nodes.get(0) : FACTORY.rowValueConstructor(nodes), FACTORY.subquery(innerSelect, false), false, true);
ParseNode extractedCondition = whereRewriter.getExtractedCondition();
if (extractedCondition != null) {
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java
new file mode 100644
index 0000000..741e4df
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.parse;
+
+public class CreateFunctionStatement extends MutableStatement {
+ private final PFunction functionInfo;
+ private final boolean temporary;
+
+ public CreateFunctionStatement(PFunction functionInfo, boolean temporary) {
+ this.functionInfo = functionInfo;
+ this.temporary = temporary;
+ }
+
+ @Override
+ public int getBindCount() {
+ return 0;
+ }
+
+ public PFunction getFunctionInfo() {
+ return functionInfo;
+ }
+
+ public boolean isTemporary() {
+ return temporary;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateIndexStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateIndexStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateIndexStatement.java
index 5f52f59..6da0ff3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateIndexStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateIndexStatement.java
@@ -19,6 +19,7 @@ package org.apache.phoenix.parse;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.phoenix.schema.PTable.IndexType;
@@ -36,10 +37,11 @@ public class CreateIndexStatement extends SingleTableStatement {
private final boolean ifNotExists;
private final IndexType indexType;
private final boolean async;
+ private final Map<String, UDFParseNode> udfParseNodes;
public CreateIndexStatement(NamedNode indexTableName, NamedTableNode dataTable,
IndexKeyConstraint indexKeyConstraint, List<ColumnName> includeColumns, List<ParseNode> splits,
- ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, IndexType indexType, boolean async, int bindCount) {
+ ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, IndexType indexType, boolean async, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
super(dataTable, bindCount);
this.indexTableName =TableName.create(dataTable.getName().getSchemaName(),indexTableName.getName());
this.indexKeyConstraint = indexKeyConstraint == null ? IndexKeyConstraint.EMPTY : indexKeyConstraint;
@@ -49,6 +51,7 @@ public class CreateIndexStatement extends SingleTableStatement {
this.ifNotExists = ifNotExists;
this.indexType = indexType;
this.async = async;
+ this.udfParseNodes = udfParseNodes;
}
public IndexKeyConstraint getIndexConstraint() {
@@ -84,4 +87,7 @@ public class CreateIndexStatement extends SingleTableStatement {
return async;
}
+ public Map<String, UDFParseNode> getUdfParseNodes() {
+ return udfParseNodes;
+ }
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/DMLStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/DMLStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/DMLStatement.java
index 46bd907..3b9bd97 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/DMLStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/DMLStatement.java
@@ -17,11 +17,18 @@
*/
package org.apache.phoenix.parse;
+import java.util.Map;
+
public class DMLStatement extends SingleTableStatement {
- public DMLStatement(NamedTableNode table, int bindCount) {
+ private final Map<String, UDFParseNode> udfParseNodes;
+
+ public DMLStatement(NamedTableNode table, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
super(table, bindCount);
+ this.udfParseNodes = udfParseNodes;
}
-
+ public Map<String, UDFParseNode> getUdfParseNodes() {
+ return udfParseNodes;
+ }
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteStatement.java
index d295e85..276e6aa 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteStatement.java
@@ -19,6 +19,7 @@ package org.apache.phoenix.parse;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import org.apache.phoenix.jdbc.PhoenixStatement.Operation;
@@ -28,8 +29,8 @@ public class DeleteStatement extends DMLStatement implements FilterableStatement
private final LimitNode limit;
private final HintNode hint;
- public DeleteStatement(NamedTableNode table, HintNode hint, ParseNode whereNode, List<OrderByNode> orderBy, LimitNode limit, int bindCount) {
- super(table, bindCount);
+ public DeleteStatement(NamedTableNode table, HintNode hint, ParseNode whereNode, List<OrderByNode> orderBy, LimitNode limit, int bindCount, Map<String, UDFParseNode> udfParseNodes) {
+ super(table, bindCount, udfParseNodes);
this.whereNode = whereNode;
this.orderBy = orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy;
this.limit = limit;
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/DropFunctionStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/DropFunctionStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/DropFunctionStatement.java
new file mode 100644
index 0000000..a959eb7
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/DropFunctionStatement.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.parse;
+
+public class DropFunctionStatement extends MutableStatement {
+
+ private final String functionName;
+ private final boolean ifExists;
+ public DropFunctionStatement(String functionName, boolean ifExists) {
+ this.functionName = functionName;
+ this.ifExists = ifExists;
+ }
+
+ @Override
+ public int getBindCount() {
+ return 0;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public boolean ifExists() {
+ return ifExists;
+ }
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
index 9764f52..bd13fb5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
@@ -38,6 +38,9 @@ import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.LiteralExpression;
import org.apache.phoenix.expression.function.AggregateFunction;
import org.apache.phoenix.expression.function.FunctionExpression;
+import org.apache.phoenix.expression.function.UDFExpression;
+import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.parse.PFunction.FunctionArgument;
import org.apache.phoenix.schema.ArgumentTypeMismatchException;
import org.apache.phoenix.schema.ValueRangeExcpetion;
import org.apache.phoenix.schema.types.PDataType;
@@ -58,7 +61,7 @@ import com.google.common.collect.ImmutableSet;
*/
public class FunctionParseNode extends CompoundParseNode {
private final String name;
- private final BuiltInFunctionInfo info;
+ private BuiltInFunctionInfo info;
FunctionParseNode(String name, List<ParseNode> children, BuiltInFunctionInfo info) {
super(children);
@@ -84,6 +87,7 @@ public class FunctionParseNode extends CompoundParseNode {
}
public boolean isAggregate() {
+ if(getInfo()==null) return false;
return getInfo().isAggregate();
}
@@ -113,9 +117,17 @@ public class FunctionParseNode extends CompoundParseNode {
}
private static Constructor<? extends FunctionExpression> getExpressionCtor(Class<? extends FunctionExpression> clazz) {
+ return getExpressionCtor(clazz, null);
+ }
+
+ private static Constructor<? extends FunctionExpression> getExpressionCtor(Class<? extends FunctionExpression> clazz, PFunction function) {
Constructor<? extends FunctionExpression> ctor;
try {
- ctor = clazz.getDeclaredConstructor(List.class);
+ if(function == null) {
+ ctor = clazz.getDeclaredConstructor(List.class);
+ } else {
+ ctor = clazz.getDeclaredConstructor(List.class, PFunction.class);
+ }
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -223,8 +235,24 @@ public class FunctionParseNode extends CompoundParseNode {
* @throws SQLException
*/
public Expression create(List<Expression> children, StatementContext context) throws SQLException {
+ return create(children, null, context);
+ }
+
+ /**
+ * Entry point for parser to instantiate compiled representation of built-in function
+ * @param children Compiled expressions for child nodes
+ * @param function
+ * @param context Query context for accessing state shared across the processing of multiple clauses
+ * @return compiled representation of built-in function
+ * @throws SQLException
+ */
+ public Expression create(List<Expression> children, PFunction function, StatementContext context) throws SQLException {
try {
- return info.getFuncCtor().newInstance(children);
+ if(function == null) {
+ return info.getFuncCtor().newInstance(children);
+ } else {
+ return info.getFuncCtor().newInstance(children, function);
+ }
} catch (InstantiationException e) {
throw new SQLException(e);
} catch (IllegalAccessException e) {
@@ -275,9 +303,9 @@ public class FunctionParseNode extends CompoundParseNode {
private final boolean isAggregate;
private final int requiredArgCount;
- BuiltInFunctionInfo(Class<? extends FunctionExpression> f, BuiltInFunction d) {
+ public BuiltInFunctionInfo(Class<? extends FunctionExpression> f, BuiltInFunction d) {
this.name = SchemaUtil.normalizeIdentifier(d.name());
- this.funcCtor = d.nodeClass() == FunctionParseNode.class ? getExpressionCtor(f) : null;
+ this.funcCtor = d.nodeClass() == FunctionParseNode.class ? getExpressionCtor(f, null) : null;
this.nodeCtor = d.nodeClass() == FunctionParseNode.class ? null : getParseNodeCtor(d.nodeClass());
this.args = new BuiltInFunctionArgInfo[d.args().length];
int requiredArgCount = 0;
@@ -291,6 +319,22 @@ public class FunctionParseNode extends CompoundParseNode {
this.isAggregate = AggregateFunction.class.isAssignableFrom(f);
}
+ public BuiltInFunctionInfo(PFunction function) {
+ this.name = SchemaUtil.normalizeIdentifier(function.getFunctionName());
+ this.funcCtor = getExpressionCtor(UDFExpression.class, function);
+ this.nodeCtor = getParseNodeCtor(UDFParseNode.class);
+ this.args = new BuiltInFunctionArgInfo[function.getFunctionArguments().size()];
+ int requiredArgCount = 0;
+ for (int i = 0; i < args.length; i++) {
+ this.args[i] = new BuiltInFunctionArgInfo(function.getFunctionArguments().get(i));
+ if (this.args[i].getDefaultValue() == null) {
+ requiredArgCount = i + 1;
+ }
+ }
+ this.requiredArgCount = requiredArgCount;
+ this.isAggregate = AggregateFunction.class.isAssignableFrom(UDFExpression.class);
+ }
+
public int getRequiredArgCount() {
return requiredArgCount;
}
@@ -365,6 +409,27 @@ public class FunctionParseNode extends CompoundParseNode {
}
}
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ BuiltInFunctionArgInfo(FunctionArgument arg) {
+ PDataType dataType =
+ arg.isArrayType() ? PDataType.fromTypeId(PDataType.sqlArrayType(SchemaUtil
+ .normalizeIdentifier(SchemaUtil.normalizeIdentifier(arg
+ .getArgumentType())))) : PDataType.fromSqlTypeName(SchemaUtil
+ .normalizeIdentifier(arg.getArgumentType()));
+ this.allowedValues = Collections.emptySet();
+ this.allowedTypes = new Class[] { dataType.getClass() };
+ this.isConstant = arg.isConstant();
+ this.defaultValue =
+ arg.getDefaultValue() == null ? null : getExpFromConstant((String) arg
+ .getDefaultValue().getValue());
+ this.minValue =
+ arg.getMinValue() == null ? null : getExpFromConstant((String) arg
+ .getMinValue().getValue());
+ this.maxValue =
+ arg.getMaxValue() == null ? null : getExpFromConstant((String) arg
+ .getMaxValue().getValue());
+ }
+
private LiteralExpression getExpFromConstant(String strValue) {
LiteralExpression exp = null;
if (strValue.length() > 0) {
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/IndexExpressionParseNodeRewriter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/IndexExpressionParseNodeRewriter.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/IndexExpressionParseNodeRewriter.java
index 0273041..9f7b2bf 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/IndexExpressionParseNodeRewriter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/IndexExpressionParseNodeRewriter.java
@@ -37,12 +37,12 @@ public class IndexExpressionParseNodeRewriter extends ParseNodeRewriter {
private final Map<ParseNode, ParseNode> indexedParseNodeToColumnParseNodeMap;
- public IndexExpressionParseNodeRewriter(PTable index, PhoenixConnection connection) throws SQLException {
+ public IndexExpressionParseNodeRewriter(PTable index, PhoenixConnection connection, Map<String, UDFParseNode> udfParseNodes) throws SQLException {
indexedParseNodeToColumnParseNodeMap = Maps.newHashMapWithExpectedSize(index.getColumns().size());
NamedTableNode tableNode = NamedTableNode.create(null,
TableName.create(index.getParentSchemaName().getString(), index.getParentTableName().getString()),
Collections.<ColumnDef> emptyList());
- ColumnResolver dataResolver = FromCompiler.getResolver(tableNode, connection);
+ ColumnResolver dataResolver = FromCompiler.getResolver(tableNode, connection, udfParseNodes);
StatementContext context = new StatementContext(new PhoenixStatement(connection), dataResolver);
IndexStatementRewriter rewriter = new IndexStatementRewriter(dataResolver, null);
ExpressionCompiler expressionCompiler = new ExpressionCompiler(context);
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedNode.java
index 3f1becc..1957f0e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedNode.java
@@ -27,7 +27,7 @@ public class NamedNode {
public static NamedNode caseSensitiveNamedNode(String name) {
return new NamedNode(name,true);
}
-
+
NamedNode(String name, boolean isCaseSensitive) {
this.name = name;
this.isCaseSensitive = isCaseSensitive;
http://git-wip-us.apache.org/repos/asf/phoenix/blob/66bd3e35/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java
new file mode 100644
index 0000000..351bec7
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.parse;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.phoenix.coprocessor.generated.PFunctionProtos;
+import org.apache.phoenix.coprocessor.generated.PFunctionProtos.PFunctionArg;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.schema.PMetaDataEntity;
+import org.apache.phoenix.schema.PName;
+import org.apache.phoenix.schema.PNameFactory;
+import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.util.SizedUtil;
+
+import com.google.protobuf.HBaseZeroCopyByteString;
+
+public class PFunction implements PMetaDataEntity {
+
+ private PName tenantId = null;
+ private final PName functionName;
+ private List<FunctionArgument> args;
+ private PName className;
+ private PName jarPath;
+ private PName returnType;
+ private PTableKey functionKey;
+ private long timeStamp;
+ private int estimatedSize;
+ private boolean temporary;
+
+ public PFunction(long timeStamp) { // For index delete marker
+ this.timeStamp = timeStamp;
+ this.args = Collections.emptyList();
+ this.functionName = null;
+ }
+
+ public PFunction(String functionName, List<FunctionArgument> args, String returnType,
+ String className, String jarPath) {
+ this(functionName,args,returnType,className, jarPath, HConstants.LATEST_TIMESTAMP);
+ }
+
+ public PFunction(String functionName, List<FunctionArgument> args, String returnType,
+ String className, String jarPath, long timeStamp) {
+ this(null, functionName, args, returnType, className, jarPath, timeStamp);
+ }
+
+ public PFunction(PName tenantId, String functionName, List<FunctionArgument> args, String returnType,
+ String className, String jarPath, long timeStamp) {
+ this(tenantId, functionName, args, returnType, className, jarPath, timeStamp, false);
+ }
+
+ public PFunction(PFunction function, boolean temporary) {
+ this(function.getTenantId(), function.getFunctionName(), function.getFunctionArguments(),
+ function.getReturnType(), function.getClassName(), function.getJarPath(), function
+ .getTimeStamp(), temporary);
+ }
+
+ public PFunction(PName tenantId, String functionName, List<FunctionArgument> args, String returnType,
+ String className, String jarPath, long timeStamp, boolean temporary) {
+ this.tenantId = tenantId;
+ this.functionName = PNameFactory.newName(functionName);
+ if (args == null){
+ this.args = new ArrayList<FunctionArgument>();
+ } else {
+ this.args = args;
+ }
+ this.className = PNameFactory.newName(className);
+ this.jarPath = jarPath == null ? null : PNameFactory.newName(jarPath);
+ this.returnType = PNameFactory.newName(returnType);
+ this.functionKey = new PTableKey(this.tenantId, this.functionName.getString());
+ this.timeStamp = timeStamp;
+ int estimatedSize = SizedUtil.OBJECT_SIZE * 2 + 23 * SizedUtil.POINTER_SIZE + 4 * SizedUtil.INT_SIZE + 2 * SizedUtil.LONG_SIZE + 2 * SizedUtil.INT_OBJECT_SIZE +
+ PNameFactory.getEstimatedSize(tenantId) +
+ PNameFactory.getEstimatedSize(this.functionName) +
+ PNameFactory.getEstimatedSize(this.className) +
+ (jarPath==null?0:PNameFactory.getEstimatedSize(this.jarPath));
+ this.temporary = temporary;
+ }
+
+ public PFunction(PFunction function) {
+ this(function.getTenantId(), function.getFunctionName(), function.getFunctionArguments(),
+ function.getReturnType(), function.getClassName(), function.getJarPath(), function
+ .getTimeStamp());
+ }
+
+ public String getFunctionName() {
+ return functionName == null ? null : functionName.getString();
+ }
+
+ public List<FunctionArgument> getFunctionArguments() {
+ return args;
+ }
+
+ public String getClassName() {
+ return className.getString();
+ }
+
+ public String getJarPath() {
+ return jarPath == null ? null : jarPath.getString();
+ }
+
+ public String getReturnType() {
+ return returnType.getString();
+ }
+
+ public PTableKey getKey() {
+ return this.functionKey;
+ }
+
+ public long getTimeStamp() {
+ return this.timeStamp;
+ }
+
+ public PName getTenantId() {
+ return this.tenantId;
+ }
+
+ public boolean isTemporaryFunction() {
+ return temporary;
+ }
+
+ public static class FunctionArgument {
+ private final PName argumentType;
+ private final boolean isArrayType;
+ private final boolean isConstant;
+ private final LiteralExpression defaultValue;
+ private final LiteralExpression minValue;
+ private final LiteralExpression maxValue;
+ private short argPosition;
+
+ public FunctionArgument(String argumentType, boolean isArrayType, boolean isConstant, LiteralExpression defaultValue,
+ LiteralExpression minValue, LiteralExpression maxValue) {
+ this.argumentType = PNameFactory.newName(argumentType);
+ this.isArrayType = isArrayType;
+ this.isConstant = isConstant;
+ this.defaultValue = defaultValue;
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ }
+ public FunctionArgument(String argumentType, boolean isArrayType, boolean isConstant, LiteralExpression defaultValue,
+ LiteralExpression minValue, LiteralExpression maxValue, short argPosition) {
+ this(argumentType, isArrayType, isConstant, defaultValue, minValue, maxValue);
+ this.argPosition = argPosition;
+ }
+
+ public String getArgumentType() {
+ return argumentType.getString();
+ }
+
+ public boolean isConstant() {
+ return isConstant;
+ }
+
+ public boolean isArrayType() {
+ return isArrayType;
+ }
+
+ public LiteralExpression getDefaultValue() {
+ return defaultValue;
+ }
+
+ public LiteralExpression getMinValue() {
+ return minValue;
+ }
+
+ public LiteralExpression getMaxValue() {
+ return maxValue;
+ }
+
+ public short getArgPosition() {
+ return argPosition;
+ }
+ }
+
+ public static PFunctionProtos.PFunction toProto(PFunction function) {
+ PFunctionProtos.PFunction.Builder builder = PFunctionProtos.PFunction.newBuilder();
+ if(function.getTenantId() != null){
+ builder.setTenantId(HBaseZeroCopyByteString.wrap(function.getTenantId().getBytes()));
+ }
+ builder.setFunctionName(function.getFunctionName());
+ builder.setClassname(function.getClassName());
+ if (function.getJarPath() != null) {
+ builder.setJarPath(function.getJarPath());
+ }
+ builder.setReturnType(function.getReturnType());
+ builder.setTimeStamp(function.getTimeStamp());
+ for(FunctionArgument arg: function.getFunctionArguments()) {
+ PFunctionProtos.PFunctionArg.Builder argBuilder = PFunctionProtos.PFunctionArg.newBuilder();
+ argBuilder.setArgumentType(arg.getArgumentType());
+ argBuilder.setIsArrayType(arg.isArrayType);
+ argBuilder.setIsConstant(arg.isConstant);
+ if(arg.getDefaultValue() != null) {
+ argBuilder.setDefaultValue((String)arg.getDefaultValue().getValue());
+ }
+ if(arg.getMinValue() != null) {
+ argBuilder.setMinValue((String)arg.getMinValue().getValue());
+ }
+ if(arg.getMaxValue() != null) {
+ argBuilder.setMaxValue((String)arg.getMaxValue().getValue());
+ }
+ builder.addArguments(argBuilder.build());
+ }
+ return builder.build();
+ }
+
+ public static PFunction createFromProto(
+ org.apache.phoenix.coprocessor.generated.PFunctionProtos.PFunction function) {
+ PName tenantId = null;
+ if(function.hasTenantId()){
+ tenantId = PNameFactory.newName(function.getTenantId().toByteArray());
+ }
+ String functionName = function.getFunctionName();
+ long timeStamp = function.getTimeStamp();
+ String className = function.getClassname();
+ String jarPath = function.getJarPath();
+ String returnType = function.getReturnType();
+ List<FunctionArgument> args = new ArrayList<FunctionArgument>(function.getArgumentsCount());
+ for(PFunctionArg arg: function.getArgumentsList()) {
+ String argType = arg.getArgumentType();
+ boolean isArrayType = arg.hasIsArrayType()?arg.getIsArrayType():false;
+ boolean isConstant = arg.hasIsConstant()?arg.getIsConstant():false;
+ String defaultValue = arg.hasDefaultValue()?arg.getDefaultValue():null;
+ String minValue = arg.hasMinValue()?arg.getMinValue():null;
+ String maxValue = arg.hasMaxValue()?arg.getMaxValue():null;
+ args.add(new FunctionArgument(argType, isArrayType, isConstant,
+ defaultValue == null ? null : LiteralExpression.newConstant(defaultValue),
+ minValue == null ? null : LiteralExpression.newConstant(minValue),
+ maxValue == null ? null : LiteralExpression.newConstant(maxValue)));
+ }
+ return new PFunction(tenantId,functionName, args, returnType, className, jarPath, timeStamp);
+ }
+
+ public int getEstimatedSize() {
+ return estimatedSize;
+ }
+}
+