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

org.apache.hive.hplsql.Exec 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;

import static java.util.Collections.singletonList;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.hive.hplsql.objects.MethodDictionary.__GETITEM__;
import static org.apache.hive.hplsql.objects.MethodDictionary.__SETITEM__;
import static org.apache.hive.hplsql.objects.MethodParams.Arity.UNARY;

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.UncheckedIOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Stack;
import java.util.stream.Collectors;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.hadoop.hive.metastore.IMetaStoreClient;
import org.apache.hadoop.hive.metastore.api.StoredProcedure;
import org.apache.hadoop.hive.metastore.api.StoredProcedureRequest;
import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.hive.hplsql.Var.Type;
import org.apache.hive.hplsql.executor.JdbcQueryExecutor;
import org.apache.hive.hplsql.executor.Metadata;
import org.apache.hive.hplsql.executor.QueryException;
import org.apache.hive.hplsql.executor.QueryExecutor;
import org.apache.hive.hplsql.executor.QueryResult;
import org.apache.hive.hplsql.functions.BuiltinFunctions;
import org.apache.hive.hplsql.functions.FunctionDatetime;
import org.apache.hive.hplsql.functions.FunctionMisc;
import org.apache.hive.hplsql.functions.FunctionRegistry;
import org.apache.hive.hplsql.functions.FunctionString;
import org.apache.hive.hplsql.functions.HmsFunctionRegistry;
import org.apache.hive.hplsql.functions.InMemoryFunctionRegistry;
import org.apache.hive.hplsql.objects.DbmOutput;
import org.apache.hive.hplsql.objects.DbmOutputClass;
import org.apache.hive.hplsql.objects.HplObject;
import org.apache.hive.hplsql.objects.Method;
import org.apache.hive.hplsql.objects.Table;
import org.apache.hive.hplsql.objects.TableClass;
import org.apache.hive.hplsql.objects.UtlFile;
import org.apache.hive.hplsql.objects.UtlFileClass;
import org.apache.hive.hplsql.packages.HmsPackageRegistry;
import org.apache.hive.hplsql.packages.InMemoryPackageRegistry;
import org.apache.hive.hplsql.packages.PackageRegistry;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * HPL/SQL script executor
 *
 */
public class Exec extends HplsqlBaseVisitor implements Closeable {

  private static final Logger LOG = LoggerFactory.getLogger(Exec.class);

  public static final String VERSION = "HPL/SQL 0.3.31";
  public static final String ERRORCODE = "ERRORCODE";
  public static final String SQLCODE = "SQLCODE";
  public static final String SQLSTATE = "SQLSTATE";
  public static final String HOSTCODE = "HOSTCODE";

  Exec exec;
  private IMetaStoreClient msc;
  FunctionRegistry functions;
  private BuiltinFunctions builtinFunctions;
  QueryExecutor queryExecutor;
  private HplSqlSessionState hplSqlSession;
  private PackageRegistry packageRegistry = new InMemoryPackageRegistry();
  private boolean packageLoading = false;
  private Map types = new HashMap<>();

  public enum OnError {EXCEPTION, SETERROR, STOP}

  // Scopes of execution (code blocks) with own local variables, parameters and exception handlers
  Stack scopes = new Stack<>();
  Scope globalScope;
  Scope currentScope;
  
  Stack stack = new Stack<>();
  Stack labels = new Stack<>();
  Stack callStack = new Stack<>();
  
  Stack signals = new Stack<>();
  Signal currentSignal;
  Scope currentHandlerScope;
  boolean resignal = false;
  
  HashMap managedTables = new HashMap<>();
  HashMap objectMap = new HashMap<>();
  HashMap objectConnMap = new HashMap<>();
  HashMap> returnCursors = new HashMap<>();
  HashMap packages = new HashMap<>();
  
  Package currentPackageDecl = null;
  
  public ArrayList stmtConnList = new ArrayList<>();
      
  Arguments arguments = new Arguments();
  public Conf conf;
  Expression expr;
  Converter converter;
  Meta meta;
  Select select;
  Stmt stmt;
  Conn conn;
  Console console = Console.STANDARD;

  int rowCount = 0;  

  StringBuilder localUdf = new StringBuilder();
  boolean initRoutines = false;
  public boolean buildSql = false;
  public boolean inCallStmt = false;
  boolean udfRegistered = false;
  boolean udfRun = false;
    
  boolean dotHplsqlrcExists = false;
  boolean hplsqlrcExists = false;
  
  boolean trace = false; 
  boolean info = true;
  boolean offline = false;
  
  public Exec() {
    exec = this;
    queryExecutor = new JdbcQueryExecutor(this);
  }

  public Exec(Conf conf, Console console, QueryExecutor queryExecutor, IMetaStoreClient msc, HplSqlSessionState hplSqlSession) {
    this.conf = conf;
    this.exec = this;
    this.console = console;
    this.queryExecutor = queryExecutor;
    this.msc = msc;
    this.hplSqlSession = hplSqlSession;
  }
  
  Exec(Exec exec) {
    this.exec = exec;
    this.console = exec.console;
    this.queryExecutor = exec.queryExecutor;
  }

  /** 
   * Set a variable using a value from the parameter or the stack 
   */
  public Var setVariable(String name, Var value) {
    if (value == null || value == Var.Empty) {
      if (exec.stack.empty()) {
        return Var.Empty;
      }
      value = exec.stack.pop();
    }
    if (name.startsWith("hplsql.")) {
      exec.conf.setOption(name, value.toString());
      return Var.Empty;
    }
    Var var = findVariable(name);
    if (var != null) {
      var.cast(value);
    }
    else {
      var = new Var(value);
      var.setName(name);
      if(exec.currentScope != null && !exec.buildSql) {
        exec.currentScope.addVariable(var);
      }
    }    
    return var;
  }
  
  public Var setVariable(String name) {
    return setVariable(name, Var.Empty);
  }

  public Var setVariable(String name, String value) {
    return setVariable(name, new Var(value));
  }

  public Var setVariable(String name, int value) {
    return setVariable(name, new Var(Long.valueOf(value)));
  }

  /** 
   * Set variable to NULL 
   */
  public Var setVariableToNull(String name) {
    Var var = findVariable(name);
    if (var != null) {
      var.removeValue();
    }
    else {
      var = new Var();
      var.setName(name);
      if(exec.currentScope != null) {
        exec.currentScope.addVariable(var);
      }
    }    
    return var;
  }
  
  /**
   * Add a local variable to the current scope
   */
  public void addVariable(Var var) {
    if (currentPackageDecl != null) {
      currentPackageDecl.addVariable(var);
    }
    else if (exec.currentScope != null) {
      exec.currentScope.addVariable(var);
    }
  }
  
  /**
   * Add a condition handler to the current scope
   */
  public void addHandler(Handler handler) {
    if (exec.currentScope != null) {
      exec.currentScope.addHandler(handler);
    }
  }
  
  /**
   * Add a return cursor visible to procedure callers and clients
   */
  public void addReturnCursor(Var var) {
    String routine = callStackPeek();
    ArrayList cursors = returnCursors.computeIfAbsent(routine, k -> new ArrayList<>());
    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) {
    exec.stack.push(var);  
  }
  
  /**
   * Push a string value to the stack
   */
  public void stackPush(String val) {
    exec.stack.push(new Var(val));  
  }
  
  public void stackPush(StringBuilder val) {
    stackPush(val.toString());  
  }
  
  /**
   * Push a boolean value to the stack
   */
  public void stackPush(Boolean val) {
    exec.stack.push(new Var(val));  
  }

  /**
   * Select a value from the stack, but not remove
   */
  public Var stackPeek() {
    return exec.stack.peek();  
  }
  
  /**
   * Pop a value from the stack
   */
  public Var stackPop() {
    if (!exec.stack.isEmpty()) {
      return exec.stack.pop();
    }
    return Var.Empty;
  }    
  
  /**
   * 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 
   */
  public Var findVariable(String name) {
    Var var;
    String name1 = name.toUpperCase();
    String name1a = null;
    String name2 = null;
    Scope cur = exec.currentScope;
    Package pack;
    Package packCallContext = exec.getPackageCallContext();
    ArrayList qualified = exec.meta.splitIdentifier(name);
    if (qualified != null) {
      name1 = qualified.get(0).toUpperCase();
      name2 = qualified.get(1).toUpperCase();
      pack = findPackage(name1);
      if (pack != null) {        
        var = pack.findVariable(name2);
        if (var != null) {
          return var;
        }
      }
    }    
    if (name1.startsWith(":")) {
      name1a = name1.substring(1);
    }    
    while (cur != null) {
      var = findVariable(cur.vars, name1);
      if (var == null && name1a != null) {
        var = findVariable(cur.vars, name1a);
      }
      if (var == null && packCallContext != null) {
        var = packCallContext.findVariable(name1);
      }
      if (var != null) {
        return var;
      }
      if (cur.type == Scope.Type.ROUTINE) {
        cur = exec.globalScope;
      }
      else {
        cur = cur.parent;
      }
    }    
    return null;
  }
  
  public Var findVariable(Var name) {
    return findVariable(name.getName());
  }
  
  Var findVariable(Map vars, String name) {
    return vars.get(name.toUpperCase());
  }
  
  /**
   * 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;
  }
  
  /**
   * Find the package by name
   */
  Package findPackage(String name) {
    Package pkg = packages.get(name.toUpperCase());
    if (pkg != null) {
      return pkg;
    }
    Optional source = exec.packageRegistry.getPackage(name);
    if (source.isPresent()) {
      HplsqlLexer lexer = new HplsqlLexer(new ANTLRInputStream(source.get()));
      CommonTokenStream tokens = new CommonTokenStream(lexer);
      HplsqlParser parser = newParser(tokens);
      exec.packageLoading = true;
      try {
        visit(parser.program());
      } finally {
        exec.packageLoading = false;
      }
    } else {
      return null;
    }
    return packages.get(name.toUpperCase());
  }
  
