package com.chenyang.druid.sql.dialect.hive.visitor;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.ast.SQLAdhocTableSource;
import com.chenyang.druid.sql.ast.SQLCommentHint;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLLimit;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLOrderBy;
import com.chenyang.druid.sql.ast.SQLWindow;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableExchangePartition;
import com.chenyang.druid.sql.ast.statement.SQLAssignItem;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLCreateIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLExternalRecordFormat;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLMergeStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectGroupByClause;
import com.chenyang.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.chenyang.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.chenyang.druid.sql.dialect.hive.ast.HiveGrantPrinciple;
import com.chenyang.druid.sql.dialect.hive.ast.HiveInsert;
import com.chenyang.druid.sql.dialect.hive.ast.HiveMultiInsertStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveAlterDatabaseStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveCreateFunctionStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveCreateTableStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveInsertStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveLoadDataStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveMsckRepairStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveSelectQueryBlock;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveShowStatement;
import com.chenyang.druid.sql.visitor.SQLASTOutputVisitor;
import com.chenyang.druid.sql.visitor.VisitorFeature;
import java.util.List;
import java.util.Map;

public class HiveOutputVisitor extends SQLASTOutputVisitor implements HiveASTVisitor {
   public HiveOutputVisitor(Appendable appender) {
      super(appender, DbType.hive);
      super.quote = '`';
   }

   public HiveOutputVisitor(Appendable appender, DbType dbType) {
      super(appender, dbType);
      super.quote = '`';
   }

   public HiveOutputVisitor(Appendable appender, boolean parameterized) {
      super(appender, parameterized);
      super.quote = '`';
      this.dbType = DbType.hive;
   }

   public boolean visit(HiveInsert x) {
      if (x.hasBeforeComment()) {
         this.printlnComments(x.getBeforeCommentsDirect());
      }

      if (x.isOverwrite()) {
         this.print0(this.ucase ? "INSERT OVERWRITE TABLE " : "insert overwrite table ");
      } else {
         this.print0(this.ucase ? "INSERT INTO TABLE " : "insert into table ");
      }

      x.getTableSource().accept(this);
      List<SQLAssignItem> partitions = x.getPartitions();
      if (partitions != null) {
         int partitionsSize = partitions.size();
         if (partitionsSize > 0) {
            this.print0(this.ucase ? " PARTITION (" : " partition (");

            for(int i = 0; i < partitionsSize; ++i) {
               if (i != 0) {
                  this.print0(", ");
               }

               SQLAssignItem assign = (SQLAssignItem)partitions.get(i);
               assign.getTarget().accept(this);
               if (assign.getValue() != null) {
                  this.print('=');
                  assign.getValue().accept(this);
               }
            }

            this.print(')');
         }

         this.println();
      }

      SQLSelect select = x.getQuery();
      List<SQLInsertStatement.ValuesClause> valuesList = x.getValuesList();
      if (select != null) {
         select.accept(this);
      } else if (!valuesList.isEmpty()) {
         this.print0(this.ucase ? "VALUES " : "values ");
         this.printAndAccept(valuesList, ", ");
      }

      return false;
   }

   public void endVisit(HiveInsert x) {
   }

   public boolean visit(SQLExternalRecordFormat x) {
      return this.hiveVisit(x);
   }

   public boolean visit(HiveMultiInsertStatement x) {
      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         this.visit((SQLWithSubqueryClause)with);
         this.println();
      }

      SQLTableSource from = x.getFrom();
      if (x.getFrom() != null) {
         if (from instanceof SQLSubqueryTableSource) {
            SQLSelect select = ((SQLSubqueryTableSource)from).getSelect();
            this.print0(this.ucase ? "FROM (" : "from (");
            ++this.indentCount;
            this.println();
            select.accept(this);
            --this.indentCount;
            this.println();
            this.print0(") ");
            this.print0(x.getFrom().getAlias());
         } else {
            this.print0(this.ucase ? "FROM " : "from ");
            from.accept(this);
         }

         this.println();
      }

      for(int i = 0; i < x.getItems().size(); ++i) {
         HiveInsert insert = (HiveInsert)x.getItems().get(i);
         if (i != 0) {
            this.println();
         }

         insert.accept(this);
      }

