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);

Reply via email to