  /**
   * Enter a new scope
   */
  public void enterScope(Scope scope) {
    exec.scopes.push(scope);
  }
  
  public void enterScope(Scope.Type type) {
    enterScope(type, null);
  }
  
  public void enterScope(Scope.Type type, Package pack) {
    exec.currentScope = new Scope(exec.currentScope, type, pack);
    enterScope(exec.currentScope);
  }
  
  public void enterGlobalScope() {
    globalScope = new Scope(Scope.Type.GLOBAL);
    currentScope = globalScope;
    enterScope(globalScope);
  }

  /**
   * Leave the current scope
   */
  public void leaveScope() {
    if (!exec.signals.empty()) {
      Scope scope = exec.scopes.peek();
      Signal signal = exec.signals.peek();
      if (exec.conf.onError != OnError.SETERROR) {
        runExitHandler();
      }
      if (signal.type == Signal.Type.LEAVE_ROUTINE && scope.type == Scope.Type.ROUTINE) {
        exec.signals.pop();
      }
    }
    exec.currentScope = exec.scopes.pop().getParent();
  }
  
  /**
   * Send a signal
   */
  public void signal(Signal signal) {
    exec.signals.push(signal);
  }
  
  public void signal(Signal.Type type, String value, Exception exception) {
    signal(new Signal(type, value, exception));
  }
  
  public void signal(Signal.Type type, String value) {
    setSqlCode(SqlCodes.ERROR);
    signal(type, value, null);   
  }
  
  public void signal(Signal.Type type) {
    setSqlCode(SqlCodes.ERROR);
    signal(type, null, null);   
  }
  
  public void signal(Query query) {
    setSqlCode(query.getException());
    signal(Signal.Type.SQLEXCEPTION, query.errorText(), query.getException());
  }

  public void signal(QueryResult query) {
    setSqlCode(query.exception());
    signal(Signal.Type.SQLEXCEPTION, query.errorText(), query.exception());
  }

  public void signal(Exception exception) {
    setSqlCode(exception);
    signal(Signal.Type.SQLEXCEPTION, exception.getMessage(), exception);
  }
  
  /**
   * Resignal the condition
   */
  public void resignal() {
    resignal(exec.currentSignal);
  }
  
  public void resignal(Signal signal) {
    if (signal != null) {
      exec.resignal = true;
      signal(signal);
    }
  }

  /**
   * Run CONTINUE handlers 
   */
  boolean runContinueHandler() {
    Scope cur = exec.currentScope;    
    exec.currentSignal = exec.signals.pop(); 
    while (cur != null) {
      for (Handler h : cur.handlers) {
        if (h.execType != Handler.ExecType.CONTINUE) {
          continue;
        }
        if ((h.type != Signal.Type.USERDEFINED && h.type == exec.currentSignal.type) ||
            (h.type == Signal.Type.USERDEFINED && h.type == exec.currentSignal.type &&
             h.value.equalsIgnoreCase(exec.currentSignal.value))) {
          trace(h.ctx, "CONTINUE HANDLER");
          enterScope(Scope.Type.HANDLER);
          exec.currentHandlerScope = h.scope; 
          visit(h.ctx.single_block_stmt());
          leaveScope(); 
          exec.currentSignal = null;
          return true;
        }
      }      
      cur = cur.parent;
    } 
    exec.signals.push(exec.currentSignal);
    exec.currentSignal = null;
    return false;
  }
  
  /**
   * Run EXIT handler defined for the current scope 
   */
  boolean runExitHandler() {
    exec.currentSignal = exec.signals.pop();
    for (Handler h : currentScope.handlers) {
      if (h.execType != Handler.ExecType.EXIT) {
        continue;
      }
      if ((h.type != Signal.Type.USERDEFINED && h.type == exec.currentSignal.type) ||
          (h.type == Signal.Type.USERDEFINED && h.type == exec.currentSignal.type &&
           h.value.equalsIgnoreCase(currentSignal.value))) {
        trace(h.ctx, "EXIT HANDLER");
        enterScope(Scope.Type.HANDLER);
        exec.currentHandlerScope = h.scope; 
        visit(h.ctx.single_block_stmt());
        leaveScope(); 
        exec.currentSignal = null;
        return true;
      }        
    }    
    exec.signals.push(exec.currentSignal);
    exec.currentSignal = null;
    return false;
  }
    
  /**
   * Pop the last signal
   */
  public Signal signalPop() {
    if (!exec.signals.empty()) {
      return exec.signals.pop();
    }
    return null;
  }
  
  /**
   * Peek the last signal
   */
  public Signal signalPeek() {
    if (!exec.signals.empty()) {
      return exec.signals.peek();
    }
    return null;
  }
  
  /**
   * Pop the current label
   */
  public String labelPop() {
    if(!exec.labels.empty()) {
      return exec.labels.pop();
    }
    return "";
  }
  
  /**
   * Execute a SQL query (SELECT)
   */
  public Query executeQuery(ParserRuleContext ctx, Query query, String connProfile) {
    if (!exec.offline) {
      exec.rowCount = 0;
      exec.conn.executeQuery(query, connProfile);
      return query;
    }
    setSqlNoData();
    info(ctx, "Not executed - offline mode set");
    return query;
  }

  /**
   * Register JARs, FILEs and CREATE TEMPORARY FUNCTION for UDF call
   */
  public void registerUdf() {
    if (udfRegistered) {
      return;
    }
    ArrayList sql = new ArrayList<>();
    String dir = Utils.getExecDir();
    addJar(sql, dir, "hive-hplsql");
    addJar(sql, dir, "antlr4-runtime");
    if(!conf.getLocation().equals("")) {
      sql.add("ADD FILE " + conf.getLocation());
    } else {
      sql.add("ADD FILE " + dir + Conf.SITE_XML);
    }
    if (dotHplsqlrcExists) {
      sql.add("ADD FILE " + dir + Conf.DOT_HPLSQLRC);
    }
    if (hplsqlrcExists) {
      sql.add("ADD FILE " + dir + Conf.HPLSQLRC);
    }
    String lu = createLocalUdf();
    if (lu != null) {
      sql.add("ADD FILE " + lu);
    }
    sql.add("CREATE TEMPORARY FUNCTION hplsql AS 'org.apache.hive.hplsql.udf.Udf'");
    exec.conn.addPreSql(exec.conf.defaultConnection, sql);
    udfRegistered = true;
  }

  private static void addJar(ArrayList sql, String dir, String jarNamePrefix) {
    String jarName = findJarLike(dir, jarNamePrefix);
    if (isNotBlank(jarName)) {
      sql.add("ADD JAR " + Paths.get(dir, jarName));
    }
  }

  private static String findJarLike(String dir, String prefix) {
    String[] files = new File(dir).list((dir1, name) -> name.startsWith(prefix) && name.endsWith(".jar"));
    if (files == null || files.length < 1) {
      LOG.warn("No jar file found in directory '{}' with prefix '{}'", dir, prefix);
      return null;
    }

    return files[0];
  }

