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.dialect.postgresql.parser;
  
  import com.alibaba.druid.sql.ast.SQLDataType;
  import com.alibaba.druid.sql.ast.SQLExpr;
  import com.alibaba.druid.sql.ast.SQLName;
  import com.alibaba.druid.sql.ast.SQLObject;
  import com.alibaba.druid.sql.ast.SQLParameter;
  import com.alibaba.druid.sql.ast.SQLStatement;
  import com.alibaba.druid.sql.ast.expr.*;
  import com.alibaba.druid.sql.ast.statement.*;
  import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGAttrExpr;
  import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.*;
  import com.alibaba.druid.sql.parser.*;
  import com.alibaba.druid.util.FnvHash;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import static com.alibaba.druid.sql.parser.Token.*;
  
  public class PGSQLStatementParser extends SQLStatementParser {
      public static final String TIME_ZONE = "TIME ZONE";
      public static final String TIME = "TIME";
      public static final String LOCAL = "LOCAL";
  
      public PGSQLStatementParser(PGExprParser parser) {
          super(parser);
      }
  
      public PGSQLStatementParser(String sql) {
          super(new PGExprParser(sql));
      }
  
      public PGSQLStatementParser(String sql, SQLParserFeature... features) {
          super(new PGExprParser(sql, features));
      }
  
      public PGSQLStatementParser(Lexer lexer) {
          super(new PGExprParser(lexer));
      }
  
      public PGSelectParser createSQLSelectParser() {
          return new PGSelectParser(this.exprParser, selectListCache);
      }
  
      public SQLUpdateStatement parseUpdateStatement() {
          accept(Token.UPDATE);
  
          PGUpdateStatement updateStatement = new PGUpdateStatement();
  
          SQLSelectParser selectParser = this.exprParser.createSelectParser();
          SQLTableSource tableSource = selectParser.parseTableSource();
          updateStatement.setTableSource(tableSource);
  
          parseUpdateSet(updateStatement);
  
          if (lexer.token() == Token.FROM) {
              lexer.nextToken();
              SQLTableSource from = selectParser.parseTableSource();
              updateStatement.setFrom(from);
          }
  
          if (lexer.token() == (Token.WHERE)) {
              lexer.nextToken();
              updateStatement.setWhere(this.exprParser.expr());
          }
  
          if (lexer.token() == Token.RETURNING) {
              lexer.nextToken();
  
              for (; ; ) {
                  updateStatement.getReturning().add(this.exprParser.expr());
                  if (lexer.token() == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
                  break;
              }
          }
  
          return updateStatement;
      }
  
      public PGInsertStatement parseInsert() {
          PGInsertStatement stmt = new PGInsertStatement();
  
          if (lexer.token() == Token.INSERT) {
              lexer.nextToken();
              accept(Token.INTO);
  
              SQLName tableName = this.exprParser.name();
              stmt.setTableName(tableName);
  
              if (lexer.token() == Token.AS) {
                  lexer.nextToken();
                  stmt.setAlias(lexer.stringVal());
                  lexer.nextToken();
              } else if (lexer.token() == Token.IDENTIFIER) {
                  stmt.setAlias(lexer.stringVal());
                  lexer.nextToken();
              }
  
          }
  
          if (lexer.token() == Token.DEFAULT) {
              lexer.nextToken();
              accept(Token.VALUES);
              stmt.setDefaultValues(true);
          }
  
          if (lexer.token() == (Token.LPAREN)) {
              lexer.nextToken();
              this.exprParser.exprList(stmt.getColumns(), stmt);
              accept(Token.RPAREN);
          }
  
          if (lexer.token() == (Token.VALUES)) {
              lexer.nextToken();
  
              for (; ; ) {
                  accept(Token.LPAREN);
                  SQLInsertStatement.ValuesClause valuesCaluse = new SQLInsertStatement.ValuesClause();
                  this.exprParser.exprList(valuesCaluse.getValues(), valuesCaluse);
                  stmt.addValueCause(valuesCaluse);
  
                  accept(Token.RPAREN);
                  if (lexer.token() == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
                  break;
              }
          }
  
          if (lexer.token() == Token.WITH) {
              PGSelectStatement nextWithQuery = (PGSelectStatement) this.parseWith();
              stmt.setQuery(nextWithQuery.getSelect());
          } else if (lexer.token() == Token.SELECT) {
              SQLQueryExpr queryExpr = (SQLQueryExpr) this.exprParser.expr();
              stmt.setQuery(queryExpr.getSubQuery());
          }
  
          if (lexer.token() == Token.ON) {
              lexer.nextToken();
              if (lexer.identifierEquals(FnvHash.Constants.CONFLICT)) {
                  lexer.nextToken();
  
                  if (lexer.token() == Token.LPAREN) {
                      lexer.nextToken();
                      List<SQLExpr> onConflictTarget = new ArrayList<SQLExpr>();
                      this.exprParser.exprList(onConflictTarget, stmt);
                      stmt.setOnConflictTarget(onConflictTarget);
                      accept(Token.RPAREN);
                  }
  
                  if (lexer.token() == Token.ON) {
                      lexer.nextToken();
                      accept(Token.CONSTRAINT);
                      SQLName constraintName = this.exprParser.name();
                      stmt.setOnConflictConstraint(constraintName);
                  }
  
                  if (lexer.token() == Token.WHERE) {
                      lexer.nextToken();
                      SQLExpr where = this.exprParser.expr();
                      stmt.setOnConflictWhere(where);
                  }
  
                  if (lexer.token() == Token.DO) {
                      lexer.nextToken();
  
                      if (lexer.identifierEquals(FnvHash.Constants.NOTHING)) {
                          lexer.nextToken();
                          stmt.setOnConflictDoNothing(true);
                      } else {
                          accept(Token.UPDATE);
                          accept(Token.SET);
  
                          for (; ; ) {
                              SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
                              stmt.addConflicUpdateItem(item);
  
                              if (lexer.token() != Token.COMMA) {
                                  break;
                              }
  
                              lexer.nextToken();
                          }
                          if (lexer.token() == Token.WHERE) {
                              lexer.nextToken();
                              SQLExpr where = this.exprParser.expr();
                              stmt.setOnConflictUpdateWhere(where);
                          }
                      }
                  }
              }
          }
  
          if (lexer.token() == Token.RETURNING) {
              lexer.nextToken();
              SQLExpr returning = this.exprParser.expr();
  
              if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  SQLListExpr list = new SQLListExpr();
                  list.addItem(returning);
  
                  this.exprParser.exprList(list.getItems(), list);
  
                  returning = list;
              }
  
              stmt.setReturning(returning);
          }
          return stmt;
      }
  
      @Override
      public PGCreateDatabaseStatement parseCreateDatabase() {
          accept(CREATE);
          accept(DATABASE);
          PGCreateDatabaseStatement stmt = new PGCreateDatabaseStatement(dbType);
  
          if (lexer.hasComment() && lexer.isKeepComments()) {
              stmt.addBeforeComment(lexer.readAndResetComments());
          }
  
          stmt.setName(this.exprParser.identifier());
  
          if (lexer.token() == WITH) {
              lexer.nextToken();
              stmt.setHaveWith(true);
          }
  
          for (;;) {
              if (lexer.token() == Token.SEMI || lexer.isEOF()) {
                  break;
              }
  
              boolean isAttr = lexer.identifierEquals(FnvHash.Constants.TEMPLATE) ||
                               lexer.identifierEquals(FnvHash.Constants.OWNER);
  
              if (isAttr) {
                  SQLIdentifierExpr attrName = this.exprParser.identifier();
  
                  PGAttrExpr attrExpr = new PGAttrExpr();
                  if (lexer.token() == EQ) {
                      lexer.nextToken();
                      attrExpr.setMode(PGAttrExpr.PGExprMode.EQ);
                  } else {
                      attrExpr.setMode(PGAttrExpr.PGExprMode.EMPTY);
                  }
  
                  attrExpr.setName(attrName);
                  attrExpr.setValue(this.exprParser.identifier());
                  stmt.getStats().add(attrExpr);
              } else {
                  throw new ParserException("TODO " + lexer.info());
              }
          }
  
          return stmt;
      }
  
      @Override
      public PGCreateSchemaStatement parseCreateSchema() {
          accept(Token.CREATE);
          accept(Token.SCHEMA);
  
          PGCreateSchemaStatement stmt = new PGCreateSchemaStatement();
          if (lexer.token() == Token.IF) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
  
              stmt.setIfNotExists(true);
          }
  
          if (lexer.token() == Token.IDENTIFIER) {
              if (lexer.identifierEquals("AUTHORIZATION")) {
                  lexer.nextToken();
                  stmt.setAuthorization(true);
  
                  SQLIdentifierExpr userName = (SQLIdentifierExpr) this.exprParser.expr();
                  stmt.setUserName(userName);
              } else {
                  stmt.setSchemaName(this.exprParser.name());
  
                  if (lexer.identifierEquals("AUTHORIZATION")) {
                      lexer.nextToken();
                      stmt.setAuthorization(true);
  
                      SQLIdentifierExpr userName = (SQLIdentifierExpr) this.exprParser.expr();
                      stmt.setUserName(userName);
                  }
              }
          }
  
          while (lexer.token() != SEMI && !lexer.isEOF()) {
              if (lexer.token() == Token.CREATE) {
                  Lexer.SavePoint mark = lexer.markOut();
                  lexer.nextToken();
                  if (lexer.token() == Token.TABLE) {
                      lexer.reset(mark);
                      stmt.getCreateStatements().add(this.parseCreateTable());
                      continue;
                  } else if (lexer.token() == VIEW) {
                      lexer.reset(mark);
                      stmt.getCreateStatements().add(this.parseCreateView());
                      continue;
                  } else if (lexer.token() == INDEX) {
                      lexer.reset(mark);
                      stmt.getCreateStatements().add(this.parseCreateIndex());
                      continue;
                  } else if (lexer.token() == SEQUENCE) {
                      lexer.reset(mark);
                      stmt.getCreateStatements().add(this.parseCreateSequence());
                      continue;
                  } else if (lexer.token() == TRIGGER) {
                      lexer.reset(mark);
                      stmt.getCreateStatements().add(this.parseCreateTrigger());
                      continue;
                  }
              }
  
              throw new ParserException("syntax error. " + lexer.info());
          }
  
          return stmt;
      }
  
      protected SQLStatement alterSchema() {
          accept(Token.ALTER);
          accept(Token.SCHEMA);
  
          PGAlterSchemaStatement stmt = new PGAlterSchemaStatement();
          stmt.setSchemaName(this.exprParser.name());
  
          if (lexer.identifierEquals(FnvHash.Constants.RENAME)) {
              lexer.nextToken();
              accept(Token.TO);
              stmt.setNewName(this.exprParser.identifier());
          } else if (lexer.identifierEquals(FnvHash.Constants.OWNER)) {
              lexer.nextToken();
              accept(Token.TO);
              stmt.setNewOwner(this.exprParser.identifier());
          }
  
          return stmt;
      }
  
      //with force
      @Override
      public PGDropDatabaseStatement parseDropDatabaseOrSchema(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          PGDropDatabaseStatement stmt = new PGDropDatabaseStatement(getDbType());
  
          if (lexer.token() == Token.SCHEMA) {
              lexer.nextToken();
          } else {
              accept(Token.DATABASE);
          }
  
          if (lexer.token() == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          stmt.setName(this.exprParser.name());
  
          if (lexer.token() == WITH) {
              lexer.nextToken();
              stmt.setUsingWith(true);
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.FORCE)) {
              lexer.nextToken();
              stmt.setForce(true);
          }
  
          return stmt;
      }
  
      public PGDropSchemaStatement parseDropSchema(boolean physical) {
          PGDropSchemaStatement stmt = new PGDropSchemaStatement();
  
          if (lexer.token() == Token.SCHEMA) {
              lexer.nextToken();
          } else {
              accept(Token.DATABASE);
          }
  
          if (lexer.token() == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setSchemaName(name);
          stmt.getMultipleNames().add(name);
  
          while (lexer.token() == COMMA) {
              lexer.nextToken();
              stmt.getMultipleNames().add(this.exprParser.name());
          }
  
          if (lexer.token() == Token.CASCADE) {
              lexer.nextToken();
              stmt.setCascade(true);
          } else {
              stmt.setCascade(false);
          }
          if (lexer.token() == Token.RESTRICT) {
              lexer.nextToken();
              stmt.setRestrict(true);
          }
  
          return stmt;
      }
  
      public PGDeleteStatement parseDeleteStatement() {
          lexer.nextToken();
          PGDeleteStatement deleteStatement = new PGDeleteStatement();
  
          if (lexer.token() == (Token.FROM)) {
              lexer.nextToken();
          }
          if (lexer.token() == (Token.ONLY)) {
              lexer.nextToken();
              deleteStatement.setOnly(true);
          }
  
          SQLName tableName = exprParser.name();
  
          deleteStatement.setTableName(tableName);
  
          if (lexer.token() == Token.AS) {
              accept(Token.AS);
          }
          if (lexer.token() == Token.IDENTIFIER) {
              deleteStatement.setAlias(lexer.stringVal());
              lexer.nextToken();
          }
  
          if (lexer.token() == Token.USING) {
              lexer.nextToken();
  
              SQLTableSource tableSource = createSQLSelectParser().parseTableSource();
              deleteStatement.setUsing(tableSource);
          }
  
          if (lexer.token() == (Token.WHERE)) {
              lexer.nextToken();
  
              if (lexer.token() == Token.CURRENT) {
                  lexer.nextToken();
                  accept(Token.OF);
                  SQLName cursorName = this.exprParser.name();
                  SQLExpr where = new SQLCurrentOfCursorExpr(cursorName);
                  deleteStatement.setWhere(where);
              } else {
                  SQLExpr where = this.exprParser.expr();
                  deleteStatement.setWhere(where);
              }
          }
  
          if (lexer.token() == Token.RETURNING) {
              lexer.nextToken();
              accept(Token.STAR);
              deleteStatement.setReturning(true);
          }
  
          return deleteStatement;
      }
  
      public boolean parseStatementListDialect(List<SQLStatement> statementList) {
          switch (lexer.token()) {
              case BEGIN:
              case START: {
                  PGStartTransactionStatement stmt = parseBegin();
                  statementList.add(stmt);
                  return true;
              }
              case END: {
                  PGEndTransactionStatement stmt = parseEnd();
                  statementList.add(stmt);
                  return true;
              }
              case WITH:
                  statementList.add(parseWith());
                  return true;
              case DO:
                  PGDoStatement pgDoStatement = parseDo();
                  statementList.add(pgDoStatement);
                  return true;
              default:
                  if (lexer.identifierEquals(FnvHash.Constants.CONNECT)) {
                      SQLStatement stmt = parseConnectTo();
                      statementList.add(stmt);
                      return true;
                  }
  
                  break;
          }
  
          String strVal = lexer.stringVal();
          if (strVal.equalsIgnoreCase("ANALYZE")) {
              PGAnalyzeStatement stmt = this.parseAnalyzeTable();
              statementList.add(stmt);
              return true;
          }
          if (strVal.equalsIgnoreCase("VACUUM")) {
              PGVacuumStatement stmt = this.parseVacuumTable();
              statementList.add(stmt);
              return true;
          }
          return false;
      }
  
      public PGDoStatement parseDo() {
          PGDoStatement stmt = new PGDoStatement();
          stmt.setDbType(dbType);
  
          accept(Token.DO);
          SQLStatement block = this.parseBlock();
          stmt.setBlock(block);
  
          return stmt;
      }
  
      public SQLBlockStatement parseBlock() {
          SQLBlockStatement block = new SQLBlockStatement();
          block.setDbType(dbType);
  
          if (lexer.token() == Token.VARIANT) {
              String dollarQuotedStr = lexer.stringVal();
              if (!dollarQuotedStr.endsWith("$")) {
                  throw new ParserException("syntax error. " + lexer.info());
              }
              block.setIsDollarQuoted(true);
              String dollarQuoteTagName = dollarQuotedStr.substring(1, dollarQuotedStr.length() - 1);
              block.setDollarQuoteTagName(dollarQuoteTagName);
              lexer.nextToken();
          }
  
          String labelName = null;
          if (lexer.token() == Token.IDENTIFIER) {
              labelName = lexer.stringVal();
              lexer.nextToken();
          }
  
          if (labelName != null) {
              block.setLabelName(labelName);
          }
  
          if (lexer.token() == Token.DECLARE) {
              lexer.nextToken();
          }
          if (lexer.token() == Token.IDENTIFIER || lexer.token() == Token.CURSOR) {
              parseParameters(block.getParameters(), block);
              for (SQLParameter param : block.getParameters()) {
                  param.setParent(block);
              }
          }
  
          accept(Token.BEGIN);
          List<SQLStatement> statementList = block.getStatementList();
          this.parseStatementList(statementList, -1, block);
          if (lexer.token() != Token.END
              && !statementList.isEmpty()
              && (statementList.get(statementList.size() - 1) instanceof SQLCommitStatement
              || statementList.get(statementList.size() - 1) instanceof SQLRollbackStatement)) {
              block.setEndOfCommit(true);
              return block;
          }
          accept(Token.END);
  
          Token token = lexer.token();
          if (token != Token.SEMI) {
              // "END label;" or "END label $$ LANGUAGE plpgsql;"
              if (token == Token.IDENTIFIER) {
                  String endLabel = lexer.stringVal();
                  block.setEndLabel(endLabel);
                  if (!block.getLabelName().equals(endLabel)) {
                      printError(lexer.token());
                  }
                  acceptIdentifier(endLabel);
              }
              if (lexer.token() == Token.VARIANT) {
                  parseEndDollarQuote(block);
              }
          }
          accept(Token.SEMI);
  
          if (lexer.token() == Token.VARIANT) {
              // "$$ LANGUAGE plpgsql;"
              parseEndDollarQuote(block);
              accept(Token.SEMI);
          }
          return block;
      }
  
      private void parseEndDollarQuote(SQLBlockStatement block) {
          String dollarQuotedStr = lexer.stringVal();
          if (!dollarQuotedStr.endsWith("$")) {
              throw new ParserException("syntax error. " + lexer.info());
          }
          String dollarQuoteTagName = dollarQuotedStr.substring(1, dollarQuotedStr.length() - 1);
          if (!block.getDollarQuoteTagName().equals(dollarQuoteTagName)) {
              printError(lexer.token());
          }
          lexer.nextToken();
          if (lexer.token() != Token.SEMI) {
              accept(Token.LANGUAGE);
              block.setLanguage(lexer.stringVal());
              acceptIdentifier(block.getLanguage());
          }
      }
  
      private void parseParameters(List<SQLParameter> parameters, SQLObject parent) {
          for (;;) {
              SQLParameter parameter = new SQLParameter();
              parameter.setParent(parent);
  
              SQLName name;
              SQLDataType dataType = null;
              name = this.exprParser.name();
              if (lexer.token() == Token.IN) {
                  lexer.nextToken();
  
                  if (lexer.token() == Token.OUT) {
                      lexer.nextToken();
                      parameter.setParamType(SQLParameter.ParameterType.INOUT);
                  } else {
                      parameter.setParamType(SQLParameter.ParameterType.IN);
                  }
              } else if (lexer.token() == Token.OUT) {
                  lexer.nextToken();
  
                  if (lexer.token() == Token.IN) {
                      lexer.nextToken();
                      parameter.setParamType(SQLParameter.ParameterType.INOUT);
                  } else {
                      parameter.setParamType(SQLParameter.ParameterType.OUT);
                  }
              } else if (lexer.token() == Token.INOUT) {
                  lexer.nextToken();
                  parameter.setParamType(SQLParameter.ParameterType.INOUT);
              }
  
              dataType = this.exprParser.parseDataType(false);
  
              if (lexer.token() == Token.NOT) {
                  lexer.nextToken();
                  accept(Token.NULL);
                  parameter.setNotNull(true);
              }
  
              if (lexer.token() == Token.COLONEQ || lexer.token() == Token.DEFAULT) {
                  lexer.nextToken();
                  parameter.setDefaultValue(this.exprParser.expr());
              }
  
              parameter.setName(name);
              parameter.setDataType(dataType);
  
              parameters.add(parameter);
              Token token = lexer.token();
              if (token == Token.COMMA || token == Token.SEMI || token == Token.IS) {
                  lexer.nextToken();
              }
  
              token = lexer.token();
              if (token != Token.BEGIN
                      && token != Token.RPAREN
                      && token != Token.EOF
                      && token != Token.FUNCTION
                      && !lexer.identifierEquals("DETERMINISTIC")) {
                  continue;
              }
  
              break;
          }
      }
  
      protected PGStartTransactionStatement parseBegin() {
          PGStartTransactionStatement stmt = new PGStartTransactionStatement();
          if (lexer.token() == Token.START) {
              lexer.nextToken();
              acceptIdentifier("TRANSACTION");
          } else {
              accept(Token.BEGIN);
              stmt.setUseBegin(true);
          }
  
          return stmt;
      }
  
      @Override
      public PGEndTransactionStatement parseEnd() {
          PGEndTransactionStatement stmt = new PGEndTransactionStatement();
          accept(Token.END);
          return stmt;
      }
  
      public SQLStatement parseConnectTo() {
          acceptIdentifier("CONNECT");
          accept(Token.TO);
  
          PGConnectToStatement stmt = new PGConnectToStatement();
          SQLName target = this.exprParser.name();
          stmt.setTarget(target);
  
          return stmt;
      }
  
      public PGSelectStatement parseSelect() {
          PGSelectParser selectParser = createSQLSelectParser();
          SQLSelect select = selectParser.select();
          return new PGSelectStatement(select);
      }
  
      public SQLStatement parseWith() {
          SQLWithSubqueryClause with = this.parseWithQuery();
          // PGWithClause with = this.parseWithClause();
          if (lexer.token() == Token.INSERT) {
              PGInsertStatement stmt = this.parseInsert();
              stmt.setWith(with);
              return stmt;
          }
  
          if (lexer.token() == Token.SELECT) {
              PGSelectStatement stmt = this.parseSelect();
              stmt.getSelect().setWithSubQuery(with);
              return stmt;
          }
  
          if (lexer.token() == Token.DELETE) {
              PGDeleteStatement stmt = this.parseDeleteStatement();
              stmt.setWith(with);
              return stmt;
          }
  
          if (lexer.token() == Token.UPDATE) {
              PGUpdateStatement stmt = (PGUpdateStatement) this.parseUpdateStatement();
              stmt.setWith(with);
              return stmt;
          }
  
          throw new ParserException("TODO. " + lexer.info());
      }
  
      protected SQLAlterTableAlterColumn parseAlterColumn() {
          if (lexer.token() == Token.COLUMN) {
              lexer.nextToken();
          }
  
          SQLColumnDefinition column = this.exprParser.parseColumn();
          column.setDbType(dbType);
          SQLAlterTableAlterColumn alterColumn = new SQLAlterTableAlterColumn();
          alterColumn.setColumn(column);
  
          if (column.getDataType() == null && column.getConstraints().isEmpty()) {
              if (lexer.token() == Token.SET) {
                  lexer.nextToken();
                  if (lexer.token() == Token.NOT) {
                      lexer.nextToken();
                      accept(Token.NULL);
                      alterColumn.setSetNotNull(true);
                  } else {
                      accept(Token.DEFAULT);
                      SQLExpr defaultValue = this.exprParser.expr();
                      alterColumn.setSetDefault(defaultValue);
                  }
              } else if (lexer.token() == Token.DROP) {
                  lexer.nextToken();
                  if (lexer.token() == Token.NOT) {
                      lexer.nextToken();
                      accept(Token.NULL);
                      alterColumn.setDropNotNull(true);
                  } else {
                      accept(Token.DEFAULT);
                      alterColumn.setDropDefault(true);
                  }
              }
          }
          return alterColumn;
      }
  
      public SQLStatement parseShow() {
          accept(Token.SHOW);
          PGShowStatement stmt = new PGShowStatement(dbType);
          switch (lexer.token()) {
              case ALL:
                  stmt.setExpr(new SQLIdentifierExpr(Token.ALL.name()));
                  lexer.nextToken();
                  break;
              default:
                  stmt.setExpr(this.exprParser.expr());
                  break;
          }
          return stmt;
      }
  
      @Override
      public SQLStatement parseCommit() {
          SQLCommitStatement stmt = new SQLCommitStatement();
          stmt.setDbType(this.dbType);
          lexer.nextToken();
          return stmt;
      }
  
      @Override
      public SQLStatement parseSet() {
          accept(Token.SET);
          Token token = lexer.token();
          String range = "";
  
          SQLSetStatement.Option option = null;
          if (token == Token.SESSION) {
              lexer.nextToken();
              range = Token.SESSION.name();
              option = SQLSetStatement.Option.SESSION;
          } else if (token == Token.IDENTIFIER && LOCAL.equalsIgnoreCase(lexer.stringVal())) {
              range = LOCAL;
              option = SQLSetStatement.Option.LOCAL;
              lexer.nextToken();
          }
  
          long hash = lexer.hashLCase();
          String parameter = lexer.stringVal();
          SQLExpr paramExpr;
          List<SQLExpr> values = new ArrayList<SQLExpr>();
          if (hash == FnvHash.Constants.TIME) {
              lexer.nextToken();
              acceptIdentifier("ZONE");
              paramExpr = new SQLIdentifierExpr("TIME ZONE");
              String value = lexer.stringVal();
              if (lexer.token() == Token.IDENTIFIER) {
                  values.add(new SQLIdentifierExpr(value.toUpperCase()));
              } else {
                  values.add(new SQLCharExpr(value));
              }
              lexer.nextToken();
  //            return new PGSetStatement(range, TIME_ZONE, exprs);
          } else if (hash == FnvHash.Constants.ROLE) {
              paramExpr = new SQLIdentifierExpr(parameter);
              lexer.nextToken();
              values.add(this.exprParser.primary());
              lexer.nextToken();
          } else if ("schema".equalsIgnoreCase(parameter) || "names".equalsIgnoreCase(parameter)) {
              paramExpr = new SQLIdentifierExpr(parameter);
              lexer.nextToken();
              String value = lexer.stringVal();
              values.add(new SQLCharExpr(value));
              lexer.nextToken();
          } else {
              paramExpr = new SQLIdentifierExpr(parameter);
              lexer.nextToken();
  
              while (!lexer.isEOF()) {
                  lexer.nextToken();
                  if (lexer.token() == Token.LITERAL_CHARS) {
                      values.add(new SQLCharExpr(lexer.stringVal()));
                  } else if (lexer.token() == Token.LITERAL_INT) {
                      values.add(new SQLIdentifierExpr(lexer.numberString()));
                  } else if (lexer.identifierEquals(FnvHash.Constants.JSON_SET)
                          || lexer.identifierEquals(FnvHash.Constants.JSONB_SET)) {
                      SQLExpr json_set = this.exprParser.expr();
                      values.add(json_set);
                  } else {
                      values.add(new SQLIdentifierExpr(lexer.stringVal()));
                  }
                  // skip comma
                  lexer.nextToken();
                  if (lexer.token() == SEMI) {
                      break;
                  }
              }
          }
  
          // value | 'value' | DEFAULT
  
          SQLExpr valueExpr;
          if (values.size() == 1) {
              valueExpr = values.get(0);
          } else {
              SQLListExpr listExpr = new SQLListExpr();
              for (SQLExpr value : values) {
                  listExpr.addItem(value);
              }
              valueExpr = listExpr;
          }
          SQLSetStatement stmt = new SQLSetStatement(paramExpr, valueExpr, dbType);
          stmt.setUseSet(true);
          stmt.setOption(option);
          return stmt;
      }
  
      public SQLCreateIndexStatement parseCreateIndex() {
          accept(Token.CREATE);
          SQLCreateIndexStatement stmt = new SQLCreateIndexStatement(getDbType());
          if (lexer.token() == Token.UNIQUE) {
              lexer.nextToken();
              if (lexer.identifierEquals("CLUSTERED")) {
                  lexer.nextToken();
                  stmt.setType("UNIQUE CLUSTERED");
              } else {
                  stmt.setType("UNIQUE");
              }
          } else if (lexer.identifierEquals("FULLTEXT")) {
              stmt.setType("FULLTEXT");
              lexer.nextToken();
          } else if (lexer.identifierEquals("NONCLUSTERED")) {
              stmt.setType("NONCLUSTERED");
              lexer.nextToken();
          }
  
          accept(Token.INDEX);
  
          if (lexer.token() == Token.IF) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
              stmt.setIfNotExists(true);
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.CONCURRENTLY)) {
              lexer.nextToken();
              stmt.setConcurrently(true);
          }
  
          if (lexer.token() != Token.ON) {
              stmt.setName(this.exprParser.name());
          }
  
          accept(Token.ON);
  
          stmt.setTable(this.exprParser.name());
  
          if (lexer.token() == Token.USING) {
              lexer.nextToken();
              String using = lexer.stringVal();
              accept(Token.IDENTIFIER);
              stmt.setUsing(using);
          }
  
          accept(Token.LPAREN);
  
          for (; ; ) {
              SQLSelectOrderByItem item;
              item = this.exprParser.parseSelectOrderByItem();
  
              if (lexer.identifierEquals("jsonb_path_ops") && item.getExpr() instanceof SQLIdentifierExpr) {
                  String ident = ((SQLIdentifierExpr) item.getExpr()).getName() + " " + lexer.stringVal();
                  lexer.nextToken();
                  item.setExpr(new SQLIdentifierExpr(ident));
              }
  
              item.setParent(stmt);
              stmt.addItem(item);
              if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
          accept(Token.RPAREN);
  
          if (lexer.token() == Token.WITH) {
              lexer.nextToken();
              accept(Token.LPAREN);
  
              for (; ; ) {
                  String optionName = lexer.stringVal();
                  accept(Token.IDENTIFIER);
                  accept(Token.EQ);
                  SQLExpr option = this.exprParser.expr();
                  option.setParent(stmt);
  
                  stmt.addOption(optionName, option);
  
                  if (lexer.token() == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
  
                  break;
              }
              accept(Token.RPAREN);
          }
  
          if (lexer.token() == Token.TABLESPACE) {
              lexer.nextToken();
              SQLName tablespace = this.exprParser.name();
              stmt.setTablespace(tablespace);
          }
  
          return stmt;
      }
  
      public PGCreateTableParser getSQLCreateTableParser() {
          return new PGCreateTableParser(this.exprParser);
      }
  
      public PGAnalyzeStatement parseAnalyzeTable() {
          PGAnalyzeStatement stmt = new PGAnalyzeStatement(this.dbType);
          acceptIdentifier("ANALYZE");
          Lexer.SavePoint mark = lexer.mark();
          String strVal = lexer.stringVal();
          for (; ; ) {
              if (strVal.equalsIgnoreCase("VERBOSE")) {
                  stmt.setVerbose(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("SKIP_LOCKED")) {
                  stmt.setSkipLocked(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else {
                  lexer.reset(mark);
                  break;
              }
          }
          List<SQLName> names = new ArrayList<SQLName>();
          this.exprParser.names(names, stmt);
  
          for (SQLName name : names) {
              SQLExprTableSource sqlExprTableSource = new SQLExprTableSource(name);
              sqlExprTableSource.setParent(stmt);
              stmt.getTableSources().add(sqlExprTableSource);
          }
          return stmt;
      }
  
      public PGVacuumStatement parseVacuumTable() {
          PGVacuumStatement stmt = new PGVacuumStatement(this.dbType);
          acceptIdentifier("VACUUM");
          Lexer.SavePoint mark = lexer.mark();
          String strVal = lexer.stringVal();
          for (; ; ) {
              if (Token.SEMI.equals(lexer.token())) {
                  stmt.setAfterSemi(true);
                  return stmt;
              }
              if (lexer.isEOF()) {
                  lexer.nextToken();
                  return stmt;
              }
              if (strVal.equalsIgnoreCase("FULL")) {
                  stmt.setFull(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("FREEZE")) {
                  stmt.setFreeze(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("VERBOSE")) {
                  stmt.setVerbose(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("ANALYZE")) {
                  stmt.setAnalyze(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("DISABLE_PAGE_SKIPPING")) {
                  stmt.setDisablePageSkipping(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("SKIP_LOCKED")) {
                  stmt.setSkipLocked(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("PROCESS_TOAST")) {
                  stmt.setProcessToast(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("TRUNCATE")) {
                  stmt.setTruncate(true);
                  lexer.nextToken();
                  mark = lexer.mark();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("DELTAMERGE")) { // gaussDb
                  stmt.setDeltaMerge(true);
                  lexer.nextToken();
                  mark = lexer.markOut();
                  strVal = lexer.stringVal();
                  continue;
              } else if (strVal.equalsIgnoreCase("HDFSDIRECTORY")) { // gaussDb
                  stmt.setHdfsDirectory(true);
                  lexer.nextToken();
                  mark = lexer.markOut();
                  strVal = lexer.stringVal();
                  continue;
              } else {
                  lexer.reset(mark);
                  break;
              }
          }
          List<SQLName> names = new ArrayList<SQLName>();
          this.exprParser.names(names, stmt);
          for (SQLName name : names) {
              SQLExprTableSource sqlExprTableSource = new SQLExprTableSource(name);
              sqlExprTableSource.setParent(stmt);
              stmt.getTableSources().add(sqlExprTableSource);
          }
          return stmt;
      }
  
      public PGAlterDatabaseStatement alterDatabase() {
          accept(Token.ALTER);
          if (!lexer.nextIf(Token.SCHEMA)) {
              accept(Token.DATABASE);
          }
  
          PGAlterDatabaseStatement stmt = new PGAlterDatabaseStatement(dbType);
          stmt.setDatabaseName(this.exprParser.identifier());
          if ("RENAME".equalsIgnoreCase(lexer.stringVal())) {
              lexer.nextToken();
              accept(Token.TO);
              stmt.setRenameToName(this.exprParser.identifier());
          }
          if ("OWNER".equalsIgnoreCase(lexer.stringVal())) {
              lexer.nextToken();
              accept(Token.TO);
              stmt.setOwnerToName(this.exprParser.identifier());
          }
          if ("REFRESH".equalsIgnoreCase(lexer.stringVal())) {
              lexer.nextToken();
              acceptIdentifier("COLLATION");
              acceptIdentifier("VERSION");
              stmt.setRefreshCollationVersion(true);
          }
          if (Token.SET.equals(lexer.token())) {
              lexer.nextToken();
              if (Token.TABLESPACE.equals(lexer.token())) {
                  lexer.nextToken();
                  stmt.setSetTableSpaceName(this.exprParser.identifier());
              } else {
                  stmt.setSetParameterName(this.exprParser.identifier());
                  if (Token.TO.equals(lexer.token())) {
                      lexer.nextToken();
                      stmt.setSetParameterValue(this.exprParser.expr());
                  } else {
                      accept(Token.EQ);
                      stmt.setUseEquals(true);
                      stmt.setSetParameterValue(this.exprParser.expr());
                  }
              }
          }
          if ("RESET".equalsIgnoreCase(lexer.stringVal())) {
              lexer.nextToken();
              stmt.setResetParameterName(this.exprParser.identifier());
          }
  
          if (lexer.token() == WITH) {
              lexer.nextToken();
              stmt.setHaveWith(true);
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.ALLOW_CONNECTIONS)) {
              lexer.nextToken();
              Token valueToken = lexer.token();
              if (valueToken == TRUE) {
                  lexer.nextToken();
                  stmt.setAllowConnections(true);
              } else if (valueToken == FALSE) {
                  lexer.nextToken();
                  stmt.setAllowConnections(false);
              }
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.IS_TEMPLATE)) {
              lexer.nextToken();
              Token valueToken = lexer.token();
              if (valueToken == TRUE) {
                  lexer.nextToken();
                  stmt.setSetTemplateMark(true);
              } else if (valueToken == FALSE) {
                  lexer.nextToken();
                  stmt.setSetTemplateMark(false);
              }
          }
  
          return stmt;
      }
  
      @Override
      public SQLStatement parseCreateUser() {
          accept(Token.CREATE);
          accept(Token.USER);
          SQLCreateUserStatement stmt = new SQLCreateUserStatement();
          stmt.setDbType(dbType);
          stmt.setUser(this.exprParser.name());
          if (lexer.token() == Token.WITH) {
              accept(Token.WITH);
              stmt.setPostgresqlWith(true);
          }
          if (lexer.identifierEquals("ENCRYPTED")) {
              stmt.setPostgresqlEncrypted(true);
              lexer.nextToken();
          }
          if (lexer.identifierEquals("PASSWORD")) {
              lexer.nextToken();
          }
          stmt.setPassword(this.exprParser.primary());
          return stmt;
      }
  
      @Override
      protected boolean alterTableAfterNameRest(SQLAlterTableStatement stmt) {
          if (lexer.identifierEquals("CHANGEOWNER") && lexer.identifierEquals("OWNER")) {
              alterTableOwner(stmt);
          } else {
              return true;
          }
          return false;
      }
  
      @Override
      protected void alterTableAlterComma() {
          if (lexer.token() == Token.COMMA) {
              lexer.nextToken();
          }
      }
  
      @Override
      public void parseAlterDropRest(SQLAlterTableStatement stmt, SQLAlterTableDropColumnItem item) {
          item.getColumns().add(this.exprParser.name());
  
          if (lexer.token() == Token.CASCADE) {
              item.setCascade(true);
              lexer.nextToken();
          }
          if (RESTRICT == lexer.token()) {
              item.setRestrict(true);
              lexer.nextToken();
          }
          stmt.addItem(item);
  
          if (lexer.token() == Token.COMMA) {
              lexer.nextToken();
          }
      }
  }
  