PHOENIX-1978 UDF ArgumentTypeMismatchException(Rajeshbabu)
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/b3ed60bb Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/b3ed60bb Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/b3ed60bb Branch: refs/heads/json Commit: b3ed60bb935a09c3ed07a6d77502136c9b8a6eef Parents: e54c99d Author: Rajeshbabu Chintaguntla <rajeshb...@apache.org> Authored: Fri Jun 5 09:02:31 2015 +0530 Committer: Rajeshbabu Chintaguntla <rajeshb...@apache.org> Committed: Fri Jun 5 09:02:31 2015 +0530 ---------------------------------------------------------------------- .../phoenix/end2end/UserDefinedFunctionsIT.java | 58 ++++++++++++++++++-- phoenix-core/src/main/antlr3/PhoenixSQL.g | 17 +++--- 2 files changed, 61 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/b3ed60bb/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java index 868e19d..c6bd62f 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java @@ -58,6 +58,8 @@ import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.schema.FunctionAlreadyExistsException; import org.apache.phoenix.schema.FunctionNotFoundException; import org.apache.phoenix.schema.ValueRangeExcpetion; +import org.apache.phoenix.schema.types.PDataType; +import org.apache.phoenix.schema.types.PArrayDataType; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; @@ -121,11 +123,31 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{ .append(" ptr.set(PInteger.INSTANCE.toBytes((Integer)sum));\n") .append(" return true;\n") .append(" }\n").toString(); - + private static String ARRAY_INDEX_EVALUATE_METHOD = + new StringBuffer() + .append(" public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {\n") + .append(" Expression indexExpr = children.get(1);\n") + .append(" if (!indexExpr.evaluate(tuple, ptr)) {\n") + .append(" return false;\n") + .append(" } else if (ptr.getLength() == 0) {\n") + .append(" return true;\n") + .append(" }\n") + .append(" // Use Codec to prevent Integer object allocation\n") + .append(" int index = PInteger.INSTANCE.getCodec().decodeInt(ptr, indexExpr.getSortOrder());\n") + .append(" if(index < 0) {\n") + .append(" throw new ParseException(\"Index cannot be negative :\" + index);\n") + .append(" }\n") + .append(" Expression arrayExpr = children.get(0);\n") + .append(" return PArrayDataType.positionAtArrayElement(tuple, ptr, index, arrayExpr, getDataType(),getMaxLength());\n") + .append(" }\n").toString(); + + private static String MY_REVERSE_CLASS_NAME = "MyReverse"; private static String MY_SUM_CLASS_NAME = "MySum"; - private static String MY_REVERSE_PROGRAM = getProgram(MY_REVERSE_CLASS_NAME, STRING_REVERSE_EVALUATE_METHOD, "PVarchar"); - private static String MY_SUM_PROGRAM = getProgram(MY_SUM_CLASS_NAME, SUM_COLUMN_VALUES_EVALUATE_METHOD, "PInteger"); + private static String MY_ARRAY_INDEX_CLASS_NAME = "MyArrayIndex"; + private static String MY_REVERSE_PROGRAM = getProgram(MY_REVERSE_CLASS_NAME, STRING_REVERSE_EVALUATE_METHOD, "return PVarchar.INSTANCE;"); + private static String MY_SUM_PROGRAM = getProgram(MY_SUM_CLASS_NAME, SUM_COLUMN_VALUES_EVALUATE_METHOD, "return PInteger.INSTANCE;"); + private static String MY_ARRAY_INDEX_PROGRAM = getProgram(MY_ARRAY_INDEX_CLASS_NAME, ARRAY_INDEX_EVALUATE_METHOD, "return PDataType.fromTypeId(children.get(0).getDataType().getSqlType()- PDataType.ARRAY_TYPE_BASE);"); private static Properties EMPTY_PROPS = new Properties(); @@ -144,6 +166,8 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{ .append("import org.apache.phoenix.schema.types.PInteger;\n") .append("import org.apache.phoenix.schema.types.PVarchar;\n") .append("import org.apache.phoenix.util.StringUtil;\n") + .append("import org.apache.phoenix.schema.types.PArrayDataType;\n") + .append("import org.apache.phoenix.parse.ParseException;\n") .append("public class "+className+" extends ScalarFunction{\n") .append(" public static final String NAME = \"MY_REVERSE\";\n") .append(" public "+className+"() {\n") @@ -159,7 +183,7 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{ .append(" }\n") .append(" @Override\n") .append(" public PDataType getDataType() {\n") - .append(" return "+returnType+".INSTANCE;\n") + .append(returnType+"\n") .append(" }\n") .append(" @Override\n") .append(" public String getName() {\n") @@ -181,6 +205,8 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{ UDFExpression.setConfig(conf); compileTestClass(MY_REVERSE_CLASS_NAME, MY_REVERSE_PROGRAM, 1); compileTestClass(MY_SUM_CLASS_NAME, MY_SUM_PROGRAM, 2); + compileTestClass(MY_ARRAY_INDEX_CLASS_NAME, MY_ARRAY_INDEX_PROGRAM, 3); + String clientPort = util.getConfiguration().get(QueryServices.ZOOKEEPER_PORT_ATTRIB); url = JDBC_PROTOCOL + JDBC_PROTOCOL_SEPARATOR + LOCALHOST + JDBC_PROTOCOL_SEPARATOR @@ -265,6 +291,28 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{ } catch(FunctionNotFoundException e) { } + conn.createStatement().execute("CREATE TABLE TESTTABLE10(ID VARCHAR NOT NULL, NAME VARCHAR ARRAY, CITY VARCHAR ARRAY CONSTRAINT pk PRIMARY KEY (ID) )"); + conn.createStatement().execute("create function UDF_ARRAY_ELEM(VARCHAR ARRAY, INTEGER) returns VARCHAR as 'org.apache.phoenix.end2end."+MY_ARRAY_INDEX_CLASS_NAME+"' using jar " + + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar3.jar"+"'"); + conn.createStatement().execute("UPSERT INTO TESTTABLE10(ID,NAME,CITY) VALUES('111', ARRAY['JOHN','MIKE','BOB'], ARRAY['NYC','LA','SF'])"); + conn.createStatement().execute("UPSERT INTO TESTTABLE10(ID,NAME,CITY) VALUES('112', ARRAY['CHEN','CARL','ALICE'], ARRAY['BOSTON','WASHINGTON','PALO ALTO'])"); + conn.commit(); + rs = conn.createStatement().executeQuery("SELECT ID, UDF_ARRAY_ELEM(NAME, 2) FROM TESTTABLE10"); + assertTrue(rs.next()); + assertEquals("111", rs.getString(1)); + assertEquals("MIKE", rs.getString(2)); + assertTrue(rs.next()); + assertEquals("112", rs.getString(1)); + assertEquals("CARL", rs.getString(2)); + assertFalse(rs.next()); + rs = conn2.createStatement().executeQuery("SELECT ID, UDF_ARRAY_ELEM(NAME, 2) FROM TESTTABLE10"); + assertTrue(rs.next()); + assertEquals("111", rs.getString(1)); + assertEquals("MIKE", rs.getString(2)); + assertTrue(rs.next()); + assertEquals("112", rs.getString(1)); + assertEquals("CARL", rs.getString(2)); + assertFalse(rs.next()); } @Test @@ -326,7 +374,7 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{ rs = tenant2Conn.createStatement().executeQuery("select * from t7 where MYFUNCTION(k1)=12"); assertTrue(rs.next()); assertEquals(1, rs.getInt(1)); - assertEquals(2, rs.getInt(2)); + assertEquals(2, rs.getInt(2)); assertEquals("jock", rs.getString(3)); assertFalse(rs.next()); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/b3ed60bb/phoenix-core/src/main/antlr3/PhoenixSQL.g ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g index d2bb241..4f7cb34 100644 --- a/phoenix-core/src/main/antlr3/PhoenixSQL.g +++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g @@ -917,17 +917,16 @@ zero_or_more_expressions returns [List<ParseNode> ret] zero_or_more_data_types returns [List<FunctionArgument> ret] @init{ret = new ArrayList<FunctionArgument>(); } - : (dt = identifier (LPAREN l=NUMBER (COMMA s=NUMBER)? RPAREN)? ar=ARRAY? (lsq=LSQUARE (a=NUMBER)? RSQUARE)? (c = CONSTANT)? (DEFAULTVALUE EQ dv = value_expression)? (MINVALUE EQ minv = value_expression)? (MAXVALUE EQ maxv = value_expression)? - {$ret.add(new FunctionArgument(dt, ar != null || lsq != null, c!=null, - dv == null ? null : LiteralExpression.newConstant(((LiteralParseNode)dv).getValue()), - minv == null ? null : LiteralExpression.newConstant(((LiteralParseNode)minv).getValue()), - maxv == null ? null : LiteralExpression.newConstant(((LiteralParseNode)maxv).getValue())));})? (COMMA (dt = identifier (LPAREN l=NUMBER (COMMA s=NUMBER)? RPAREN)? ar=ARRAY? (lsq=LSQUARE (a=NUMBER)? RSQUARE)? (c = CONSTANT)? (DEFAULTVALUE EQ dv = value_expression)? (MINVALUE EQ minv = value_expression)? (MAXVALUE EQ maxv = value_expression)? - {$ret.add(new FunctionArgument(dt, ar != null || lsq != null, c!=null, + : (fa = function_argument {$ret.add(fa);})? (COMMA fa = function_argument {$ret.add(fa);})* + ; + +function_argument returns [FunctionArgument ret] + : (dt = identifier (LPAREN l=NUMBER (COMMA s=NUMBER)? RPAREN)? ar=ARRAY? (lsq=LSQUARE (a=NUMBER)? RSQUARE)? (c = CONSTANT)? (DEFAULTVALUE EQ dv = value_expression)? (MINVALUE EQ minv = value_expression)? (MAXVALUE EQ maxv = value_expression)? + { $ret = new FunctionArgument(dt, ar != null || lsq != null, c!=null, dv == null ? null : LiteralExpression.newConstant(((LiteralParseNode)dv).getValue()), minv == null ? null : LiteralExpression.newConstant(((LiteralParseNode)minv).getValue()), - maxv == null ? null : LiteralExpression.newConstant(((LiteralParseNode)maxv).getValue())));} ))* -; - + maxv == null ? null : LiteralExpression.newConstant(((LiteralParseNode)maxv).getValue()));}) + ; value_expression_list returns [List<ParseNode> ret] @init{ret = new ArrayList<ParseNode>(); } : LPAREN e = value_expression {$ret.add(e);} (COMMA e = value_expression {$ret.add(e);} )* RPAREN