  /**
   * Initialize options
   */
  void initOptions() {
    for (Entry item : exec.conf) {
      String key = item.getKey();
      String value = item.getValue();
      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(17), value);
      } else if (key.startsWith(Conf.CONN_CONVERT)) {
        exec.conf.setConnectionConvert(key.substring(20), value);
      } else if (key.startsWith("hplsql.conn.")) {
        exec.conn.addConnection(key.substring(12), value);
      } else if (key.startsWith("hplsql.")) {
        exec.conf.setOption(key, value);
      }
    }    
  }
  
  /**
   * Set SQLCODE
   */
  public void setSqlCode(int sqlcode) {
    Long code = (long) sqlcode;
    Var var = findVariable(SQLCODE);
    if (var != null) {
      var.setValue(code);
    }
    var = findVariable(ERRORCODE);
    if (var != null) {
      var.setValue(code);
    }
  }
  
  public void setSqlCode(Exception exception) {
    if (exception instanceof QueryException) {
      setSqlCode(((QueryException) exception).getErrorCode());
      setSqlState(((QueryException) exception).getSQLState());
    } else {
      setSqlCode(SqlCodes.ERROR);
      setSqlState("02000");
    }    
  }
  
  /**
   * Set SQLSTATE
   */
  public void setSqlState(String sqlstate) {
    Var var = findVariable(SQLSTATE);
    if (var != null) {
      var.setValue(sqlstate);
    }
  }

  public void setResultListener(ResultListener resultListener) {
    select.setResultListener(resultListener);
  }

  /**
   * Set HOSTCODE
   */
  public void setHostCode(int code) {
    Var var = findVariable(HOSTCODE);
    if (var != null) {
      var.setValue(Long.valueOf(code));
    }
  }
  
  /**
   * Set successful execution for SQL
   */
  public void setSqlSuccess() {
    setSqlCode(SqlCodes.SUCCESS);
    setSqlState("00000");
  }
  
  /**
   * Set SQL_NO_DATA as the result of SQL execution
   */
  public void setSqlNoData() {
    setSqlCode(SqlCodes.NO_DATA_FOUND);
    setSqlState("01000");
  }

  public Integer run(String[] args) throws Exception {
    if (!parseArguments(args)) {
      return -1;
    }
    init();
    try {
      parseAndEval(arguments);
    } finally {
      close();
    }
    return getProgramReturnCode();
  }

  public Var parseAndEval(Arguments arguments)  {
    ParseTree tree;
    try (InputStream input = sourceStream(arguments)) {
      tree = parse(input);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
    Var result = null;
    try {
      result = evaluate(tree, arguments.main);
    } catch (HplValidationException e) {
      signal(Signal.Type.VALIDATION, e.getMessage(), e);
    }
    if (result != null) {
      console.printLine(result.toString());
    }
    return result;
  }

  private Var evaluate(ParseTree tree, String execMain) {
    if (tree == null) {
      return null;
    }
    if (execMain != null) {
      initRoutines = true;
      visit(tree);
      initRoutines = false;
      exec.functions.exec(execMain.toUpperCase(), null);
    }
    else {
      visit(tree);
    }
    if (!exec.stack.isEmpty()) {
      return exec.stackPop();
    }
    return null;
  }

  @Override
  public void close() {
    leaveScope();
    cleanup();
    printExceptions();
  }

  private InputStream sourceStream(Arguments arguments) throws FileNotFoundException {
    return arguments.execString != null
            ? new ByteArrayInputStream(arguments.execString.getBytes(StandardCharsets.UTF_8))
            : new FileInputStream(arguments.fileName);
  }

  /**
   * Initialize PL/HQL
   */
  public void init() {
    enterGlobalScope();
    // specify the default log4j2 properties file.
    System.setProperty("log4j.configurationFile", "hive-log4j2.properties");
    if (conf == null) {
      conf = new Conf();
    }
    conf.init();
    conn = new Conn(this);
    meta = new Meta(this, queryExecutor);
    initOptions();
    
    expr = new Expression(this);
    select = new Select(this, queryExecutor);
    stmt = new Stmt(this, queryExecutor);
    converter = new Converter(this);

    builtinFunctions = new BuiltinFunctions(this, queryExecutor);
    new FunctionDatetime(this, queryExecutor).register(builtinFunctions);
    new FunctionMisc(this, queryExecutor).register(builtinFunctions);
    new FunctionString(this, queryExecutor).register(builtinFunctions);
    if (msc != null) {
      functions = new HmsFunctionRegistry(this, msc, builtinFunctions, hplSqlSession);
      packageRegistry = new HmsPackageRegistry(msc, hplSqlSession);
    } else {
      functions = new InMemoryFunctionRegistry(this, builtinFunctions);
    }
    addVariable(new Var(ERRORCODE, Var.Type.BIGINT, 0L));
    addVariable(new Var(SQLCODE, Var.Type.BIGINT, 0L));
    addVariable(new Var(SQLSTATE, Var.Type.STRING, "00000"));
    addVariable(new Var(HOSTCODE, Var.Type.BIGINT, 0L)); 
    for (Map.Entry v : arguments.getVars().entrySet()) {
      addVariable(new Var(v.getKey(), Var.Type.STRING, v.getValue()));
    }
    includeRcFile();
    registerBuiltins();
  }

  private ParseTree parse(InputStream input) throws IOException {
    HplsqlLexer lexer = new HplsqlLexer(new ANTLRInputStream(input));
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    HplsqlParser parser = newParser(tokens);
    ParseTree tree = parser.program();
    if (trace) {
      console.printError("Configuration file: " + conf.getLocation());
      console.printError("Parser tree: " + tree.toStringTree(parser));
    }
    return tree;
  }

  protected void registerBuiltins() {
    Var dbmVar = new Var(Type.HPL_OBJECT, "DBMS_OUTPUT");
    DbmOutput dbms = DbmOutputClass.INSTANCE.newInstance();
    dbms.initialize(console);
    dbmVar.setValue(dbms);
    dbmVar.setConstant(true);
    addVariable(dbmVar);

    Var utlFileVar = new Var(Type.HPL_OBJECT, "UTL_FILE");
    UtlFile utlFile = UtlFileClass.INSTANCE.newInstance();
    utlFileVar.setValue(utlFile);
    utlFileVar.setConstant(true);
    addVariable(utlFileVar);
  }

  private HplsqlParser newParser(CommonTokenStream tokens) {
    HplsqlParser parser = new HplsqlParser(tokens);
    // the default listener logs into stdout, overwrite it with a custom listener that uses beeline console
    parser.removeErrorListeners();
    parser.addErrorListener(new SyntaxErrorReporter(console));
    return parser;
  }

  /**
   * Parse command line arguments
   */
  public boolean parseArguments(String[] args) {
    boolean parsed = arguments.parse(args);
    if (parsed && arguments.hasVersionOption()) {
      console.printError(VERSION);
      return false;
    }
    if (!parsed || arguments.hasHelpOption() ||
      (arguments.getExecString() == null && arguments.getFileName() == null)) {
      arguments.printHelp();
      return false;
    }    
    String execString = arguments.getExecString();
    String execFile = arguments.getFileName();
    if (arguments.hasTraceOption()) {
      trace = true;
    }
    if (arguments.hasOfflineOption()) {
      offline = true;
    }
    if (execString != null && execFile != null) {
      console.printError("The '-e' and '-f' options cannot be specified simultaneously.");
      return false;
    }   
    return true;
  }
  
  /**
   * Include statements from .hplsqlrc and hplsql rc files
   */
  void includeRcFile() {
    if (includeFile(Conf.DOT_HPLSQLRC, false)) {
      dotHplsqlrcExists = true;
    }
    else {
      if (includeFile(Conf.HPLSQLRC, false)) {
        hplsqlrcExists = true;
      }
    }
    if (udfRun) {
      includeFile(Conf.HPLSQL_LOCALS_SQL, true);
    }
  }
  
  /**
   * Include statements from a file
   */
  boolean includeFile(String file, boolean showError) {
    try {
      String content = FileUtils.readFileToString(new java.io.File(file), "UTF-8");
      if (content != null && !content.isEmpty()) {
        if (trace) {
          trace(null, "INCLUDE CONTENT " + file + " (non-empty)");
        }
        new Exec(this).include(content);
        return true;
      }
    } 
    catch (Exception e) {
      if (showError) {
        error(null, "INCLUDE file error: " + e.getMessage());
      }
    } 
    return false;
  }
  
  /**
   * Execute statements from an include file
   */
  void include(String content) throws Exception {
    InputStream input = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
    HplsqlLexer lexer = new HplsqlLexer(new ANTLRInputStream(input));
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    HplsqlParser parser = newParser(tokens);
    ParseTree tree = parser.program(); 
    visit(tree);    
  }
  
  /**
   * Start executing HPL/SQL script
   */
  @Override 
  public Integer visitProgram(HplsqlParser.ProgramContext ctx) {
    return visitChildren(ctx);
  }
  
  /**
   * Enter BEGIN-END block
   */
  @Override  
  public Integer visitBegin_end_block(HplsqlParser.Begin_end_blockContext ctx) { 
    enterScope(Scope.Type.BEGIN_END);
    Integer rc = visitChildren(ctx); 
    leaveScope();
    return rc;
  }

  /**
   * Free resources before exit
   */
  void cleanup() {
    for (Map.Entry i : managedTables.entrySet()) {
      String sql = "DROP TABLE IF EXISTS " + i.getValue();
      QueryResult query = queryExecutor.executeQuery(sql, null);
      query.close();
      if (trace) {
        trace(null, sql);        
      }      
    }
  }
  
  /**
   * Output information about unhandled exceptions
   */
  public void printExceptions() {
    List userDefinedSignals = new ArrayList<>();
    while (!signals.empty()) {
      Signal sig = signals.pop();
      // if signal type is user defined then don't handle here
      if (sig.type == Signal.Type.USERDEFINED) {
        userDefinedSignals.add(sig);
      } else if (sig.type == Signal.Type.VALIDATION) {
        error(((HplValidationException)sig.exception).getCtx(), sig.exception.getMessage());
      } else if (sig.type == Signal.Type.SQLEXCEPTION) {
        console.printError("Unhandled exception in HPL/SQL");
      } else if (sig.type == Signal.Type.UNSUPPORTED_OPERATION) {
        console.printError(sig.value == null ? "Unsupported operation" : sig.value);
      } else if (sig.exception != null) {
        console.printError("HPL/SQL error: " + ExceptionUtils.getStackTrace(sig.exception));
      } else if (sig.value != null) {
        console.printError(sig.value);
      } else {
        trace(null, "Signal: " + sig.type);
      }
    }
    // if there are any user defined signals then push them back to signals stack to handle them later.
    for (int i = userDefinedSignals.size() - 1; i >= 0; i--) {
      exec.signals.push(userDefinedSignals.get(i));
    }
  } 
  
  /**
   * Get the program return code
   */
  Integer getProgramReturnCode() {
    int rc = 0;
    if (!signals.empty()) {
      Signal sig = signals.pop();
      if ((sig.type == Signal.Type.LEAVE_PROGRAM || sig.type == Signal.Type.LEAVE_ROUTINE) && 
        sig.value != null) {
        try {
          rc = Integer.parseInt(sig.value);
        }
        catch(NumberFormatException e) {
          rc = 1;
        }
      }
    }
    return rc;
  }

  /**
   * Executing a statement
   */
  @Override 
  public Integer visitStmt(HplsqlParser.StmtContext ctx) {
    if (ctx.semicolon_stmt() != null) {
      return 0;
    }
    if (initRoutines && ctx.create_procedure_stmt() == null && ctx.create_function_stmt() == null) {
      return 0;
    }
    if (exec.resignal) {
      if (exec.currentScope != exec.currentHandlerScope.parent) {
        return 0;
      }
      exec.resignal = false;
    }
    if (!exec.signals.empty() && exec.conf.onError != OnError.SETERROR) {
      if (!runContinueHandler()) {
        return 0;
      }
    }
    Var prev = stackPop();
    if (prev != null && prev.value != null) {
      console.printLine(prev.toString());
    }
    return visitChildren(ctx); 
  }
  
  /**
   * Executing or building SELECT statement
   */
  @Override 
  public Integer visitSelect_stmt(HplsqlParser.Select_stmtContext ctx) { 
    return exec.select.select(ctx);
  }
  
  @Override 
  public Integer visitCte_select_stmt(HplsqlParser.Cte_select_stmtContext ctx) { 
    return exec.select.cte(ctx); 
  }

  @Override 
  public Integer visitFullselect_stmt(HplsqlParser.Fullselect_stmtContext ctx) { 
    return exec.select.fullselect(ctx);
  }
  
  @Override 
  public Integer visitSubselect_stmt(HplsqlParser.Subselect_stmtContext ctx) { 
    return exec.select.subselect(ctx);
  }  
  
  @Override 
  public Integer visitSelect_list(HplsqlParser.Select_listContext ctx) { 
    return exec.select.selectList(ctx); 
  }
  
  @Override 
  public Integer visitFrom_clause(HplsqlParser.From_clauseContext ctx) { 
    return exec.select.from(ctx); 
  }
  
  @Override 
  public Integer visitFrom_table_name_clause(HplsqlParser.From_table_name_clauseContext ctx) { 
    return exec.select.fromTable(ctx); 
  }
  
  @Override 
  public Integer visitFrom_subselect_clause(HplsqlParser.From_subselect_clauseContext ctx) { 
    return exec.select.fromSubselect(ctx); 
  }
  
  @Override 
  public Integer visitFrom_join_clause(HplsqlParser.From_join_clauseContext ctx) { 
    return exec.select.fromJoin(ctx); 
  }
  
  @Override 
  public Integer visitFrom_table_values_clause(HplsqlParser.From_table_values_clauseContext ctx) { 
    return exec.select.fromTableValues(ctx); 
  }
  
  @Override 
  public Integer visitWhere_clause(HplsqlParser.Where_clauseContext ctx) { 
    return exec.select.where(ctx); 
  }  
  
  @Override 
  public Integer visitSelect_options_item(HplsqlParser.Select_options_itemContext ctx) { 
    return exec.select.option(ctx); 
  }

  /**
   * Column name
   */
  @Override 
  public Integer visitColumn_name(HplsqlParser.Column_nameContext ctx) {
    stackPush(meta.normalizeIdentifierPart(ctx.getText()));
    return 0; 
  }
  
  /**
   * Table name
   */
  @Override 
  public Integer visitTable_name(HplsqlParser.Table_nameContext ctx) {
    String name = ctx.getText();
    String nameUp = name.toUpperCase();
    String nameNorm = meta.normalizeObjectIdentifier(name);
    String actualName = exec.managedTables.get(nameUp);
    String conn = exec.objectConnMap.get(nameUp);
    if (conn == null) {
      conn = conf.defaultConnection;
    }
    stmtConnList.add(conn);    
    if (actualName != null) {
      stackPush(actualName);
      return 0;
    }
    actualName = exec.objectMap.get(nameUp);
    if (actualName != null) {
      stackPush(actualName);
      return 0;
    }
    stackPush(nameNorm);
    return 0; 
  }

  /**
   * SQL INSERT statement
   */
  @Override 
  public Integer visitInsert_stmt(HplsqlParser.Insert_stmtContext ctx) { 
    return exec.stmt.insert(ctx); 
  }
  
  /**
   * INSERT DIRECTORY statement
   */
  @Override 
  public Integer visitInsert_directory_stmt(HplsqlParser.Insert_directory_stmtContext ctx) { 
    return exec.stmt.insertDirectory(ctx); 
  }
    
  /**
   * EXCEPTION block
   */
  @Override 
  public Integer visitException_block_item(HplsqlParser.Exception_block_itemContext ctx) { 
    if (exec.signals.empty()) {
      return 0;
    }
    if (exec.conf.onError == OnError.SETERROR || exec.conf.onError == OnError.STOP) {
      exec.signals.pop();
      return 0;
    }
    if (ctx.L_ID().toString().equalsIgnoreCase("OTHERS")) {
      trace(ctx, "EXCEPTION HANDLER");
      exec.signals.pop();
      enterScope(Scope.Type.HANDLER);
      visit(ctx.block());
      leaveScope(); 
    }
    return 0;
  }
    
  /**
   * DECLARE variable statement
   */
  @Override
  public Integer visitDeclare_var_item(HplsqlParser.Declare_var_itemContext ctx) { 
    String type = null;
    TableClass userDefinedType = null;
    Row row = null;
    String len = null;
    String scale = null;
    Var default_ = null;
    if (ctx.dtype().T_ROWTYPE() != null) {
      row = meta.getRowDataType(ctx, exec.conf.defaultConnection, ctx.dtype().qident().getText());
      if (row == null) {
        type = Var.DERIVED_ROWTYPE;
      }
    }
    else {
      type = getDataType(ctx);
      if (ctx.dtype_len() != null) {
        len = ctx.dtype_len().L_INT(0).getText();
        if (ctx.dtype_len().L_INT(1) != null) {
          scale = ctx.dtype_len().L_INT(1).getText();
        }
      }    
      if (ctx.dtype_default() != null) {
        default_ = evalPop(ctx.dtype_default());
      }
      userDefinedType = types.get(type);
      if (userDefinedType != null) {
        type = Type.HPL_OBJECT.name();
      }

    }
	  int cnt = ctx.ident().size();        // Number of variables declared with the same data type and default
	  for (int i = 0; i < cnt; i++) {  	    
	    String name = ctx.ident(i).getText();
	    if (row == null) {
	      Var var = new Var(name, type, len, scale, default_);
	      if (userDefinedType != null && default_ == null) {
	        var.setValue(userDefinedType.newInstance());
        }
	      exec.addVariable(var);
	      if (ctx.T_CONSTANT() != null) {
	        var.setConstant(true);
	      }
	      if (trace) {
	        if (default_ != null) {
	          trace(ctx, "DECLARE " + name + " " + type + " = " + var.toSqlString());
	        }
	        else {
	          trace(ctx, "DECLARE " + name + " " + type);
	        }
	      }
	    }
	    else {
	      exec.addVariable(new Var(name, row));
	      if (trace) {
          trace(ctx, "DECLARE " + name + " " + ctx.dtype().getText());
        }
	    }
	  }	
	  return 0;
  }
  
  /**
   * Get the variable data type
   */
  String getDataType(HplsqlParser.Declare_var_itemContext ctx) {
    String type;
    if (ctx.dtype().T_TYPE() != null) {
      type = meta.getDataType(ctx, exec.conf.defaultConnection, ctx.dtype().qident().getText());
      if (type == null) {
        type = Var.DERIVED_TYPE; 
      }
    }
    else {
      type = getFormattedText(ctx.dtype());
    }
    return type;
  }
  
  /**
   * 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
   */
  @Override 
  public Integer visitDeclare_cursor_item(HplsqlParser.Declare_cursor_itemContext ctx) { 
    return exec.stmt.declareCursor(ctx); 
  }
  
  /**
   * DESCRIBE statement
   */
  @Override 
  public Integer visitDescribe_stmt(HplsqlParser.Describe_stmtContext ctx) {
    return exec.stmt.describe(ctx);
  }
  
  /**
   * DROP statement
   */
  @Override 
  public Integer visitDrop_stmt(HplsqlParser.Drop_stmtContext ctx) { 
    return exec.stmt.drop(ctx); 
  }
  
  /**
   * OPEN cursor statement
   */
  @Override 
  public Integer visitOpen_stmt(HplsqlParser.Open_stmtContext ctx) { 
    return exec.stmt.open(ctx); 
  }  
  
  /**
   * FETCH cursor statement
   */
  @Override 
  public Integer visitFetch_stmt(HplsqlParser.Fetch_stmtContext ctx) { 
    return exec.stmt.fetch(ctx);
  }

  /**
   * CLOSE cursor statement
   */
  @Override 
  public Integer visitClose_stmt(HplsqlParser.Close_stmtContext ctx) { 
    return exec.stmt.close(ctx); 
  }
  
  /**
   * CMP statement
   */
  @Override 
  public Integer visitCmp_stmt(HplsqlParser.Cmp_stmtContext ctx) { 
    return new Cmp(exec, queryExecutor).run(ctx);
  }
  
  /**
   * COPY statement
   */
  @Override 
  public Integer visitCopy_stmt(HplsqlParser.Copy_stmtContext ctx) { 
    return new Copy(exec, queryExecutor).run(ctx);
  }

  /**
   * COPY FROM LOCAL statement
   */
  @Override 
  public Integer visitCopy_from_local_stmt(HplsqlParser.Copy_from_local_stmtContext ctx) { 
    return new Copy(exec, queryExecutor).runFromLocal(ctx);
  }
  
  /**
   * DECLARE HANDLER statement
   */
  @Override 
  public Integer visitDeclare_handler_item(HplsqlParser.Declare_handler_itemContext ctx) {
    trace(ctx, "DECLARE HANDLER");
    Handler.ExecType execType = Handler.ExecType.EXIT;
    Signal.Type type = Signal.Type.SQLEXCEPTION;
    String value = null;
    if (ctx.T_CONTINUE() != null) {
      execType = Handler.ExecType.CONTINUE;
    }    
    if (ctx.ident() != null) {
      type = Signal.Type.USERDEFINED;
      value = ctx.ident().getText();
    }
    else if (ctx.T_NOT() != null && ctx.T_FOUND() != null) {
      type = Signal.Type.NOTFOUND;
    }
    addHandler(new Handler(execType, type, value, exec.currentScope, ctx));
    return 0; 
  }
  
  /**
   * DECLARE CONDITION
   */
  @Override 
  public Integer visitDeclare_condition_item(HplsqlParser.Declare_condition_itemContext ctx) { 
    return 0; 
  }
  
  /**
   * DECLARE TEMPORARY TABLE statement 
   */
  @Override 
  public Integer visitDeclare_temporary_table_item(HplsqlParser.Declare_temporary_table_itemContext ctx) { 
    return exec.stmt.declareTemporaryTable(ctx); 
  }
  
  /**
   * CREATE TABLE statement
   */
  @Override 
  public Integer visitCreate_table_stmt(HplsqlParser.Create_table_stmtContext ctx) { 
    return exec.stmt.createTable(ctx); 
  } 
  
  @Override 
  public Integer visitCreate_table_options_hive_item(HplsqlParser.Create_table_options_hive_itemContext ctx) { 
    return exec.stmt.createTableHiveOptions(ctx); 
  }
  
  @Override 
  public Integer visitCreate_table_options_ora_item(HplsqlParser.Create_table_options_ora_itemContext ctx) { 
    return 0; 
  }
  
  @Override 
  public Integer visitCreate_table_options_td_item(HplsqlParser.Create_table_options_td_itemContext ctx) { 
    return 0; 
  }
  
  @Override 
  public Integer visitCreate_table_options_mssql_item(HplsqlParser.Create_table_options_mssql_itemContext ctx) { 
    return 0; 
  }
  
  @Override 
  public Integer visitCreate_table_options_db2_item(HplsqlParser.Create_table_options_db2_itemContext ctx) { 
    return 0; 
  }
  
  @Override 
  public Integer visitCreate_table_options_mysql_item(HplsqlParser.Create_table_options_mysql_itemContext ctx) { 
    return exec.stmt.createTableMysqlOptions(ctx); 
  }
  
  /**
   * CREATE LOCAL TEMPORARY | VOLATILE TABLE statement 
   */
  @Override 
  public Integer visitCreate_local_temp_table_stmt(HplsqlParser.Create_local_temp_table_stmtContext ctx) { 
    return exec.stmt.createLocalTemporaryTable(ctx); 
  }
  
  /**
   * ALTER TABLE statement
   */
  @Override 
  public Integer visitAlter_table_stmt(HplsqlParser.Alter_table_stmtContext ctx) { 
    return 0; 
  } 
  
  /**
   * CREATE DATABASE | SCHEMA statement
   */
  @Override 
  public Integer visitCreate_database_stmt(HplsqlParser.Create_database_stmtContext ctx) {
    return exec.stmt.createDatabase(ctx);
  }
  
  /**
   * CREATE FUNCTION statement
   */
  @Override 
  public Integer visitCreate_function_stmt(HplsqlParser.Create_function_stmtContext ctx) {
    exec.functions.addUserFunction(ctx);
    addLocalUdf(ctx);
    return 0; 
  }

  /**
   * CREATE PACKAGE specification statement
   */
  @Override 
  public Integer visitCreate_package_stmt(HplsqlParser.Create_package_stmtContext ctx) { 
    String name = ctx.ident(0).getText().toUpperCase();
    if (exec.packageLoading) {
      exec.currentPackageDecl = new Package(name, exec, builtinFunctions);
      exec.packages.put(name, exec.currentPackageDecl);
      exec.currentPackageDecl.createSpecification(ctx);
      exec.currentPackageDecl = null;
    } else {
      trace(ctx, "CREATE PACKAGE");
      exec.packages.remove(name);
      exec.packageRegistry.createPackageHeader(name, getFormattedText(ctx), ctx.T_REPLACE() != null);
    }
    return 0; 
  }

  /**
   * CREATE PACKAGE body statement
   */
  @Override 
  public Integer visitCreate_package_body_stmt(HplsqlParser.Create_package_body_stmtContext ctx) {
    String name = ctx.ident(0).getText().toUpperCase();
    if (exec.packageLoading) {
      exec.currentPackageDecl = exec.packages.get(name);
      if (exec.currentPackageDecl == null) {
        exec.currentPackageDecl = new Package(name, exec, builtinFunctions);
        exec.currentPackageDecl.setAllMembersPublic(true);
        exec.packages.put(name, exec.currentPackageDecl);
      }
      exec.currentPackageDecl.createBody(ctx);
      exec.currentPackageDecl = null;
    } else {
      trace(ctx, "CREATE PACKAGE BODY");
      exec.packages.remove(name);
      exec.packageRegistry.createPackageBody(name,  getFormattedText(ctx), ctx.T_REPLACE() != null);
    }
    return 0;
  }

  /**
   * CREATE PROCEDURE statement
   */
  @Override 
  public Integer visitCreate_procedure_stmt(HplsqlParser.Create_procedure_stmtContext ctx) {
    exec.functions.addUserProcedure(ctx);
    addLocalUdf(ctx);                      // Add procedures as they can be invoked by functions
    return 0; 
  }

  public void dropProcedure(HplsqlParser.Drop_stmtContext ctx, String name, boolean checkIfExists) {
    if (checkIfExists && !functions.exists(name)) {
      trace(ctx, name + " DOES NOT EXIST");
      return;
    }
    functions.remove(name);
    trace(ctx, name + " DROPPED");
  }

  public void dropPackage(HplsqlParser.Drop_stmtContext ctx, String name, boolean checkIfExists) {
    if (checkIfExists && !packageRegistry.getPackage(name).isPresent()) {
      trace(ctx, name + " DOES NOT EXIST");
      return;
    }
    packages.remove(name);
    packageRegistry.dropPackage(name);
    trace(ctx, name + " DROPPED");
  }

  /**
   * CREATE INDEX statement
   */
  @Override 
  public Integer visitCreate_index_stmt(HplsqlParser.Create_index_stmtContext ctx) { 
    return 0; 
  }
  
  /**
   * Add functions and procedures defined in the current script
   */
  void addLocalUdf(ParserRuleContext ctx) {
    if (exec == this) {                              
      localUdf.append(Exec.getFormattedText(ctx));
      localUdf.append("\n");
    }
  }
  
  /**
   * Save local functions and procedures to a file (will be added to the distributed cache) 
   */
  String createLocalUdf() {
    if(localUdf.length() == 0) {
      return null;
    }
    try {
      String file = System.getProperty("user.dir") + "/" + Conf.HPLSQL_LOCALS_SQL; 
      PrintWriter writer = new PrintWriter(file, "UTF-8");
      writer.print(localUdf);
      writer.close();
      return file;
    } 
    catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
      
  /**
   * Assignment statement for single value
   */
  @Override 
  public Integer visitAssignment_stmt_single_item(HplsqlParser.Assignment_stmt_single_itemContext ctx) { 
    String name = ctx.qident().getText();
    visit(ctx.expr());    
    Var var = setVariable(name);
    StringBuilder assignments = new StringBuilder();
    String previousAssignment = stackPop().toString();
    if (previousAssignment != null) {
      assignments.append(previousAssignment);
      assignments.append(", ");
    }
    assignments.append(name);
    assignments.append(" = ");
    assignments.append(var.toString());
    if (exec.buildSql) {
      stackPush(assignments);
    }
    if (trace) {
      trace(ctx, "SET " + name + " = " + var.toSqlString());      
    }    
    return 0;
  }  

  /**
   * Assignment statement for multiple values
   */
  @Override 
  public Integer visitAssignment_stmt_multiple_item(HplsqlParser.Assignment_stmt_multiple_itemContext ctx) { 
    int cnt = ctx.ident().size();
    int ecnt = ctx.expr().size();
    StringBuilder identifiers = new StringBuilder("(");
    StringBuilder expressions = new StringBuilder("(");
    for (int i = 0; i < cnt; i++) {
      String name = ctx.ident(i).getText();
      if (i < ecnt) {
        visit(ctx.expr(i));
        Var var = setVariable(name);
        if (i > 0) {
          identifiers.append(", ");
          expressions.append(", ");
        }
        identifiers.append(name);
        expressions.append(var.toString());
        if (trace) {
          trace(ctx, "SET " + name + " = " + var.toString());      
        } 
      }      
    }
    identifiers.append(")");
    expressions.append(")");
    if (exec.buildSql) {
      stackPush(identifiers.toString() + " = " + expressions.toString());
    }
    return 0; 
  }
  
  /**
   * Assignment from SELECT statement 
   */
  @Override 
  public Integer visitAssignment_stmt_select_item(HplsqlParser.Assignment_stmt_select_itemContext ctx) { 
    return stmt.assignFromSelect(ctx); 
  }

  @Override
  public Integer visitAssignment_stmt_collection_item(HplsqlParser.Assignment_stmt_collection_itemContext ctx) {
    HplsqlParser.Expr_funcContext lhs = ctx.expr_func();
    Var var = findVariable(lhs.ident().getText());
    if (var == null || var.type != Type.HPL_OBJECT) {
      stackPush(Var.Null);
      return 0;
    }
    UNARY.check(lhs.ident().getText(), lhs.expr_func_params().func_param());
    Var index = evalPop(lhs.expr_func_params().func_param(0));
    Var value = evalPop(ctx.expr());
    dispatch(ctx, (HplObject) var.value, __SETITEM__, Arrays.asList(index, value));
    return 0;
  }

  /**
   * Evaluate an expression
   */
  @Override 
  public Integer visitExpr(HplsqlParser.ExprContext ctx) { 
    if (exec.buildSql) {
      exec.expr.execSql(ctx);
    }
    else {
      exec.expr.exec(ctx);
    }
    return 0;
  }

  /**
   * Evaluate a boolean expression
   */
  @Override 
  public Integer visitBool_expr(HplsqlParser.Bool_exprContext ctx) {
    if (exec.buildSql) {
      exec.expr.execBoolSql(ctx);
    }
    else {
      exec.expr.execBool(ctx);
    }
    return 0; 
  }
  
  @Override 
  public Integer visitBool_expr_binary(HplsqlParser.Bool_expr_binaryContext ctx) {
    if (exec.buildSql) {
      exec.expr.execBoolBinarySql(ctx);
    }
    else {
      exec.expr.execBoolBinary(ctx);
    }
    return 0; 
  }
  
  @Override 
  public Integer visitBool_expr_unary(HplsqlParser.Bool_expr_unaryContext ctx) {
    if (exec.buildSql) {
      exec.expr.execBoolUnarySql(ctx);
    }
    else {
      exec.expr.execBoolUnary(ctx);
    }
    return 0; 
  }
   
  /**
   * Static SELECT statement (i.e. unquoted) or expression
   */
  @Override 
  public Integer visitExpr_select(HplsqlParser.Expr_selectContext ctx) {
    if (ctx.select_stmt() != null) {
      stackPush(new Var(evalPop(ctx.select_stmt())));
    }
    else {
      visit(ctx.expr());
    }
    return 0; 
  }
  
  /**
   * File path (unquoted) or expression
   */
  @Override 
  public Integer visitExpr_file(HplsqlParser.Expr_fileContext ctx) {
    if (ctx.file_name() != null) {
      stackPush(new Var(ctx.file_name().getText()));
    }
    else {
      visit(ctx.expr());
    }
    return 0; 
  }
  
  /**
   * Cursor attribute %ISOPEN, %FOUND and %NOTFOUND
   */
  @Override 
  public Integer visitExpr_cursor_attribute(HplsqlParser.Expr_cursor_attributeContext ctx) {
    exec.expr.execCursorAttribute(ctx);
    return 0; 
  }

  /**
   * Function call
   */
  @Override
  public Integer visitExpr_func(HplsqlParser.Expr_funcContext ctx) {
    return functionCall(ctx, ctx.ident(), ctx.expr_func_params());
  }

  private int functionCall(ParserRuleContext ctx, HplsqlParser.IdentContext ident, HplsqlParser.Expr_func_paramsContext params) {
    String name = ident.getText();
    if (exec.buildSql && !builtinFunctions.exists(name)) {
      exec.execSql(name, params);
    } else {
      name = name.toUpperCase();
      Package packCallContext = exec.getPackageCallContext();
      ArrayList qualified = exec.meta.splitIdentifier(name);
      boolean executed = false;
      if (qualified != null) {
        Package pack = findPackage(qualified.get(0));
        if (pack != null) {
          executed = pack.execFunc(qualified.get(1), params);
        }
      }
      if (!executed && packCallContext != null) {
        executed = packCallContext.execFunc(name, params);
      }
      if (!executed) {
        if (!exec.functions.exec(name, params)) {
          Var var = findVariable(name);
          if (var != null && var.type == Type.HPL_OBJECT) {
            stackPush(dispatch(ctx, (HplObject)var.value, __GETITEM__, params));
          } else {
            throw new UndefinedIdentException(ctx, name);
          }
        }
      }
    }
    return 0;
  }

  private Var dispatch(ParserRuleContext ctx, HplObject obj, String methodName, HplsqlParser.Expr_func_paramsContext paramCtx) {
    List params = paramCtx == null
            ? Collections.emptyList()
            : paramCtx.func_param().stream().map(this::evalPop).collect(Collectors.toList());
    return dispatch(ctx, obj, methodName, params);
  }

  private Var dispatch(ParserRuleContext ctx, HplObject obj, String methodName, List params) {
    Method method = obj.hplClass().methodDictionary().get(ctx, methodName);
    return method.call(obj, params);
  }

  /**
   * @return either 1 rowtype OR 1 single column table OR n single column tables
   */
  public List intoTables(ParserRuleContext ctx, List names) {
    List
tables = new ArrayList<>(); for (String name : names) { Var var = findVariable(name); if (var == null) { trace(ctx, "Variable not found: " + name); } else if (var.type == Type.HPL_OBJECT && var.value instanceof Table) { tables.add((Table)var.value); } else { throw new TypeException(ctx, Table.class, var.type, var.value); } } if (tables.size() > 1 && tables.stream().anyMatch(tbl -> tbl.hplClass().rowType())) { throw new TypeException(ctx, "rowtype table should not be used when selecting into multiple tables"); } return tables; } /** * User-defined function in a SQL query */ public void execSql(String name, HplsqlParser.Expr_func_paramsContext ctx) { try { if (execUserSql(ctx, name)) { return; } } catch (TException e) { throw new HplValidationException(ctx, e); } StringBuilder sql = new StringBuilder(); sql.append(name); sql.append("("); if (ctx != null) { int cnt = ctx.func_param().size(); for (int i = 0; i < cnt; i++) { sql.append(evalPop(ctx.func_param(i).expr())); if (i + 1 < cnt) { sql.append(", "); } } } sql.append(")"); Var var = new Var(Type.SQL_STRING, sql); exec.stackPush(var); } /** * Execute a HPL/SQL user-defined function in a query. * For example converts: select fn(col) from table to select hplsql('fn(:1)', col) from table */ private boolean execUserSql(HplsqlParser.Expr_func_paramsContext ctx, String name) throws TException { if (!functions.exists(name)) { return false; } StringBuilder sql = new StringBuilder(); sql.append("hplsql('"); sql.append(name); sql.append("("); int cnt = ctx.func_param().size(); for (int i = 0; i < cnt; i++) { sql.append(":").append(i + 1); if (i + 1 < cnt) { sql.append(", "); } } sql.append(")'"); if (cnt > 0) { sql.append(", "); } for (int i = 0; i < cnt; i++) { sql.append(evalPop(ctx.func_param(i).expr())); if (i + 1 < cnt) { sql.append(", "); } } sql.append(", \""); sql.append(getStoredProcedure(name.toUpperCase())); sql.append("\")"); Var var = new Var(Type.HPL_SQL_UDF, sql); exec.stackPush(var); exec.registerUdf(); return true; } /** * Get stored procedure from HMS * * @param functionName name of the procedure * @return procedure */ private String getStoredProcedure(String functionName) throws TException { SessionState sessionState = SessionState.get(); StoredProcedure storedProcedure = getMsc().getStoredProcedure( new StoredProcedureRequest( sessionState != null ? sessionState.getCurrentCatalog() : hplSqlSession.currentCatalog(), sessionState != null ? sessionState.getCurrentDatabase() : hplSqlSession.currentDatabase(), functionName)); return storedProcedure != null ? storedProcedure.getSource() : null; } /** * Aggregate or window function call */ @Override public Integer visitExpr_agg_window_func(HplsqlParser.Expr_agg_window_funcContext ctx) { Var var = new Var(Type.SQL_STRING, (Object) Exec.getFormattedText(ctx)); exec.stackPush(var); return 0; } /** * Function with specific syntax */ @Override public Integer visitExpr_spec_func(HplsqlParser.Expr_spec_funcContext ctx) { if (exec.buildSql) { exec.builtinFunctions.specExecSql(ctx); } else { exec.builtinFunctions.specExec(ctx); } return 0; } /** * INCLUDE statement */ @Override public Integer visitInclude_stmt(@NotNull HplsqlParser.Include_stmtContext ctx) { return exec.stmt.include(ctx); } /** * IF statement (PL/SQL syntax) */ @Override public Integer visitIf_plsql_stmt(HplsqlParser.If_plsql_stmtContext ctx) { return exec.stmt.ifPlsql(ctx); } /** * IF statement (Transact-SQL syntax) */ @Override public Integer visitIf_tsql_stmt(HplsqlParser.If_tsql_stmtContext ctx) { return exec.stmt.ifTsql(ctx); } /** * IF statement (BTEQ syntax) */ @Override public Integer visitIf_bteq_stmt(HplsqlParser.If_bteq_stmtContext ctx) { return exec.stmt.ifBteq(ctx); } /** * USE statement */ @Override public Integer visitUse_stmt(HplsqlParser.Use_stmtContext ctx) { return exec.stmt.use(ctx); } /** * VALUES statement */ @Override public Integer visitValues_into_stmt(HplsqlParser.Values_into_stmtContext ctx) { return exec.stmt.values(ctx); } /** * WHILE statement */ @Override public Integer visitWhile_stmt(HplsqlParser.While_stmtContext ctx) { return exec.stmt.while_(ctx); } @Override public Integer visitUnconditional_loop_stmt(HplsqlParser.Unconditional_loop_stmtContext ctx) { return exec.stmt.unconditionalLoop(ctx); } /** * FOR cursor statement */ @Override public Integer visitFor_cursor_stmt(HplsqlParser.For_cursor_stmtContext ctx) { return exec.stmt.forCursor(ctx); } /** * FOR (integer range) statement */ @Override public Integer visitFor_range_stmt(HplsqlParser.For_range_stmtContext ctx) { return exec.stmt.forRange(ctx); } /** * EXEC, EXECUTE and EXECUTE IMMEDIATE statement to execute dynamic SQL */ @Override public Integer visitExec_stmt(HplsqlParser.Exec_stmtContext ctx) { exec.inCallStmt = true; Integer rc = exec.stmt.exec(ctx); exec.inCallStmt = false; return rc; } /** * CALL statement */ @Override public Integer visitCall_stmt(HplsqlParser.Call_stmtContext ctx) { exec.inCallStmt = true; try { if (ctx.expr_func() != null) { functionCall(ctx, ctx.expr_func().ident(), ctx.expr_func().expr_func_params()); } else if (ctx.expr_dot() != null) { visitExpr_dot(ctx.expr_dot()); } else if (ctx.ident() != null) { functionCall(ctx, ctx.ident(), null); } } finally { exec.inCallStmt = false; } return 0; } /** * EXIT statement (leave the specified loop with a condition) */ @Override public Integer visitExit_stmt(HplsqlParser.Exit_stmtContext ctx) { return exec.stmt.exit(ctx); } /** * BREAK statement (leave the innermost loop unconditionally) */ @Override public Integer visitBreak_stmt(HplsqlParser.Break_stmtContext ctx) { return exec.stmt.break_(ctx); } /** * LEAVE statement (leave the specified loop unconditionally) */ @Override public Integer visitLeave_stmt(HplsqlParser.Leave_stmtContext ctx) { return exec.stmt.leave(ctx); } /** * PRINT statement */ @Override public Integer visitPrint_stmt(HplsqlParser.Print_stmtContext ctx) { return exec.stmt.print(ctx); } /** * QUIT statement */ @Override public Integer visitQuit_stmt(HplsqlParser.Quit_stmtContext ctx) { return exec.stmt.quit(ctx); } /** * SIGNAL statement */ @Override public Integer visitSignal_stmt(HplsqlParser.Signal_stmtContext ctx) { return exec.stmt.signal(ctx); } /** * SUMMARY statement */ @Override public Integer visitSummary_stmt(HplsqlParser.Summary_stmtContext ctx) { return exec.stmt.summary(ctx); } /** * RESIGNAL statement */ @Override public Integer visitResignal_stmt(HplsqlParser.Resignal_stmtContext ctx) { return exec.stmt.resignal(ctx); } /** * RETURN statement */ @Override public Integer visitReturn_stmt(HplsqlParser.Return_stmtContext ctx) { return exec.stmt.return_(ctx); } /** * SET session options */ @Override public Integer visitSet_current_schema_option(HplsqlParser.Set_current_schema_optionContext ctx) { return exec.stmt.setCurrentSchema(ctx); } /** * TRUNCATE statement */ @Override public Integer visitTruncate_stmt(HplsqlParser.Truncate_stmtContext ctx) { return exec.stmt.truncate(ctx); } @Override public Integer visitCreate_table_type_stmt(HplsqlParser.Create_table_type_stmtContext ctx) { String name = ctx.ident().getText(); String index = ctx.dtype().getText(); if (!"BINARY_INTEGER".equalsIgnoreCase(index)) { throw new TypeException(ctx, "Unsupported table index: " + index + " Use: BINARY_INTEGER"); } HplsqlParser.Tbl_typeContext tblType = ctx.tbl_type(); if (tblType.sql_type() != null) { String dbTable = tblType.sql_type().qident().getText(); if (tblType.sql_type().T_ROWTYPE() != null) { Row rowType = meta.getRowDataType(ctx, exec.conf.defaultConnection, dbTable); exec.addType(new TableClass(name, rowType.columnDefinitions(), true)); } else if (dbTable.contains(".")) { // column type String column = dbTable.substring(dbTable.indexOf(".") + 1); String colType = meta.getDataType(ctx, exec.conf.defaultConnection, dbTable); exec.addType(new TableClass(name, singletonList(new ColumnDefinition(column, ColumnType.parse(colType))), false)); } else { throw new TypeException(ctx, "Invalid table type attribute. Expected %TYPE or %ROWTYPE"); } if (trace) { trace(ctx, "CREATE TABLE TYPE: " + name + " TYPE: " + dbTable + " INDEX: " + index); } } else { String colType = tblType.dtype().getText(); exec.addType(new TableClass(name, singletonList(ColumnDefinition.unnamed(ColumnType.parse(colType))), false)); if (trace) { trace(ctx, "CREATE TABLE TYPE: " + name + " TYPE: " + colType + " INDEX: " + index); } } return 1; } private void addType(TableClass tableClass) { types.put(tableClass.typeName(), tableClass); } public TableClass getType(String name) { return types.get(name); } /** * MAP OBJECT statement */ @Override public Integer visitMap_object_stmt(HplsqlParser.Map_object_stmtContext ctx) { String source = ctx.ident(0).getText(); String target = null; String conn = null; if (ctx.T_TO() != null) { target = ctx.ident(1).getText(); exec.objectMap.put(source.toUpperCase(), target); } if (ctx.T_AT() != null) { if (ctx.T_TO() == null) { conn = ctx.ident(1).getText(); } else { conn = ctx.ident(2).getText(); } exec.objectConnMap.put(source.toUpperCase(), conn); } if (trace) { String log = "MAP OBJECT " + source; if (target != null) { log += " AS " + target; } if (conn != null) { log += " AT " + conn; } trace(ctx, log); } return 0; } /** * UPDATE statement */ @Override public Integer visitUpdate_stmt(HplsqlParser.Update_stmtContext ctx) { return stmt.update(ctx); } /** * DELETE statement */ @Override public Integer visitDelete_stmt(HplsqlParser.Delete_stmtContext ctx) { return stmt.delete(ctx); } /** * MERGE statement */ @Override public Integer visitMerge_stmt(HplsqlParser.Merge_stmtContext ctx) { return stmt.merge(ctx); } /** * Run a Hive command line */ @Override public Integer visitHive(@NotNull HplsqlParser.HiveContext ctx) { trace(ctx, "HIVE"); ArrayList cmd = new ArrayList<>(); cmd.add("hive"); Var params = new Var(Var.Type.STRINGLIST, cmd); stackPush(params); visitChildren(ctx); stackPop(); try { String[] cmdarr = new String[cmd.size()]; cmd.toArray(cmdarr); if(trace) { trace(ctx, "HIVE Parameters: " + Utils.toString(cmdarr, ' ')); } if (!offline) { Process p = Runtime.getRuntime().exec(cmdarr); new StreamGobbler(p.getInputStream(), console).start(); new StreamGobbler(p.getErrorStream(), console).start(); int rc = p.waitFor(); if (trace) { trace(ctx, "HIVE Process exit code: " + rc); } } } catch (Exception e) { setSqlCode(SqlCodes.ERROR); signal(Signal.Type.SQLEXCEPTION, e.getMessage(), e); return -1; } return 0; } @Override @SuppressWarnings("unchecked") public Integer visitHive_item(HplsqlParser.Hive_itemContext ctx) { Var params = stackPeek(); ArrayList a = (ArrayList)params.value; String param = ctx.getChild(1).getText(); switch (param) { case "e": a.add("-e"); a.add(evalPop(ctx.expr()).toString()); break; case "f": a.add("-f"); a.add(evalPop(ctx.expr()).toString()); break; case "hiveconf": a.add("-hiveconf"); a.add(ctx.L_ID().toString() + "=" + evalPop(ctx.expr()).toString()); break; } return 0; } /** * Executing OS command */ @Override public Integer visitHost_cmd(HplsqlParser.Host_cmdContext ctx) { trace(ctx, "HOST"); execHost(ctx, ctx.start.getInputStream().getText( new org.antlr.v4.runtime.misc.Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()))); return 0; } @Override public Integer visitHost_stmt(HplsqlParser.Host_stmtContext ctx) { trace(ctx, "HOST"); execHost(ctx, evalPop(ctx.expr()).toString()); return 0; } public void execHost(ParserRuleContext ctx, String cmd) { try { if (trace) { trace(ctx, "HOST Command: " + cmd); } Process p = Runtime.getRuntime().exec(cmd); new StreamGobbler(p.getInputStream(), console).start(); new StreamGobbler(p.getErrorStream(), console).start(); int rc = p.waitFor(); if (trace) { trace(ctx, "HOST Process exit code: " + rc); } setHostCode(rc); } catch (Exception e) { setHostCode(1); signal(Signal.Type.SQLEXCEPTION); } } /** * Standalone expression (as a statement) */ @Override public Integer visitExpr_stmt(HplsqlParser.Expr_stmtContext ctx) { visitChildren(ctx); return 0; } /** * String concatenation operator */ @Override public Integer visitExpr_concat(HplsqlParser.Expr_concatContext ctx) { if (exec.buildSql) { exec.expr.operatorConcatSql(ctx); } else { exec.expr.operatorConcat(ctx); } return 0; } @Override public Integer visitExpr_dot_method_call(HplsqlParser.Expr_dot_method_callContext ctx) { if (exec.buildSql) { exec.stackPush(new Var(Var.Type.IDENT, ctx.getText())); return 0; } Var var = ctx.ident() != null ? findVariable(ctx.ident().getText()) : evalPop(ctx.expr_func(0)); if (var == null && ctx.ident() != null) { Package pkg = findPackage(ctx.ident().getText()); String pkgFuncName = ctx.expr_func(0).ident().getText().toUpperCase(); boolean executed = pkg.execFunc(pkgFuncName, ctx.expr_func(0).expr_func_params()); Package packCallContext = exec.getPackageCallContext(); if (!executed && packCallContext != null) { packCallContext.execFunc(pkgFuncName, ctx.expr_func(0).expr_func_params()); } return 0; } HplsqlParser.Expr_funcContext method = ctx.expr_func(ctx.expr_func().size() - 1); switch (var.type) { case HPL_OBJECT: Var result = dispatch(ctx, (HplObject) var.value, method.ident().getText(), method.expr_func_params()); stackPush(result); return 0; default: throw new TypeException(ctx, var.type + " is not an object"); } } @Override public Integer visitExpr_dot_property_access(HplsqlParser.Expr_dot_property_accessContext ctx) { if (exec.buildSql) { exec.stackPush(new Var(Var.Type.IDENT, ctx.getText())); return 0; } Var var = ctx.expr_func() != null ? evalPop(ctx.expr_func()) : findVariable(ctx.ident(0).getText()); String property = ctx.ident(ctx.ident().size() -1).getText(); if (var == null && ctx.expr_func() == null) { Package pkg = findPackage(ctx.ident(0).getText()); Var variable = pkg.findVariable(property); if (variable != null) { stackPush(variable); } else { Package packCallContext = exec.getPackageCallContext(); stackPush(packCallContext.findVariable(property)); } return 0; } switch (var.type) { case HPL_OBJECT: Var result = dispatch(ctx, (HplObject) var.value, property, Collections.emptyList()); stackPush(result); return 0; case ROW: stackPush(((Row)var.value).getValue(property)); return 0; default: throw new TypeException(ctx, var.type + " is not an object/row"); } } /** * Simple CASE expression */ @Override public Integer visitExpr_case_simple(HplsqlParser.Expr_case_simpleContext ctx) { if (exec.buildSql) { exec.expr.execSimpleCaseSql(ctx); } else { exec.expr.execSimpleCase(ctx); } return 0; } /** * Searched CASE expression */ @Override public Integer visitExpr_case_searched(HplsqlParser.Expr_case_searchedContext ctx) { if (exec.buildSql) { exec.expr.execSearchedCaseSql(ctx); } else { exec.expr.execSearchedCase(ctx); } return 0; } /** * GET DIAGNOSTICS EXCEPTION statement */ @Override public Integer visitGet_diag_stmt_exception_item(HplsqlParser.Get_diag_stmt_exception_itemContext ctx) { return exec.stmt.getDiagnosticsException(ctx); } /** * GET DIAGNOSTICS ROW_COUNT statement */ @Override public Integer visitGet_diag_stmt_rowcount_item(HplsqlParser.Get_diag_stmt_rowcount_itemContext ctx) { return exec.stmt.getDiagnosticsRowCount(ctx); } /** * GRANT statement */ @Override public Integer visitGrant_stmt(HplsqlParser.Grant_stmtContext ctx) { trace(ctx, "GRANT"); return 0; } /** * Label */ @Override public Integer visitLabel(HplsqlParser.LabelContext ctx) { if (ctx.L_ID() != null) { exec.labels.push(ctx.L_ID().toString()); } else { String label = ctx.L_LABEL().getText(); if (label.endsWith(":")) { label = label.substring(0, label.length() - 1); } exec.labels.push(label); } return 0; } /** * Identifier */ @Override public Integer visitIdent(HplsqlParser.IdentContext ctx) { boolean hasSub = false; String ident = ctx.getText(); String actualIdent = ident; if (ident.startsWith("-")) { hasSub = true; actualIdent = ident.substring(1); } Var var = findVariable(actualIdent); if (var != null) { if (!exec.buildSql) { if (hasSub) { Var var1 = new Var(var); var1.negate(); exec.stackPush(var1); } else { exec.stackPush(var); } } else { exec.stackPush(new Var(ident, Type.VARIABLE, var.toSqlString())); } } else { if (exec.buildSql || exec.inCallStmt) { exec.stackPush(new Var(Var.Type.IDENT, ident)); } else { ident = ident.toUpperCase(); if (!exec.functions.exec(ident, null)) { throw new UndefinedIdentException(ctx, ident); } } } return 0; } /** * Single quoted string literal */ @Override public Integer visitSingle_quotedString(HplsqlParser.Single_quotedStringContext ctx) { exec.stackPush(Utils.unquoteString(ctx.getText())); return 0; } /** * Integer literal, signed or unsigned */ @Override public Integer visitInt_number(HplsqlParser.Int_numberContext ctx) { exec.stack.push(new Var(Long.valueOf(ctx.getText()))); return 0; } /** * Interval expression (INTERVAL '1' DAY i.e) */ @Override public Integer visitExpr_interval(HplsqlParser.Expr_intervalContext ctx) { int num = evalPop(ctx.expr()).intValue(); Interval interval = new Interval().set(num, ctx.interval_item().getText()); stackPush(new Var(interval)); return 0; } /** * Decimal literal, signed or unsigned */ @Override public Integer visitDec_number(HplsqlParser.Dec_numberContext ctx) { stackPush(new Var(new BigDecimal(ctx.getText()))); return 0; } /** * Boolean literal */ @Override public Integer visitBool_literal(HplsqlParser.Bool_literalContext ctx) { boolean val = true; if (ctx.T_FALSE() != null) { val = false; } stackPush(new Var(val)); return 0; } /** * NULL constant */ @Override public Integer visitNull_const(HplsqlParser.Null_constContext ctx) { stackPush(new Var()); return 0; } /** * DATE 'YYYY-MM-DD' literal */ @Override public Integer visitDate_literal(HplsqlParser.Date_literalContext ctx) { if (!exec.buildSql) { String str = evalPop(ctx.string()).toString(); stackPush(new Var(Var.Type.DATE, Utils.toDate(str))); } else { stackPush(getFormattedText(ctx)); } return 0; } /** * TIMESTAMP 'YYYY-MM-DD HH:MI:SS.FFF' literal */ @Override public Integer visitTimestamp_literal(HplsqlParser.Timestamp_literalContext ctx) { if (!exec.buildSql) { String str = evalPop(ctx.string()).toString(); int len = str.length(); int precision = 0; if (len > 19 && len <= 29) { precision = len - 20; if (precision > 3) { precision = 3; } } stackPush(new Var(Utils.toTimestamp(str), precision)); } else { stackPush(getFormattedText(ctx)); } return 0; } /** * Get the package context within which the current routine is executed */ Package getPackageCallContext() { Scope cur = exec.currentScope; while (cur != null) { if (cur.type == Scope.Type.ROUTINE) { return cur.pack; } cur = cur.parent; } return null; } /** * Define the connection profile to execute the current statement */ public String getStatementConnection() { if (exec.stmtConnList.contains(exec.conf.defaultConnection)) { return exec.conf.defaultConnection; } else if (!exec.stmtConnList.isEmpty()) { return exec.stmtConnList.get(0); } return exec.conf.defaultConnection; } /** * Define the connection profile for the specified object * @return */ String getObjectConnection(String name) { String conn = exec.objectConnMap.get(name.toUpperCase()); if (conn != null) { return conn; } return exec.conf.defaultConnection; } /** * Get the connection (open the new connection if not available) * @throws Exception */ Connection getConnection(String conn) throws Exception { if (conn == null || conn.equalsIgnoreCase("default")) { conn = exec.conf.defaultConnection; } return exec.conn.getConnection(conn); } /** * Return the connection to the pool */ void returnConnection(String name, Connection conn) { exec.conn.returnConnection(name, conn); } /** * Define the database type by profile name */ Conn.Type getConnectionType(String conn) { return exec.conn.getTypeByProfile(conn); } /** * Get the current database type */ public Conn.Type getConnectionType() { return getConnectionType(exec.conf.defaultConnection); } /** * Add managed temporary table */ public void addManagedTable(String name, String managedName) { exec.managedTables.put(name, managedName); } /** * Get node text including spaces */ String getText(ParserRuleContext ctx) { return ctx.start.getInputStream().getText(new org.antlr.v4.runtime.misc.Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex())); } String getText(ParserRuleContext ctx, Token start, Token stop) { return ctx.start.getInputStream().getText(new org.antlr.v4.runtime.misc.Interval(start.getStartIndex(), stop.getStopIndex())); } /** * Append the text preserving the formatting (space symbols) between tokens */ void append(StringBuilder str, String appendStr, Token start, Token stop) { String spaces = start.getInputStream().getText(new org.antlr.v4.runtime.misc.Interval(start.getStartIndex(), stop.getStopIndex())); spaces = spaces.substring(start.getText().length(), spaces.length() - stop.getText().length()); str.append(spaces); str.append(appendStr); } void append(StringBuilder str, TerminalNode start, TerminalNode stop) { String text = start.getSymbol().getInputStream().getText(new org.antlr.v4.runtime.misc.Interval(start.getSymbol().getStartIndex(), stop.getSymbol().getStopIndex())); str.append(text); } /** * Get the first non-null node */ TerminalNode nvl(TerminalNode t1, TerminalNode t2) { if (t1 != null) { return t1; } return t2; } /** * Evaluate the expression and pop value from the stack */ public Var evalPop(ParserRuleContext ctx) { visit(ctx); if (!exec.stack.isEmpty()) { return exec.stackPop(); } return Var.Empty; } /** * Evaluate the data type and length * */ String evalPop(HplsqlParser.DtypeContext type, HplsqlParser.Dtype_lenContext len) { if (isConvert(exec.conf.defaultConnection)) { return exec.converter.dataType(type, len); } return getText(type, type.getStart(), len == null ? type.getStop() : len.getStop()); } /** * Get formatted text between 2 tokens */ public static String getFormattedText(ParserRuleContext ctx, int startIndex, int endIndex) { return ctx.start.getInputStream().getText( new org.antlr.v4.runtime.misc.Interval(startIndex, endIndex)); } /** * Get formatted text between 2 tokens */ public static String getFormattedText(ParserRuleContext ctx) { return getFormattedText(ctx, ctx.start.getStartIndex(), ctx.stop.getStopIndex()); } /** * Flag whether executed from UDF or not */ public void setUdfRun(boolean udfRun) { this.udfRun = udfRun; } /** * Whether on-the-fly SQL conversion is required for the connection */ boolean isConvert(String connName) { return exec.conf.getConnectionConvert(connName); } /** * Increment the row count */ public int incRowCount() { return exec.rowCount++; } /** * Set the row count */ public void setRowCount(int rowCount) { exec.rowCount = rowCount; } /** * Trace information */ public void trace(ParserRuleContext ctx, String message) { if (!trace) { return; } if (ctx != null) { console.printLine("Ln:" + ctx.getStart().getLine() + " " + message); } else { console.printLine(message); } } /** * Trace values retrived from the database */ public void trace(ParserRuleContext ctx, Var var, Metadata meta, int idx) { if (var.type != Var.Type.ROW) { trace(ctx, "COLUMN: " + meta.columnName(idx) + ", " + meta.columnTypeName(idx)); trace(ctx, "SET " + var.getName() + " = " + var.toString()); } else { Row row = (Row)var.value; int cnt = row.size(); for (int j = 1; j <= cnt; j++) { Var v = row.getValue(j - 1); trace(ctx, "COLUMN: " + meta.columnName(j) + ", " + meta.columnTypeName(j)); trace(ctx, "SET " + v.getName() + " = " + v.toString()); } } } /** * Informational messages */ public void info(ParserRuleContext ctx, String message) { if (!info) { return; } if (ctx != null) { console.printError("Ln:" + ctx.getStart().getLine() + " " + message); } else { console.printError(message); } } /** * Error message */ public void error(ParserRuleContext ctx, String message) { if (ctx != null) { console.printError("Ln:" + ctx.getStart().getLine() + " " + message); } else { console.printError(message); } } public Stack getStack() { return exec.stack; } public int getRowCount() { return exec.rowCount; } public Conf getConf() { return exec.conf; } public Meta getMeta() { return exec.meta; } public boolean getTrace() { return exec.trace; } public boolean getInfo() { return exec.info; } public boolean getOffline() { return exec.offline; } public Console getConsole() { return console; } public void setQueryExecutor(QueryExecutor queryExecutor) { this.queryExecutor = queryExecutor; } public IMetaStoreClient getMsc() { return msc; } }