diff --git hplsql/pom.xml hplsql/pom.xml index fc1c527..73dd47f 100644 --- hplsql/pom.xml +++ hplsql/pom.xml @@ -79,6 +79,12 @@ ${junit.version} test + + org.apache.hive + hive-jdbc + 1.2.1 + test + diff --git hplsql/src/main/antlr4/org/apache/hive/hplsql/Hplsql.g4 hplsql/src/main/antlr4/org/apache/hive/hplsql/Hplsql.g4 index 852716b..70312b2 100644 --- hplsql/src/main/antlr4/org/apache/hive/hplsql/Hplsql.g4 +++ hplsql/src/main/antlr4/org/apache/hive/hplsql/Hplsql.g4 @@ -33,6 +33,8 @@ single_block_stmt : // Single BEGIN END blo stmt : assignment_stmt + | allocate_cursor_stmt + | associate_locator_stmt | break_stmt | call_stmt | close_stmt @@ -117,6 +119,14 @@ assignment_stmt_select_item : (ident | (T_OPEN_P ident (T_COMMA ident)* T_CLOSE_P)) T_COLON? T_EQUAL T_OPEN_P select_stmt T_CLOSE_P ; +allocate_cursor_stmt: + T_ALLOCATE ident T_CURSOR T_FOR ((T_RESULT T_SET) | T_PROCEDURE) ident + ; + +associate_locator_stmt : + T_ASSOCIATE (T_RESULT T_SET)? (T_LOCATOR | T_LOCATORS) T_OPEN_P ident (T_COMMA ident)* T_CLOSE_P T_WITH T_PROCEDURE ident + ; + break_stmt : T_BREAK ; @@ -151,12 +161,15 @@ declare_condition_item : // Condition declaration ; declare_cursor_item : // Cursor declaration - (T_CURSOR ident | ident T_CURSOR) declare_cursor_return? (T_IS | T_AS | T_FOR) (select_stmt | expr ) + (T_CURSOR ident | ident T_CURSOR) (cursor_with_return | cursor_without_return)? (T_IS | T_AS | T_FOR) (select_stmt | expr ) + ; + +cursor_with_return : + T_WITH T_RETURN T_ONLY? (T_TO (T_CALLER | T_CLIENT))? ; -declare_cursor_return : +cursor_without_return : T_WITHOUT T_RETURN - | T_WITH T_RETURN T_ONLY? (T_TO (T_CALLER | T_CLIENT))? ; declare_handler_item : // Condition handler declaration @@ -238,6 +251,7 @@ dtype : // Data types | T_INT | T_INTEGER | T_NUMBER + | T_RESULT_SET_LOCATOR T_VARYING | T_SMALLINT | T_STRING | T_TIMESTAMP @@ -261,7 +275,7 @@ dtype_default : // Default clause in variable declaration ; create_function_stmt : - (T_ALTER | T_CREATE (T_OR T_REPLACE)? | T_REPLACE) T_FUNCTION ident create_routine_params create_function_return (T_AS | T_IS)? single_block_stmt + (T_ALTER | T_CREATE (T_OR T_REPLACE)? | T_REPLACE) T_FUNCTION ident create_routine_params? create_function_return (T_AS | T_IS)? single_block_stmt ; create_function_return : @@ -269,7 +283,7 @@ create_function_return : ; create_procedure_stmt : - (T_ALTER | T_CREATE (T_OR T_REPLACE)? | T_REPLACE) (T_PROCEDURE | T_PROC) ident create_routine_params create_routine_options? (T_AS | T_IS)? label? single_block_stmt (ident T_SEMICOLON)? + (T_ALTER | T_CREATE (T_OR T_REPLACE)? | T_REPLACE) (T_PROCEDURE | T_PROC) ident create_routine_params? create_routine_options? (T_AS | T_IS)? label? single_block_stmt (ident T_SEMICOLON)? ; create_routine_params : @@ -287,7 +301,7 @@ create_routine_options : create_routine_option : T_LANGUAGE T_SQL | T_SQL T_SECURITY (T_CREATOR | T_DEFINER | T_INVOKER | T_OWNER) - | T_DYNAMIC T_RESULT T_SETS L_INT + | T_DYNAMIC? T_RESULT T_SETS L_INT ; drop_stmt : // DROP statement @@ -886,10 +900,12 @@ null_const : // NULL constant non_reserved_words : // Tokens that are not reserved words and can be used as identifiers T_ACTIVITY_COUNT | T_ALL + | T_ALLOCATE | T_ALTER | T_AND | T_AS - | T_ASC + | T_ASC + | T_ASSOCIATE | T_AT | T_AVG | T_BATCHSIZE @@ -1004,6 +1020,8 @@ non_reserved_words : // Tokens that are not reserved words | T_LIMIT | T_LINES | T_LOCAL + | T_LOCATOR + | T_LOCATORS | T_LOGGED | T_LOOP | T_MAP @@ -1042,6 +1060,7 @@ non_reserved_words : // Tokens that are not reserved words | T_REPLACE | T_RESIGNAL | T_RESULT + | T_RESULT_SET_LOCATOR | T_RETURN | T_RETURNS | T_REVERSE @@ -1092,6 +1111,7 @@ non_reserved_words : // Tokens that are not reserved words | T_VAR | T_VARCHAR | T_VARCHAR2 + | T_VARYING | T_VARIANCE | T_VOLATILE // T_WHEN reserved word @@ -1104,10 +1124,12 @@ non_reserved_words : // Tokens that are not reserved words // Lexer rules T_ALL : A L L ; +T_ALLOCATE : A L L O C A T E ; T_ALTER : A L T E R ; T_AND : A N D ; T_AS : A S ; T_ASC : A S C ; +T_ASSOCIATE : A S S O C I A T E ; T_AT : A T ; T_AVG : A V G ; T_BATCHSIZE : B A T C H S I Z E ; @@ -1214,6 +1236,8 @@ T_LIKE : L I K E ; T_LIMIT : L I M I T ; T_LINES : L I N E S ; T_LOCAL : L O C A L ; +T_LOCATOR : L O C A T O R ; +T_LOCATORS : L O C A T O R S ; T_LOGGED : L O G G E D ; T_LOOP : L O O P ; T_MAP : M A P ; @@ -1249,6 +1273,7 @@ T_REGEXP : R E G E X P ; T_REPLACE : R E P L A C E ; T_RESIGNAL : R E S I G N A L ; T_RESULT : R E S U L T ; +T_RESULT_SET_LOCATOR : R E S U L T '_' S E T '_' L O C A T O R ; T_RETURN : R E T U R N ; T_RETURNS : R E T U R N S ; T_REVERSE : R E V E R S E ; @@ -1296,6 +1321,7 @@ T_VALUES : V A L U E S ; T_VAR : V A R ; T_VARCHAR : V A R C H A R ; T_VARCHAR2 : V A R C H A R '2' ; +T_VARYING : V A R Y I N G ; T_VOLATILE : V O L A T I L E ; T_WHEN : W H E N ; T_WHERE : W H E R E ; diff --git hplsql/src/main/java/org/apache/hive/hplsql/Conn.java hplsql/src/main/java/org/apache/hive/hplsql/Conn.java index 828fbc3..ac4b521 100644 --- hplsql/src/main/java/org/apache/hive/hplsql/Conn.java +++ hplsql/src/main/java/org/apache/hive/hplsql/Conn.java @@ -41,10 +41,12 @@ Exec exec; Timer timer = new Timer(); boolean trace = false; + boolean info = false; Conn(Exec e) { exec = e; trace = exec.getTrace(); + info = exec.getInfo(); } /** @@ -59,8 +61,8 @@ public Query executeQuery(Query query, String connName) { ResultSet rs = stmt.executeQuery(query.sql); timer.stop(); query.set(conn, stmt, rs); - if (trace) { - exec.trace(null, "Query executed successfully (" + timer.format() + ")"); + if (info) { + exec.info(null, "Query executed successfully (" + timer.format() + ")"); } } catch (Exception e) { query.setError(e); @@ -169,8 +171,8 @@ Connection openConnection(String connStr) throws Exception { timer.start(); Connection conn = DriverManager.getConnection(url, usr, pwd); timer.stop(); - if (trace) { - exec.trace(null, "Open connection: " + url + " (" + timer.format() + ")"); + if (info) { + exec.info(null, "Open connection: " + url + " (" + timer.format() + ")"); } return conn; } diff --git hplsql/src/main/java/org/apache/hive/hplsql/Exec.java hplsql/src/main/java/org/apache/hive/hplsql/Exec.java index 40fdc82..b35344f 100644 --- hplsql/src/main/java/org/apache/hive/hplsql/Exec.java +++ hplsql/src/main/java/org/apache/hive/hplsql/Exec.java @@ -39,6 +39,7 @@ import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.tree.ParseTree; import org.apache.commons.io.FileUtils; +import org.apache.hive.hplsql.Var.Type; import org.apache.hive.hplsql.functions.*; /** @@ -63,6 +64,7 @@ Stack stack = new Stack(); Stack labels = new Stack(); + Stack callStack = new Stack(); Stack signals = new Stack(); Signal currentSignal; @@ -72,9 +74,10 @@ HashMap managedTables = new HashMap(); HashMap objectMap = new HashMap(); HashMap objectConnMap = new HashMap(); + HashMap> returnCursors = new HashMap>(); public ArrayList stmtConnList = new ArrayList(); - + Arguments arguments = new Arguments(); public Conf conf; Expression expr; @@ -183,6 +186,32 @@ public void addHandler(Handler handler) { } /** + * Add a return cursor visible to procedure callers and clients + */ + public void addReturnCursor(Var var) { + String routine = callStackPeek(); + ArrayList cursors = returnCursors.get(routine); + if (cursors == null) { + cursors = new ArrayList(); + returnCursors.put(routine, cursors); + } + cursors.add(var); + } + + /** + * Get the return cursor defined in the specified procedure + */ + public Var consumeReturnCursor(String routine) { + ArrayList cursors = returnCursors.get(routine.toUpperCase()); + if (cursors == null) { + return null; + } + Var var = cursors.get(0); + cursors.remove(0); + return var; + } + + /** * Push a value to the stack */ public void stackPush(Var var) { @@ -224,6 +253,33 @@ public Var stackPop() { return null; } + /** + * Push a value to the call stack + */ + public void callStackPush(String val) { + exec.callStack.push(val.toUpperCase()); + } + + /** + * Select a value from the call stack, but not remove + */ + public String callStackPeek() { + if (!exec.callStack.isEmpty()) { + return exec.callStack.peek(); + } + return null; + } + + /** + * Pop a value from the call stack + */ + public String callStackPop() { + if (!exec.callStack.isEmpty()) { + return exec.callStack.pop(); + } + return null; + } + /** * Find an existing variable by name */ @@ -250,6 +306,17 @@ public Var findVariable(Var name) { } /** + * Find a cursor variable by name + */ + public Var findCursor(String name) { + Var cursor = exec.findVariable(name); + if (cursor != null && cursor.type == Type.CURSOR) { + return cursor; + } + return null; + } + + /** * Enter a new scope */ public void enterScope(Scope.Type type) { @@ -286,10 +353,12 @@ public void signal(Signal.Type type, String value, Exception exception) { } public void signal(Signal.Type type, String value) { + setSqlCode(-1); signal(type, value, null); } public void signal(Signal.Type type) { + setSqlCode(-1); signal(type, null, null); } @@ -480,20 +549,20 @@ void initOptions() { Entry item = (Entry)i.next(); String key = (String)item.getKey(); String value = (String)item.getValue(); - if (key == null || value == null) { + if (key == null || value == null || !key.startsWith("hplsql.")) { continue; } else if (key.compareToIgnoreCase(Conf.CONN_DEFAULT) == 0) { exec.conf.defaultConnection = value; } else if (key.startsWith("hplsql.conn.init.")) { - exec.conn.addConnectionInit(key.substring(16), value); + exec.conn.addConnectionInit(key.substring(17), value); } else if (key.startsWith(Conf.CONN_CONVERT)) { - exec.conf.setConnectionConvert(key.substring(19), value); + exec.conf.setConnectionConvert(key.substring(20), value); } else if (key.startsWith("hplsql.conn.")) { - exec.conn.addConnection(key.substring(11), value); + exec.conn.addConnection(key.substring(12), value); } else if (key.startsWith("hplsql.")) { exec.conf.setOption(key, value); @@ -940,7 +1009,7 @@ public Integer visitException_block_item(HplsqlParser.Exception_block_itemContex */ @Override public Integer visitDeclare_var_item(HplsqlParser.Declare_var_itemContext ctx) { - String type = ctx.dtype().getText(); + String type = getFormattedText(ctx.dtype()); String len = null; String scale = null; Var default_ = null; @@ -969,6 +1038,22 @@ public Integer visitDeclare_var_item(HplsqlParser.Declare_var_itemContext ctx) { } return 0; } + + /** + * ALLOCATE CURSOR statement + */ + @Override + public Integer visitAllocate_cursor_stmt(HplsqlParser.Allocate_cursor_stmtContext ctx) { + return exec.stmt.allocateCursor(ctx); + } + + /** + * ASSOCIATE LOCATOR statement + */ + @Override + public Integer visitAssociate_locator_stmt(HplsqlParser.Associate_locator_stmtContext ctx) { + return exec.stmt.associateLocator(ctx); + } /** * DECLARE cursor statement diff --git hplsql/src/main/java/org/apache/hive/hplsql/Query.java hplsql/src/main/java/org/apache/hive/hplsql/Query.java index 23d963f..eaaaa67 100644 --- hplsql/src/main/java/org/apache/hive/hplsql/Query.java +++ hplsql/src/main/java/org/apache/hive/hplsql/Query.java @@ -35,6 +35,8 @@ ResultSet rs; Exception exception; + boolean withReturn = false; + Query() { } @@ -103,6 +105,13 @@ public void setSelectCtx(ParserRuleContext sqlSelect) { } /** + * Set whether the cursor is returned to the caller + */ + public void setWithReturn(boolean withReturn) { + this.withReturn = withReturn; + } + + /** * Set an execution error */ public void setError(Exception e) { @@ -133,6 +142,13 @@ public Connection getConnection() { } /** + * Check if the cursor defined as a return cursor to client + */ + public boolean getWithReturn() { + return withReturn; + } + + /** * Return error information */ public boolean error() { diff --git hplsql/src/main/java/org/apache/hive/hplsql/Stmt.java hplsql/src/main/java/org/apache/hive/hplsql/Stmt.java index acc4907..bfb76cd 100644 --- hplsql/src/main/java/org/apache/hive/hplsql/Stmt.java +++ hplsql/src/main/java/org/apache/hive/hplsql/Stmt.java @@ -48,6 +48,58 @@ } /** + * ALLOCATE CURSOR statement + */ + public Integer allocateCursor(HplsqlParser.Allocate_cursor_stmtContext ctx) { + trace(ctx, "ALLOCATE CURSOR"); + String name = ctx.ident(0).getText(); + Var cur = null; + if (ctx.T_PROCEDURE() != null) { + cur = exec.consumeReturnCursor(ctx.ident(1).getText()); + } + else if (ctx.T_RESULT() != null) { + cur = exec.findVariable(ctx.ident(1).getText()); + if (cur != null && cur.type != Type.RS_LOCATOR) { + cur = null; + } + } + if (cur == null) { + trace(ctx, "Cursor for procedure not found: " + name); + exec.signal(Signal.Type.SQLEXCEPTION); + return -1; + } + exec.addVariable(new Var(name, Type.CURSOR, cur.value)); + return 0; + } + + /** + * ASSOCIATE LOCATOR statement + */ + public Integer associateLocator(HplsqlParser.Associate_locator_stmtContext ctx) { + trace(ctx, "ASSOCIATE LOCATOR"); + int cnt = ctx.ident().size(); + if (cnt < 2) { + return -1; + } + String procedure = ctx.ident(cnt - 1).getText(); + for (int i = 0; i < cnt - 1; i++) { + Var cur = exec.consumeReturnCursor(procedure); + if (cur != null) { + String name = ctx.ident(i).getText(); + Var loc = exec.findVariable(name); + if (loc == null) { + loc = new Var(name, Type.RS_LOCATOR, cur.value); + exec.addVariable(loc); + } + else { + loc.setValue(cur.value); + } + } + } + return 0; + } + + /** * DECLARE cursor statement */ public Integer declareCursor(HplsqlParser.Declare_cursor_itemContext ctx) { @@ -62,7 +114,11 @@ public Integer declareCursor(HplsqlParser.Declare_cursor_itemContext ctx) { else if (ctx.select_stmt() != null) { query.setSelectCtx(ctx.select_stmt()); } - exec.addVariable(new Var(name, Type.CURSOR, query)); + if (ctx.cursor_with_return() != null) { + query.setWithReturn(true); + } + Var var = new Var(name, Type.CURSOR, query); + exec.addVariable(var); return 0; } @@ -262,6 +318,9 @@ else if (query.sqlSelect != null) { else if (!exec.getOffline()) { exec.setSqlCode(0); } + if (query.getWithReturn()) { + exec.addReturnCursor(var); + } } else { trace(ctx, "Cursor not found: " + cursor); @@ -278,8 +337,8 @@ else if (!exec.getOffline()) { public Integer fetch(HplsqlParser.Fetch_stmtContext ctx) { trace(ctx, "FETCH"); String name = ctx.L_ID(0).toString(); - Var cursor = exec.findVariable(name); - if (cursor == null || cursor.type != Type.CURSOR) { + Var cursor = exec.findCursor(name); + if (cursor == null) { trace(ctx, "Cursor not found: " + name); exec.setSqlCode(-1); exec.signal(Signal.Type.SQLEXCEPTION); @@ -319,9 +378,11 @@ else if(trace) { } else { exec.setSqlCode(100); - exec.signal(Signal.Type.NOTFOUND); } } + else { + exec.setSqlCode(-1); + } } catch (SQLException e) { exec.setSqlCode(e); diff --git hplsql/src/main/java/org/apache/hive/hplsql/Utils.java hplsql/src/main/java/org/apache/hive/hplsql/Utils.java index da0d878..1815deb 100644 --- hplsql/src/main/java/org/apache/hive/hplsql/Utils.java +++ hplsql/src/main/java/org/apache/hive/hplsql/Utils.java @@ -286,4 +286,11 @@ public static String formatBytesPerSec(long bytes, long msElapsed) { float bytesPerSec = ((float)bytes)/msElapsed*1000; return Utils.formatSizeInBytes((long)bytesPerSec, "/sec"); } + + /** + * Note. This stub is to resolve name conflict with ANTLR generated source using org.antlr.v4.runtime.misc.Utils.join + */ + static String join(T[] array, String separator) { + return org.antlr.v4.runtime.misc.Utils.join(array, separator); + } } diff --git hplsql/src/main/java/org/apache/hive/hplsql/Var.java hplsql/src/main/java/org/apache/hive/hplsql/Var.java index 0a4ead2..87b42f9 100644 --- hplsql/src/main/java/org/apache/hive/hplsql/Var.java +++ hplsql/src/main/java/org/apache/hive/hplsql/Var.java @@ -32,7 +32,7 @@ public class Var { // Data types - public enum Type {BOOL, CURSOR, DATE, DEC, FILE, IDENT, BIGINT, INTERVAL, STRING, STRINGLIST, TIMESTAMP, NULL}; + public enum Type {BOOL, CURSOR, DATE, DEC, FILE, IDENT, BIGINT, INTERVAL, RS_LOCATOR, STRING, STRINGLIST, TIMESTAMP, NULL}; public static Var Empty = new Var(); public static Var Null = new Var(Type.NULL); @@ -194,6 +194,10 @@ public Var setValue(Long val) { return this; } + public void setValue(Object value) { + this.value = value; + } + /** * Set the new value from a result set */ @@ -244,6 +248,9 @@ else if (type.equalsIgnoreCase("TIMESTAMP")) { else if (type.equalsIgnoreCase("UTL_FILE.FILE_TYPE")) { return Type.FILE; } + else if (type.toUpperCase().startsWith("RESULT_SET_LOCATOR")) { + return Type.RS_LOCATOR; + } return Type.NULL; } diff --git hplsql/src/main/java/org/apache/hive/hplsql/functions/Function.java hplsql/src/main/java/org/apache/hive/hplsql/functions/Function.java index 9895b5e..394598b 100644 --- hplsql/src/main/java/org/apache/hive/hplsql/functions/Function.java +++ hplsql/src/main/java/org/apache/hive/hplsql/functions/Function.java @@ -188,8 +188,12 @@ public boolean execProc(String name) { return false; } exec.enterScope(Scope.Type.ROUTINE); - setCallParameters(procCtx.create_routine_params()); + exec.callStackPush(name); + if (procCtx.create_routine_params() != null) { + setCallParameters(procCtx.create_routine_params()); + } visit(procCtx.single_block_stmt()); + exec.callStackPop(); exec.leaveScope(); return true; } @@ -208,8 +212,12 @@ public boolean execProc(HplsqlParser.Expr_func_paramsContext ctx, String name) { } HashMap out = new HashMap(); exec.enterScope(Scope.Type.ROUTINE); - setCallParameters(ctx, procCtx.create_routine_params(), out); + exec.callStackPush(name); + if (procCtx.create_routine_params() != null) { + setCallParameters(ctx, procCtx.create_routine_params(), out); + } visit(procCtx.single_block_stmt()); + exec.callStackPop(); exec.leaveScope(); for (Map.Entry i : out.entrySet()) { // Set OUT parameters exec.setVariable(i.getKey(), i.getValue()); diff --git hplsql/src/main/resources/hplsql-site.xml hplsql/src/main/resources/hplsql-site.xml new file mode 100644 index 0000000..1a3202a --- /dev/null +++ hplsql/src/main/resources/hplsql-site.xml @@ -0,0 +1,95 @@ + + + hplsql.conn.default + hiveconn + The default connection profile + + + hplsql.conn.hiveconn + org.apache.hive.jdbc.HiveDriver;jdbc:hive2:// + HiveServer2 JDBC connection (embedded mode) + + + hplsql.conn.init.hiveconn + + set mapred.job.queue.name=default; + set hive.execution.engine=mr; + use default; + + Statements for execute after connection to the database + + + hplsql.conn.convert.hiveconn + true + Convert SQL statements before execution + + + hplsql.conn.hive1conn + org.apache.hadoop.hive.jdbc.HiveDriver;jdbc:hive:// + Hive embedded JDBC (not requiring HiveServer) + + + hplsql.conn.hive2conn + org.apache.hive.jdbc.HiveDriver;jdbc:hive2://localhost:10000;hive;hive + HiveServer2 JDBC connection + + + hplsql.conn.init.hive2conn + + set mapred.job.queue.name=default; + set hive.execution.engine=mr; + use default; + + Statements for execute after connection to the database + + + hplsql.conn.convert.hive2conn + true + Convert SQL statements before execution + + + hplsql.conn.db2conn + com.ibm.db2.jcc.DB2Driver;jdbc:db2://localhost:50001/dbname;user;password + IBM DB2 connection + + + hplsql.conn.tdconn + com.teradata.jdbc.TeraDriver;jdbc:teradata://localhost/database=dbname,logmech=ldap;user;password + Teradata connection + + + hplsql.conn.mysqlconn + com.mysql.jdbc.Driver;jdbc:mysql://localhost/test;user;password + MySQL connection + + + hplsql.dual.table + default.dual + Single row, single column table for internal operations + + + hplsql.insert.values + native + How to execute INSERT VALUES statement: native (default) and select + + + hplsql.onerror + exception + Error handling behavior: exception (default), seterror and stop + + + hplsql.temp.tables + native + Temporary tables: native (default) and managed + + + hplsql.temp.tables.schema + + Schema for managed temporary tables + + + hplsql.temp.tables.location + /tmp/plhql + LOcation for managed temporary tables in HDFS + + \ No newline at end of file diff --git hplsql/src/test/java/org/apache/hive/hplsql/TestHplsqlDb.java hplsql/src/test/java/org/apache/hive/hplsql/TestHplsqlDb.java new file mode 100644 index 0000000..5845c0a --- /dev/null +++ hplsql/src/test/java/org/apache/hive/hplsql/TestHplsqlDb.java @@ -0,0 +1,76 @@ +/** + * 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.hive.hplsql; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.StringReader; +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for HPL/SQL (Hive connection required) + */ +public class TestHplsqlDb { + + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + @Test + public void testCreateProcedureReturnCursor() throws Exception { + run("create_procedure_return_cursor"); + } + + @Test + public void testCreateProcedureReturnCursor2() throws Exception { + run("create_procedure_return_cursor2"); + } + + /** + * Run a test file + */ + void run(String testFile) throws Exception { + System.setOut(new PrintStream(out)); + Exec exec = new Exec(); + String[] args = { "-f", "src/test/queries/db/" + testFile + ".sql", "-trace" }; + exec.run(args); + String s = getTestOutput(out.toString()).trim(); + FileUtils.writeStringToFile(new java.io.File("target/tmp/log/" + testFile + ".out.txt"), s); + String t = FileUtils.readFileToString(new java.io.File("src/test/results/db/" + testFile + ".out.txt"), "utf-8").trim(); + System.setOut(null); + Assert.assertEquals(s, t); + } + + /** + * Get test output + */ + String getTestOutput(String s) throws Exception { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = new BufferedReader(new StringReader(s)); + String line = null; + while ((line = reader.readLine()) != null) { + if (!line.startsWith("log4j:")) { + sb.append(line); + sb.append("\n"); + } + } + return sb.toString(); + } +} diff --git hplsql/src/test/java/org/apache/hive/hplsql/TestHplsqlLocal.java hplsql/src/test/java/org/apache/hive/hplsql/TestHplsqlLocal.java index ee2be66..65595ff 100644 --- hplsql/src/test/java/org/apache/hive/hplsql/TestHplsqlLocal.java +++ hplsql/src/test/java/org/apache/hive/hplsql/TestHplsqlLocal.java @@ -300,11 +300,7 @@ void run(String testFile) throws Exception { System.setOut(new PrintStream(out)); Exec exec = new Exec(); String[] args = { "-f", "src/test/queries/local/" + testFile + ".sql", "-trace" }; - exec.init(args); - Var result = exec.run(); - if (result != null) { - System.out.println(result.toString()); - } + exec.run(args); String s = getTestOutput(out.toString()).trim(); FileUtils.writeStringToFile(new java.io.File("target/tmp/log/" + testFile + ".out.txt"), s); String t = FileUtils.readFileToString(new java.io.File("src/test/results/local/" + testFile + ".out.txt"), "utf-8").trim(); diff --git hplsql/src/test/queries/db/create_procedure_return_cursor.sql hplsql/src/test/queries/db/create_procedure_return_cursor.sql new file mode 100644 index 0000000..d954863 --- /dev/null +++ hplsql/src/test/queries/db/create_procedure_return_cursor.sql @@ -0,0 +1,53 @@ +CREATE PROCEDURE spResultSet1 + DYNAMIC RESULT SETS 1 +BEGIN + DECLARE cur1 CURSOR WITH RETURN FOR + SELECT 'A', 'A1' FROM src LIMIT 3; + OPEN cur1; +END; + +CREATE PROCEDURE spResultSet2 + DYNAMIC RESULT SETS 2 +BEGIN + DECLARE cur1 CURSOR WITH RETURN FOR + SELECT 'B', 'B1' FROM src LIMIT 5; + DECLARE cur2 CURSOR WITH RETURN FOR + SELECT 'C', 'C1' FROM src LIMIT 7; + OPEN cur1; + OPEN cur2; +END; + +DECLARE v1 VARCHAR(10); +DECLARE v2 VARCHAR(10); + +CALL spResultSet1; +ALLOCATE c1 CURSOR FOR PROCEDURE spResultSet1; + +FETCH c1 INTO v1, v2; +WHILE (SQLCODE = 0) +DO + PRINT v1 || ' - ' || v2; + FETCH c1 INTO v1, v2; +END WHILE; +CLOSE c1; + +CALL spResultSet2; +ALLOCATE c2 CURSOR FOR PROCEDURE spResultSet2; + +FETCH c2 INTO v1, v2; +WHILE (SQLCODE = 0) +DO + PRINT v1 || ' - ' || v2; + FETCH c2 INTO v1, v2; +END WHILE; +CLOSE c2; + +ALLOCATE c3 CURSOR FOR PROCEDURE spResultSet2; + +FETCH c3 INTO v1, v2; +WHILE (SQLCODE = 0) +DO + PRINT v1 || ' - ' || v2; + FETCH c3 INTO v1, v2; +END WHILE; +CLOSE c3; diff --git hplsql/src/test/queries/db/create_procedure_return_cursor2.sql hplsql/src/test/queries/db/create_procedure_return_cursor2.sql new file mode 100644 index 0000000..a9a1ffe --- /dev/null +++ hplsql/src/test/queries/db/create_procedure_return_cursor2.sql @@ -0,0 +1,59 @@ +CREATE PROCEDURE spResultSet1 + DYNAMIC RESULT SETS 1 +BEGIN + DECLARE cur1 CURSOR WITH RETURN FOR + SELECT 'A', 'A1' FROM src LIMIT 3; + OPEN cur1; +END; + +CREATE PROCEDURE spResultSet2 + DYNAMIC RESULT SETS 2 +BEGIN + DECLARE cur1 CURSOR WITH RETURN FOR + SELECT 'B', 'B1' FROM src LIMIT 5; + DECLARE cur2 CURSOR WITH RETURN FOR + SELECT 'C', 'C1' FROM src LIMIT 7; + OPEN cur1; + OPEN cur2; +END; + +DECLARE v1 VARCHAR(10); +DECLARE v2 VARCHAR(10); +DECLARE loc1 RESULT_SET_LOCATOR VARYING; +DECLARE loc2 RESULT_SET_LOCATOR VARYING; + +CALL spResultSet1; + +ASSOCIATE RESULT SET LOCATOR (loc1) WITH PROCEDURE spResultSet1; +ALLOCATE c1 CURSOR FOR RESULT SET loc1; + +FETCH c1 INTO v1, v2; +WHILE (SQLCODE = 0) +DO + PRINT v1 || ' - ' || v2; + FETCH c1 INTO v1, v2; +END WHILE; +CLOSE c1; + +CALL spResultSet2; + +ASSOCIATE RESULT SET LOCATOR (loc1, loc2) WITH PROCEDURE spResultSet2; +ALLOCATE c2 CURSOR FOR RESULT SET loc1; + +FETCH c2 INTO v1, v2; +WHILE (SQLCODE = 0) +DO + PRINT v1 || ' - ' || v2; + FETCH c2 INTO v1, v2; +END WHILE; +CLOSE c2; + +ALLOCATE c3 CURSOR FOR RESULT SET loc2; + +FETCH c3 INTO v1, v2; +WHILE (SQLCODE = 0) +DO + PRINT v1 || ' - ' || v2; + FETCH c3 INTO v1, v2; +END WHILE; +CLOSE c3; diff --git hplsql/src/test/results/db/create_procedure_return_cursor.out.txt hplsql/src/test/results/db/create_procedure_return_cursor.out.txt new file mode 100644 index 0000000..81aa6c8 --- /dev/null +++ hplsql/src/test/results/db/create_procedure_return_cursor.out.txt @@ -0,0 +1,135 @@ +Ln:1 CREATE PROCEDURE spResultSet1 +Ln:9 CREATE PROCEDURE spResultSet2 +Ln:20 DECLARE v1 VARCHAR +Ln:21 DECLARE v2 VARCHAR +EXEC PROCEDURE spResultSet1 +Ln:4 DECLARE CURSOR cur1 +Ln:6 OPEN +Ln:6 cur1: SELECT 'A', 'A1' FROM src LIMIT 3 +Ln:24 ALLOCATE CURSOR +Ln:26 FETCH +Ln:26 COLUMN: _c0, string +Ln:26 SET v1 = A +Ln:26 COLUMN: _c1, string +Ln:26 SET v2 = A1 +Ln:27 WHILE - ENTERED +Ln:29 PRINT +A - A1 +Ln:30 FETCH +Ln:30 COLUMN: _c0, string +Ln:30 SET v1 = A +Ln:30 COLUMN: _c1, string +Ln:30 SET v2 = A1 +Ln:29 PRINT +A - A1 +Ln:30 FETCH +Ln:30 COLUMN: _c0, string +Ln:30 SET v1 = A +Ln:30 COLUMN: _c1, string +Ln:30 SET v2 = A1 +Ln:29 PRINT +A - A1 +Ln:30 FETCH +Ln:27 WHILE - LEFT +Ln:32 CLOSE +EXEC PROCEDURE spResultSet2 +Ln:12 DECLARE CURSOR cur1 +Ln:14 DECLARE CURSOR cur2 +Ln:16 OPEN +Ln:16 cur1: SELECT 'B', 'B1' FROM src LIMIT 5 +Ln:17 OPEN +Ln:17 cur2: SELECT 'C', 'C1' FROM src LIMIT 7 +Ln:35 ALLOCATE CURSOR +Ln:37 FETCH +Ln:37 COLUMN: _c0, string +Ln:37 SET v1 = B +Ln:37 COLUMN: _c1, string +Ln:37 SET v2 = B1 +Ln:38 WHILE - ENTERED +Ln:40 PRINT +B - B1 +Ln:41 FETCH +Ln:41 COLUMN: _c0, string +Ln:41 SET v1 = B +Ln:41 COLUMN: _c1, string +Ln:41 SET v2 = B1 +Ln:40 PRINT +B - B1 +Ln:41 FETCH +Ln:41 COLUMN: _c0, string +Ln:41 SET v1 = B +Ln:41 COLUMN: _c1, string +Ln:41 SET v2 = B1 +Ln:40 PRINT +B - B1 +Ln:41 FETCH +Ln:41 COLUMN: _c0, string +Ln:41 SET v1 = B +Ln:41 COLUMN: _c1, string +Ln:41 SET v2 = B1 +Ln:40 PRINT +B - B1 +Ln:41 FETCH +Ln:41 COLUMN: _c0, string +Ln:41 SET v1 = B +Ln:41 COLUMN: _c1, string +Ln:41 SET v2 = B1 +Ln:40 PRINT +B - B1 +Ln:41 FETCH +Ln:38 WHILE - LEFT +Ln:43 CLOSE +Ln:45 ALLOCATE CURSOR +Ln:47 FETCH +Ln:47 COLUMN: _c0, string +Ln:47 SET v1 = C +Ln:47 COLUMN: _c1, string +Ln:47 SET v2 = C1 +Ln:48 WHILE - ENTERED +Ln:50 PRINT +C - C1 +Ln:51 FETCH +Ln:51 COLUMN: _c0, string +Ln:51 SET v1 = C +Ln:51 COLUMN: _c1, string +Ln:51 SET v2 = C1 +Ln:50 PRINT +C - C1 +Ln:51 FETCH +Ln:51 COLUMN: _c0, string +Ln:51 SET v1 = C +Ln:51 COLUMN: _c1, string +Ln:51 SET v2 = C1 +Ln:50 PRINT +C - C1 +Ln:51 FETCH +Ln:51 COLUMN: _c0, string +Ln:51 SET v1 = C +Ln:51 COLUMN: _c1, string +Ln:51 SET v2 = C1 +Ln:50 PRINT +C - C1 +Ln:51 FETCH +Ln:51 COLUMN: _c0, string +Ln:51 SET v1 = C +Ln:51 COLUMN: _c1, string +Ln:51 SET v2 = C1 +Ln:50 PRINT +C - C1 +Ln:51 FETCH +Ln:51 COLUMN: _c0, string +Ln:51 SET v1 = C +Ln:51 COLUMN: _c1, string +Ln:51 SET v2 = C1 +Ln:50 PRINT +C - C1 +Ln:51 FETCH +Ln:51 COLUMN: _c0, string +Ln:51 SET v1 = C +Ln:51 COLUMN: _c1, string +Ln:51 SET v2 = C1 +Ln:50 PRINT +C - C1 +Ln:51 FETCH +Ln:48 WHILE - LEFT +Ln:53 CLOSE \ No newline at end of file diff --git hplsql/src/test/results/db/create_procedure_return_cursor2.out.txt hplsql/src/test/results/db/create_procedure_return_cursor2.out.txt new file mode 100644 index 0000000..40f2c33 --- /dev/null +++ hplsql/src/test/results/db/create_procedure_return_cursor2.out.txt @@ -0,0 +1,139 @@ +Ln:1 CREATE PROCEDURE spResultSet1 +Ln:9 CREATE PROCEDURE spResultSet2 +Ln:20 DECLARE v1 VARCHAR +Ln:21 DECLARE v2 VARCHAR +Ln:22 DECLARE loc1 RESULT_SET_LOCATOR VARYING +Ln:23 DECLARE loc2 RESULT_SET_LOCATOR VARYING +EXEC PROCEDURE spResultSet1 +Ln:4 DECLARE CURSOR cur1 +Ln:6 OPEN +Ln:6 cur1: SELECT 'A', 'A1' FROM src LIMIT 3 +Ln:27 ASSOCIATE LOCATOR +Ln:28 ALLOCATE CURSOR +Ln:30 FETCH +Ln:30 COLUMN: _c0, string +Ln:30 SET v1 = A +Ln:30 COLUMN: _c1, string +Ln:30 SET v2 = A1 +Ln:31 WHILE - ENTERED +Ln:33 PRINT +A - A1 +Ln:34 FETCH +Ln:34 COLUMN: _c0, string +Ln:34 SET v1 = A +Ln:34 COLUMN: _c1, string +Ln:34 SET v2 = A1 +Ln:33 PRINT +A - A1 +Ln:34 FETCH +Ln:34 COLUMN: _c0, string +Ln:34 SET v1 = A +Ln:34 COLUMN: _c1, string +Ln:34 SET v2 = A1 +Ln:33 PRINT +A - A1 +Ln:34 FETCH +Ln:31 WHILE - LEFT +Ln:36 CLOSE +EXEC PROCEDURE spResultSet2 +Ln:12 DECLARE CURSOR cur1 +Ln:14 DECLARE CURSOR cur2 +Ln:16 OPEN +Ln:16 cur1: SELECT 'B', 'B1' FROM src LIMIT 5 +Ln:17 OPEN +Ln:17 cur2: SELECT 'C', 'C1' FROM src LIMIT 7 +Ln:40 ASSOCIATE LOCATOR +Ln:41 ALLOCATE CURSOR +Ln:43 FETCH +Ln:43 COLUMN: _c0, string +Ln:43 SET v1 = B +Ln:43 COLUMN: _c1, string +Ln:43 SET v2 = B1 +Ln:44 WHILE - ENTERED +Ln:46 PRINT +B - B1 +Ln:47 FETCH +Ln:47 COLUMN: _c0, string +Ln:47 SET v1 = B +Ln:47 COLUMN: _c1, string +Ln:47 SET v2 = B1 +Ln:46 PRINT +B - B1 +Ln:47 FETCH +Ln:47 COLUMN: _c0, string +Ln:47 SET v1 = B +Ln:47 COLUMN: _c1, string +Ln:47 SET v2 = B1 +Ln:46 PRINT +B - B1 +Ln:47 FETCH +Ln:47 COLUMN: _c0, string +Ln:47 SET v1 = B +Ln:47 COLUMN: _c1, string +Ln:47 SET v2 = B1 +Ln:46 PRINT +B - B1 +Ln:47 FETCH +Ln:47 COLUMN: _c0, string +Ln:47 SET v1 = B +Ln:47 COLUMN: _c1, string +Ln:47 SET v2 = B1 +Ln:46 PRINT +B - B1 +Ln:47 FETCH +Ln:44 WHILE - LEFT +Ln:49 CLOSE +Ln:51 ALLOCATE CURSOR +Ln:53 FETCH +Ln:53 COLUMN: _c0, string +Ln:53 SET v1 = C +Ln:53 COLUMN: _c1, string +Ln:53 SET v2 = C1 +Ln:54 WHILE - ENTERED +Ln:56 PRINT +C - C1 +Ln:57 FETCH +Ln:57 COLUMN: _c0, string +Ln:57 SET v1 = C +Ln:57 COLUMN: _c1, string +Ln:57 SET v2 = C1 +Ln:56 PRINT +C - C1 +Ln:57 FETCH +Ln:57 COLUMN: _c0, string +Ln:57 SET v1 = C +Ln:57 COLUMN: _c1, string +Ln:57 SET v2 = C1 +Ln:56 PRINT +C - C1 +Ln:57 FETCH +Ln:57 COLUMN: _c0, string +Ln:57 SET v1 = C +Ln:57 COLUMN: _c1, string +Ln:57 SET v2 = C1 +Ln:56 PRINT +C - C1 +Ln:57 FETCH +Ln:57 COLUMN: _c0, string +Ln:57 SET v1 = C +Ln:57 COLUMN: _c1, string +Ln:57 SET v2 = C1 +Ln:56 PRINT +C - C1 +Ln:57 FETCH +Ln:57 COLUMN: _c0, string +Ln:57 SET v1 = C +Ln:57 COLUMN: _c1, string +Ln:57 SET v2 = C1 +Ln:56 PRINT +C - C1 +Ln:57 FETCH +Ln:57 COLUMN: _c0, string +Ln:57 SET v1 = C +Ln:57 COLUMN: _c1, string +Ln:57 SET v2 = C1 +Ln:56 PRINT +C - C1 +Ln:57 FETCH +Ln:54 WHILE - LEFT +Ln:59 CLOSE \ No newline at end of file