      return false;
   }

   public void endVisit(HiveMultiInsertStatement x) {
   }

   public boolean visit(HiveInsertStatement x) {
      if (x.hasBeforeComment()) {
         this.printlnComments(x.getBeforeCommentsDirect());
      }

      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         this.visit((SQLWithSubqueryClause)with);
         this.println();
      }

      if (x.isOverwrite()) {
         this.print0(this.ucase ? "INSERT OVERWRITE TABLE " : "insert overwrite table ");
      } else {
         this.print0(this.ucase ? "INSERT INTO TABLE " : "insert into table ");
      }

      if (x.local) {
         this.print0("LOCAL");
      }

      if (x.directory) {
         this.print0("DIRECTORY");
      }

      x.getTableSource().accept(this);
      List<SQLExpr> columns = x.getColumns();
      if (columns.size() > 0) {
         this.print('(');
         this.printAndAccept(columns, ", ");
         this.print(')');
      }

      List<SQLAssignItem> partitions = x.getPartitions();
      int partitionSize = 0;
      if (partitions != null) {
         partitionSize = partitions.size();
      }

      if (partitionSize > 0) {
         this.print0(this.ucase ? " PARTITION (" : " partition (");

         for(int i = 0; i < partitionSize; ++i) {
            if (i != 0) {
               this.print0(", ");
            }

            SQLAssignItem assign = (SQLAssignItem)partitions.get(i);
            assign.getTarget().accept(this);
            if (assign.getValue() != null) {
               this.print('=');
               assign.getValue().accept(this);
            }
         }

         this.print(')');
      }

      if (x.isIfNotExists()) {
         this.print0(this.ucase ? " IF NOT EXISTS" : " if not exists");
      }

      this.println();
      SQLSelect select = x.getQuery();
      List<SQLInsertStatement.ValuesClause> valuesList = x.getValuesList();
      if (select != null) {
         select.accept(this);
      } else if (!valuesList.isEmpty()) {
         this.print0(this.ucase ? "VALUES " : "values ");
         this.printAndAccept(valuesList, ", ");
      }

      return false;
   }

   public void endVisit(HiveInsertStatement x) {
   }

   public boolean visit(SQLMergeStatement.MergeUpdateClause x) {
      this.print0(this.ucase ? "WHEN MATCHED " : "when matched ");
      ++this.indentCount;
      SQLExpr where = x.getWhere();
      if (where != null) {
         ++this.indentCount;
         if (SQLBinaryOpExpr.isAnd(where)) {
            this.println();
         } else {
            this.print(' ');
         }

         this.print0(this.ucase ? "AND " : "and ");
         this.printExpr(where, this.parameterized);
         --this.indentCount;
         this.println();
      }

      this.print0(this.ucase ? "UPDATE SET " : "update set ");
      this.printAndAccept(x.getItems(), ", ");
      --this.indentCount;
      SQLExpr deleteWhere = x.getDeleteWhere();
      if (deleteWhere != null) {
         this.println();
         this.print0(this.ucase ? "WHEN MATCHED AND " : "when matched and ");
         this.printExpr(deleteWhere, this.parameterized);
         this.print0(this.ucase ? " DELETE" : " delete");
      }

      return false;
   }

   public boolean visit(HiveCreateFunctionStatement x) {
      this.print0(this.ucase ? "CREATE " : "create ");
      if (x.isOrReplace()) {
         this.print0(this.ucase ? "OR REPLACE " : "or replace ");
      }

      if (x.isTemporary()) {
         this.print0(this.ucase ? "TEMPORARY " : "temporary ");
      }

      this.print0(this.ucase ? "FUNCTION " : "function ");
      x.getName().accept(this);
      SQLExpr className = x.getClassName();
      if (className != null) {
         this.print0(this.ucase ? " AS " : " as ");
         className.accept(this);
      }

      ++this.indentCount;
      SQLExpr location = x.getLocationn();
      if (location != null) {
         this.println();
         HiveCreateFunctionStatement.ResourceType resourceType = x.getResourceType();
         if (resourceType != null) {
            this.print0(this.ucase ? "USING " : "using ");
            this.print0(resourceType.name());
            this.print(' ');
         } else {
            this.print0(this.ucase ? "LOCATION " : "location ");
         }

         location.accept(this);
      }

      SQLExpr symbol = x.getSymbol();
      if (symbol != null) {
         this.println();
         this.print0(this.ucase ? "SYMBOL = " : "symbol = ");
         symbol.accept(this);
      }

      --this.indentCount;
      return false;
   }

   public void endVisit(HiveCreateFunctionStatement x) {
   }

   public boolean visit(HiveLoadDataStatement x) {
      this.print0(this.ucase ? "LOAD DATA " : "load data ");
      if (x.isLocal()) {
         this.print0(this.ucase ? "LOCAL " : "local ");
      }

      this.print0(this.ucase ? "INPATH " : "inpath ");
      x.getInpath().accept(this);
      if (x.isOverwrite()) {
         this.print0(this.ucase ? " OVERWRITE INTO TABLE " : " overwrite into table ");
      } else {
         this.print0(this.ucase ? " INTO TABLE " : " into table ");
      }

      x.getInto().accept(this);
      if (x.getPartition().size() > 0) {
         this.print0(this.ucase ? " PARTITION (" : " partition (");
         this.printAndAccept(x.getPartition(), ", ");
         this.print(')');
      }

      return false;
   }

   public void endVisit(HiveLoadDataStatement x) {
   }

   public boolean visit(HiveMsckRepairStatement x) {
      List<SQLCommentHint> headHints = x.getHeadHintsDirect();
      if (headHints != null) {
         for(SQLCommentHint hint : headHints) {
            hint.accept(this);
            this.println();
         }
      }

      this.print0(this.ucase ? "MSCK REPAIR" : "msck repair");
      SQLName database = x.getDatabase();
      if (database != null) {
         this.print0(this.ucase ? " DATABASE " : " database ");
         database.accept(this);
      }

      SQLExprTableSource table = x.getTable();
      if (table != null) {
         this.print0(this.ucase ? " TABLE " : " table ");
         table.accept(this);
      }

      if (x.isAddPartitions()) {
         this.print0(this.ucase ? " ADD PARTITIONS" : " add partitions");
      }

      return false;
   }

   public void endVisit(HiveMsckRepairStatement x) {
   }

   public boolean visit(SQLAlterTableExchangePartition x) {
      this.print0(this.ucase ? "EXCHANGE PARTITION (" : "exchange partition (");
      this.printAndAccept(x.getPartitions(), ", ");
      this.print0(this.ucase ? ") WITH TABLE " : ") with table ");
      x.getTable().accept(this);
      Boolean validation = x.getValidation();
      if (validation != null) {
         if (validation) {
            this.print0(this.ucase ? " WITH VALIDATION" : " with validation");
         } else {
            this.print0(this.ucase ? " WITHOUT VALIDATION" : " without validation");
         }
      }

      return false;
   }

   public boolean visit(SQLCreateIndexStatement x) {
      this.print0(this.ucase ? "CREATE " : "create ");
      this.print0(this.ucase ? "INDEX " : "index ");
      x.getName().accept(this);
      this.print0(this.ucase ? " ON TABLE " : " on table ");
      x.getTable().accept(this);
      this.print0(" (");
      this.printAndAccept(x.getItems(), ", ");
      this.print(')');
      String type = x.getType();
      if (type != null) {
         this.print0(this.ucase ? " AS " : " as ");
         this.print0(type);
      }

      if (x.isDeferedRebuild()) {
         this.print0(this.ucase ? " WITH DEFERRED REBUILD" : " with deferred rebuild");
      }

      if (x.getProperties().size() > 0) {
         this.print0(this.ucase ? " IDXPROPERTIES (" : " idxproperties (");
         this.printAndAccept(x.getProperties(), ", ");
         this.print(')');
      }

      String using = x.getUsing();
      if (using != null) {
         this.print0(this.ucase ? " USING " : " using ");
         this.print0(using);
      }

      SQLExpr comment = x.getComment();
      if (comment != null) {
         this.print0(this.ucase ? " COMMENT " : " comment ");
         comment.accept(this);
      }

      SQLTableSource in = x.getIn();
      if (in != null) {
         this.print0(this.ucase ? " IN TABLE " : " in table ");
         in.accept(this);
      }

      SQLExternalRecordFormat format = x.getRowFormat();
      if (format != null) {
         this.println();
         this.print0(this.ucase ? "ROW FORMAT DELIMITED " : "row rowFormat delimited ");
         this.visit(format);
      }

      SQLName storedAs = x.getStoredAs();
      if (storedAs != null) {
         this.print0(this.ucase ? " STORED BY " : " stored by ");
         storedAs.accept(this);
      }

      if (x.getTableProperties().size() > 0) {
         this.print0(this.ucase ? " TBLPROPERTIES (" : " tblproperties (");
         this.printAndAccept(x.getTableProperties(), ", ");
         this.print(')');
      }

      return false;
   }

   public boolean visit(SQLCharExpr x, boolean parameterized) {
      String text = x.getText();
      if (text == null) {
         this.print0(this.ucase ? "NULL" : "null");
      } else {
         StringBuilder buf = new StringBuilder(text.length() + 2);
         buf.append('\'');

         for(int i = 0; i < text.length(); ++i) {
            char ch = text.charAt(i);
            switch (ch) {
               case '\u0000':
                  buf.append("\\0");
                  break;
               case '\b':
                  buf.append("\\b");
                  break;
               case '\t':
                  buf.append("\\t");
                  break;
               case '\n':
                  buf.append("\\n");
                  break;
               case '\r':
                  buf.append("\\r");
                  break;
               case '\'':
                  buf.append("\\'");
                  break;
               case '\\':
                  buf.append("\\\\");
                  break;
               default:
                  if (ch == 9733) {
                     buf.append("\\u2605");
                  } else if (ch == 9660) {
                     buf.append("\\u25bc");
                  } else {
                     buf.append(ch);
                  }
            }
         }

         buf.append('\'');
         this.print0(buf.toString());
      }

      return false;
   }

   protected void printCreateTable(HiveCreateTableStatement x, boolean printSelect) {
      SQLObject parent = x.getParent();
      if (!(parent instanceof SQLAdhocTableSource)) {
         this.print0(this.ucase ? "CREATE " : "create ");
      }

      if (x.isExternal()) {
         this.print0(this.ucase ? "EXTERNAL " : "external ");
      }

      SQLCreateTableStatement.Type tableType = x.getType();
      if (SQLCreateTableStatement.Type.TEMPORARY.equals(tableType)) {
         this.print0(this.ucase ? "TEMPORARY " : "temporary ");
      }

      if (x.type == "table") {
         this.print0(this.ucase ? "TABLE " : "table ");
      }

      if (x.type == "view") {
         this.print0(this.ucase ? "VIEW " : "view ");
      }

      if (x.isIfNotExists()) {
         this.print0(this.ucase ? "IF NOT EXISTS " : "if not exists ");
      }

      this.printTableSourceExpr(x.getName());
      this.printTableElements(x.getTableElementList());
      SQLExprTableSource inherits = x.getInherits();
      if (inherits != null) {
         this.print0(this.ucase ? " INHERITS (" : " inherits (");
         inherits.accept(this);
         this.print(')');
      }

      SQLExpr using = x.getUsing();
      if (using != null) {
         this.println();
         this.print0(this.ucase ? "USING " : "using ");
         using.accept(this);
      }

      List<SQLAssignItem> tableOptions = x.getTableOptions();
      if (tableOptions.size() > 0) {
         this.println();
         this.print0(this.ucase ? "OPTIONS (" : "options (");
         this.incrementIndent();
         this.println();
         int i = 0;

         for(SQLAssignItem option : tableOptions) {
            if (i != 0) {
               this.print(",");
               this.println();
            }

            String key = option.getTarget().toString();
            boolean unquote = false;
            char c0;
            if (key.length() > 0 && (c0 = key.charAt(0)) != '"' && c0 != '`' && c0 != '\'') {
               unquote = true;
            }

            if (unquote) {
               this.print('\'');
            }

            this.print0(key);
            if (unquote) {
               this.print('\'');
            }

            this.print0(" = ");
            option.getValue().accept(this);
            ++i;
         }

         this.decrementIndent();
         this.println();
         this.print(')');
      }

      SQLExpr comment = x.getComment();
      if (comment != null) {
         this.println();
         this.print0(this.ucase ? "COMMENT " : "comment ");
         comment.accept(this);
      }

      List<SQLAssignItem> mappedBy = x.getMappedBy();
      if (mappedBy != null && mappedBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "MAPPED BY (" : "mapped by (");
         this.printAndAccept(mappedBy, ", ");
         this.print0(this.ucase ? ")" : ")");
      }

      int partitionSize = x.getPartitionColumns().size();
      if (partitionSize > 0) {
         this.println();
         this.print0(this.ucase ? "PARTITIONED BY (" : "partitioned by (");
         ++this.indentCount;
         this.println();

         for(int i = 0; i < partitionSize; ++i) {
            SQLColumnDefinition column = (SQLColumnDefinition)x.getPartitionColumns().get(i);
            column.accept(this);
            if (i != partitionSize - 1) {
               this.print(',');
            }

            if (this.isPrettyFormat() && column.hasAfterComment()) {
               this.print(' ');
               this.printlnComment(column.getAfterCommentsDirect());
            }

            if (i != partitionSize - 1) {
               this.println();
            }
         }

         --this.indentCount;
         this.println();
         this.print(')');
      }

      List<SQLSelectOrderByItem> clusteredBy = x.getClusteredBy();
      if (clusteredBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "CLUSTERED BY (" : "clustered by (");
         this.printAndAccept(clusteredBy, ",");
         this.print(')');
      }

      List<SQLExpr> skewedBy = x.getSkewedBy();
      if (skewedBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "SKEWED BY (" : "skewed by (");
         this.printAndAccept(skewedBy, ",");
         this.print(')');
         List<SQLExpr> skewedByOn = x.getSkewedByOn();
         if (skewedByOn.size() > 0) {
            this.print0(this.ucase ? " ON (" : " on (");
            this.printAndAccept(skewedByOn, ",");
            this.print(')');
         }
      }

      SQLExternalRecordFormat format = x.getRowFormat();
      if (format != null) {
         this.println();
         this.print0(this.ucase ? "ROW FORMAT" : "row rowFormat");
         if (format.getSerde() == null) {
            this.print0(this.ucase ? " DELIMITED" : " delimited ");
         }

         this.visit(format);
      }

      Map<String, SQLObject> serdeProperties = x.getSerdeProperties();
      this.printSerdeProperties(serdeProperties);
      List<SQLSelectOrderByItem> sortedBy = x.getSortedBy();
      if (sortedBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "SORTED BY (" : "sorted by (");
         this.printAndAccept(sortedBy, ", ");
         this.print(')');
      }

      int buckets = x.getBuckets();
      if (buckets > 0) {
         this.println();
         this.print0(this.ucase ? "INTO " : "into ");
         this.print(buckets);
         this.print0(this.ucase ? " BUCKETS" : " buckets");
      }

      SQLExprTableSource like = x.getLike();
      if (like != null) {
         this.println();
         this.print0(this.ucase ? "LIKE " : "like ");
         like.accept(this);
      }

      SQLExpr storedAs = x.getStoredAs();
      if (storedAs != null) {
         this.println();
         this.print0(this.ucase ? "STORED AS" : "stored as");
         if (storedAs instanceof SQLIdentifierExpr) {
            this.print(' ');
            this.printExpr(storedAs, this.parameterized);
         } else {
            this.incrementIndent();
            this.println();
            this.printExpr(storedAs, this.parameterized);
            this.decrementIndent();
         }
      }

      SQLExpr location = x.getLocation();
      if (location != null) {
         this.println();
         this.print0(this.ucase ? "LOCATION " : "location ");
         this.printExpr(location, this.parameterized);
      }

      this.printTblProperties(x);
      SQLExpr metaLifeCycle = x.getMetaLifeCycle();
      if (metaLifeCycle != null) {
         this.println();
         this.print0(this.ucase ? "META LIFECYCLE " : "meta lifecycle ");
         this.printExpr(metaLifeCycle);
      }

      SQLSelect select = x.getSelect();
      if (printSelect && select != null) {
         this.println();
         if (x.isLikeQuery()) {
            this.print0(this.ucase ? "LIKE" : "like");
         } else {
            this.print0(this.ucase ? "AS" : "as");
         }

         this.println();
         this.visit((SQLSelect)select);
      }

   }

   public boolean visit(HiveGrantPrinciple x) {
      if (x.isUser()) {
         this.print0(this.ucase ? "USER " : "user ");
      } else if (x.isRole()) {
         this.print0(this.ucase ? "ROLE " : "role ");
      }

      if (x.getExpr() != null) {
         x.getExpr().accept(this);
      }

      return false;
   }

   public void endVisit(HiveGrantPrinciple x) {
   }

   public boolean visit(HiveCreateTableStatement x) {
      SQLObject parent = x.getParent();
      if (!(parent instanceof SQLAdhocTableSource)) {
         this.print0(this.ucase ? "CREATE " : "create ");
      }

      if (x.isExternal()) {
         this.print0(this.ucase ? "EXTERNAL " : "external ");
      }

      SQLCreateTableStatement.Type tableType = x.getType();
      if (SQLCreateTableStatement.Type.TEMPORARY.equals(tableType)) {
         this.print0(this.ucase ? "TEMPORARY " : "temporary ");
      }

      if (x.type.equals("table")) {
         this.print0(this.ucase ? "TABLE " : "table");
      }

      if (x.type.equals("view")) {
         this.print0(this.ucase ? "VIEW " : "view");
      }

      if (x.type.equals("materialized")) {
         this.print0(this.ucase ? "MATERIALIZED VIEW " : "materialized view");
      }

      if (x.isIfNotExists()) {
         this.print0(this.ucase ? "IF NOT EXISTS " : "if not exists ");
      }

      this.printTableSourceExpr(x.getName());
      this.printTableElements(x.getTableElementList());
      SQLExprTableSource inherits = x.getInherits();
      if (inherits != null) {
         this.print0(this.ucase ? " INHERITS (" : " inherits (");
         inherits.accept(this);
         this.print(')');
      }

      SQLExpr using = x.getUsing();
      if (using != null) {
         this.println();
         this.print0(this.ucase ? "USING " : "using ");
         using.accept(this);
      }

      List<SQLAssignItem> tableOptions = x.getTableOptions();
      if (tableOptions.size() > 0) {
         this.println();
         this.print0(this.ucase ? "OPTIONS (" : "options (");
         this.incrementIndent();
         this.println();
         int i = 0;

         for(SQLAssignItem option : tableOptions) {
            if (i != 0) {
               this.print(",");
               this.println();
            }

            String key = option.getTarget().toString();
            boolean unquote = false;
            char c0;
            if (key.length() > 0 && (c0 = key.charAt(0)) != '"' && c0 != '`' && c0 != '\'') {
               unquote = true;
            }

            if (unquote) {
               this.print('\'');
            }

            this.print0(key);
            if (unquote) {
               this.print('\'');
            }

            this.print0(" = ");
            option.getValue().accept(this);
            ++i;
         }

         this.decrementIndent();
         this.println();
         this.print(')');
      }

      SQLExpr comment = x.getComment();
      if (comment != null) {
         this.println();
         this.print0(this.ucase ? "COMMENT " : "comment ");
         comment.accept(this);
      }

      List<SQLAssignItem> mappedBy = x.getMappedBy();
      if (mappedBy != null && mappedBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "MAPPED BY (" : "mapped by (");
         this.printAndAccept(mappedBy, ", ");
         this.print0(this.ucase ? ")" : ")");
      }

      int partitionSize = x.getPartitionColumns().size();
      if (partitionSize > 0) {
         this.println();
         this.print0(this.ucase ? "PARTITIONED BY (" : "partitioned by (");
         ++this.indentCount;
         this.println();

         for(int i = 0; i < partitionSize; ++i) {
            SQLColumnDefinition column = (SQLColumnDefinition)x.getPartitionColumns().get(i);
            column.accept(this);
            if (i != partitionSize - 1) {
               this.print(',');
            }

            if (this.isPrettyFormat() && column.hasAfterComment()) {
               this.print(' ');
               this.printlnComment(column.getAfterCommentsDirect());
            }

            if (i != partitionSize - 1) {
               this.println();
            }
         }

         --this.indentCount;
         this.println();
         this.print(')');
      }

      List<SQLSelectOrderByItem> clusteredBy = x.getClusteredBy();
      if (clusteredBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "CLUSTERED BY (" : "clustered by (");
         this.printAndAccept(clusteredBy, ",");
         this.print(')');
      }

      List<SQLExpr> skewedBy = x.getSkewedBy();
      if (skewedBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "SKEWED BY (" : "skewed by (");
         this.printAndAccept(skewedBy, ",");
         this.print(')');
         List<SQLExpr> skewedByOn = x.getSkewedByOn();
         if (skewedByOn.size() > 0) {
            this.print0(this.ucase ? " ON (" : " on (");
            this.printAndAccept(skewedByOn, ",");
            this.print(')');
         }
      }

      SQLExternalRecordFormat format = x.getRowFormat();
      if (format != null) {
         this.println();
         this.print0(this.ucase ? "ROW FORMAT" : "row rowFormat");
         if (format.getSerde() == null) {
            this.print0(this.ucase ? " DELIMITED" : " delimited ");
         }

         this.visit(format);
      }

      Map<String, SQLObject> serdeProperties = x.getSerdeProperties();
      this.printSerdeProperties(serdeProperties);
      List<SQLSelectOrderByItem> sortedBy = x.getSortedBy();
      if (sortedBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "SORTED BY (" : "sorted by (");
         this.printAndAccept(sortedBy, ", ");
         this.print(')');
      }

      int buckets = x.getBuckets();
      if (buckets > 0) {
         this.println();
         this.print0(this.ucase ? "INTO " : "into ");
         this.print(buckets);
         this.print0(this.ucase ? " BUCKETS" : " buckets");
      }

      SQLExprTableSource like = x.getLike();
      if (like != null) {
         this.println();
         this.print0(this.ucase ? "LIKE " : "like ");
         like.accept(this);
      }

      SQLExpr storedAs = x.getStoredAs();
      if (storedAs != null) {
         this.println();
         this.print0(this.ucase ? "STORED AS" : "stored as");
         if (storedAs instanceof SQLIdentifierExpr) {
            this.print(' ');
            this.printExpr(storedAs, this.parameterized);
         } else {
            this.incrementIndent();
            this.println();
            this.printExpr(storedAs, this.parameterized);
            this.decrementIndent();
         }
      }

      SQLExpr location = x.getLocation();
      if (location != null) {
         this.println();
         this.print0(this.ucase ? "LOCATION " : "location ");
         this.printExpr(location, this.parameterized);
      }

      this.printTblProperties(x);
      SQLExpr metaLifeCycle = x.getMetaLifeCycle();
      if (metaLifeCycle != null) {
         this.println();
         this.print0(this.ucase ? "META LIFECYCLE " : "meta lifecycle ");
         this.printExpr(metaLifeCycle);
      }

      SQLSelect select = x.getSelect();
      if (select != null) {
         this.println();
         this.print0(this.ucase ? "AS" : "as");
         this.println();
         this.visit((SQLSelect)select);
      }

      return false;
   }

   public boolean visit(HiveShowStatement x) {
      if (x.isAbort) {
         this.print0(this.ucase ? "ABORT " : "abort ");
         this.print0(this.ucase ? "TRANSACTIONS" : "transactions");
         return false;
      } else {
         this.print0(this.ucase ? "SHOW " : "show ");
         this.print0(x.showType);
         this.print0(" ");
         if (x.partitionsName != null) {
            x.partitionsName.accept(this);
            this.print0(" ");
         }

         if (x.on != null) {
            this.print0(this.ucase ? "ON " : "on ");
            x.on.accept(this);
            this.print0(" ");
         }

         if (x.from != null) {
            this.print0(this.ucase ? "FROM " : "from ");
            x.from.accept(this);
            this.print0(" ");
         }

         if (x.in != null) {
            this.print0(this.ucase ? "IN " : "in ");
            x.in.accept(this);
            this.print0(" ");
         }

         if (x.from2 != null) {
            this.print0(this.ucase ? "FROM " : "from ");
            x.from2.accept(this);
            this.print0(" ");
         }

         if (x.in2 != null) {
            this.print0(this.ucase ? "IN " : "in ");
            x.in2.accept(this);
            this.print0(" ");
         }

         if (x.like != null) {
            this.print0(this.ucase ? "LIKE " : "like ");
            x.like.accept(this);
            this.print0(" ");
         }

         List<SQLAssignItem> partitions = x.getPartitions();
         if (partitions != null) {
            int partitionsSize = partitions.size();
            if (partitionsSize > 0) {
               this.print0(this.ucase ? " PARTITION (" : " partition (");

               for(int i = 0; i < partitionsSize; ++i) {
                  if (i != 0) {
                     this.print0(", ");
                  }

                  SQLAssignItem assign = (SQLAssignItem)partitions.get(i);
                  assign.getTarget().accept(this);
                  if (assign.getValue() != null) {
                     this.print('=');
                     assign.getValue().accept(this);
                  }
               }

               this.print(')');
            }

            this.println();
         }

         if (x.other != null) {
            x.other.accept(this);
            this.print0(" ");
         }

         if (x.showTypeValue != null) {
            x.showTypeValue.accept(this);
            this.print0(" ");
         }

         if (x.tableName != null) {
            this.print0(this.ucase ? "ON TABLE " : "on table ");
            x.tableName.accept(this);
            this.print0(" ");
         }

         if (x.isAllTable) {
            this.print0(this.ucase ? "ON ALL " : "on all ");
         }

         return false;
      }
   }

   public void endVisit(HiveShowStatement x) {
   }

   public boolean visit(HiveAlterDatabaseStatement x) {
      this.print0(this.ucase ? "ALTER DATABASE " : "alter database ");
      x.getName().accept(this);
      if (x.isUpgradeDataDirectoryName()) {
         this.print0(this.ucase ? " UPGRADE DATA DIRECTORY NAME" : " upgrade data directory name");
      }

      if (x.getProperties().size() > 0) {
         this.print0(this.ucase ? " SET DBPROPERTIES (" : "set dbproperties (");
         this.printAndAccept(x.getProperties(), ", ");
         this.print(')');
      }

      if (x.user != null) {
         this.print0(this.ucase ? " SET OWNER " : " set owner ");
         x.user.accept(this);
      }

      if (x.location != null) {
         this.print0(this.ucase ? " SET LOCATION " : " set location ");
         x.location.accept(this);
      }

      if (x.managedlocation != null) {
         this.print0(this.ucase ? " SET MANAGEDLOCATION " : " set managedlocation ");
         x.managedlocation.accept(this);
      }

      return false;
   }

   public void endVisit(HiveAlterDatabaseStatement x) {
   }

   public boolean visit(HiveSelectQueryBlock x) {
      boolean bracket = x.isBracket();
      if (bracket) {
         this.print('(');
      }

      if (!this.isParameterized() && this.isPrettyFormat() && x.hasBeforeComment()) {
         this.printlnComments(x.getBeforeCommentsDirect());
      }

      String cachedSelectList = x.getCachedSelectList();
      if (cachedSelectList != null) {
         if (!this.isEnabled(VisitorFeature.OutputSkipSelectListCacheString)) {
            this.print0(cachedSelectList);
         }
      } else {
         this.print0(this.ucase ? "SELECT " : "select ");
         int i = 0;

         for(int size = x.getHintsSize(); i < size; ++i) {
            SQLCommentHint hint = (SQLCommentHint)x.getHints().get(i);
            hint.accept(this);
            this.print(' ');
         }

         switch (x.getDistionOption()) {
            case 1:
               this.print0(this.ucase ? "ALL " : "all ");
               break;
            case 2:
               this.print0(this.ucase ? "DISTINCT " : "distinct ");
               break;
            case 3:
               this.print0(this.ucase ? "UNIQUE " : "unique ");
               break;
            case 4:
               this.print0(this.ucase ? "DISTINCTROW " : "distinctrow ");
         }

         if (x.isHignPriority()) {
            this.print0(this.ucase ? "HIGH_PRIORITY " : "high_priority ");
         }

         if (x.isStraightJoin()) {
            this.print0(this.ucase ? "STRAIGHT_JOIN " : "straight_join ");
         }

         if (x.isSmallResult()) {
            this.print0(this.ucase ? "SQL_SMALL_RESULT " : "sql_small_result ");
         }

         if (x.isBigResult()) {
            this.print0(this.ucase ? "SQL_BIG_RESULT " : "sql_big_result ");
         }

         if (x.isBufferResult()) {
            this.print0(this.ucase ? "SQL_BUFFER_RESULT " : "sql_buffer_result ");
         }

         if (x.getCache() != null) {
            if (x.getCache()) {
               this.print0(this.ucase ? "SQL_CACHE " : "sql_cache ");
            } else {
               this.print0(this.ucase ? "SQL_NO_CACHE " : "sql_no_cache ");
            }
         }

         if (x.isCalcFoundRows()) {
            this.print0(this.ucase ? "SQL_CALC_FOUND_ROWS " : "sql_calc_found_rows ");
         }

         this.printSelectList(x.getSelectList());
         SQLName forcePartition = x.getForcePartition();
         if (forcePartition != null) {
            this.println();
            this.print0(this.ucase ? "FORCE PARTITION " : "force partition ");
            this.printExpr(forcePartition, this.parameterized);
         }

         SQLExprTableSource into = x.getInto();
         if (into != null) {
            this.println();
            this.print0(this.ucase ? "INTO " : "into ");
            this.printTableSource(into);
         }
      }

      SQLTableSource from = x.getFrom();
      if (from != null) {
         this.println();
         this.print0(this.ucase ? "FROM " : "from ");
         this.printTableSource(from);
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         this.println();
         this.print0(this.ucase ? "WHERE " : "where ");
         this.printExpr(where, this.parameterized);
      }

      this.printHierarchical(x);
      SQLSelectGroupByClause groupBy = x.getGroupBy();
      if (groupBy != null) {
         this.println();
         this.visit((SQLSelectGroupByClause)groupBy);
      }

      List<SQLWindow> windows = x.getWindows();
      if (windows != null && windows.size() > 0) {
         this.println();
         this.print0(this.ucase ? "WINDOW " : "window ");
         this.printAndAccept(windows, ", ");
      }

      SQLOrderBy orderBy = x.getOrderBy();
      if (orderBy != null) {
         this.println();
         this.visit((SQLOrderBy)orderBy);
      }

      List<SQLSelectOrderByItem> distributeBy = x.getDistributeByDirect();
      if (distributeBy != null && distributeBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "DISTRIBUTE BY " : "distribute by ");
         this.printAndAccept(distributeBy, ", ");
      }

      List<SQLSelectOrderByItem> sortBy = x.getSortByDirect();
      if (sortBy != null && sortBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "SORT BY " : "sort by ");
         this.printAndAccept(sortBy, ", ");
      }

      List<SQLSelectOrderByItem> clusterBy = x.getClusterByDirect();
      if (clusterBy != null && clusterBy.size() > 0) {
         this.println();
         this.print0(this.ucase ? "CLUSTER BY " : "cluster by ");
         this.printAndAccept(clusterBy, ", ");
      }

      SQLLimit limit = x.getLimit();
      if (limit != null) {
         this.println();
         this.visit((SQLLimit)limit);
      }

      SQLName procedureName = x.getProcedureName();
      if (procedureName != null) {
         this.print0(this.ucase ? " PROCEDURE " : " procedure ");
         procedureName.accept(this);
         if (!x.getProcedureArgumentList().isEmpty()) {
            this.print('(');
            this.printAndAccept(x.getProcedureArgumentList(), ", ");
            this.print(')');
         }
      }

      if (x.isForUpdate()) {
         this.println();
         this.print0(this.ucase ? "FOR UPDATE" : "for update");
         if (x.isNoWait()) {
            this.print0(this.ucase ? " NOWAIT" : " nowait");
         } else if (x.getWaitTime() != null) {
            this.print0(this.ucase ? " WAIT " : " wait ");
            x.getWaitTime().accept(this);
         }

         if (x.isSkipLocked()) {
            this.print0(this.ucase ? " SKIP LOCKED" : " skip locked");
         }
      }

      if (x.isForShare()) {
         this.println();
         this.print0(this.ucase ? "FOR SHARE" : "for share");
      }

      if (x.isLockInShareMode()) {
         this.println();
         this.print0(this.ucase ? "LOCK IN SHARE MODE" : "lock in share mode");
      }

      if (bracket) {
         this.print(')');
      }

      return false;
   }

   public void endVisit(HiveSelectQueryBlock x) {
   }
}
