http://git-wip-us.apache.org/repos/asf/phoenix/blob/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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 031e8c3..05bb7ec 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; @@ -226,7 +227,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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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 663a4a8..23add8f 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 @@ -63,6 +63,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; @@ -117,7 +118,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; @@ -228,7 +229,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) { @@ -238,8 +239,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 @@ -764,6 +773,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) @@ -783,6 +804,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/fa26c1cd/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/fa26c1cd/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 ca110fc..51fb027 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 @@ -41,6 +41,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; @@ -76,11 +77,13 @@ import org.apache.phoenix.parse.AlterIndexStatement; 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; @@ -92,6 +95,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; @@ -100,6 +104,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; @@ -329,19 +334,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); @@ -354,6 +362,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; @@ -502,13 +511,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); @@ -517,13 +529,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); @@ -546,16 +561,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); } @@ -857,19 +932,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 @@ -887,14 +962,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 @@ -911,6 +990,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) { @@ -1354,4 +1438,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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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/fa26c1cd/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; + } +} +