content: /*
   * Copyright 1999-2017 Alibaba Group Holding Ltd.
   *
   * Licensed 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 com.alibaba.druid.sql.parser;
  
  import com.alibaba.druid.DbType;
  import com.alibaba.druid.sql.SQLUtils;
  import com.alibaba.druid.sql.ast.SQLExpr;
  import com.alibaba.druid.sql.ast.SQLName;
  import com.alibaba.druid.sql.ast.SQLStatement;
  import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
  import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.athena.parser.AthenaExprParser;
  import com.alibaba.druid.sql.dialect.athena.parser.AthenaLexer;
  import com.alibaba.druid.sql.dialect.athena.parser.AthenaStatementParser;
  import com.alibaba.druid.sql.dialect.bigquery.parser.BigQueryExprParser;
  import com.alibaba.druid.sql.dialect.bigquery.parser.BigQueryLexer;
  import com.alibaba.druid.sql.dialect.bigquery.parser.BigQueryStatementParser;
  import com.alibaba.druid.sql.dialect.blink.parser.BlinkStatementParser;
  import com.alibaba.druid.sql.dialect.clickhouse.parser.CKExprParser;
  import com.alibaba.druid.sql.dialect.clickhouse.parser.CKLexer;
  import com.alibaba.druid.sql.dialect.clickhouse.parser.CKStatementParser;
  import com.alibaba.druid.sql.dialect.databricks.parser.DatabricksExprParser;
  import com.alibaba.druid.sql.dialect.databricks.parser.DatabricksLexer;
  import com.alibaba.druid.sql.dialect.databricks.parser.DatabricksStatementParser;
  import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;
  import com.alibaba.druid.sql.dialect.db2.parser.DB2ExprParser;
  import com.alibaba.druid.sql.dialect.db2.parser.DB2Lexer;
  import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser;
  import com.alibaba.druid.sql.dialect.doris.parser.DorisExprParser;
  import com.alibaba.druid.sql.dialect.doris.parser.DorisLexer;
  import com.alibaba.druid.sql.dialect.doris.parser.DorisStatementParser;
  import com.alibaba.druid.sql.dialect.gaussdb.parser.GaussDbExprParser;
  import com.alibaba.druid.sql.dialect.gaussdb.parser.GaussDbLexer;
  import com.alibaba.druid.sql.dialect.gaussdb.parser.GaussDbStatementParser;
  import com.alibaba.druid.sql.dialect.h2.parser.H2ExprParser;
  import com.alibaba.druid.sql.dialect.h2.parser.H2Lexer;
  import com.alibaba.druid.sql.dialect.h2.parser.H2StatementParser;
  import com.alibaba.druid.sql.dialect.hive.parser.HiveExprParser;
  import com.alibaba.druid.sql.dialect.hive.parser.HiveLexer;
  import com.alibaba.druid.sql.dialect.hive.parser.HiveStatementParser;
  import com.alibaba.druid.sql.dialect.hologres.parser.HologresExprParser;
  import com.alibaba.druid.sql.dialect.hologres.parser.HologresLexer;
  import com.alibaba.druid.sql.dialect.hologres.parser.HologresStatementParser;
  import com.alibaba.druid.sql.dialect.impala.parser.ImpalaExprParser;
  import com.alibaba.druid.sql.dialect.impala.parser.ImpalaLexer;
  import com.alibaba.druid.sql.dialect.impala.parser.ImpalaStatementParser;
  import com.alibaba.druid.sql.dialect.informix.parser.InformixStatementParser;
  import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser;
  import com.alibaba.druid.sql.dialect.mysql.parser.MySqlLexer;
  import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
  import com.alibaba.druid.sql.dialect.odps.ast.OdpsSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.odps.parser.OdpsExprParser;
  import com.alibaba.druid.sql.dialect.odps.parser.OdpsLexer;
  import com.alibaba.druid.sql.dialect.odps.parser.OdpsStatementParser;
  import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.oracle.parser.OracleExprParser;
  import com.alibaba.druid.sql.dialect.oracle.parser.OracleLexer;
  import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser;
  import com.alibaba.druid.sql.dialect.oscar.ast.stmt.OscarSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.oscar.parser.OscarExprParser;
  import com.alibaba.druid.sql.dialect.oscar.parser.OscarLexer;
  import com.alibaba.druid.sql.dialect.oscar.visitor.OscarStatementParser;
  import com.alibaba.druid.sql.dialect.phoenix.parser.PhoenixExprParser;
  import com.alibaba.druid.sql.dialect.phoenix.parser.PhoenixLexer;
  import com.alibaba.druid.sql.dialect.phoenix.parser.PhoenixStatementParser;
  import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.postgresql.parser.PGExprParser;
  import com.alibaba.druid.sql.dialect.postgresql.parser.PGLexer;
  import com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser;
  import com.alibaba.druid.sql.dialect.presto.parser.PrestoExprParser;
  import com.alibaba.druid.sql.dialect.presto.parser.PrestoLexer;
  import com.alibaba.druid.sql.dialect.presto.parser.PrestoStatementParser;
  import com.alibaba.druid.sql.dialect.redshift.parser.RedshiftExprParser;
  import com.alibaba.druid.sql.dialect.redshift.parser.RedshiftLexer;
  import com.alibaba.druid.sql.dialect.redshift.parser.RedshiftStatementParser;
  import com.alibaba.druid.sql.dialect.snowflake.SnowflakeExprParser;
  import com.alibaba.druid.sql.dialect.snowflake.SnowflakeLexer;
  import com.alibaba.druid.sql.dialect.snowflake.SnowflakeStatementParser;
  import com.alibaba.druid.sql.dialect.spark.parser.SparkExprParser;
  import com.alibaba.druid.sql.dialect.spark.parser.SparkLexer;
  import com.alibaba.druid.sql.dialect.spark.parser.SparkStatementParser;
  import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerExprParser;
  import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser;
  import com.alibaba.druid.sql.dialect.starrocks.parser.StarRocksExprParser;
  import com.alibaba.druid.sql.dialect.starrocks.parser.StarRocksLexer;
  import com.alibaba.druid.sql.dialect.starrocks.parser.StarRocksStatementParser;
  import com.alibaba.druid.sql.dialect.supersql.parser.SuperSqlExprParser;
  import com.alibaba.druid.sql.dialect.supersql.parser.SuperSqlLexer;
  import com.alibaba.druid.sql.dialect.supersql.parser.SuperSqlStatementParser;
  import com.alibaba.druid.sql.dialect.synapse.parser.SynapseExprParser;
  import com.alibaba.druid.sql.dialect.synapse.parser.SynapseLexer;
  import com.alibaba.druid.sql.dialect.synapse.parser.SynapseStatementParser;
  import com.alibaba.druid.sql.dialect.teradata.parser.TDExprParser;
  import com.alibaba.druid.sql.dialect.teradata.parser.TDLexer;
  import com.alibaba.druid.sql.dialect.teradata.parser.TDStatementParser;
  import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor;
  import com.alibaba.druid.sql.visitor.VisitorFeature;
  import com.alibaba.druid.util.StringUtils;
  
  import java.util.*;
  
  public class SQLParserUtils {
      public static SQLStatementParser createSQLStatementParser(String sql, DbType dbType) {
          SQLParserFeature[] features;
          if (DbType.odps == dbType || DbType.mysql == dbType) {
              features = new SQLParserFeature[]{SQLParserFeature.KeepComments};
          } else {
              features = new SQLParserFeature[]{};
          }
          return createSQLStatementParser(sql, dbType, features);
      }
  
      public static SQLStatementParser createSQLStatementParser(String sql, DbType dbType, boolean keepComments) {
          SQLParserFeature[] features;
          if (keepComments) {
              features = new SQLParserFeature[]{SQLParserFeature.KeepComments};
          } else {
              features = new SQLParserFeature[]{};
          }
  
          return createSQLStatementParser(sql, dbType, features);
      }
  
      public static SQLStatementParser createSQLStatementParser(String sql, String dbType, SQLParserFeature... features) {
          return createSQLStatementParser(sql, dbType == null ? null : DbType.valueOf(dbType), features);
      }
  
      public static SQLStatementParser createSQLStatementParser(String sql, DbType dbType, SQLParserFeature... features) {
          if (sql.indexOf("\r\n") != -1) {
              // com.alibaba.druid.sql.parser.Lexer only recognizes Linux newline '\n'.
              sql = sql.replace("\r\n", "\n");
          }
  
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          switch (dbType) {
              case oracle:
              case oceanbase_oracle:
              case polardb2:
                  return new OracleStatementParser(sql, features);
              case mysql:
              case tidb:
              case mariadb:
              case goldendb:
              case oceanbase:
              case drds:
              case polardbx: {
                  return new MySqlStatementParser(sql, features);
              }
              case elastic_search: {
                  MySqlStatementParser parser = new MySqlStatementParser(sql, features);
                  parser.dbType = dbType;
                  parser.exprParser.dbType = dbType;
                  return parser;
              }
              case postgresql:
              case greenplum:
              case edb:
                  return new PGSQLStatementParser(sql, features);
              case gaussdb:
                  return new GaussDbStatementParser(sql, features);
              case hologres:
                  return new HologresStatementParser(sql, features);
              case redshift:
                  return new RedshiftStatementParser(sql, features);
              case sqlserver:
              case jtds:
                  return new SQLServerStatementParser(sql, features);
              case synapse:
                  return new SynapseStatementParser(sql, features);
              case h2:
              case lealone:
                  return new H2StatementParser(sql, features);
              case blink:
                  return new BlinkStatementParser(sql, features);
              case db2:
                  return new DB2StatementParser(sql, features);
              case odps:
                  return new OdpsStatementParser(sql, features);
              case phoenix:
                  return new PhoenixStatementParser(sql);
              case hive:
                  return new HiveStatementParser(sql, features);
              case presto:
              case trino:
                  return new PrestoStatementParser(sql, features);
              case supersql:
                  return new SuperSqlStatementParser(sql, features);
              case athena:
                  return new AthenaStatementParser(sql, features);
              case bigquery:
                  return new BigQueryStatementParser(sql, features);
              case snowflake:
                  return new SnowflakeStatementParser(sql, features);
              case spark:
                  return new SparkStatementParser(sql);
              case databricks:
                  return new DatabricksStatementParser(sql, features);
              case clickhouse:
                  return new CKStatementParser(sql);
              case starrocks:
                  return new StarRocksStatementParser(sql);
              case informix:
                  return new InformixStatementParser(sql, features);
              case impala:
                  return new ImpalaStatementParser(sql, features);
              case doris:
                  return new DorisStatementParser(sql, features);
              case oscar:
                  return new OscarStatementParser(sql, features);
              case teradata:
                  return new TDStatementParser(sql, features);
              default:
                  return new SQLStatementParser(sql, dbType, features);
          }
      }
  
      public static SQLExprParser createExprParser(String sql, DbType dbType, SQLParserFeature... features) {
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          switch (dbType) {
              case oracle:
                  return new OracleExprParser(sql, features);
              case mysql:
              case mariadb:
                  return new MySqlExprParser(sql, features);
              case elastic_search: {
                  MySqlExprParser parser = new MySqlExprParser(sql, features);
                  parser.dbType = dbType;
                  return parser;
              }
              case h2:
              case lealone:
                  return new H2ExprParser(sql, features);
              case postgresql:
              case greenplum:
              case edb:
                  return new PGExprParser(sql, features);
              case gaussdb:
                  return new GaussDbExprParser(sql, features);
              case hologres:
                  return new HologresExprParser(sql, features);
              case redshift:
                  return new RedshiftExprParser(sql, features);
              case sqlserver:
              case jtds:
                  return new SQLServerExprParser(sql, features);
              case synapse:
                  return new SynapseExprParser(sql, features);
              case db2:
                  return new DB2ExprParser(sql, features);
              case odps:
                  return new OdpsExprParser(sql, features);
              case phoenix:
                  return new PhoenixExprParser(sql, features);
              case presto:
              case trino:
                  return new PrestoExprParser(sql, features);
              case supersql:
                  return new SuperSqlExprParser(sql, features);
              case athena:
                  return new AthenaExprParser(sql, features);
              case hive:
                  return new HiveExprParser(sql, features);
              case spark:
                  return new SparkExprParser(sql, features);
              case databricks:
                  return new DatabricksExprParser(sql, features);
              case bigquery:
                  return new BigQueryExprParser(sql, features);
              case snowflake:
                  return new SnowflakeExprParser(sql, features);
              case clickhouse:
                  return new CKExprParser(sql, features);
              case oscar:
                  return new OscarExprParser(sql, features);
              case starrocks:
                  return new StarRocksExprParser(sql, features);
              case impala:
                  return new ImpalaExprParser(sql, features);
              case doris:
                  return new DorisExprParser(sql, features);
              case teradata:
                  return new TDExprParser(sql, features);
              default:
                  return new SQLExprParser(sql, dbType, features);
          }
      }
  
      public static Lexer createLexer(String sql, DbType dbType) {
          return createLexer(sql, dbType, new SQLParserFeature[0]);
      }
  
      public static Lexer createLexer(String sql, DbType dbType, SQLParserFeature... features) {
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          switch (dbType) {
              case oracle:
                  return new OracleLexer(sql, features);
              case mysql:
              case mariadb:
                  return new MySqlLexer(sql, features);
              case elastic_search: {
                  MySqlLexer lexer = new MySqlLexer(sql, features);
                  lexer.dbType = dbType;
                  return lexer;
              }
              case h2:
              case lealone:
                  return new H2Lexer(sql, features);
              case postgresql:
              case greenplum:
              case edb:
                  return new PGLexer(sql, features);
              case gaussdb:
                  return new GaussDbLexer(sql, features);
              case hologres:
                  return new HologresLexer(sql, features);
              case redshift:
                  return new RedshiftLexer(sql, features);
              case db2:
                  return new DB2Lexer(sql, features);
              case odps:
                  return new OdpsLexer(sql, features);
              case phoenix:
                  return new PhoenixLexer(sql, features);
              case presto:
              case trino:
                  return new PrestoLexer(sql, features);
              case supersql:
                  return new SuperSqlLexer(sql, features);
              case athena:
                  return new AthenaLexer(sql, features);
              case synapse:
                  return new SynapseLexer(sql, features);
              case spark:
                  return new SparkLexer(sql);
              case databricks:
                  return new DatabricksLexer(sql);
              case oscar:
                  return new OscarLexer(sql, features);
              case clickhouse:
                  return new CKLexer(sql, features);
              case starrocks:
                  return new StarRocksLexer(sql, features);
              case hive:
                  return new HiveLexer(sql, features);
              case bigquery:
                  return new BigQueryLexer(sql, features);
              case snowflake:
                  return new SnowflakeLexer(sql, features);
              case impala:
                  return new ImpalaLexer(sql, features);
              case doris:
                  return new DorisLexer(sql, features);
              case teradata:
                  return new TDLexer(sql, features);
              default: {
                  Lexer lexer = new Lexer(sql, null, dbType);
                  for (SQLParserFeature feature : features) {
                      lexer.config(feature, true);
                  }
                  return lexer;
              }
          }
      }
  
      public static SQLSelectQueryBlock createSelectQueryBlock(DbType dbType) {
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          switch (dbType) {
              case mysql:
                  return new MySqlSelectQueryBlock();
              case oracle:
                  return new OracleSelectQueryBlock();
              case db2:
                  return new DB2SelectQueryBlock();
              case postgresql:
              case greenplum:
              case edb:
              case hologres:
              case redshift:
                  return new PGSelectQueryBlock();
              case odps:
                  return new OdpsSelectQueryBlock();
              case sqlserver:
                  return new SQLServerSelectQueryBlock();
              case synapse:
                  return new SQLServerSelectQueryBlock();
              case oscar:
                  return new OscarSelectQueryBlock();
              default:
                  return new SQLSelectQueryBlock(dbType);
          }
      }
  
      public static SQLType getSQLType(String sql, DbType dbType) {
          Lexer lexer = createLexer(sql, dbType);
          return lexer.scanSQLType();
      }
  
      public static SQLType getSQLTypeV2(String sql, DbType dbType) {
          Lexer lexer = createLexer(sql, dbType);
          return lexer.scanSQLTypeV2();
      }
  
      public static boolean startsWithHint(String sql, DbType dbType) {
          Lexer lexer = createLexer(sql, dbType);
          lexer.nextToken();
          return lexer.token() == Token.HINT;
      }
  
      public static boolean containsAny(String sql, DbType dbType, Token token) {
          Lexer lexer = createLexer(sql, dbType);
          for (; ; ) {
              lexer.nextToken();
              final Token tok = lexer.token;
              switch (tok) {
                  case EOF:
                  case ERROR:
                      return false;
                  default:
                      if (tok == token) {
                          return true;
                      }
                      break;
              }
          }
      }
  
      public static boolean containsAny(String sql, DbType dbType, Token token1, Token token2) {
          Lexer lexer = createLexer(sql, dbType);
          for (; ; ) {
              lexer.nextToken();
              final Token tok = lexer.token;
              switch (tok) {
                  case EOF:
                  case ERROR:
                      return false;
                  default:
                      if (tok == token1 || tok == token2) {
                          return true;
                      }
                      break;
              }
          }
      }
  
      public static boolean containsAny(String sql, DbType dbType, Token token1, Token token2, Token token3) {
          Lexer lexer = createLexer(sql, dbType);
          for (; ; ) {
              lexer.nextToken();
              final Token tok = lexer.token;
              switch (tok) {
                  case EOF:
                  case ERROR:
                      return false;
                  default:
                      if (tok == token1 || tok == token2 || tok == token3) {
                          return true;
                      }
                      break;
              }
          }
      }
  
      public static boolean containsAny(String sql, DbType dbType, Token... tokens) {
          if (tokens == null) {
              return false;
          }
  
          Lexer lexer = createLexer(sql, dbType);
          for (; ; ) {
              lexer.nextToken();
              final Token tok = lexer.token;
              switch (tok) {
                  case EOF:
                  case ERROR:
                      return false;
                  default:
                      for (int i = 0; i < tokens.length; i++) {
                          if (tokens[i] == tok) {
                              return true;
                          }
                      }
                      break;
              }
          }
      }
  
      public static Object getSimpleSelectValue(String sql, DbType dbType) {
          return getSimpleSelectValue(sql, dbType, null);
      }
  
      public static Object getSimpleSelectValue(String sql, DbType dbType, SimpleValueEvalHandler handler) {
          Lexer lexer = createLexer(sql, dbType);
          lexer.nextToken();
  
          if (lexer.token != Token.SELECT && lexer.token != Token.VALUES) {
              return null;
          }
  
          lexer.nextTokenValue();
  
          SQLExpr expr = null;
          Object value;
          switch (lexer.token) {
              case LITERAL_INT:
                  value = lexer.integerValue();
                  break;
              case LITERAL_CHARS:
              case LITERAL_NCHARS:
                  value = lexer.stringVal();
                  break;
              case LITERAL_FLOAT:
                  value = lexer.decimalValue();
                  break;
              default:
                  if (handler == null) {
                      return null;
                  }
  
                  expr = new SQLExprParser(lexer).expr();
                  try {
                      value = handler.eval(expr);
                  } catch (Exception error) {
                      // skip
                      value = null;
                  }
                  break;
          }
  
          lexer.nextToken();
  
          if (lexer.token == Token.FROM) {
              lexer.nextToken();
              if (lexer.token == Token.DUAL) {
                  lexer.nextToken();
              } else {
                  return null;
              }
          }
          if (lexer.token != Token.EOF) {
              return null;
          }
  
          return value;
      }
  
      public static interface SimpleValueEvalHandler {
          Object eval(SQLExpr expr);
      }
  
      public static String replaceBackQuote(String sql, DbType dbType) {
          int i = sql.indexOf('`');
  
          if (i == -1) {
              return sql;
          }
  
          char[] chars = sql.toCharArray();
          Lexer lexer = SQLParserUtils.createLexer(sql, dbType);
  
          int len = chars.length;
          int off = 0;
  
          for_:
          for (; ; ) {
              lexer.nextToken();
  
              int p0, p1;
              char c0, c1;
              switch (lexer.token) {
                  case IDENTIFIER:
                      p0 = lexer.startPos + off;
                      p1 = lexer.pos - 1 + off;
                      c0 = chars[p0];
                      c1 = chars[p1];
                      if (c0 == '`' && c1 == '`') {
                          if (p1 - p0 > 2 && chars[p0 + 1] == '\'' && chars[p1 - 1] == '\'') {
                              System.arraycopy(chars, p0 + 1, chars, p0, p1 - p0 - 1);
                              System.arraycopy(chars, p1 + 1, chars, p1 - 1, chars.length - p1 - 1);
                              len -= 2;
                              off -= 2;
                          } else {
                              chars[p0] = '"';
                              chars[p1] = '"';
                          }
  
                      }
                      break;
                  case EOF:
                  case ERROR:
                      break for_;
                  default:
                      break;
              }
          }
  
          return new String(chars, 0, len);
      }
  
      public static String addBackQuote(String sql, DbType dbType) {
          if (StringUtils.isEmpty(sql)) {
              return sql;
          }
          SQLStatementParser parser = createSQLStatementParser(sql, dbType);
          StringBuilder buf = new StringBuilder(sql.length() + 20);
          SQLASTOutputVisitor out = SQLUtils.createOutputVisitor(buf, DbType.mysql);
          out.config(VisitorFeature.OutputNameQuote, true);
  
          SQLType sqlType = getSQLType(sql, dbType);
          if (sqlType == SQLType.INSERT) {
              parser.config(SQLParserFeature.InsertReader, true);
  
              SQLInsertStatement stmt = (SQLInsertStatement) parser.parseStatement();
              int startPos = parser.getLexer().startPos;
  
              stmt.accept(out);
  
              if (stmt.getQuery() == null) {
                  buf.append(' ');
                  buf.append(sql, startPos, sql.length());
              }
          } else {
              SQLStatement stmt = parser.parseStatement();
              stmt.accept(out);
          }
  
          return buf.toString();
      }
  
      public static List<String> split(String sql, DbType dbType) {
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          {
              Lexer lexer = createLexer(sql, dbType);
              lexer.nextToken();
  
              boolean script = false;
              if (dbType == DbType.odps && lexer.token == Token.VARIANT) {
                  script = true;
              }
  
              if (script) {
                  return Collections.singletonList(sql);
              }
          }
  
          List list = new ArrayList();
  
          Lexer lexer = createLexer(sql, dbType);
          lexer.config(SQLParserFeature.SkipComments, false);
          lexer.config(SQLParserFeature.KeepComments, true);
  
          boolean set = false, paiOrJar = false;
          int start = 0;
          Token token = lexer.token;
          for (; lexer.token != Token.EOF; ) {
              if (token == Token.SEMI) {
                  int len = lexer.startPos - start;
                  if (len > 0) {
                      String lineSql = sql.substring(start, lexer.startPos);
                      lineSql = lineSql.trim();
                      if (!lineSql.isEmpty()) {
                          list.add(lineSql);
                      }
                  }
                  start = lexer.startPos + 1;
                  set = false;
              } else if (token == Token.CREATE) {
                  lexer.nextToken();
  
                  if (lexer.token == Token.FUNCTION || lexer.identifierEquals("FUNCTION")) {
                      lexer.nextToken();
                      lexer.nextToken();
                      if (lexer.token == Token.AS) {
                          lexer.nextToken();
                          if (lexer.token == Token.LITERAL_CHARS) {
                              lexer.nextToken();
                              token = lexer.token;
                              continue;
                          }
                      }
                      lexer.startPos = sql.length();
                      break;
                  }
  
                  token = lexer.token;
                  continue;
              } else if (set && token == Token.EQ && dbType == DbType.odps) {
                  lexer.nextTokenForSet();
                  token = lexer.token;
                  continue;
              }
  
              if (lexer.identifierEquals("USING")) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("jar")) {
                      lexer.nextToken();
                  }
              }
  
              if (lexer.token == Token.SET) {
                  set = true;
              }
  
              if (lexer.identifierEquals("ADD") && (dbType == DbType.hive || dbType == DbType.odps || dbType == DbType.spark)) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("JAR")) {
                      lexer.nextPath();
                  }
              } else {
                  lexer.nextToken();
              }
              token = lexer.token;
          }
  
          if (start != sql.length() && token != Token.SEMI) {
              int end = lexer.startPos;
              if (end > sql.length()) {
                  end = sql.length();
              }
              String splitSql = sql.substring(start, end).trim();
              if (!paiOrJar) {
                  splitSql = removeComment(splitSql, dbType).trim();
              } else {
                  if (splitSql.endsWith(";")) {
                      splitSql = splitSql.substring(0, splitSql.length() - 1).trim();
                  }
              }
              if (!splitSql.isEmpty()) {
                  list.add(splitSql);
              }
          }
  
          return list;
      }
  
      public static List<String> splitAndRemoveComment(String sql, DbType dbType) {
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          boolean containsCommentAndSemi = false;
          {
              Lexer lexer = createLexer(sql, dbType);
              lexer.config(SQLParserFeature.SkipComments, false);
              lexer.config(SQLParserFeature.KeepComments, true);
  
              while (lexer.token != Token.EOF) {
                  if (lexer.token == Token.LINE_COMMENT
                          || lexer.token == Token.MULTI_LINE_COMMENT
                          || lexer.token == Token.SEMI) {
                      containsCommentAndSemi = true;
                      break;
                  }
                  lexer.nextToken();
              }
  
              if (!containsCommentAndSemi) {
                  return Collections.singletonList(sql);
              }
          }
  
          {
              Lexer lexer = createLexer(sql, dbType);
              lexer.nextToken();
  
              boolean script = false;
              if (dbType == DbType.odps && lexer.token == Token.VARIANT) {
                  script = true;
              }
  
              if (script || lexer.identifierEquals("pai") || lexer.identifierEquals("jar") || lexer.identifierEquals("copy")) {
                  return Collections.singletonList(sql);
              }
          }
  
          List list = new ArrayList();
  
          Lexer lexer = createLexer(sql, dbType);
          lexer.config(SQLParserFeature.SkipComments, false);
          lexer.config(SQLParserFeature.KeepComments, true);
          lexer.nextToken();
  
          boolean set = false, paiOrJar = false;
          int start = 0;
          Token preToken = null;
          int prePos = 0;
          Token token = lexer.token;
          Token startToken = lexer.token;
          while (token == Token.LINE_COMMENT || token == Token.MULTI_LINE_COMMENT) {
              lexer.nextToken();
              token = lexer.token;
              startToken = token;
              start = lexer.startPos;
          }
  
          for (int tokens = 1; lexer.token != Token.EOF; ) {
              if (token == Token.SEMI) {
                  int len = lexer.startPos - start;
                  if (len > 0) {
                      String lineSql = sql.substring(start, lexer.startPos);
                      String splitSql = set
                              ? removeLeftComment(lineSql, dbType)
                              : removeComment(lineSql, dbType
                      ).trim();
                      if (!splitSql.isEmpty()) {
                          list.add(splitSql);
                      }
                  }
                  lexer.nextToken();
                  token = lexer.token;
                  start = lexer.startPos;
                  startToken = token;
                  set = false;
                  tokens = token == Token.LINE_COMMENT || token == Token.MULTI_LINE_COMMENT ? 0 : 1;
                  continue;
              } else if (token == Token.MULTI_LINE_COMMENT) {
                  int len = lexer.startPos - start;
                  if (len > 0) {
                      String splitSql = removeComment(
                              sql.substring(start, lexer.startPos),
                              dbType
                      ).trim();
                      if (!splitSql.isEmpty()) {
                          list.add(splitSql);
                      }
                  }
                  lexer.nextToken();
                  token = lexer.token;
                  start = lexer.startPos;
                  startToken = token;
                  tokens = token == Token.LINE_COMMENT || token == Token.MULTI_LINE_COMMENT ? 0 : 1;
                  continue;
              } else if (token == Token.CREATE) {
                  lexer.nextToken();
  
                  if (lexer.token == Token.FUNCTION || lexer.identifierEquals("FUNCTION")) {
                      lexer.nextToken();
                      lexer.nextToken();
                      if (lexer.token == Token.AS) {
                          lexer.nextToken();
                          if (lexer.token == Token.LITERAL_CHARS) {
                              lexer.nextToken();
                              token = lexer.token;
                              continue;
                          }
                      }
                      lexer.startPos = sql.length();
                      break;
                  }
  
                  token = lexer.token;
                  continue;
              } else if (set && token == Token.EQ && dbType == DbType.odps) {
                  lexer.nextTokenForSet();
                  token = lexer.token;
                  continue;
              } else if (dbType == DbType.odps
                      && (preToken == null || preToken == Token.LINE_COMMENT || preToken == Token.SEMI)
                      && (lexer.identifierEquals("pai") || lexer.identifierEquals("jar") || lexer.identifierEquals("copy"))) {
                  lexer.scanLineArgument();
                  paiOrJar = true;
              }
  
              if (lexer.identifierEquals("USING")) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("jar")) {
                      lexer.nextToken();
                  }
              }
  
              if (lexer.token == Token.SET) {
                  set = true;
              }
  
              prePos = lexer.pos;
              if (lexer.identifierEquals("ADD") && (dbType == DbType.hive || dbType == DbType.odps || dbType == DbType.spark)) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("JAR")) {
                      lexer.nextPath();
                  }
              } else {
                  lexer.nextToken();
              }
              preToken = token;
              token = lexer.token;
              if (token == Token.LINE_COMMENT
                      && tokens == 0) {
                  start = lexer.pos;
                  startToken = token;
              }
  
              if (token != Token.LINE_COMMENT && token != Token.MULTI_LINE_COMMENT && token != Token.SEMI) {
                  tokens++;
              }
          }
  
          if (start != sql.length() && token != Token.SEMI) {
              int end = lexer.startPos;
              if (end > sql.length()) {
                  end = sql.length();
              }
              String splitSql = sql.substring(start, end).trim();
              if (!paiOrJar) {
                  splitSql = removeComment(splitSql, dbType).trim();
              } else {
                  if (splitSql.endsWith(";")) {
                      splitSql = splitSql.substring(0, splitSql.length() - 1).trim();
                  }
              }
              if (!splitSql.isEmpty()) {
                  list.add(splitSql);
              }
          }
  
          return list;
      }
  
      public static String removeLeftComment(String sql, DbType dbType) {
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          sql = sql.trim();
          if (sql.startsWith("jar")) {
              return sql;
          }
  
          boolean containsComment = false;
          {
              Lexer lexer = createLexer(sql, dbType);
              lexer.config(SQLParserFeature.SkipComments, false);
              lexer.config(SQLParserFeature.KeepComments, true);
  
              while (lexer.token != Token.EOF) {
                  if (lexer.token == Token.LINE_COMMENT || lexer.token == Token.MULTI_LINE_COMMENT) {
                      containsComment = true;
                      break;
                  }
                  lexer.nextToken();
              }
  
              if (!containsComment) {
                  return sql;
              }
          }
  
          StringBuilder sb = new StringBuilder();
  
          Lexer lexer = createLexer(sql, dbType);
          lexer.config(SQLParserFeature.SkipComments, false);
          lexer.config(SQLParserFeature.KeepComments, true);
          lexer.nextToken();
  
          int start = 0;
          for (; lexer.token != Token.EOF; lexer.nextToken()) {
              if (lexer.token == Token.LINE_COMMENT || lexer.token == Token.MULTI_LINE_COMMENT) {
                  continue;
              }
              start = lexer.startPos;
              break;
          }
  
          if (start != sql.length()) {
              sb.append(sql.substring(start, sql.length()));
          }
  
          return sb.toString();
      }
  
      public static String removeComment(String sql, DbType dbType) {
          if (dbType == null) {
              dbType = DbType.other;
          }
  
          sql = sql.trim();
          if (sql.startsWith("jar") || sql.startsWith("JAR")) {
              return sql;
          }
  
          if ((sql.startsWith("pai") || sql.startsWith("PAI")) && sql.indexOf(';') == -1) {
              return sql;
          }
  
          boolean containsComment = false;
          {
              Lexer lexer = createLexer(sql, dbType);
              lexer.config(SQLParserFeature.SkipComments, false);
              lexer.config(SQLParserFeature.KeepComments, true);
  
              while (lexer.token != Token.EOF) {
                  if (lexer.token == Token.LINE_COMMENT || lexer.token == Token.MULTI_LINE_COMMENT) {
                      containsComment = true;
                      break;
                  }
                  lexer.nextToken();
              }
  
              if (!containsComment) {
                  return sql;
              }
          }
  
          StringBuilder sb = new StringBuilder();
  
          Lexer lexer = createLexer(sql, dbType);
          lexer.config(SQLParserFeature.SkipComments, false);
          lexer.config(SQLParserFeature.KeepComments, true);
  
          int start = 0;
          Token token = lexer.token;
          for (; lexer.token != Token.EOF; ) {
              if (token == Token.LINE_COMMENT) {
                  int len = lexer.startPos - start;
                  if (len > 0) {
                      sb.append(sql.substring(start, lexer.startPos));
                  }
                  start = lexer.startPos + lexer.stringVal().length();
                  if (lexer.startPos > 1 && lexer.text.charAt(lexer.startPos - 1) == '\n') {
                      while (start + 1 < lexer.text.length() && lexer.text.charAt(start) == '\n') {
                          start = start + 1;
                      }
                  }
              } else if (token == Token.MULTI_LINE_COMMENT) {
                  int len = lexer.startPos - start;
                  if (len > 0) {
                      sb.append(sql.substring(start, lexer.startPos));
                  }
                  start = lexer.startPos + lexer.stringVal().length();
              }
  
              if (lexer.identifierEquals("ADD")) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("JAR")) {
                      lexer.nextPath();
                  }
              } else {
                  lexer.nextToken();
              }
              token = lexer.token;
          }
  
          if (start != sql.length() && token != Token.LINE_COMMENT && token != Token.MULTI_LINE_COMMENT) {
              sb.append(sql.substring(start, sql.length()));
          }
  
          return sb.toString();
      }
  
      public static List<String> getTables(String sql, DbType dbType) {
          Set<String> tables = new LinkedHashSet<>();
  
          boolean set = false;
          Lexer lexer = createLexer(sql, dbType);
          lexer.nextToken();
  
          SQLExprParser exprParser;
          switch (dbType) {
              case odps:
                  exprParser = new OdpsExprParser(lexer);
                  break;
              case mysql:
                  exprParser = new MySqlExprParser(lexer);
                  break;
              default:
                  exprParser = new SQLExprParser(lexer);
                  break;
          }
  
          for_:
          for (; lexer.token != Token.EOF; ) {
              switch (lexer.token) {
                  case CREATE:
                  case DROP:
                  case ALTER:
                      set = false;
                      lexer.nextToken();
  
                      if (lexer.token == Token.TABLE) {
                          lexer.nextToken();
  
                          if (lexer.token == Token.IF) {
                              lexer.nextToken();
  
                              if (lexer.token == Token.NOT) {
                                  lexer.nextToken();
                              }
  
                              if (lexer.token == Token.EXISTS) {
                                  lexer.nextToken();
                              }
                          }
  
                          SQLName name = exprParser.name();
                          tables.add(name.toString());
  
                          if (lexer.token == Token.AS) {
                              lexer.nextToken();
                          }
                      }
                      continue for_;
                  case FROM:
                  case JOIN:
                      lexer.nextToken();
                      if (lexer.token != Token.LPAREN
                              && lexer.token != Token.VALUES
                      ) {
                          SQLName name = exprParser.name();
                          tables.add(name.toString());
                      }
                      continue for_;
                  case SEMI:
                      set = false;
                      break;
                  case SET:
                      set = true;
                      break;
                  case EQ:
                      if (set && dbType == DbType.odps) {
                          lexer.nextTokenForSet();
                          continue for_;
                      }
                      break;
                  default:
                      break;
              }
  
              lexer.nextToken();
  
          }
  
          return new ArrayList<>(tables);
      }
  }
  