Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HIVE-27490: HPL/SQL says it support default value for parameters but not considering them when no value is passed #5084

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.antlr.v4.runtime.ParserRuleContext;
Expand Down Expand Up @@ -132,39 +133,77 @@ private boolean execProc(String name, HplsqlParser.Expr_func_paramsContext ctx)
/**
* 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);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions hplsql/src/test/results/local/arity.out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void testHplSqlProcedure() throws Throwable {
"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
Expand Down Expand Up @@ -888,6 +888,104 @@ public void testTableAliasInColumnName() throws Throwable {
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);
Expand Down