This is an automated email from the ASF dual-hosted git repository.
krisztiankasa pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push:
new ff057c2b96e HIVE-27490: HPL/SQL says it support default value for
parameters but not considering them when no value is passed (Dayakar M,
reviewed by Krisztian Kasa)
ff057c2b96e is described below
commit ff057c2b96e8dffecb243fac0321a979d3a1573a
Author: Dayakar M <[email protected]>
AuthorDate: Fri Mar 1 10:18:28 2024 +0530
HIVE-27490: HPL/SQL says it support default value for parameters but not
considering them when no value is passed (Dayakar M, reviewed by Krisztian Kasa)
---
.../hplsql/functions/InMemoryFunctionRegistry.java | 95 ++++++++++++++------
hplsql/src/test/results/local/arity.out.txt | 1 +
.../apache/hive/beeline/TestHplSqlViaBeeLine.java | 100 ++++++++++++++++++++-
3 files changed, 167 insertions(+), 29 deletions(-)
diff --git
a/hplsql/src/main/java/org/apache/hive/hplsql/functions/InMemoryFunctionRegistry.java
b/hplsql/src/main/java/org/apache/hive/hplsql/functions/InMemoryFunctionRegistry.java
index 262e8c74bc9..767cb50e221 100644
---
a/hplsql/src/main/java/org/apache/hive/hplsql/functions/InMemoryFunctionRegistry.java
+++
b/hplsql/src/main/java/org/apache/hive/hplsql/functions/InMemoryFunctionRegistry.java
@@ -20,6 +20,7 @@ package org.apache.hive.hplsql.functions;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.antlr.v4.runtime.ParserRuleContext;
@@ -132,39 +133,77 @@ public class InMemoryFunctionRegistry implements
FunctionRegistry {
/**
* Set parameters for user-defined function call
*/
- public static void setCallParameters(String procName,
HplsqlParser.Expr_func_paramsContext actual, ArrayList<Var> actualValues,
- HplsqlParser.Create_routine_paramsContext formal,
- HashMap<String, Var> out,
- Exec exec) {
- if (actual == null || actual.func_param() == null || actualValues == null)
{
+ public static void setCallParameters(String procName,
HplsqlParser.Expr_func_paramsContext actual,
+ ArrayList<Var> actualValues, HplsqlParser.Create_routine_paramsContext
formal, HashMap<String, Var> out,
+ Exec exec) {
+ // if it is a non-parameter function then just return.
+ if (actual == null && formal == null) {
return;
}
- int actualCnt = actualValues.size();
- int formalCnt = formal.create_routine_param_item().size();
- if (formalCnt != actualCnt) {
- throw new ArityException(actual.getParent(), procName, formalCnt,
actualCnt);
+ int actualCnt = (actualValues == null) ? 0 : actualValues.size();
+ List<HplsqlParser.Create_routine_param_itemContext> routineParamItem =
formal.create_routine_param_item();
+ int formalCnt = routineParamItem.size();
+ ParserRuleContext ruleContext = (actual == null) ? null :
actual.getParent();
+ if (actualCnt > formalCnt) {
+ throw new ArityException(ruleContext, procName, formalCnt, actualCnt);
}
+ Map<String, Integer> defaultParamNamesVsIndexes = new HashMap<>();
+ if (actualCnt != formalCnt) {
+ populateDefaultParamDetails(routineParamItem,
defaultParamNamesVsIndexes);
+ }
+
+ // set the passed params
for (int i = 0; i < actualCnt; i++) {
- HplsqlParser.ExprContext a = actual.func_param(i).expr();
- HplsqlParser.Create_routine_param_itemContext p =
getCallParameter(actual, formal, i);
- String name = p.ident().getText();
- String type = p.dtype().getText();
- String len = null;
- String scale = null;
- if (p.dtype_len() != null) {
- len = p.dtype_len().L_INT(0).getText();
- if (p.dtype_len().L_INT(1) != null) {
- scale = p.dtype_len().L_INT(1).getText();
- }
+ HplsqlParser.ExprContext exprContext = actual.func_param(i).expr();
+ HplsqlParser.Create_routine_param_itemContext paramItemContext =
getCallParameter(actual, formal, i);
+ Var value = actualValues.get(i);
+ // for any default param value is passed then remove it from default
param list
+ defaultParamNamesVsIndexes.remove(paramItemContext.ident().getText());
+ setCallParameter(actual, out, exec, exprContext, paramItemContext,
value);
+ }
+ // set the remaining default params
+ for (int index : defaultParamNamesVsIndexes.values()) {
+ HplsqlParser.Create_routine_param_itemContext paramItemContext =
formal.create_routine_param_item().get(index);
+ HplsqlParser.ExprContext exprContext =
paramItemContext.dtype_default().expr();
+ Var value = exec.evalPop(paramItemContext.dtype_default().expr());
+ setCallParameter(actual, out, exec, exprContext, paramItemContext,
value);
+ }
+ // if actual param count + remaining default param count is lesser than
formal param count then throw exception as some params are missing
+ if ((actualCnt + defaultParamNamesVsIndexes.size()) != formalCnt) {
+ throw new ArityException(ruleContext, procName, formalCnt, actualCnt);
+ }
+ }
+
+ private static void
populateDefaultParamDetails(List<HplsqlParser.Create_routine_param_itemContext>
routineParamItem,
+ Map<String, Integer> defaultParamNamesVsIndexes) {
+ int formalCnt = routineParamItem.size();
+ for (int i = 0; i < formalCnt; i++) {
+ HplsqlParser.Create_routine_param_itemContext routineParamItemContext =
routineParamItem.get(i);
+ if (routineParamItemContext.dtype_default() != null) {
+
defaultParamNamesVsIndexes.put(routineParamItemContext.ident().getText(), i);
+ }
+ }
+ }
+
+ private static void setCallParameter(HplsqlParser.Expr_func_paramsContext
actual, HashMap<String, Var> out, Exec exec,
+ HplsqlParser.ExprContext a,
HplsqlParser.Create_routine_param_itemContext p, Var value) {
+ String name = p.ident().getText();
+ String type = p.dtype().getText();
+ String len = null;
+ String scale = null;
+ if (p.dtype_len() != null) {
+ len = p.dtype_len().L_INT(0).getText();
+ if (p.dtype_len().L_INT(1) != null) {
+ scale = p.dtype_len().L_INT(1).getText();
}
- Var var = setCallParameter(name, type, len, scale, actualValues.get(i),
exec);
- exec.trace(actual, "SET PARAM " + name + " = " + var.toString());
- if (out != null && a.expr_atom() != null && a.expr_atom().qident() !=
null &&
- (p.T_OUT() != null || p.T_INOUT() != null)) {
- String actualName = a.expr_atom().qident().getText();
- if (actualName != null) {
- out.put(actualName, var);
- }
+ }
+ Var variable = setCallParameter(name, type, len, scale, value, exec);
+ exec.trace(actual, "SET PARAM " + name + " = " + variable.toString());
+ if (out != null && a.expr_atom() != null && a.expr_atom()
+ .qident() != null && (p.T_OUT() != null || p.T_INOUT() != null)) {
+ String actualName = a.expr_atom().qident().getText();
+ if (actualName != null) {
+ out.put(actualName, variable);
}
}
}
diff --git a/hplsql/src/test/results/local/arity.out.txt
b/hplsql/src/test/results/local/arity.out.txt
index ccf4e589e75..43542c011a2 100644
--- a/hplsql/src/test/results/local/arity.out.txt
+++ b/hplsql/src/test/results/local/arity.out.txt
@@ -7,4 +7,5 @@ a=1
Ln:4 PRINT
b=2
Ln:8 EXEC PROCEDURE P
+Ln:8 SET PARAM a = 1
Ln:8 wrong number of arguments in call to 'P'. Expected 2 got 1.
diff --git
a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestHplSqlViaBeeLine.java
b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestHplSqlViaBeeLine.java
index eb2285ba16c..819a5b12b02 100644
---
a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestHplSqlViaBeeLine.java
+++
b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestHplSqlViaBeeLine.java
@@ -107,7 +107,7 @@ public class TestHplSqlViaBeeLine {
"p1();\n" +
"SELECT * FROM result;\n" +
"/\n";
- testScriptFile(SCRIPT_TEXT, args(), "Hello world");
+ testScriptFile(SCRIPT_TEXT, args(), "wrong number of arguments in call to
'p1'. Expected 1 got 0.", OutStream.ERR);
}
@Test
@@ -888,6 +888,104 @@ public class TestHplSqlViaBeeLine {
testScriptFile(SCRIPT_TEXT, args(), "2023 = Hive");
}
+ @Test
+ public void testHplSqlProcedureCallingWithAllDefaultValues() throws
Throwable {
+ String SCRIPT_TEXT =
+ "DROP TABLE IF EXISTS result;\n" +
+ "CREATE TABLE result (s string);\n" +
+ "CREATE PROCEDURE p1(s STRING DEFAULT 'default_val', num NUMBER
DEFAULT 123)\n" +
+ "BEGIN\n" +
+ "INSERT INTO result VALUES(s || ' = ' || num);\n" +
+ "END;\n" +
+ "p1();\n" +
+ "SELECT * FROM result;" ;
+ testScriptFile(SCRIPT_TEXT, args(), "default_val = 123");
+ }
+
+ @Test
+ public void testHplSqlProcedureCallingWithSomeDefaultValues() throws
Throwable {
+ String SCRIPT_TEXT =
+ "DROP TABLE IF EXISTS result;\n" +
+ "CREATE TABLE result (s string);\n" +
+ "CREATE PROCEDURE p1(s STRING DEFAULT 'default_val', num NUMBER
DEFAULT 123)\n" +
+ "BEGIN\n" +
+ "INSERT INTO result VALUES(s || ' = ' || num);\n" +
+ "END;\n" +
+ "p1('Pass_Value');\n" +
+ "SELECT * FROM result;" ;
+ testScriptFile(SCRIPT_TEXT, args(), "Pass_Value = 123");
+ }
+
+ @Test
+ public void testHplSqlProcedureWithDefaultValues() throws Throwable {
+ String SCRIPT_TEXT =
+ "DROP TABLE IF EXISTS result;\n" +
+ "CREATE TABLE result (s string);\n" +
+ "CREATE PROCEDURE p1(s STRING DEFAULT 'default_val', num
NUMBER)\n" +
+ "BEGIN\n" +
+ "INSERT INTO result VALUES(s || ' = ' || num);\n" +
+ "END;\n" +
+ "p1(111);\n" +
+ "SELECT * FROM result;" ;
+ testScriptFile(SCRIPT_TEXT, args(), "wrong number of arguments in call to
'p1'. Expected 2 got 1.", OutStream.ERR);
+ }
+
+ @Test
+ public void testHplSqlProcedureWithSomeDefaultValues() throws Throwable {
+ String SCRIPT_TEXT =
+ "DROP TABLE IF EXISTS result;\n" +
+ "CREATE TABLE result (s string);\n" +
+ "CREATE PROCEDURE p1(s STRING, num NUMBER DEFAULT 123)\n" +
+ "BEGIN\n" +
+ "INSERT INTO result VALUES(s || ' = ' || num);\n" +
+ "END;\n" +
+ "p1('Passed_Val');\n" +
+ "SELECT * FROM result;" ;
+ testScriptFile(SCRIPT_TEXT, args(), "Passed_Val = 123");
+ }
+
+ @Test
+ public void
testHplSqlProcedureWithDefaultParamCallingWithNamedParameterBinding() throws
Throwable {
+ String SCRIPT_TEXT =
+ "DROP TABLE IF EXISTS result;\n" +
+ "CREATE TABLE result (s string);\n" +
+ "CREATE PROCEDURE p1(s STRING DEFAULT 'default_val', num
NUMBER)\n" +
+ "BEGIN\n" +
+ "INSERT INTO result VALUES(s || ' = ' || num);\n" +
+ "END;\n" +
+ "p1(num => 111);\n" +
+ "SELECT * FROM result;" ;
+ testScriptFile(SCRIPT_TEXT, args(), "default_val = 111");
+ }
+
+ @Test
+ public void
testHplSqlProcedureWithAllDefaultParamsCallingWithNamedParameterBinding()
throws Throwable {
+ String SCRIPT_TEXT =
+ "DROP TABLE IF EXISTS result;\n" +
+ "CREATE TABLE result (s string);\n" +
+ "CREATE PROCEDURE p1(s1 STRING default 'Default S1', s2 string
default 'Default S2')\n" +
+ "BEGIN\n" +
+ "INSERT INTO result VALUES(s1 || '=' || s2);\n" +
+ "END;\n" +
+ "p1(s2 => 'PassedValue S2');\n" +
+ "SELECT * FROM result;" ;
+ testScriptFile(SCRIPT_TEXT, args(), "Default S1=PassedValue S2");
+ }
+
+ @Test
+ public void testHplSqlProcedureWithoutParameters() throws Throwable {
+ String SCRIPT_TEXT =
+ "DROP TABLE IF EXISTS result;\n" +
+ "CREATE TABLE result (s string);\n" +
+ "CREATE PROCEDURE p1()\n" +
+ "BEGIN\n" +
+ "INSERT INTO result VALUES('No param');\n" +
+ "END;\n" +
+ "p1('none');\n" +
+ "SELECT * FROM result;" ;
+ testScriptFile(SCRIPT_TEXT, args(), "wrong number of arguments in call to
'p1'. Expected 0 got 1.", OutStream.ERR);
+ }
+
private static List<String> args() {
return Arrays.asList("-d", BeeLine.BEELINE_DEFAULT_JDBC_DRIVER,
"-u", miniHS2.getBaseJdbcURL() + ";mode=hplsql", "-n", userName);