All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hive.hplsql.functions.InMemoryFunctionRegistry Maven / Gradle / Ivy

The newest version!
/*
 * 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.functions;

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

import org.antlr.v4.runtime.ParserRuleContext;
import org.apache.hive.hplsql.ArityException;
import org.apache.hive.hplsql.Exec;
import org.apache.hive.hplsql.HplsqlParser;
import org.apache.hive.hplsql.Scope;
import org.apache.hive.hplsql.Var;
import org.apache.hive.hplsql.objects.TableClass;

interface FuncCommand {
  void run(HplsqlParser.Expr_func_paramsContext ctx);
}

interface FuncSpecCommand {
  void run(HplsqlParser.Expr_spec_funcContext ctx);
}

/**
 * HPL/SQL functions
 */
public class InMemoryFunctionRegistry implements FunctionRegistry {
  Exec exec;
  private BuiltinFunctions builtinFunctions;
  HashMap funcMap = new HashMap<>();
  HashMap procMap = new HashMap<>();
  boolean trace = false;
  
  public InMemoryFunctionRegistry(Exec e, BuiltinFunctions builtinFunctions) {
    this.exec = e;
    this.trace = exec.getTrace();
    this.builtinFunctions = builtinFunctions;
  }

  @Override
  public boolean exists(String name) {
    return funcMap.containsKey(name) || procMap.containsKey(name);
  }

  @Override
  public void remove(String name) {
    funcMap.remove(name);
    procMap.remove(name);
  }

  @Override
  public boolean exec(String name, HplsqlParser.Expr_func_paramsContext ctx) {
    if (builtinFunctions.exec(name, ctx)) {
      return true;
    }
    if (execFunction(name, ctx)) {
      return true;
    }
    return (procMap.get(name) != null && execProc(name, ctx));
  }

  /**
   * Execute a user-defined function
   */
  private boolean execFunction(String name, HplsqlParser.Expr_func_paramsContext ctx) {
    HplsqlParser.Create_function_stmtContext userCtx = funcMap.get(name);
    if (userCtx == null) {
      return false;
    }
    if (trace) {
      trace(ctx, "EXEC FUNCTION " + name);
    }
    ArrayList actualParams = getActualCallParameters(ctx);
    exec.enterScope(Scope.Type.ROUTINE);
    setCallParameters(name, ctx, actualParams, userCtx.create_routine_params(), null, exec);
    if (userCtx.declare_block_inplace() != null) {
      visit(userCtx.declare_block_inplace());
    }
    visit(userCtx.single_block_stmt());
    exec.leaveScope(); 
    return true;
  }

  /**
   * Execute a stored procedure using CALL or EXEC statement passing parameters
   */
  private boolean execProc(String name, HplsqlParser.Expr_func_paramsContext ctx) {
    if (trace) {
      trace(ctx == null ? null : ctx.getParent(), "EXEC PROCEDURE " + name);
    }
    HplsqlParser.Create_procedure_stmtContext procCtx = procMap.get(name);
    if (procCtx == null) {
      trace(ctx.getParent(), "Procedure not found");
      return false;
    }    
    ArrayList actualParams = getActualCallParameters(ctx);
    HashMap out = new HashMap<>();
    exec.enterScope(Scope.Type.ROUTINE);
    exec.callStackPush(name);
    if (procCtx.declare_block_inplace() != null) {
      visit(procCtx.declare_block_inplace());
    }
    if (procCtx.create_routine_params() != null) {
      setCallParameters(name, ctx, actualParams, procCtx.create_routine_params(), out, exec);
    }
    visit(procCtx.proc_block());
    exec.callStackPop();
    exec.leaveScope();       
    for (Map.Entry i : out.entrySet()) {      // Set OUT parameters
      exec.setVariable(i.getKey(), i.getValue());
    }
    return true;
  }

  /**
   * Set parameters for user-defined function call
   */
  public static void setCallParameters(String procName, HplsqlParser.Expr_func_paramsContext actual,
      ArrayList actualValues, HplsqlParser.Create_routine_paramsContext formal, HashMap out,
      Exec exec) {
    // if it is a non-parameter function then just return.
    if (actual == null && formal == null) {
      return;
    }
    int actualCnt = (actualValues == null) ? 0 : actualValues.size();
    List 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 defaultParamNamesVsIndexes = new HashMap<>();
    if (actualCnt != formalCnt) {
      populateDefaultParamDetails(routineParamItem, defaultParamNamesVsIndexes);
    }

    // set the passed params
    for (int i = 0; i < actualCnt; i++) {
      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 routineParamItem,
      Map 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 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 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);
      }
    }
  }

  /**
   * Create a function or procedure parameter and set its value
   */
  static Var setCallParameter(String name, String typeName, String len, String scale, Var value, Exec exec) {
    TableClass hplClass = exec.getType(typeName);
    Var var =new Var(name, hplClass == null ? typeName : Var.Type.HPL_OBJECT.name(), len, scale, null);
    if (hplClass != null) {
      var.setValue(hplClass.newInstance());
    }
    var.cast(value);
    exec.addVariable(var);
    return var;
  }
  
  /**
   * Get call parameter definition by name (if specified) or position
   */
  static HplsqlParser.Create_routine_param_itemContext getCallParameter(HplsqlParser.Expr_func_paramsContext actual,
      HplsqlParser.Create_routine_paramsContext formal, int pos) {
    String named ;
    int out_pos = pos;
    if (actual.func_param(pos).ident() != null) {
      named = actual.func_param(pos).ident().getText();
      int cnt = formal.create_routine_param_item().size();
      for (int i = 0; i < cnt; i++) {
        if (named.equalsIgnoreCase(formal.create_routine_param_item(i).ident().getText())) {
          out_pos = i;
          break;
        }
      }
    }
    return formal.create_routine_param_item(out_pos);
  }  
  
  /**
   * Evaluate actual call parameters
   */
  public ArrayList getActualCallParameters(HplsqlParser.Expr_func_paramsContext actual) {
    if (actual == null || actual.func_param() == null) {
      return null;
    }
    int cnt = actual.func_param().size();
    ArrayList values = new ArrayList<>(cnt);
    for (int i = 0; i < cnt; i++) {
      values.add(evalPop(actual.func_param(i).expr()));
    }
    return values;
  }

  @Override
  public void addUserFunction(HplsqlParser.Create_function_stmtContext ctx) {
    String name = ctx.ident().getText().toUpperCase();
    if (builtinFunctions.exists(name)) {
      exec.info(ctx, name + " is a built-in function which cannot be redefined.");
      return;
    }
    if (trace) {
      trace(ctx, "CREATE FUNCTION " + name);
    }
    funcMap.put(name.toUpperCase(), ctx);
  }

  @Override
  public void addUserProcedure(HplsqlParser.Create_procedure_stmtContext ctx) {
    String name = ctx.ident(0).getText().toUpperCase();
    if (builtinFunctions.exists(name)) {
      exec.info(ctx, name + " is a built-in function which cannot be redefined.");
      return;
    }
    if (trace) {
      trace(ctx, "CREATE PROCEDURE " + name);
    }
    procMap.put(name.toUpperCase(), ctx);
  }

  /**
   * Evaluate the expression and pop value from the stack
   */
  private Var evalPop(ParserRuleContext ctx) {
    exec.visit(ctx);
    return exec.stackPop();  
  }

  /**
   * Execute rules
   */
  private Integer visit(ParserRuleContext ctx) {
    return exec.visit(ctx);  
  }

  private void trace(ParserRuleContext ctx, String message) {
    if (trace) {
      exec.trace(ctx, message);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy