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.mysql.visitor;
  
  import com.alibaba.druid.DbType;
  import com.alibaba.druid.sql.ast.*;
  import com.alibaba.druid.sql.ast.expr.*;
  import com.alibaba.druid.sql.ast.statement.*;
  import com.alibaba.druid.sql.dialect.mysql.MySQL;
  import com.alibaba.druid.sql.dialect.mysql.ast.*;
  import com.alibaba.druid.sql.dialect.mysql.ast.clause.*;
  import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionType;
  import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement;
  import com.alibaba.druid.sql.dialect.mysql.ast.expr.*;
  import com.alibaba.druid.sql.dialect.mysql.ast.statement.*;
  import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement.TableSpaceOption;
  import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement.UserSpecification;
  import com.alibaba.druid.sql.visitor.ExportParameterVisitorUtils;
  import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor;
  import com.alibaba.druid.sql.visitor.VisitorFeature;
  import com.alibaba.druid.util.FnvHash;
  
  import java.security.AccessControlException;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  
  public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTVisitor {
      {
          this.dbType = DbType.mysql;
          this.shardingSupport = true;
      }
  
      public MySqlOutputVisitor(StringBuilder appender) {
          super(appender, DbType.mysql, MySQL.DIALECT);
      }
  
      public MySqlOutputVisitor(StringBuilder appender, boolean parameterized) {
          super(appender, DbType.mysql, MySQL.DIALECT, parameterized);
  
          try {
              configFromProperty();
          } catch (AccessControlException e) {
              // skip
          }
      }
  
      private static boolean shardingSupportChecked;
  
      public void configFromProperty() {
          if (this.parameterized && !shardingSupportChecked) {
              shardingSupportChecked = true;
  
              String property = System.getProperties().getProperty("fastsql.parameterized.shardingSupport");
              if ("true".equals(property)) {
                  this.setShardingSupport(true);
              } else if ("false".equals(property)) {
                  this.setShardingSupport(false);
              }
          }
      }
  
      public boolean isShardingSupport() {
          return this.parameterized
                  && shardingSupport;
      }
  
      public void setShardingSupport(boolean shardingSupport) {
          this.shardingSupport = shardingSupport;
      }
  
      @Override
      public boolean visit(SQLSelectQueryBlock select) {
          if (select instanceof MySqlSelectQueryBlock) {
              return visit((MySqlSelectQueryBlock) select);
          }
  
          return super.visit(select);
      }
  
      public boolean visit(MySqlSelectQueryBlock x) {
          final boolean bracket = x.isParenthesized();
          if (bracket) {
              print('(');
          }
  
          if ((!isParameterized()) && isPrettyFormat() && x.hasBeforeComment()) {
              printlnComments(x.getBeforeCommentsDirect());
          }
  
          String cachedSelectList = x.getCachedSelectList();
  
          if (cachedSelectList != null) {
              if (!isEnabled(VisitorFeature.OutputSkipSelectListCacheString)) {
                  print0(cachedSelectList);
              }
          } else {
              print0(ucase ? "SELECT " : "select ");
  
              for (int i = 0, size = x.getHintsSize(); i < size; ++i) {
                  SQLCommentHint hint = x.getHints().get(i);
                  hint.accept(this);
                  print(' ');
              }
  
              switch (x.getDistionOption()) {
                  case SQLSetQuantifier.ALL:
                      print0(ucase ? "ALL " : "all ");
                      break;
                  case SQLSetQuantifier.DISTINCT:
                      print0(ucase ? "DISTINCT " : "distinct ");
                      break;
                  case SQLSetQuantifier.DISTINCTROW:
                      print0(ucase ? "DISTINCTROW " : "distinctrow ");
                      break;
                  case SQLSetQuantifier.UNIQUE:
                      print0(ucase ? "UNIQUE " : "unique ");
                      break;
                  default:
                      break;
              }
  
              if (x.isHignPriority()) {
                  print0(ucase ? "HIGH_PRIORITY " : "high_priority ");
              }
  
              if (x.isStraightJoin()) {
                  print0(ucase ? "STRAIGHT_JOIN " : "straight_join ");
              }
  
              if (x.isSmallResult()) {
                  print0(ucase ? "SQL_SMALL_RESULT " : "sql_small_result ");
              }
  
              if (x.isBigResult()) {
                  print0(ucase ? "SQL_BIG_RESULT " : "sql_big_result ");
              }
  
              if (x.isBufferResult()) {
                  print0(ucase ? "SQL_BUFFER_RESULT " : "sql_buffer_result ");
              }
  
              if (x.getCache() != null) {
                  if (x.getCache().booleanValue()) {
                      print0(ucase ? "SQL_CACHE " : "sql_cache ");
                  } else {
                      print0(ucase ? "SQL_NO_CACHE " : "sql_no_cache ");
                  }
              }
  
              if (x.isCalcFoundRows()) {
                  print0(ucase ? "SQL_CALC_FOUND_ROWS " : "sql_calc_found_rows ");
              }
  
              printSelectList(x.getSelectList());
  
              SQLName forcePartition = x.getForcePartition();
              if (forcePartition != null) {
                  println();
                  print0(ucase ? "FORCE PARTITION " : "force partition ");
                  printExpr(forcePartition, parameterized);
              }
  
              SQLExprTableSource into = x.getInto();
              if (into != null) {
                  println();
                  print0(ucase ? "INTO " : "into ");
                  printTableSource(into);
              }
          }
  
          printFrom(x);
  
          printWhere(x);
  
          printHierarchical(x);
  
          printGroupBy(x);
          printWindow(x);
          printOrderBy(x);
  
          printLimit(x);
  
          SQLName procedureName = x.getProcedureName();
          if (procedureName != null) {
              print0(ucase ? " PROCEDURE " : " procedure ");
              procedureName.accept(this);
              if (!x.getProcedureArgumentList().isEmpty()) {
                  print('(');
                  printAndAccept(x.getProcedureArgumentList(), ", ");
                  print(')');
              }
          }
  
          if (x.isForUpdate()) {
              println();
              print0(ucase ? "FOR UPDATE" : "for update");
              if (x.isNoWait()) {
                  print0(ucase ? " NOWAIT" : " nowait");
              } else if (x.getWaitTime() != null) {
                  print0(ucase ? " WAIT " : " wait ");
                  x.getWaitTime().accept(this);
              }
  
              if (x.isSkipLocked()) {
                  print0(ucase ? " SKIP LOCKED" : " skip locked");
              }
          }
  
          if (x.isForShare()) {
              println();
              print0(ucase ? "FOR SHARE" : "for share");
          }
  
          if (x.isLockInShareMode()) {
              println();
              print0(ucase ? "LOCK IN SHARE MODE" : "lock in share mode");
          }
  
          if ((!isParameterized()) && isPrettyFormat() && x.hasAfterComment()) {
              printAfterComments(x.getAfterCommentsDirect());
          }
  
          if (bracket) {
              print(')');
          }
  
          return false;
      }
  
      @Override
      protected void printFrom(SQLSelectQueryBlock x) {
          SQLTableSource from = x.getFrom();
          if (from == null) {
              return;
          }
          println();
          print0(ucase ? "FROM " : "from ");
          if (x.getCommentsAfterFrom() != null) {
              printAfterComments(x.getCommentsAfterFrom());
              println();
          }
          printTableSource(from);
      }
  
      public boolean visit(SQLColumnDefinition x) {
          boolean parameterized = this.parameterized;
          this.parameterized = false;
  
          String columnName = replaceQuota(x.getName().getSimpleName());
          printName0(columnName);
  
          SQLDataType dataType = x.getDataType();
          if (dataType != null) {
              print(' ');
              dataType.accept(this);
          }
  
          SQLExpr generatedAlawsAs = x.getGeneratedAlwaysAs();
          if (generatedAlawsAs != null) {
              print0(ucase ? " GENERATED ALWAYS AS (" : " generated always as (");
              printExpr(generatedAlawsAs);
              print(')');
          }
  
          if (x.isVirtual()) {
              print0(ucase ? " VIRTUAL" : " virtual");
          }
  
          if (x.isVisible()) {
              print0(ucase ? " VISIBLE" : " visible");
          }
  
          final SQLExpr charsetExpr = x.getCharsetExpr();
          if (charsetExpr != null) {
              print0(ucase ? " CHARACTER SET " : " character set ");
              charsetExpr.accept(this);
          }
  
          final SQLExpr collateExpr = x.getCollateExpr();
          if (collateExpr != null) {
              print0(ucase ? " COLLATE " : " collate ");
              collateExpr.accept(this);
          }
  
          for (SQLColumnConstraint item : x.getConstraints()) {
              if (item instanceof SQLColumnReference) {
                  continue;
              }
              print(' ');
              item.accept(this);
          }
  
          final SQLExpr defaultExpr = x.getDefaultExpr();
          if (defaultExpr != null) {
              print0(ucase ? " DEFAULT " : " default ");
              defaultExpr.accept(this);
          }
  
          final SQLExpr storage = x.getStorage();
          if (storage != null) {
              print0(ucase ? " STORAGE " : " storage ");
              storage.accept(this);
          }
  
          final SQLExpr format = x.getFormat();
          if (format != null) {
              printUcase(" COLUMN_FORMAT ");
              format.accept(this);
          }
  
          SQLExpr onUpdate = x.getOnUpdate();
          if (onUpdate != null) {
              print0(ucase ? " ON UPDATE " : " on update ");
  
              onUpdate.accept(this);
          }
  
          if (x.getJsonIndexAttrsExpr() != null) {
              print0(ucase ? " JSONINDEXATTRS '" : " jsonindexattrs '");
              x.getJsonIndexAttrsExpr().accept(this);
              print0("' ");
          }
  
          boolean autoIncrement = x.isAutoIncrement();
          if (!autoIncrement && x.getGeneratedAlwaysAs() == null && x.getIdentity() != null) {
              autoIncrement = true;
          }
  
          if (autoIncrement) {
              print0(ucase ? " AUTO_INCREMENT" : " auto_increment");
          }
  
          if (x.getDelimiterTokenizer() != null) {
              print0(ucase ? " DELIMITER_TOKENIZER " : " delimiter_tokenizer ");
              x.getDelimiterTokenizer().accept(this);
          }
          if (x.getNlpTokenizer() != null) {
              print0(ucase ? " NLP_TOKENIZER " : " nlp_tokenizer ");
              x.getNlpTokenizer().accept(this);
          }
          if (x.getValueType() != null) {
              print0(ucase ? " VALUE_TYPE " : " value_type ");
              x.getValueType().accept(this);
          }
  
          final AutoIncrementType sequenceType = x.getSequenceType();
          if (sequenceType != null) {
              print0(ucase ? " BY " : " by ");
              print0(ucase ? sequenceType.getKeyword() : sequenceType.getKeyword().toLowerCase());
          }
  
          final SQLExpr unitCount = x.getUnitCount();
          if (unitCount != null) {
              print0(ucase ? " UNIT COUNT " : " unit count ");
              printExpr(unitCount);
          }
  
          final SQLExpr unitIndex = x.getUnitIndex();
          if (unitIndex != null) {
              print0(ucase ? " INDEX " : " index ");
              printExpr(unitIndex);
          }
  
          if (x.getStep() != null) {
              print0(ucase ? " STEP " : " STEP ");
              printExpr(x.getStep());
          }
  
          SQLExpr delimiter = x.getDelimiter();
          if (delimiter != null) {
              print0(ucase ? " DELIMITER " : " delimiter ");
              delimiter.accept(this);
          }
  
          if (x.isDisableIndex() == true) {
              print0(ucase ? " DISABLEINDEX TRUE" : " disableindex true");
          }
  
          final SQLAnnIndex annIndex = x.getAnnIndex();
          if (annIndex != null) {
              print(' ');
              annIndex.accept(this);
          }
  
          if (x.getComment() != null) {
              print0(ucase ? " COMMENT " : " comment ");
              x.getComment().accept(this);
          }
  
          if (x.getAsExpr() != null) {
              print0(ucase ? " AS (" : " as (");
              x.getAsExpr().accept(this);
              print(')');
          }
  
          if (x.isStored()) {
              print0(ucase ? " STORED" : " stored");
          }
  
          if (x.getEncode() != null) {
              print0(ucase ? " ENCODE=" : " encode=");
              x.getEncode().accept(this);
          }
  
          if (x.getCompression() != null) {
              print0(ucase ? " COMPRESSION=" : " compression=");
              x.getCompression().accept(this);
          }
  
          List<SQLAssignItem> colProperties = x.getColPropertiesDirect();
          if (colProperties != null && colProperties.size() > 0) {
              print0(ucase ? " COLPROPERTIES (" : " colproperties (");
              printAndAccept(colProperties, ", ");
              print0(ucase ? ")" : ")");
          }
  
          for (SQLColumnConstraint item : x.getConstraints()) {
              if (item instanceof SQLColumnReference) {
                  print(' ');
                  item.accept(this);
              }
          }
  
          this.parameterized = parameterized;
          return false;
      }
  
      public boolean visit(SQLDataType x) {
          printDataType(x);
  
          if (x instanceof SQLDataTypeImpl) {
              SQLDataTypeImpl dataTypeImpl = (SQLDataTypeImpl) x;
              if (dataTypeImpl.isUnsigned()) {
                  print0(ucase ? " UNSIGNED" : " unsigned");
              }
  
              if (dataTypeImpl.isZerofill()) {
                  print0(ucase ? " ZEROFILL" : " zerofill");
              }
  
              SQLExpr indexBy = ((SQLDataTypeImpl) x).getIndexBy();
              if (indexBy != null) {
                  print0(ucase ? " INDEX BY " : " index by ");
                  indexBy.accept(this);
              }
          }
  
          if (x instanceof SQLCharacterDataType) {
              SQLCharacterDataType charType = (SQLCharacterDataType) x;
              if (charType.getCharSetName() != null) {
                  print0(ucase ? " CHARACTER SET " : " character set ");
                  print0(charType.getCharSetName());
  
                  if (charType.getCollate() != null) {
                      print0(ucase ? " COLLATE " : " collate ");
                      print0(charType.getCollate());
                  }
              }
  
              List<SQLCommentHint> hints = ((SQLCharacterDataType) x).hints;
              if (hints != null) {
                  print(' ');
                  for (SQLCommentHint hint : hints) {
                      hint.accept(this);
                  }
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(SQLCharacterDataType x) {
          printDataType(x);
  
          if (x.isHasBinary()) {
              print0(ucase ? " BINARY " : " binary ");
          }
  
          if (x.getCharSetName() != null) {
              print0(ucase ? " CHARACTER SET " : " character set ");
              print0(x.getCharSetName());
              if (x.getCollate() != null) {
                  print0(ucase ? " COLLATE " : " collate ");
                  print0(x.getCollate());
              }
          } else if (x.getCollate() != null) {
              print0(ucase ? " COLLATE " : " collate ");
              print0(x.getCollate());
          }
  
          List<SQLCommentHint> hints = ((SQLCharacterDataType) x).hints;
          if (hints != null) {
              print(' ');
              for (SQLCommentHint hint : hints) {
                  hint.accept(this);
              }
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlTableIndex x) {
          String indexType = x.getIndexType();
  
          boolean indexTypePrinted = false;
          if ("FULLTEXT".equalsIgnoreCase(indexType)) {
              print0(ucase ? "FULLTEXT " : "fulltext ");
              indexTypePrinted = true;
          } else if ("SPATIAL".equalsIgnoreCase(indexType)) {
              print0(ucase ? "SPATIAL " : "spatial ");
              indexTypePrinted = true;
          } else if ("CLUSTERED".equalsIgnoreCase(indexType)) {
              print0(ucase ? "CLUSTERED " : "clustered ");
              indexTypePrinted = true;
          } else if ("CLUSTERING".equalsIgnoreCase(indexType)) {
              print0(ucase ? "CLUSTERING " : "clustering ");
              indexTypePrinted = true;
          }
  
          if (x.getIndexDefinition().isGlobal()) {
              print0(ucase ? "GLOBAL " : "global ");
          } else if (x.getIndexDefinition().isLocal()) {
              print0(ucase ? "LOCAL " : "local ");
          }
  
          print0(ucase ? "INDEX" : "index");
          if (x.getName() != null) {
              print(' ');
              x.getName().accept(this);
          }
  
          if (indexType != null && !indexTypePrinted) {
              if ("ANN".equals(indexType)) {
                  print0(" ");
                  print0(indexType);
              }
          }
  
          if (Boolean.TRUE.equals(x.getAttribute("ads.index"))) {
              if (x.getIndexDefinition().isHashMapType()) {
                  print0(ucase ? " HASHMAP" : " hashmap");
              } else if (x.getIndexDefinition().isHashType()) {
                  print0(ucase ? " HASH" : " hash");
              }
          }
  
          String using = x.getIndexDefinition().hasOptions() ? x.getIndexDefinition().getOptions().getIndexType() : null;
          if (using != null) {
              print0(ucase ? " USING " : " using ");
              print0(using);
          }
  
          print('(');
          for (int i = 0, size = x.getColumns().size(); i < size; ++i) {
              if (i != 0) {
                  print0(", ");
              }
              x.getColumns().get(i).accept(this);
          }
          print(')');
  
          if (x.getAnalyzerName() != null) {
              print0(ucase ? " WITH ANALYZER " : " with analyzer ");
              x.getAnalyzerName().accept(this);
          } else {
              if (x.getIndexAnalyzerName() != null) {
                  print0(ucase ? " WITH INDEX ANALYZER " : " with index analyzer ");
                  x.getIndexAnalyzerName().accept(this);
              }
  
              if (x.getQueryAnalyzerName() != null) {
                  print0(ucase ? " WITH QUERY ANALYZER " : " with query analyzer ");
                  x.getQueryAnalyzerName().accept(this);
              }
  
              if (x.getWithDicName() != null) {
                  printUcase(" WITH DICT ");
                  x.getWithDicName().accept(this);
              }
          }
  
          final List<SQLName> covering = x.getCovering();
          if (null != covering && covering.size() > 0) {
              print0(ucase ? " COVERING " : " covering ");
              print('(');
              for (int i = 0, size = covering.size(); i < size; ++i) {
                  if (i != 0) {
                      print0(", ");
                  }
                  covering.get(i).accept(this);
              }
              print(')');
          }
  
          final SQLExpr dbPartitionBy = x.getDbPartitionBy();
          if (dbPartitionBy != null) {
              print0(ucase ? " DBPARTITION BY " : " dbpartition by ");
              dbPartitionBy.accept(this);
          }
  
          final SQLExpr tablePartitionBy = x.getTablePartitionBy();
          if (tablePartitionBy != null) {
              print0(ucase ? " TBPARTITION BY " : " tbpartition by ");
              tablePartitionBy.accept(this);
          }
  
          final SQLExpr tablePartitions = x.getTablePartitions();
          if (tablePartitions != null) {
              print0(ucase ? " TBPARTITIONS " : " tbpartitions ");
              tablePartitions.accept(this);
          }
  
          final SQLPartitionBy partitionBy = x.getPartitioning();
          if (partitionBy != null) {
              print0(ucase ? " PARTITION BY " : " partitions by ");
              partitionBy.accept(this);
          }
  
          /*
          final List<SQLAssignItem> options = x.getOptions();
          if (options.size() > 0) {
              for (SQLAssignItem option : options) {
                  print(' ');
                  option.accept(this);
              }
          }
  
          final SQLExpr comment = x.getComment();
          if (comment != null) {
              print0(ucase ? " COMMENT " : " comment ");
              comment.accept(this);
          }
          */
          if (x.getIndexDefinition().hasOptions()) {
              x.getIndexDefinition().getOptions().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlKey x) {
          if (x.isHasConstraint()) {
              print0(ucase ? "CONSTRAINT " : "constraint ");
              if (x.getName() != null) {
                  x.getName().accept(this);
                  print(' ');
              }
          }
  
          final String indexType = x.getIndexType();
          boolean fullText = "FULLTEXT".equalsIgnoreCase(indexType);
          boolean clustering = "CLUSTERING".equalsIgnoreCase(indexType);
          boolean clustered = "CLUSTERED".equalsIgnoreCase(indexType);
  
          if (fullText) {
              print0(ucase ? "FULLTEXT " : "fulltext ");
          } else if (clustering) {
              print0(ucase ? "CLUSTERING " : "clustering ");
          } else if (clustered) {
              print0(ucase ? "CLUSTERED " : "CLUSTERED ");
          }
  
          print0(ucase ? "KEY" : "key");
  
          SQLName name = x.getName();
          if (name != null) {
              print(' ');
              name.accept(this);
          }
  
          if (indexType != null && !fullText && !clustering && !clustered) {
              print0(ucase ? " USING " : " using ");
              print0(indexType);
          }
  
          print0(" (");
  
          for (int i = 0, size = x.getColumns().size(); i < size; ++i) {
              if (i != 0) {
                  print0(", ");
              }
              x.getColumns().get(i).accept(this);
          }
          print(')');
  
          SQLIndexDefinition indexDefinition = x.getIndexDefinition();
          if (indexDefinition.hasOptions()) {
              indexDefinition.getOptions().accept(this);
          }
  
          SQLExpr comment = x.getComment();
          if (indexDefinition.hasOptions()
                  && indexDefinition.getOptions().getComment() == comment) {
              comment = null;
          }
  
          if (comment != null) {
              print0(ucase ? " COMMENT " : " comment ");
              printExpr(comment);
          }
  
          return false;
      }
  
      public boolean visit(SQLCharExpr x, boolean parameterized) {
          if (x.hasBeforeComment()) {
              printlnComments(x.getBeforeCommentsDirect());
          }
          if (this.appender == null) {
              return false;
          }
  
          if (parameterized) {
              this.appender.append('?');
              incrementReplaceCunt();
              if (this.parameters != null) {
                  ExportParameterVisitorUtils.exportParameter(this.parameters, x);
              }
              return false;
          }
  
          this.appender.append('\'');
  
          String text = x.getText();
  
          boolean hasSpecial = false;
          for (int i = 0; i < text.length(); ++i) {
              char ch = text.charAt(i);
              if (ch == '\'' || ch == '\\' || ch == '\0') {
                  hasSpecial = true;
                  break;
              }
          }
  
          if (hasSpecial) {
              boolean regForPresto = false;
              if (isEnabled(VisitorFeature.OutputRegForPresto) && x.getParent() instanceof SQLMethodInvokeExpr) {
                  SQLMethodInvokeExpr regCall = (SQLMethodInvokeExpr) x.getParent();
                  long nameHash = regCall.methodNameHashCode64();
                  regForPresto = (x == regCall.getArguments().get(1)) && (nameHash == FnvHash.Constants.REGEXP_SUBSTR
                          || nameHash == FnvHash.Constants.REGEXP_COUNT || nameHash == FnvHash.Constants.REGEXP_EXTRACT
                          || nameHash == FnvHash.Constants.REGEXP_EXTRACT_ALL || nameHash == FnvHash.Constants.REGEXP_LIKE
                          || nameHash == FnvHash.Constants.REGEXP_REPLACE || nameHash == FnvHash.Constants.REGEXP_SPLIT);
              }
  
              for (int i = 0; i < text.length(); ++i) {
                  char ch = text.charAt(i);
                  if (ch == '\'') {
                      appender.append('\'');
                      appender.append('\'');
                  } else if (ch == '\\') {
                      appender.append('\\');
                      if (regForPresto) {
                          continue;
                      }
                      if (i < text.length() - 1 && text.charAt(i + 1) == '_') {
                          continue;
                      }
                      appender.append('\\');
                  } else if (ch == '\0') {
                      appender.append('\\');
                      appender.append('0');
                  } else {
                      appender.append(ch);
                  }
              }
          } else {
              appender.append(text);
          }
  
          appender.append('\'');
          if (x.hasAfterComment()) {
              printAfterComments(x.getAfterCommentsDirect());
          }
          return false;
      }
  
      public boolean visit(SQLVariantRefExpr x) {
          {
              int index = x.getIndex();
  
              if (inputParameters != null && index < inputParameters.size()) {
                  return super.visit(x);
              }
          }
  
          if (x.isGlobal()) {
              print0("@@global.");
          } else if (x.isSession()) {
              print0("@@session.");
          }
  
          String varName = x.getName();
          for (int i = 0; i < varName.length(); ++i) {
              char ch = varName.charAt(i);
              if (ch == '\'') {
                  if (varName.startsWith("@@") && i == 2) {
                      print(ch);
                  } else if (varName.startsWith("@") && i == 1) {
                      print(ch);
                  } else if (i != 0 && i != varName.length() - 1) {
                      print0("\\'");
                  } else {
                      print(ch);
                  }
              } else {
                  print(ch);
              }
          }
  
          String collate = (String) x.getAttribute("COLLATE");
          if (collate != null) {
              print0(ucase ? " COLLATE " : " collate ");
              print0(collate);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlPrepareStatement x) {
          print0(ucase ? "PREPARE " : "prepare ");
          x.getName().accept(this);
          print0(ucase ? " FROM " : " from ");
          x.getFrom().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlExecuteStatement x) {
          print0(ucase ? "EXECUTE " : "execute ");
          x.getStatementName().accept(this);
          if (x.getParameters().size() > 0) {
              print0(ucase ? " USING " : " using ");
              printAndAccept(x.getParameters(), ", ");
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlExecuteForAdsStatement x) {
          print0(ucase ? "EXECUTE " : "execute ");
          x.getAction().accept(this);
          print(" ");
          x.getRole().accept(this);
          print(" ");
          x.getTargetId().accept(this);
          print(" ");
          if (x.getStatus() != null) {
              x.getStatus().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlManageInstanceGroupStatement x) {
          x.getOperation().accept(this);
  
          print0(ucase ? " INSTANCE_GROUP " : " instance_group ");
          printAndAccept(x.getGroupNames(), ",");
  
          if (x.getReplication() != null) {
              print0(ucase ? " REPLICATION = " : " replication = ");
              x.getReplication().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlRaftMemberChangeStatement x) {
          print0(ucase ? "SYNC RAFT_MEMBER_CHANGE " : "sync raft_member_change ");
  
          if (x.isNoLeader()) {
              print0(ucase ? "NOLEADER " : "noleader ");
          }
  
          print0(ucase ? "SHARD=" : "shard=");
          x.getShard().accept(this);
  
          print0(ucase ? " HOST=" : " host=");
          x.getHost().accept(this);
  
          print0(ucase ? " STATUS=" : " status=");
          x.getStatus().accept(this);
  
          if (x.isForce()) {
              print0(ucase ? " FORCE" : " force");
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlRaftLeaderTransferStatement x) {
          print0(ucase ? "SYNC RAFT_LEADER_TRANSFER SHARD=" : "sync raft_leader_transfer shard=");
          x.getShard().accept(this);
  
          print0(ucase ? " FROM=" : " from=");
          x.getFrom().accept(this);
  
          print0(ucase ? " TO=" : " to=");
          x.getTo().accept(this);
  
          print0(ucase ? " TIMEOUT=" : " timeout=");
          x.getTimeout().accept(this);
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlMigrateStatement x) {
          return false;
      }
  
      @Override
      public void endVisit(MySqlMigrateStatement x) {
          print0(ucase ? "MIGRATE DATABASE " : "migrate database ");
          x.getSchema().accept(this);
  
          print0(ucase ? " SHARDS=" : "shards= ");
          x.getShardNames().accept(this);
          print0(" ");
          if (x.getMigrateType().getNumber().intValue() == 0) {
              print0(ucase ? "GROUP " : "group ");
          } else if (x.getMigrateType().getNumber().intValue() == 1) {
              print0(ucase ? "HOST " : "host ");
          }
  
          print0(ucase ? "FROM " : "from ");
          x.getFromInsId().accept(this);
  
          if (x.getFromInsIp() != null) {
              print(":");
              x.getFromInsIp().accept(this);
              print(":");
              x.getFromInsPort().accept(this);
              print(":");
              x.getFromInsStatus().accept(this);
          }
  
          print0(ucase ? " TO " : " to ");
          x.getToInsId().accept(this);
  
          if (x.getToInsIp() != null) {
              print(":");
              x.getToInsIp().accept(this);
              print(":");
              x.getToInsPort().accept(this);
              print(":");
              x.getToInsStatus().accept(this);
          }
  
      }
  
      @Override
      public boolean visit(MySqlShowClusterNameStatement x) {
          print0(ucase ? "SHOW CLUSTER NAME" : "show cluster name");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowJobStatusStatement x) {
          print0(ucase ? "SHOW " : "show ");
          if (x.isSync()) {
              print0(ucase ? "SYNC_JOB " : "sync_job ");
          } else {
              print0(ucase ? "JOB " : "job ");
          }
          print0(ucase ? "STATUS " : "status ");
  
          if (x.getWhere() != null) {
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowMigrateTaskStatusStatement x) {
          print0(ucase ? "SHOW MIGRATE TASK STATUS" : "show migrate task status");
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
          return false;
      }
  
      public boolean visit(MysqlDeallocatePrepareStatement x) {
          print0(ucase ? "DEALLOCATE PREPARE " : "deallocate prepare ");
          x.getStatementName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlDeleteStatement x) {
          final List<SQLCommentHint> headHints = x.getHeadHintsDirect();
          if (headHints != null) {
              for (SQLCommentHint hint : headHints) {
                  hint.accept(this);
                  println();
              }
          }
  
          if (this.isPrettyFormat() && x.hasBeforeComment()) {
              this.printlnComments(x.getBeforeCommentsDirect());
          }
  
          print0(ucase ? "DELETE " : "delete ");
  
          for (int i = 0, size = x.getHintsSize(); i < size; ++i) {
              SQLCommentHint hint = x.getHints().get(i);
              hint.accept(this);
              print(' ');
          }
  
          if (x.isLowPriority()) {
              print0(ucase ? "LOW_PRIORITY " : "low_priority ");
          }
  
          if (x.isQuick()) {
              print0(ucase ? "QUICK " : "quick ");
          }
  
          if (x.isIgnore()) {
              print0(ucase ? "IGNORE " : "ignore ");
          }
  
          if (x.isForceAllPartitions()) {
              print0(ucase ? "FORCE ALL PARTITIONS " : "force all partitions ");
          } else {
              SQLName partition = x.getForcePartition();
              if (partition != null) {
                  print0(ucase ? "FORCE PARTITION " : "force partition ");
                  printExpr(partition, parameterized);
                  print(' ');
              }
          }
  
          SQLTableSource from = x.getFrom();
          if (from == null) {
              print0(ucase ? "FROM " : "from ");
              if (x.isFulltextDictionary()) {
                  print0(ucase ? "FULLTEXT DICTIONARY " : "fulltext dictionary ");
              }
              x.getTableSource().accept(this);
          } else {
              x.getTableSource().accept(this);
              println();
              print0(ucase ? "FROM " : "from ");
              from.accept(this);
          }
  
          SQLTableSource using = x.getUsing();
          if (using != null) {
              println();
              print0(ucase ? "USING " : "using ");
              using.accept(this);
          }
  
          SQLExpr where = x.getWhere();
          if (where != null) {
              println();
              this.indentCount++;
              print0(ucase ? "WHERE " : "where ");
              printExpr(where, parameterized);
              this.indentCount--;
          }
  
          if (x.getOrderBy() != null) {
              println();
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              println();
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlInsertStatement x) {
          final List<SQLCommentHint> headHints = x.getHeadHintsDirect();
          if (headHints != null) {
              for (SQLCommentHint hint : headHints) {
                  hint.accept(this);
                  println();
              }
          }
  
          if (this.isPrettyFormat() && x.hasBeforeComment()) {
              this.printlnComments(x.getBeforeCommentsDirect());
          }
  
          SQLWithSubqueryClause with = x.getWith();
          if (with != null) {
              visit(with);
              println();
          }
  
          print0(ucase ? "INSERT " : "insert ");
  
          for (int i = 0, size = x.getHintsSize(); i < size; ++i) {
              SQLCommentHint hint = x.getHints().get(i);
              hint.accept(this);
              print(' ');
          }
  
          if (x.isLowPriority()) {
              print0(ucase ? "LOW_PRIORITY " : "low_priority ");
          }
  
          if (x.isDelayed()) {
              print0(ucase ? "DELAYED " : "delayed ");
          }
  
          if (x.isHighPriority()) {
              print0(ucase ? "HIGH_PRIORITY " : "high_priority ");
          }
  
          if (x.isIgnore()) {
              print0(ucase ? "IGNORE " : "ignore ");
          }
  
          if (x.isRollbackOnFail()) {
              print0(ucase ? "ROLLBACK_ON_FAIL " : "rollback_on_fail ");
          }
  
          boolean outputIntoKeyword = true;
          if (x.isOverwrite()) {
              print0(ucase ? "OVERWRITE " : "overwrite ");
          }
  
          if (outputIntoKeyword) {
              print0(ucase ? "INTO " : "into ");
          }
  
          if (x.isFulltextDictionary()) {
              print0(ucase ? "FULLTEXT DICTIONARY " : "fulltext dictionary ");
          }
  
          SQLExprTableSource tableSource = x.getTableSource();
          if (tableSource != null) {
              if (tableSource.getClass() == SQLExprTableSource.class) {
                  visit(tableSource);
              } else {
                  tableSource.accept(this);
              }
          }
  
          List<SQLAssignItem> partitions = x.getPartitions();
          if (partitions != null) {
              int partitionsSize = partitions.size();
              if (partitionsSize > 0) {
                  print0(ucase ? " PARTITION (" : " partition (");
                  for (int i = 0; i < partitionsSize; ++i) {
                      if (i != 0) {
                          print0(", ");
                      }
  
                      SQLAssignItem assign = partitions.get(i);
                      assign.getTarget().accept(this);
  
                      if (assign.getValue() != null) {
                          print('=');
                          assign.getValue().accept(this);
                      }
                  }
                  print(')');
              }
  
              if (x.isIfNotExists()) {
                  print0(ucase ? " IF NOT EXISTS " : "if not exists ");
              }
          }
  
          String columnsString = x.getColumnsString();
          if (columnsString != null) {
              if (!isEnabled(VisitorFeature.OutputSkipInsertColumnsString)) {
                  print0(columnsString);
              }
          } else {
              List<SQLExpr> columns = x.getColumns();
              if (columns.size() > 0) {
                  this.indentCount++;
                  print0(" (");
                  for (int i = 0, size = columns.size(); i < size; ++i) {
                      if (i != 0) {
                          if (i % 5 == 0) {
                              println();
                          }
                          print0(", ");
                      }
  
                      SQLExpr column = columns.get(i);
                      if (column.hasBeforeComment()) {
                          print(' ');
                          printlnComment(column.getBeforeCommentsDirect());
                          println();
                      }
                      if (column instanceof SQLIdentifierExpr) {
                          printName0(((SQLIdentifierExpr) column).getName());
                      } else {
                          printExpr(column, parameterized);
                      }
                      if (column.hasAfterComment()) {
                          print(' ');
                          printlnComment(column.getAfterCommentsDirect());
                          println();
                      }
                  }
                  print(')');
                  this.indentCount--;
              }
          }
  
          List<SQLInsertStatement.ValuesClause> valuesList = x.getValuesList();
          if (!valuesList.isEmpty()) {
              println();
              printValuesList(valuesList);
          }
  
          if (x.getQuery() != null) {
              println();
              x.getQuery().accept(this);
          }
  
          List<SQLExpr> duplicateKeyUpdate = x.getDuplicateKeyUpdate();
          if (!duplicateKeyUpdate.isEmpty()) {
              println();
              print0(ucase ? "ON DUPLICATE KEY UPDATE " : "on duplicate key update ");
              for (int i = 0, size = duplicateKeyUpdate.size(); i < size; ++i) {
                  if (i != 0) {
                      if (i % 5 == 0) {
                          println();
                      }
                      print0(", ");
                  }
                  duplicateKeyUpdate.get(i).accept(this);
              }
          }
  
          return false;
      }
  
      protected void printValuesList(List<SQLInsertStatement.ValuesClause> valuesList) {
          if (this.parameterized
                  && valuesList.size() > 1
                  && !this.parameterizedQuesUnMergeValuesList) {
              print0(ucase ? "VALUES " : "values ");
              this.indentCount++;
  
              boolean allConst = true;
              if (valuesList.size() > 1) {
                  for (int index = 0; index < valuesList.size(); index++) {
                      List<SQLExpr> values = valuesList.get(index).getValues();
                      for (int i = 0; i < values.size(); i++) {
                          SQLExpr value = values.get(i);
                          if (value instanceof SQLLiteralExpr || value instanceof SQLVariantRefExpr) {
                              continue;
                          } else if (value instanceof SQLMethodInvokeExpr && ((SQLMethodInvokeExpr) value).getArguments().isEmpty()) {
                              continue;
                          }
                          allConst = false;
                          break;
                      }
                      if (!allConst) {
                          break;
                      }
                  }
              }
  
              if (!allConst) {
                  for (int index = 0; index < valuesList.size(); index++) {
                      if (index != 0) {
                          print(',');
                          println();
                      }
                      visit(valuesList.get(index), this.parameters);
                  }
              } else if (valuesList.size() > 1 && this.parameters != null) {
                  SQLInsertStatement.ValuesClause first = valuesList.get(0);
  
                  List<Object> valuesParameters = new ArrayList<Object>(first.getValues().size());
                  visit(first, valuesParameters);
                  this.parameters.add(valuesParameters);
  
                  for (int index = 1; index < valuesList.size(); index++) {
                      List<SQLExpr> values = valuesList.get(index).getValues();
                      valuesParameters = new ArrayList<Object>(values.size());
  
                      for (int i = 0, size = values.size(); i < size; ++i) {
                          SQLExpr expr = values.get(i);
                          if (expr instanceof SQLIntegerExpr
                                  || expr instanceof SQLBooleanExpr
                                  || expr instanceof SQLNumberExpr
                                  || expr instanceof SQLCharExpr
                                  || expr instanceof SQLNCharExpr
                                  || expr instanceof SQLTimestampExpr
                                  || expr instanceof SQLDateExpr
                                  || expr instanceof SQLTimeExpr) {
                              incrementReplaceCunt();
                              ExportParameterVisitorUtils.exportParameter(valuesParameters, expr);
                          } else if (expr instanceof SQLNullExpr) {
                              incrementReplaceCunt();
                              valuesParameters.add(null);
                          } else {
                              // skip
                          }
                      }
  
                      this.parameters.add(valuesParameters);
                  }
  
                  this.incrementReplaceCunt();
              } else {
                  if (valuesList.size() > 1) {
                      this.incrementReplaceCunt();
                  }
                  visit(valuesList.get(0), this.parameters);
              }
  
              this.indentCount--;
              return;
          }
  
          print0(ucase ? "VALUES " : "values ");
          if (valuesList.size() > 1) {
              this.indentCount++;
          }
          for (int i = 0, size = valuesList.size(); i < size; ++i) {
              if (i != 0) {
                  print(',');
                  println();
              }
  
              SQLInsertStatement.ValuesClause item = valuesList.get(i);
              visit(item, parameters);
          }
          if (valuesList.size() > 1) {
              this.indentCount--;
          }
      }
  
      @Override
      public boolean visit(MySqlLoadDataInFileStatement x) {
          print0(ucase ? "LOAD DATA " : "load data ");
  
          if (x.isLowPriority()) {
              print0(ucase ? "LOW_PRIORITY " : "low_priority ");
          }
  
          if (x.isConcurrent()) {
              print0(ucase ? "CONCURRENT " : "concurrent ");
          }
  
          if (x.isLocal()) {
              print0(ucase ? "LOCAL " : "local ");
          }
  
          print0(ucase ? "INFILE " : "infile ");
  
          x.getFileName().accept(this);
  
          if (x.isReplicate()) {
              print0(ucase ? " REPLACE " : " replace ");
          }
  
          if (x.isIgnore()) {
              print0(ucase ? " IGNORE " : " ignore ");
          }
  
          print0(ucase ? " INTO TABLE " : " into table ");
          x.getTableName().accept(this);
  
          if (x.getColumnsTerminatedBy() != null || x.getColumnsEnclosedBy() != null || x.getColumnsEscaped() != null) {
              print0(ucase ? " COLUMNS" : " columns");
              if (x.getColumnsTerminatedBy() != null) {
                  print0(ucase ? " TERMINATED BY " : " terminated by ");
                  x.getColumnsTerminatedBy().accept(this);
              }
  
              if (x.getColumnsEnclosedBy() != null) {
                  if (x.isColumnsEnclosedOptionally()) {
                      print0(ucase ? " OPTIONALLY" : " optionally");
                  }
                  print0(ucase ? " ENCLOSED BY " : " enclosed by ");
                  x.getColumnsEnclosedBy().accept(this);
              }
  
              if (x.getColumnsEscaped() != null) {
                  print0(ucase ? " ESCAPED BY " : " escaped by ");
                  x.getColumnsEscaped().accept(this);
              }
          }
  
          if (x.getLinesStartingBy() != null || x.getLinesTerminatedBy() != null) {
              print0(ucase ? " LINES" : " lines");
              if (x.getLinesStartingBy() != null) {
                  print0(ucase ? " STARTING BY " : " starting by ");
                  x.getLinesStartingBy().accept(this);
              }
  
              if (x.getLinesTerminatedBy() != null) {
                  print0(ucase ? " TERMINATED BY " : " terminated by ");
                  x.getLinesTerminatedBy().accept(this);
              }
          }
  
          if (x.getIgnoreLinesNumber() != null) {
              print0(ucase ? " IGNORE " : " ignore ");
              x.getIgnoreLinesNumber().accept(this);
              print0(ucase ? " LINES" : " lines");
          }
  
          if (!x.getColumns().isEmpty()) {
              print0(" (");
              printAndAccept(x.getColumns(), ", ");
              print(')');
          }
  
          if (!x.getSetList().isEmpty()) {
              print0(ucase ? " SET " : " set ");
              printAndAccept(x.getSetList(), ", ");
          }
  
          return false;
      }
  
      public boolean visit(SQLReplaceStatement x) {
          final List<SQLCommentHint> headHints = x.getHeadHintsDirect();
          if (headHints != null) {
              for (SQLCommentHint hint : headHints) {
                  hint.accept(this);
                  println();
              }
          }
  
          print0(ucase ? "REPLACE " : "replace ");
  
          if (x.isLowPriority()) {
              print0(ucase ? "LOW_PRIORITY " : "low_priority ");
          }
  
          if (x.isDelayed()) {
              print0(ucase ? "DELAYED " : "delayed ");
          }
  
          if (x.getHints() != null && x.getHints().size() > 0) {
              printAndAccept(x.getHints(), " ");
              print0(" ");
          }
  
          print0(ucase ? "INTO " : "into ");
  
          printTableSourceExpr(x.getTableName());
  
          List<SQLExpr> columns = x.getColumns();
          if (columns.size() > 0) {
              print0(" (");
              for (int i = 0, size = columns.size(); i < size; ++i) {
                  if (i != 0) {
                      print0(", ");
                  }
  
                  SQLExpr columnn = columns.get(i);
                  printExpr(columnn, parameterized);
              }
              print(')');
          }
  
          List<SQLAssignItem> partitions = x.getPartitions();
          if (partitions != null) {
              int partitionsSize = partitions.size();
              if (partitionsSize > 0) {
                  print0(ucase ? " PARTITION (" : " partition (");
                  for (int i = 0; i < partitionsSize; ++i) {
                      if (i != 0) {
                          print0(", ");
                      }
  
                      SQLAssignItem assign = partitions.get(i);
                      assign.getTarget().accept(this);
  
                      if (assign.getValue() != null) {
                          print('=');
                          assign.getValue().accept(this);
                      }
                  }
                  print(')');
              }
          }
  
          List<SQLInsertStatement.ValuesClause> valuesClauseList = x.getValuesList();
          if (!valuesClauseList.isEmpty()) {
              println();
              print0(ucase ? "VALUES " : "values ");
              int size = valuesClauseList.size();
              if (size == 0) {
                  print0("()");
              } else {
                  for (int i = 0; i < size; ++i) {
                      if (i != 0) {
                          print0(", ");
                      }
                      visit(valuesClauseList.get(i));
                  }
              }
          }
  
          SQLQueryExpr query = x.getQuery();
          if (query != null) {
              visit(query);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(SQLStartTransactionStatement x) {
          print0(ucase ? "START TRANSACTION" : "start transaction");
          if (x.isConsistentSnapshot()) {
              print0(ucase ? " WITH CONSISTENT SNAPSHOT" : " with consistent snapshot");
          }
  
          if (x.getHints() != null && x.getHints().size() > 0) {
              print(' ');
              printAndAccept(x.getHints(), " ");
          }
  
          if (x.isBegin()) {
              print0(ucase ? " BEGIN" : " begin");
          }
  
          if (x.isWork()) {
              print0(ucase ? " WORK" : " work");
          }
  
          SQLStartTransactionStatement.IsolationLevel isolationLevel = x.getIsolationLevel();
          if (isolationLevel != null) {
              print0(" ISOLATION LEVEL ");
              print(isolationLevel.getText());
          }
  
          if (x.isReadOnly()) {
              print0(ucase ? " READ ONLY" : " read only");
          }
  
          return false;
      }
  
      public boolean visit(SQLRollbackStatement x) {
          print0(ucase ? "ROLLBACK" : "rollback");
  
          if (x.getChain() != null) {
              if (x.getChain().booleanValue()) {
                  print0(ucase ? " AND CHAIN" : " and chain");
              } else {
                  print0(ucase ? " AND NO CHAIN" : " and no chain");
              }
          }
  
          if (x.getRelease() != null) {
              if (x.getRelease().booleanValue()) {
                  print0(ucase ? " AND RELEASE" : " and release");
              } else {
                  print0(ucase ? " AND NO RELEASE" : " and no release");
              }
          }
  
          if (x.getTo() != null) {
              print0(ucase ? " TO " : " to ");
              x.getTo().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(SQLShowTablesStatement x) {
          final List<SQLCommentHint> headHints = x.getHeadHintsDirect();
          if (headHints != null) {
              for (SQLCommentHint hint : headHints) {
                  hint.accept(this);
                  println();
              }
          }
  
          if (x.isFull()) {
              print0(ucase ? "SHOW FULL TABLES" : "show full tables");
          } else {
              print0(ucase ? "SHOW TABLES" : "show tables");
          }
  
          if (x.getDatabase() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getDatabase().accept(this);
          }
  
          final SQLExpr like = x.getLike();
          if (like != null) {
              print0(ucase ? " LIKE " : " like ");
              printExpr(like);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowHMSMetaStatement x) {
          print0(ucase ? "SHOW HMSMETA " : "show hmsmeta ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowDatabaseStatusStatement x) {
          if (x.isFull()) {
              print0(ucase ? "SHOW FULL DATABASE STATUS" : "show full database status");
          } else {
              print0(ucase ? "SHOW DATABASE STATUS" : "show database status");
          }
  
          if (x.getName() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getName().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowWarningsStatement x) {
          if (x.isCount()) {
              print0(ucase ? "SHOW COUNT(*) WARNINGS" : "show count(*) warnings");
          } else {
              print0(ucase ? "SHOW WARNINGS" : "show warnings");
              if (x.getLimit() != null) {
                  print(' ');
                  x.getLimit().accept(this);
              }
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowStatusStatement x) {
          print0(ucase ? "SHOW " : "show ");
  
          if (x.isGlobal()) {
              print0(ucase ? "GLOBAL " : "global ");
          }
  
          if (x.isSession()) {
              print0(ucase ? "SESSION " : "session ");
          }
  
          print0(ucase ? "STATUS" : "status");
  
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlLoadXmlStatement x) {
          print0(ucase ? "LOAD XML " : "load xml ");
  
          if (x.isLowPriority()) {
              print0(ucase ? "LOW_PRIORITY " : "low_priority ");
          }
  
          if (x.isConcurrent()) {
              print0(ucase ? "CONCURRENT " : "concurrent ");
          }
  
          if (x.isLocal()) {
              print0(ucase ? "LOCAL " : "local ");
          }
  
          print0(ucase ? "INFILE " : "infile ");
  
          x.getFileName().accept(this);
  
          if (x.isReplicate()) {
              print0(ucase ? " REPLACE " : " replace ");
          }
  
          if (x.isIgnore()) {
              print0(ucase ? " IGNORE " : " ignore ");
          }
  
          print0(ucase ? " INTO TABLE " : " into table ");
          x.getTableName().accept(this);
  
          if (x.getCharset() != null) {
              print0(ucase ? " CHARSET " : " charset ");
              print0(x.getCharset());
          }
  
          if (x.getRowsIdentifiedBy() != null) {
              print0(ucase ? " ROWS IDENTIFIED BY " : " rows identified by ");
              x.getRowsIdentifiedBy().accept(this);
          }
  
          if (!x.getSetList().isEmpty()) {
              print0(ucase ? " SET " : " set ");
              printAndAccept(x.getSetList(), ", ");
          }
  
          return false;
      }
  
      @Override
      public boolean visit(CobarShowStatus x) {
          print0(ucase ? "SHOW COBAR_STATUS" : "show cobar_status");
          return false;
      }
  
      @Override
      public boolean visit(DrdsShowDDLJobs x) {
          print0(ucase ? "SHOW " : "show ");
          if (x.isFull()) {
              print0(ucase ? "FULL " : "full ");
          }
          print0(ucase ? "DDL" : "ddl");
          boolean first = true;
          for (Long id : x.getJobIds()) {
              if (first) {
                  first = false;
                  print0(" ");
              } else {
                  print0(", ");
              }
              print(id);
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsCancelDDLJob x) {
          print0(ucase ? "CANCEL DDL" : "cancel ddl");
          boolean first = true;
          for (Long id : x.getJobIds()) {
              if (first) {
                  first = false;
                  print0(" ");
              } else {
                  print0(", ");
              }
              print(id);
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsRecoverDDLJob x) {
          print0(ucase ? "RECOVER DDL" : "recover ddl");
          if (x.isAllJobs()) {
              print0(ucase ? " ALL" : " all");
          } else {
              boolean first = true;
              for (Long id : x.getJobIds()) {
                  if (first) {
                      first = false;
                      print0(" ");
                  } else {
                      print0(", ");
                  }
                  print(id);
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsRollbackDDLJob x) {
          print0(ucase ? "ROLLBACK DDL" : "rollback ddl");
          boolean first = true;
          for (Long id : x.getJobIds()) {
              if (first) {
                  first = false;
                  print0(" ");
              } else {
                  print0(", ");
              }
              print(id);
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsRemoveDDLJob x) {
          print0(ucase ? "REMOVE DDL" : "remove ddl");
          if (x.isAllCompleted()) {
              print0(ucase ? " ALL COMPLETED" : " all completed");
          } else if (x.isAllPending()) {
              print(ucase ? " ALL PENDING" : " all pending");
          } else {
              boolean first = true;
              for (Long id : x.getJobIds()) {
                  if (first) {
                      first = false;
                      print0(" ");
                  } else {
                      print0(", ");
                  }
                  print(id);
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsInspectDDLJobCache x) {
          print0(ucase ? "INSPECT DDL CACHE" : "inspect ddl cache");
          return false;
      }
  
      @Override
      public boolean visit(DrdsClearDDLJobCache x) {
          print0(ucase ? "CLEAR DDL CACHE" : "clear ddl cache");
          if (x.isAllJobs()) {
              print0(ucase ? " ALL" : " all");
          } else {
              boolean first = true;
              for (Long id : x.getJobIds()) {
                  if (first) {
                      first = false;
                      print0(" ");
                  } else {
                      print0(", ");
                  }
                  print(id);
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsChangeDDLJob x) {
          print0(ucase ? "CHANGE DDL " : "change ddl ");
          print(x.getJobId());
          if (x.isSkip()) {
              print0(ucase ? " SKIP" : " skip");
          } else if (x.isAdd()) {
              print0(ucase ? " ADD" : " add");
          }
  
          boolean first = true;
          for (String name : x.getGroupAndTableNameList()) {
              if (first) {
                  first = false;
                  print0(" ");
              } else {
                  print0(", ");
              }
              print(name);
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsBaselineStatement x) {
          print0(ucase ? "BASELINE " : "baseline ");
          print0(ucase ? x.getOperation().toUpperCase() : x.getOperation().toLowerCase());
  
          boolean isFirst = true;
          for (Long id : x.getBaselineIds()) {
              if (isFirst) {
                  print0(" ");
                  isFirst = false;
              } else {
                  print0(", ");
              }
              print(id);
          }
  
          SQLSelect select = x.getSelect();
          if (x.getSelect() != null) {
              print(ucase ? " SQL" : " sql");
              println();
              List<SQLCommentHint> headHints = x.getHeadHintsDirect();
              if (headHints != null) {
                  for (SQLCommentHint hint : headHints) {
                      visit((SQLCommentHint) hint);
                      println();
                  }
              }
  
              this.visit(select);
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsShowGlobalIndex x) {
          print0(ucase ? "SHOW GLOBAL INDEX" : "show global index");
          if (x.getTableName() != null) {
              print0(ucase ? " FROM " : " from ");
              printExpr(x.getTableName(), parameterized);
          }
          return false;
      }
  
      @Override
      public boolean visit(DrdsShowMetadataLock x) {
          print0(ucase ? "SHOW METADATA LOCK" : "show metadata lock");
          if (x.getSchemaName() != null) {
              print0(" ");
              printExpr(x.getSchemaName(), parameterized);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlBinlogStatement x) {
          print0(ucase ? "BINLOG " : "binlog ");
          x.getExpr().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlCreateUserStatement x) {
          print0(ucase ? "CREATE USER " : "create user ");
  
          if (x.isIfNotExists()) {
              print0(ucase ? "IF NOT EXISTS " : "if not exists ");
          }
  
          printAndAccept(x.getUsers(), ", ");
          return false;
      }
  
      @Override
      public boolean visit(UserSpecification x) {
          x.getUser().accept(this);
  
          if (x.getAuthPlugin() != null) {
              print0(ucase ? " IDENTIFIED WITH " : " identified with ");
              x.getAuthPlugin().accept(this);
              if (x.getPassword() != null) {
                  if (x.isPluginAs()) {
                      print0(ucase ? " AS " : " as ");
                  } else {
                      print0(ucase ? " BY " : " by ");
                  }
                  x.getPassword().accept(this);
              }
          } else if (x.getPassword() != null) {
              print0(ucase ? " IDENTIFIED BY " : " identified by ");
              if (x.isPasswordHash()) {
                  print0(ucase ? "PASSWORD " : "password ");
              }
              x.getPassword().accept(this);
          } else {
              if (x.isRandomPassword()) {
                  print0(ucase ? " IDENTIFIED BY " : " identified by ");
                  print0(ucase ? "RANDOM PASSWORD " : "random password ");
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlPartitionByKey x) {
          if (x.isLinear()) {
              print0(ucase ? "LINEAR KEY (" : "linear key (");
          } else {
              print0(ucase ? "KEY (" : "key (");
          }
          printAndAccept(x.getColumns(), ", ");
          print(')');
  
          printPartitionsCountAndSubPartitions(x);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowPlanCacheStatusStatement x) {
          print0(ucase ? "SHOW PLANCACHE STATUS" : "show plancache status");
          return false;
      }
  
      @Override
      public boolean visit(MySqlClearPlanCacheStatement x) {
          print0(ucase ? "CLEAR PLANCACHE" : "clear plancache");
          return false;
      }
  
      @Override
      public boolean visit(MySqlDisabledPlanCacheStatement x) {
          print0(ucase ? "DISABLED PLANCACHE" : "disabled plancache");
          return false;
      }
  
      @Override
      public boolean visit(MySqlExplainPlanCacheStatement x) {
          print0(ucase ? "EXPLAIN PLANCACHE" : "explain plancache");
          return false;
      }
  
      @Override
      public boolean visit(MySqlUpdatePlanCacheStatement x) {
          print0(ucase ? "UPDATE PLANCACHE " : "update plancache ");
          x.getFormSelect().accept(this);
          println();
          print0(ucase ? " TO " : " to ");
          println();
          x.getToSelect().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlOutFileExpr x) {
          print0(ucase ? "OUTFILE " : "outfile ");
          x.getFile().accept(this);
  
          if (x.getCharset() != null) {
              print0(ucase ? " CHARACTER SET " : " character set ");
              print0(x.getCharset());
          }
  
          if (x.getColumnsTerminatedBy() != null || x.getColumnsEnclosedBy() != null || x.getColumnsEscaped() != null) {
              print0(ucase ? " COLUMNS" : " columns");
              if (x.getColumnsTerminatedBy() != null) {
                  print0(ucase ? " TERMINATED BY " : " terminated by ");
                  x.getColumnsTerminatedBy().accept(this);
              }
  
              if (x.getColumnsEnclosedBy() != null) {
                  if (x.isColumnsEnclosedOptionally()) {
                      print0(ucase ? " OPTIONALLY" : " optionally");
                  }
                  print0(ucase ? " ENCLOSED BY " : " enclosed by ");
                  x.getColumnsEnclosedBy().accept(this);
              }
  
              if (x.getColumnsEscaped() != null) {
                  print0(ucase ? " ESCAPED BY " : " escaped by ");
                  x.getColumnsEscaped().accept(this);
              }
          }
  
          if (x.getLinesStartingBy() != null || x.getLinesTerminatedBy() != null) {
              print0(ucase ? " LINES" : " lines");
              if (x.getLinesStartingBy() != null) {
                  print0(ucase ? " STARTING BY " : " starting by ");
                  x.getLinesStartingBy().accept(this);
              }
  
              if (x.getLinesTerminatedBy() != null) {
                  print0(ucase ? " TERMINATED BY " : " terminated by ");
                  x.getLinesTerminatedBy().accept(this);
              }
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlExplainStatement x) {
          List<SQLCommentHint> hints = x.getHeadHintsDirect();
          if (null != hints) {
              for (int i = 0; i < hints.size(); i++) {
                  if (i != 0) {
                      print(' ');
                  }
                  hints.get(i).accept(this);
              }
              println();
          }
  
          String name = x.isDescribe() ? "desc" : "explain";
          print0(ucase ? name.toUpperCase() : name);
  
          hints = x.getHints();
          if (hints != null) {
              print(' ');
              for (int i = 0; i < hints.size(); i++) {
                  if (i != 0) {
                      print(' ');
                  }
                  hints.get(i).accept(this);
              }
          }
  
          String type = x.getType();
          // tbl_name [col_name | wild]
          if (x.getTableName() != null) {
              print(' ');
              x.getTableName().accept(this);
              if (x.getColumnName() != null) {
                  print(' ');
                  x.getColumnName().accept(this);
              } else if (x.getWild() != null) {
                  print(' ');
                  x.getWild().accept(this);
              }
          } else {
              if (x.isExtended()) {
                  print0(ucase ? " EXTENDED" : " extended");
              }
  
              if (x.isOptimizer()) {
                  print0(ucase ? " OPTIMIZER" : " optimizer");
              }
  
              if (x.isDependency()) {
                  print0(ucase ? " DEPENDENCY" : " dependency");
              }
  
              if (x.isAuthorization()) {
                  print0(ucase ? " AUTHORIZATION" : " authorization");
              }
  
              // [explain_type]
              String format = x.getFormat();
              if (type != null || format != null) {
                  final boolean parenthesis = x.isParenthesis();
                  if (parenthesis) {
                      print0(" (");
                  } else {
                      print(' ');
                  }
  
                  if (type != null) {
                      if (parenthesis) {
                          print0(ucase ? "TYPE " : "type ");
                      }
                      print0(type);
                  }
  
                  if (format != null) {
                      if (type != null) {
                          if (parenthesis) {
                              print0(", ");
                          } else {
                              print(' ');
                          }
                      }
  
                      print0(ucase ? "FORMAT " : "format ");
                      if (!parenthesis) {
                          print0("= ");
                      }
                      print0(format);
                  }
  
                  if (parenthesis) {
                      print(')');
                  }
              }
  
              // {explainable_stmt | FOR CONNECTION connection_id}
              if (x.getConnectionId() != null) {
                  print0(ucase ? " FOR CONNECTION " : " for connection ");
                  x.getConnectionId().accept(this);
              } else {
                  print(' ');
                  x.getStatement().accept(this);
              }
  
              if (x.isDistributeInfo()) {
                  print0(ucase ? " DISTRIBUTE INFO" : " distribute info");
              }
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlUpdateStatement x) {
          final List<SQLCommentHint> headHints = x.getHeadHintsDirect();
          if (headHints != null) {
              for (SQLCommentHint hint : headHints) {
                  hint.accept(this);
                  println();
              }
          }
  
          if (x.getWith() != null) {
              x.getWith().accept(this);
              println();
          }
  
          List<SQLExpr> returning = x.getReturning();
          if (returning != null && returning.size() > 0) {
              print0(ucase ? "SELECT " : "select ");
              printAndAccept(returning, ", ");
              println();
              print0(ucase ? "FROM " : "from ");
          }
  
          print0(ucase ? "UPDATE " : "update ");
  
          if (x.isLowPriority()) {
              print0(ucase ? "LOW_PRIORITY " : "low_priority ");
          }
  
          if (x.isIgnore()) {
              print0(ucase ? "IGNORE " : "ignore ");
          }
  
          if (x.getHints() != null && x.getHints().size() > 0) {
              printAndAccept(x.getHints(), " ");
              print0(" ");
          }
  
          if (x.isCommitOnSuccess()) {
              print0(ucase ? "COMMIT_ON_SUCCESS " : "commit_on_success ");
          }
  
          if (x.isRollBackOnFail()) {
              print0(ucase ? "ROLLBACK_ON_FAIL " : "rollback_on_fail ");
          }
  
          if (x.isQueryOnPk()) {
              print0(ucase ? "QUEUE_ON_PK " : "queue_on_pk ");
          }
  
          SQLExpr targetAffectRow = x.getTargetAffectRow();
          if (targetAffectRow != null) {
              print0(ucase ? "TARGET_AFFECT_ROW " : "target_affect_row ");
              printExpr(targetAffectRow, parameterized);
              print(' ');
          }
  
          if (x.isForceAllPartitions()) {
              print0(ucase ? "FORCE ALL PARTITIONS " : "force all partitions ");
          } else {
              SQLName partition = x.getForcePartition();
              if (partition != null) {
                  print0(ucase ? "FORCE PARTITION " : "force partition ");
                  printExpr(partition, parameterized);
                  print(' ');
              }
          }
  
          printTableSource(x.getTableSource());
  
          println();
          print0(ucase ? "SET " : "set ");
          List<SQLUpdateSetItem> items = x.getItems();
          for (int i = 0, size = items.size(); i < size; ++i) {
              if (i != 0) {
                  print0(", ");
              }
              SQLUpdateSetItem item = items.get(i);
              visit(item);
          }
  
          SQLExpr where = x.getWhere();
          if (where != null) {
              println();
              indentCount++;
              print0(ucase ? "WHERE " : "where ");
              printExpr(where, parameterized);
              indentCount--;
          }
  
          SQLOrderBy orderBy = x.getOrderBy();
          if (orderBy != null) {
              println();
              visit(orderBy);
          }
  
          SQLLimit limit = x.getLimit();
          if (limit != null) {
              println();
              visit(limit);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlSetTransactionStatement x) {
          print0(ucase ? "SET " : "set ");
          if (x.getGlobal() != null && x.getGlobal()) {
              print0(ucase ? "GLOBAL " : "global ");
          } else if (x.getSession() != null && x.getSession()) {
              print0(ucase ? "SESSION " : "session ");
          }
  
          print0(ucase ? "TRANSACTION " : "transaction ");
  
          if (x.getIsolationLevel() != null) {
              print0(ucase ? "ISOLATION LEVEL " : "isolation level ");
              print0(x.getIsolationLevel());
          }
  
          final String accessModel = x.getAccessModel();
          if (accessModel != null) {
              print0(ucase ? "READ " : "read ");
              print0(accessModel);
          }
  
          final SQLExpr policy = x.getPolicy();
          if (policy != null) {
              print0(ucase ? "POLICY " : "policy ");
              policy.accept(this);
          }
  
          return false;
      }
  
  //
  //    @Override
  //    public boolean visit(MySqlSetNamesStatement x) {
  //        print0(ucase ? "SET NAMES " : "set names ");
  //        if (x.isDefault()) {
  //            print0(ucase ? "DEFAULT" : "default");
  //        } else {
  //            print0(x.getCharSet());
  //            if (x.getCollate() != null) {
  //                print0(ucase ? " COLLATE " : " collate ");
  //                print0(x.getCollate());
  //            }
  //        }
  //        return false;
  //    }
  
  //    public boolean visit(MySqlSetCharSetStatement x) {
  //        print0(ucase ? "SET CHARACTER SET " : "set character set ");
  //        if (x.isDefault()) {
  //            print0(ucase ? "DEFAULT" : "default");
  //        } else {
  //            print0(x.getCharSet());
  //            if (x.getCollate() != null) {
  //                print0(ucase ? " COLLATE " : " collate ");
  //                print0(x.getCollate());
  //            }
  //        }
  //        return false;
  //    }
  
      @Override
      public boolean visit(MySqlShowAuthorsStatement x) {
          print0(ucase ? "SHOW AUTHORS" : "show authors");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowBinaryLogsStatement x) {
          print0(ucase ? "SHOW BINARY LOGS" : "show binary logs");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowMasterLogsStatement x) {
          print0(ucase ? "SHOW MASTER LOGS" : "show master logs");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowCollationStatement x) {
          print0(ucase ? "SHOW COLLATION" : "show collation");
          if (x.getPattern() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getPattern().accept(this);
          }
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowBinLogEventsStatement x) {
          print0(ucase ? "SHOW BINLOG EVENTS" : "show binlog events");
          if (x.getIn() != null) {
              print0(ucase ? " IN " : " in ");
              x.getIn().accept(this);
          }
          if (x.getFrom() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getFrom().accept(this);
          }
          if (x.getLimit() != null) {
              print(' ');
              x.getLimit().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowCharacterSetStatement x) {
          print0(ucase ? "SHOW CHARACTER SET" : "show character set");
          if (x.getPattern() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getPattern().accept(this);
          }
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowContributorsStatement x) {
          print0(ucase ? "SHOW CONTRIBUTORS" : "show contributors");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowCreateDatabaseStatement x) {
          print0(ucase ? "SHOW CREATE DATABASE " : "show create database ");
          if (x.isIfNotExists()) {
              print0(ucase ? "IF NOT EXISTS " : "if not exists ");
          }
          x.getDatabase().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowCreateEventStatement x) {
          print0(ucase ? "SHOW CREATE EVENT " : "show create event ");
          x.getEventName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowCreateFunctionStatement x) {
          print0(ucase ? "SHOW CREATE FUNCTION " : "show create function ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowCreateProcedureStatement x) {
          print0(ucase ? "SHOW CREATE PROCEDURE " : "show create procedure ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowCreateTriggerStatement x) {
          print0(ucase ? "SHOW CREATE TRIGGER " : "show create trigger ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowEngineStatement x) {
          print0(ucase ? "SHOW ENGINE " : "show engine ");
          x.getName().accept(this);
          print(' ');
          print0(x.getOption().name());
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowEventsStatement x) {
          print0(ucase ? "SHOW EVENTS" : "show events");
          if (x.getSchema() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getSchema().accept(this);
          }
  
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowFunctionCodeStatement x) {
          print0(ucase ? "SHOW FUNCTION CODE " : "show function code ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowFunctionStatusStatement x) {
          print0(ucase ? "SHOW FUNCTION STATUS" : "show function status");
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowEnginesStatement x) {
          if (x.isStorage()) {
              print0(ucase ? "SHOW STORAGE ENGINES" : "show storage engines");
          } else {
              print0(ucase ? "SHOW ENGINES" : "show engines");
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowErrorsStatement x) {
          if (x.isCount()) {
              print0(ucase ? "SHOW COUNT(*) ERRORS" : "show count(*) errors");
          } else {
              print0(ucase ? "SHOW ERRORS" : "show errors");
              if (x.getLimit() != null) {
                  print(' ');
                  x.getLimit().accept(this);
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowGrantsStatement x) {
          print0(ucase ? "SHOW GRANTS" : "show grants");
          SQLExpr user = x.getUser();
          if (user != null) {
              print0(ucase ? " FOR " : " for ");
              user.accept(this);
          }
  
          SQLExpr on = x.getOn();
          if (on != null) {
              print0(ucase ? " ON " : " on ");
              on.accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlUserName x) {
          String userName = x.getUserName();
  
          if (userName.length() > 0 && userName.charAt(0) == '\'') {
              print0(userName);
          } else {
              print('\'');
              print0(userName);
              print('\'');
          }
  
          String host = x.getHost();
          if (host != null) {
              print('@');
  
              if (host.length() > 0 && host.charAt(0) == '\'') {
                  print0(host);
              } else {
                  print('\'');
                  print0(host);
                  print('\'');
              }
          }
  
          String identifiedBy = x.getIdentifiedBy();
          if (identifiedBy != null) {
              print0(ucase ? " IDENTIFIED BY '" : " identified by '");
              print0(identifiedBy);
              print('\'');
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowMasterStatusStatement x) {
          print0(ucase ? "SHOW MASTER STATUS" : "show master status");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowOpenTablesStatement x) {
          print0(ucase ? "SHOW OPEN TABLES" : "show open tables");
  
          if (x.getDatabase() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getDatabase().accept(this);
          }
  
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowPluginsStatement x) {
          print0(ucase ? "SHOW PLUGINS" : "show plugins");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowPartitionsStatement x) {
          print0(ucase ? "SHOW DBPARTITIONS " : "show dbpartitions ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(SQLShowPartitionsStmt x) {
          print0(ucase ? "SHOW PARTITIONS FROM " : "show partitions from ");
          x.getTableSource().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowPrivilegesStatement x) {
          print0(ucase ? "SHOW PRIVILEGES" : "show privileges");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowProcedureCodeStatement x) {
          print0(ucase ? "SHOW PROCEDURE CODE " : "show procedure code ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowProcedureStatusStatement x) {
          print0(ucase ? "SHOW PROCEDURE STATUS" : "show procedure status");
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowProcessListStatement x) {
          return visit((SQLShowProcessListStatement) x);
      }
  
      @Override
      public boolean visit(MySqlShowProfileStatement x) {
          print0(ucase ? "SHOW PROFILE" : "show profile");
          for (int i = 0; i < x.getTypes().size(); ++i) {
              if (i == 0) {
                  print(' ');
              } else {
                  print0(", ");
              }
              print0(x.getTypes().get(i).name);
          }
  
          if (x.getForQuery() != null) {
              print0(ucase ? " FOR QUERY " : " for query ");
              x.getForQuery().accept(this);
          }
  
          if (x.getLimit() != null) {
              print(' ');
              x.getLimit().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowProfilesStatement x) {
          print0(ucase ? "SHOW PROFILES" : "show profiles");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowRelayLogEventsStatement x) {
          print0("SHOW RELAYLOG EVENTS");
  
          if (x.getLogName() != null) {
              print0(ucase ? " IN " : " in ");
              x.getLogName().accept(this);
          }
  
          if (x.getFrom() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getFrom().accept(this);
          }
  
          if (x.getLimit() != null) {
              print(' ');
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowSlaveHostsStatement x) {
          print0(ucase ? "SHOW SLAVE HOSTS" : "show slave hosts");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowSlowStatement x) {
          print0(ucase ? "SHOW " : "show ");
  
          if (x.isFull()) {
              print0(ucase ? "FULL " : "full ");
          }
          if (x.isPhysical()) {
              print0(ucase ? "PHYSICAL_SLOW" : "PHYSICAL_SLOW");
          } else {
              print0(ucase ? "SLOW" : "slow");
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowSequencesStatement x) {
          print0(ucase ? "SHOW SEQUENCES" : "show sequences");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowSlaveStatusStatement x) {
          print0(ucase ? "SHOW SLAVE STATUS" : "show slave status");
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowTableStatusStatement x) {
          final List<SQLCommentHint> headHints = x.getHeadHintsDirect();
          if (headHints != null) {
              for (SQLCommentHint hint : headHints) {
                  hint.accept(this);
                  println();
              }
          }
  
          print0(ucase ? "SHOW TABLE STATUS" : "show table status");
          if (x.getDatabase() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getDatabase().accept(this);
              if (x.getTableGroup() != null) {
                  print0(".");
                  x.getTableGroup().accept(this);
              }
          }
  
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MysqlShowDbLockStatement x) {
          print0(ucase ? "SHOW DBLOCK" : "show dblock");
          return false;
      }
  
      @Override
      public boolean visit(MysqlShowHtcStatement x) {
          print0(ucase ? "SHOW HTC" : "show htc");
          return false;
      }
  
      @Override
      public boolean visit(MysqlShowStcStatement x) {
          if (x.isHis()) {
              print0(ucase ? "SHOW STC HIS" : "show stc his");
          } else {
              print0(ucase ? "SHOW STC" : "show stc");
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowTriggersStatement x) {
          print0(ucase ? "SHOW TRIGGERS" : "show triggers");
  
          if (x.getDatabase() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getDatabase().accept(this);
          }
  
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowRuleStatement x) {
          print0(ucase ? "SHOW " : "show ");
  
          if (x.isFull()) {
              print0(ucase ? "FULL RULE" : "full rule");
          } else {
              print0(ucase ? "RULE" : "rule");
          }
  
          if (x.isVersion()) {
              print0(ucase ? " VERSION" : " version");
          }
  
          if (x.getName() != null) {
              print0(ucase ? " FROM " : " from ");
              x.getName().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowRuleStatusStatement x) {
          print0(ucase ? "SHOW RULE" : "show rule");
  
          if (x.isFull()) {
              print0(ucase ? " FULL" : " full");
          } else if (x.isVersion()) {
              print0(ucase ? " VERSION" : " version");
          }
  
          print0(ucase ? " STATUS" : " status");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowPhysicalProcesslistStatement x) {
          print0(ucase ? "SHOW" : "show");
  
          if (x.isFull()) {
              print0(ucase ? " FULL" : " full");
          }
  
          print0(ucase ? " PHYSICAL_PROCESSLIST" : " physical_processlist");
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlRenameSequenceStatement x) {
          print0(ucase ? "RENAME SEQUENCE " : "rename sequence ");
          x.getName().accept(this);
          print0(ucase ? " TO " : " to ");
          x.getTo().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlCheckTableStatement x) {
          print0(ucase ? "CHECK TABLE " : "check table ");
          printAndAccept(x.getTables(), "，");
          return false;
      }
  
      @Override
      public boolean visit(MysqlCreateFullTextCharFilterStatement x) {
          print0(ucase ? "CREATE FULLTEXT CHARFILTER " : "create fulltext charfilter ");
          x.getName().accept(this);
          println("(");
          print0("\"type\" = " + x.getTypeName());
          println(",");
          printAndAccept(x.getOptions(), ",");
  
          println();
          print0(")");
  
          return false;
      }
  
      @Override
      public boolean visit(MysqlShowFullTextStatement x) {
          print0(ucase ? "SHOW FULLTEXT " : "show fulltext ");
          if (x.getType() == FullTextType.DICTIONARY) {
              print0(ucase ? "DICTIONARIES" : "dictionaries");
          } else {
              print0(ucase ? x.getType().toString().toUpperCase() + "S" : x.getType().toString().toLowerCase() + "s");
          }
          return false;
      }
  
      @Override
      public boolean visit(MysqlShowCreateFullTextStatement x) {
          print0(ucase ? "SHOW CREATE FULLTEXT " : "show create fulltext ");
          print0(ucase ? x.getType().toString().toUpperCase() : x.getType().toString().toLowerCase());
          print0(" ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MysqlAlterFullTextStatement x) {
          print0(ucase ? "ALTER FULLTEXT " : "alter fulltext ");
          print0(ucase ? x.getType().toString().toUpperCase() : x.getType().toString().toLowerCase());
          print0(" ");
          x.getName().accept(this);
          print0(ucase ? " SET " : " set ");
          x.getItem().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(SQLAlterTableDropClusteringKey x) {
          print0(ucase ? "DROP CLUSTERED KEY " : "drop clustered key ");
          x.getKeyName().accept(this);
  
          return false;
      }
  
      @Override
      public boolean visit(MysqlDropFullTextStatement x) {
          print0(ucase ? "DROP FULLTEXT " : "drop fulltext ");
          print0(ucase ? x.getType().toString().toUpperCase() : x.getType().toString().toLowerCase());
          print0(" ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MysqlCreateFullTextTokenizerStatement x) {
          print0(ucase ? "CREATE FULLTEXT TOKENIZER " : "create fulltext tokenizer ");
          x.getName().accept(this);
          println("(");
          print0("\"type\" = " + x.getTypeName());
          if (x.getUserDefinedDict() != null) {
              println(",");
              print("\"user_defined_dict\" = " + x.getUserDefinedDict());
          }
          if (!x.getOptions().isEmpty()) {
              println(",");
              printAndAccept(x.getOptions(), ",");
          }
          println();
          print0(")");
  
          return false;
      }
  
      @Override
      public boolean visit(MysqlCreateFullTextTokenFilterStatement x) {
          print0(ucase ? "CREATE FULLTEXT TOKENFILTER " : "create fulltext tokenfilter ");
          x.getName().accept(this);
          println("(");
          println("\"type\" = " + x.getTypeName() + ",");
          printAndAccept(x.getOptions(), ",");
  
          println();
          print0(")");
          return false;
      }
  
      @Override
      public boolean visit(MysqlCreateFullTextAnalyzerStatement x) {
          print0(ucase ? "CREATE FULLTEXT ANALYZER " : "create fulltext analyzer ");
          x.getName().accept(this);
          println("(");
          print0(ucase ? "\"TOKENIZER\" = " : "\"tokenizer\" = ");
          print0(x.getTokenizer());
          println(",");
  
          if (!x.getCharfilters().isEmpty()) {
              print0(ucase ? "\"CHARFILTER\" = [" : "\"charfilter\" = [");
              for (int i = 0; i < x.getCharfilters().size(); i++) {
                  if (i != 0) {
                      print0(", ");
                  }
  
                  print0("\"" + x.getCharfilters().get(i) + "\"");
              }
              println("],");
          }
          if (!x.getTokenizers().isEmpty()) {
              print0(ucase ? "\"TOKENFILTER\" = [" : "\"tokenfilter\" = [");
              for (int i = 0; i < x.getTokenizers().size(); i++) {
                  if (i != 0) {
                      print0(", ");
                  }
  
                  print0("\"" + x.getTokenizers().get(i) + "\"");
              }
              print(']');
          }
          println();
          print(')');
          return false;
      }
  
      @Override
      public boolean visit(MysqlCreateFullTextDictionaryStatement x) {
          print0(ucase ? "CREATE FULLTEXT DICTIONARY " : "create fulltext dictionary ");
          x.getName().accept(this);
          println('(');
          x.getColumn().accept(this);
          println();
          print(')');
  
          if (x.getComment() != null) {
              println();
              print0(ucase ? "COMMENT " : "comment ");
              print0(x.getComment());
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableAlterFullTextIndex x) {
          print0(ucase ? " ALTER INDEX " : " alter index ");
          x.getIndexName().accept(this);
          print0(ucase ? " FULLTEXT " : " fulltext ");
          if (x.getAnalyzerType() != null) {
              String analyzerType = x.getAnalyzerType().toString();
              print0(ucase ? analyzerType.toUpperCase() : analyzerType.toLowerCase());
          }
          print0(ucase ? " ANALYZER = " : " analyzer = ");
          x.getAnalyzerName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlExtPartition x) {
          print0(ucase ? "EXTPARTITION (" : "extpartition (");
          incrementIndent();
          println();
          for (int i = 0; i < x.getItems().size(); i++) {
              if (i != 0) {
                  println(", ");
              }
              MySqlExtPartition.Item item = x.getItems().get(i);
              item.accept(this);
          }
          decrementIndent();
          println();
          print(')');
          return false;
      }
  
      public boolean visit(MySqlExtPartition.Item x) {
          SQLName dbPartition = x.getDbPartition();
          if (dbPartition != null) {
              print0(ucase ? "DBPARTITION " : "dbpartition ");
              dbPartition.accept(this);
  
              SQLExpr dbPartitionBy = x.getDbPartitionBy();
              if (dbPartitionBy != null) {
                  print0(ucase ? " BY " : " by ");
              }
              dbPartitionBy.accept(this);
          }
  
          SQLName tbPartition = x.getTbPartition();
          if (tbPartition != null) {
              if (dbPartition != null) {
                  print(' ');
              }
  
              print0(ucase ? "TBPARTITION " : "tbpartition ");
              tbPartition.accept(this);
  
              SQLExpr tbPartitionBy = x.getTbPartitionBy();
              if (tbPartitionBy != null) {
                  print0(ucase ? " BY " : " by ");
              }
              tbPartitionBy.accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowTraceStatement x) {
          print0(ucase ? "SHOW TRACE" : "show trace");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowBroadcastsStatement x) {
          print0(ucase ? "SHOW BROADCASTS" : "show broadcasts");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowDatasourcesStatement x) {
          print0(ucase ? "SHOW DATASOURCES" : "show datasources");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowNodeStatement x) {
          print0(ucase ? "SHOW NODE" : "show node");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowHelpStatement x) {
          print0(ucase ? "SHOW HELP" : "show help");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlFlashbackStatement x) {
          print0(ucase ? "FLASHBACK TABLE " : "flashback table ");
          x.getName().accept(this);
          print0(ucase ? " TO BEFORE DROP" : " to before drop");
  
          final SQLName renameTo = x.getRenameTo();
          if (renameTo != null) {
              print0(ucase ? " RENAME TO " : " rename to ");
              renameTo.accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowConfigStatement x) {
          print0(ucase ? "SHOW CONFIG " : "show config ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowPlanCacheStatement x) {
          print0(ucase ? "SHOW PLANCACHE PLAN" : "show plancache plan");
          println();
          x.getSelect().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowDdlStatusStatement x) {
          print0(ucase ? "SHOW DDL STATUS" : "show ddl status");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowDsStatement x) {
          print0(ucase ? "SHOW DS" : "show ds");
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlShowTopologyStatement x) {
          print0(ucase ? "SHOW " : "show ");
  
          if (x.isFull()) {
              print0(ucase ? "FULL " : "full ");
          }
  
          print0(ucase ? "TOPOLOGY FROM " : "topology from ");
  
          print0(x.getName().getSimpleName());
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          if (x.getOrderBy() != null) {
              print0(" ");
              x.getOrderBy().accept(this);
          }
  
          if (x.getLimit() != null) {
              print0(" ");
              x.getLimit().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(SQLShowVariantsStatement x) {
          print0(ucase ? "SHOW " : "show ");
  
          if (x.isGlobal()) {
              print0(ucase ? "GLOBAL " : "global ");
          }
  
          if (x.isSession()) {
              print0(ucase ? "SESSION " : "session ");
          }
  
          print0(ucase ? "VARIABLES" : "variables");
  
          if (x.getLike() != null) {
              print0(ucase ? " LIKE " : " like ");
              x.getLike().accept(this);
          }
  
          if (x.getWhere() != null) {
              print0(ucase ? " WHERE " : " where ");
              x.getWhere().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(SQLAlterTableStatement x) {
          final List<SQLCommentHint> headHints = x.getHeadHintsDirect();
          if (headHints != null) {
              for (SQLCommentHint hint : headHints) {
                  hint.accept(this);
                  println();
              }
          }
  
          print0(ucase ? "ALTER " : "alter ");
          if (x.isOnline()) {
              print0(ucase ? "ONLINE  " : "online ");
          } else if (x.isOffline()) {
              print0(ucase ? "OFFLINE  " : "offline ");
          }
          if (x.isIgnore()) {
              print0(ucase ? "IGNORE " : "ignore ");
          }
          print0(ucase ? "TABLE " : "table ");
  
          printTableSourceExpr(x.getName());
  
          this.indentCount++;
          if (x.getTableOptions().size() > 0) {
              println();
          }
          this.indentCount--;
          {
              int i = 0;
              for (SQLAssignItem item : x.getTableOptions()) {
                  SQLExpr key = item.getTarget();
                  if (i != 0) {
                      print(' ');
                  }
                  final String keyStringCase = ucase ? key.toString().toUpperCase() : key.toString().toLowerCase();
                  print0(keyStringCase);
                  if ("TABLESPACE".equalsIgnoreCase(keyStringCase)) {
                      print(' ');
                  } else {
                      print0(" = ");
                  }
                  item.getValue().accept(this);
                  i++;
              }
          }
          if (x.getItems().size() > 0) {
              if (x.getTableOptions().size() > 0) {
                  print(',');
              }
          }
          this.indentCount++;
          for (int i = 0; i < x.getItems().size(); ++i) {
              SQLAlterTableItem item = x.getItems().get(i);
              if (i != 0) {
                  print(',');
              }
              println();
              item.accept(this);
          }
  
          if (x.isRemovePatiting()) {
              println();
              print0(ucase ? "REMOVE PARTITIONING" : "remove partitioning");
          }
  
          if (x.isUpgradePatiting()) {
              println();
              print0(ucase ? "UPGRADE PARTITIONING" : "upgrade partitioning");
          }
          this.indentCount--;
         SQLPartitionBy partitionBy = x.getPartition();
          if (partitionBy != null) {
              println();
              print0(ucase ? "PARTITION BY " : "partition by ");
              partitionBy.accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(SQLAlterTableAddColumn x) {
          print0(ucase ? "ADD COLUMN " : "add column ");
  
          if (x.getColumns().size() > 1) {
              print('(');
          }
          printAndAccept(x.getColumns(), ", ");
          if (x.getFirstColumn() != null) {
              print0(ucase ? " FIRST " : " first ");
              x.getFirstColumn().accept(this);
          } else if (x.getAfterColumn() != null) {
              print0(ucase ? " AFTER " : " after ");
              x.getAfterColumn().accept(this);
          } else if (x.isFirst()) {
              print0(ucase ? " FIRST" : " first");
          }
  
          if (x.getColumns().size() > 1) {
              print(')');
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlRenameTableStatement.Item x) {
          x.getName().accept(this);
          print0(ucase ? " TO " : " to ");
          x.getTo().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlRenameTableStatement x) {
          print0(ucase ? "RENAME TABLE " : "rename table ");
          printAndAccept(x.getItems(), ", ");
          return false;
      }
  
      @Override
      public boolean visit(MySqlUseIndexHint x) {
          print0(ucase ? "USE INDEX " : "use index ");
          if (x.getOption() != null) {
              print0(ucase ? "FOR " : "for ");
              print0(x.getOption().name);
              print(' ');
          }
          print('(');
          printAndAccept(x.getIndexList(), ", ");
          print(')');
          return false;
      }
  
      @Override
      public boolean visit(MySqlIgnoreIndexHint x) {
          print0(ucase ? "IGNORE INDEX " : "ignore index ");
          if (x.getOption() != null) {
              print0(ucase ? "FOR " : "for ");
              print0(ucase ? x.getOption().name : x.getOption().nameLCase);
              print(' ');
          }
          print('(');
          printAndAccept(x.getIndexList(), ", ");
          print(')');
          return false;
      }
  
      public boolean visit(SQLExprTableSource x) {
          printTableSourceExpr(x.getExpr());
  
          final SQLTableSampling sampling = x.getSampling();
          if (sampling != null) {
              print(' ');
              sampling.accept(this);
          }
  
          if (x.getPartitionSize() > 0) {
              print0(ucase ? " PARTITION (" : " partition (");
              printlnAndAccept(x.getPartitions(), ", ");
              print(')');
          }
  
          String alias = x.getAlias();
          List<SQLName> columns = x.getColumnsDirect();
          if (alias != null) {
              print(' ');
              if (columns != null && columns.size() > 0) {
                  print0(ucase ? " AS " : " as ");
              }
              print0(alias);
          }
  
          if (columns != null && columns.size() > 0) {
              print(" (");
              printAndAccept(columns, ", ");
              print(')');
          }
  
          if (isPrettyFormat() && x.hasAfterComment()) {
              print(' ');
              printlnComment(x.getAfterCommentsDirect());
          }
  
          for (int i = 0; i < x.getHintsSize(); ++i) {
              print(' ');
              x.getHints().get(i).accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlLockTableStatement x) {
          print0(ucase ? "LOCK TABLES" : "lock tables");
          List<MySqlLockTableStatement.Item> items = x.getItems();
          if (items.size() > 0) {
              print(' ');
              printAndAccept(items, ", ");
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlLockTableStatement.Item x) {
          x.getTableSource().accept(this);
          if (x.getLockType() != null) {
              print(' ');
              print0(x.getLockType().name);
          }
  
          if (x.getHints() != null && x.getHints().size() > 0) {
              print(' ');
              printAndAccept(x.getHints(), " ");
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlUnlockTablesStatement x) {
          print0(ucase ? "UNLOCK TABLES" : "unlock tables");
          return false;
      }
  
      @Override
      public boolean visit(MySqlForceIndexHint x) {
          print0(ucase ? "FORCE INDEX " : "force index ");
          if (x.getOption() != null) {
              print0(ucase ? "FOR " : "for ");
              print0(x.getOption().name);
              print(' ');
          }
          print('(');
          printAndAccept(x.getIndexList(), ", ");
          print(')');
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableChangeColumn x) {
          print0(ucase ? "CHANGE COLUMN " : "change column ");
          x.getColumnName().accept(this);
          print(' ');
          x.getNewColumnDefinition().accept(this);
          if (x.getFirstColumn() != null) {
              print0(ucase ? " FIRST " : " first ");
              x.getFirstColumn().accept(this);
          } else if (x.getAfterColumn() != null) {
              print0(ucase ? " AFTER " : " after ");
              x.getAfterColumn().accept(this);
          } else if (x.isFirst()) {
              print0(ucase ? " FIRST" : " first");
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableModifyColumn x) {
          print0(ucase ? "MODIFY COLUMN " : "modify column ");
          x.getNewColumnDefinition().accept(this);
          if (x.getFirstColumn() != null) {
              print0(ucase ? " FIRST " : " first ");
              x.getFirstColumn().accept(this);
          } else if (x.getAfterColumn() != null) {
              print0(ucase ? " AFTER " : " after ");
              x.getAfterColumn().accept(this);
          } else if (x.isFirst()) {
              print0(ucase ? " FIRST" : " first");
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableOption x) {
          print0(x.getName());
          print0(" = ");
          print0(x.getValue().toString());
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterDatabaseSetOption x) {
          print0(ucase ? "SET " : "set ");
          printAndAccept(x.getOptions(), ", ");
  
          SQLName on = x.getOn();
          if (on != null) {
              print0(ucase ? " ON " : " on ");
              on.accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterDatabaseKillJob x) {
          print0(ucase ? "KILL " : "kill ");
          x.getJobType().accept(this);
          print0(" ");
          x.getJobId().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlHelpStatement x) {
          print0(ucase ? "HELP " : "help ");
          x.getContent().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlCharExpr x) {
          if (this.parameterized) {
              print('?');
              incrementReplaceCunt();
              if (this.parameters != null) {
                  ExportParameterVisitorUtils.exportParameter(this.parameters, x);
              }
              return false;
          }
          if (x.isParenthesized()) {
              print("(");
          }
          String charset = x.getCharset();
          String collate = x.getCollate();
          String text = x.getText();
  
          if (charset != null) {
              print(charset);
  
              long charsetHashCode = FnvHash.hashCode64(charset);
              if (charsetHashCode == FnvHash.Constants._UCS2 || charsetHashCode == FnvHash.Constants._UTF16) {
                  print(" x'");
              } else {
                  print(" '");
              }
              print(text);
              print('\'');
          } else {
              print('\'');
              print(text);
              print('\'');
          }
  
          if (collate != null) {
              print(" COLLATE ");
              print(collate);
          }
          if (x.isParenthesized()) {
              print(")");
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlUnique x) {
          visit(x.getIndexDefinition());
          return false;
      }
  
      @Override
      public boolean visit(MysqlForeignKey x) {
          if (x.isHasConstraint()) {
              print0(ucase ? "CONSTRAINT " : "constraint ");
              if (x.getName() != null) {
                  x.getName().accept(this);
                  print(' ');
              }
          }
  
          print0(ucase ? "FOREIGN KEY" : "foreign key");
  
          if (x.getIndexName() != null) {
              print(' ');
              x.getIndexName().accept(this);
          }
  
          print0(" (");
          printAndAccept(x.getReferencingColumns(), ", ");
          print(')');
  
          print0(ucase ? " REFERENCES " : " references ");
          x.getReferencedTableName().accept(this);
  
          print0(" (");
          printAndAccept(x.getReferencedColumns(), ", ");
          print(')');
  
          SQLForeignKeyImpl.Match match = x.getReferenceMatch();
          if (match != null) {
              print0(ucase ? " MATCH " : " match ");
              print0(ucase ? match.name : match.nameLCase);
          }
  
          if (x.getOnDelete() != null) {
              print0(ucase ? " ON DELETE " : " on delete ");
              print0(ucase ? x.getOnDelete().name : x.getOnDelete().nameLCase);
          }
  
          if (x.getOnUpdate() != null) {
              print0(ucase ? " ON UPDATE " : " on update ");
              print0(ucase ? x.getOnUpdate().name : x.getOnUpdate().nameLCase);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableDiscardTablespace x) {
          print0(ucase ? "DISCARD TABLESPACE" : "discard tablespace");
          return false;
      }
  
      @Override
      public boolean visit(MySqlCreateExternalCatalogStatement x) {
          print0(ucase ? "CREATE EXTERNAL CATALOG " : "create external catalog ");
  
          if (x.isIfNotExists()) {
              print0(ucase ? "IF NOT EXISTS " : "if not exists ");
          }
  
          x.getName().accept(this);
  
          print0(ucase ? " PROPERTIES (" : " properties (");
  
          for (Map.Entry<SQLName, SQLName> entry : x.getProperties().entrySet()) {
              println();
              entry.getKey().accept(this);
              print0("=");
              entry.getValue().accept(this);
          }
          print0(")");
  
          printComment(x.getComment());
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableImportTablespace x) {
          print0(ucase ? "IMPORT TABLESPACE" : "import tablespace");
          return false;
      }
  
      @Override
      public boolean visit(SQLAssignItem x) {
          String tagetString = x.getTarget().toString();
  
          boolean mysqlSpecial = false;
  
          if (DbType.mysql == dbType) {
              mysqlSpecial = "NAMES".equalsIgnoreCase(tagetString)
                      || "CHARACTER SET".equalsIgnoreCase(tagetString)
                      || "CHARSET".equalsIgnoreCase(tagetString);
          }
  
          if (!mysqlSpecial) {
              x.getTarget().accept(this);
              print0(" = ");
          } else {
              print0(ucase ? tagetString.toUpperCase() : tagetString.toLowerCase());
              print(' ');
          }
          x.getValue().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(TableSpaceOption x) {
          x.getName().accept(this);
  
          if (x.getStorage() != null) {
              print(ucase ? " STORAGE " : " storage ");
              x.getStorage().accept(this);
          }
          return false;
      }
  
      protected void visitAggregateRest(SQLAggregateExpr x) {
          boolean withGroup = x.isWithinGroup();
          if (withGroup) {
              print0(ucase ? ") WITHIN GROUP (" : ") within group (");
          }
          {
              SQLOrderBy value = x.getOrderBy();
              if (value != null) {
                  print(' ');
                  ((SQLObject) value).accept(this);
              }
          }
          {
              Object value = x.getAttribute("SEPARATOR");
              if (value != null) {
                  print0(ucase ? " SEPARATOR " : " separator ");
                  ((SQLObject) value).accept(this);
              }
          }
      }
  
      @Override
      public boolean visit(MySqlAnalyzeStatement x) {
          print0(ucase ? "ANALYZE " : "analyze ");
          if (x.isNoWriteToBinlog()) {
              print0(ucase ? "NO_WRITE_TO_BINLOG " : "no_write_to_binlog ");
          }
  
          if (x.isLocal()) {
              print0(ucase ? "LOCAL " : "local ");
          }
  
          if (!x.getAdbColumns().isEmpty()) {
              print0(ucase ? "COLUMN " : "column ");
          } else if (!x.getAdbColumnsGroup().isEmpty()) {
              print0(ucase ? "COLUMNS GROUP " : "columns group ");
          } else if (x.getAdbSchema() != null) {
              print0(ucase ? "DATABASE " : "database ");
              x.getAdbSchema().accept(this);
          } else if (!x.getTableSources().isEmpty()) {
              print0(ucase ? "TABLE " : "table ");
          }
  
          printAndAccept(x.getTableSources(), ", ");
  
          if (!x.getAdbColumns().isEmpty()) {
              print0("(");
              printAndAccept(x.getAdbColumns(), ",");
              print0(")");
              if (x.getAdbWhere() != null) {
                  println();
                  print0(ucase ? " WHERE " : " WHERE ");
                  printExpr(x.getAdbWhere());
              }
          } else if (!x.getAdbColumnsGroup().isEmpty()) {
              print0("(");
              printAndAccept(x.getAdbColumnsGroup(), ",");
              print0(")");
              if (x.getAdbWhere() != null) {
                  println();
                  print0(ucase ? " WHERE " : " WHERE ");
                  printExpr(x.getAdbWhere());
              }
          } else if (!x.getTableSources().isEmpty()) {
              if (x.getAdbWhere() != null) {
                  println();
                  print0(ucase ? " WHERE " : " WHERE ");
                  printExpr(x.getAdbWhere());
              }
          }
  
          SQLPartitionRef partition = x.getPartition();
          if (partition != null) {
              print(' ');
              partition.accept(this);
          }
  
          if (x.isComputeStatistics()) {
              print0(ucase ? " COMPUTE STATISTICS" : " compute statistics");
          }
  
          if (x.isForColums()) {
              print0(ucase ? " FOR COLUMNS" : " for columns");
          }
  
          if (x.isCacheMetadata()) {
              print0(ucase ? " CACHE METADATA" : " cache metadata");
          }
  
          if (x.isNoscan()) {
              print0(ucase ? " NOSCAN" : " noscan");
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlOptimizeStatement x) {
          print0(ucase ? "OPTIMIZE " : "optimize ");
          if (x.isNoWriteToBinlog()) {
              print0(ucase ? "NO_WRITE_TO_BINLOG " : "No_write_to_binlog ");
          }
  
          if (x.isLocal()) {
              print0(ucase ? "LOCAL " : "local ");
          }
  
          print0(ucase ? "TABLE " : "table ");
  
          printAndAccept(x.getTableSources(), ", ");
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterUserStatement x) {
          print0(ucase ? "ALTER USER" : "alter user");
  
          if (x.isIfExists()) {
              print0(ucase ? " IF EXISTS" : " if exists");
          }
  
          for (int i = 0; i < x.getAlterUsers().size(); i++) {
              if (i != 0) {
                  print(',');
              }
  
              MySqlAlterUserStatement.AlterUser alterUser = x.getAlterUsers().get(i);
              print(' ');
              alterUser.getUser().accept(this);
  
              if (alterUser.getAuthOption() != null) {
                  print(" IDENTIFIED BY ");
                  SQLCharExpr authString = alterUser.getAuthOption().getAuthString();
                  authString.accept(this);
              }
              if (alterUser.getAccountLockOption() != null) {
                  print0(ucase ? " ACCOUNT " : " account ");
                  print0(ucase ? alterUser.getAccountLockOption().toUpperCase() : alterUser.getAccountLockOption().toLowerCase());
              }
          }
  
          MySqlAlterUserStatement.PasswordOption passwordOption = x.getPasswordOption();
          if (passwordOption != null) {
              switch (passwordOption.getExpire()) {
                  case PASSWORD_EXPIRE:
                      print0(ucase ? " PASSWORD EXPIRE" : " password expire");
                      break;
                  case PASSWORD_EXPIRE_DEFAULT:
                      print0(ucase ? " PASSWORD EXPIRE DEFAULT" : " password expire default");
                      break;
                  case PASSWORD_EXPIRE_NEVER:
                      print0(ucase ? " PASSWORD EXPIRE NEVER" : " password expire never");
                      break;
                  case PASSWORD_EXPIRE_INTERVAL:
                      print0(ucase ? " PASSWORD EXPIRE INTERVAL " : " password expire interval ");
                      passwordOption.getIntervalDays().accept(this);
                      print0(ucase ? " DAY" : " day");
                      break;
                  default:
                      throw new RuntimeException("invalid password option:" + passwordOption);
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(SQLSetStatement x) {
          boolean printSet = Boolean.TRUE.equals(x.getAttribute("parser.set")) || DbType.oracle != dbType;
          if (printSet) {
              print0(ucase ? "SET " : "set ");
          }
          if (x.getMaridbSetForStatement() != null) {
              print0(ucase ? "STATEMENT " : "statement ");
          }
          SQLSetStatement.Option option = x.getOption();
          if (option != null) {
              print(option.name());
              print(' ');
          }
  
          if (option == SQLSetStatement.Option.PASSWORD) {
              print0("FOR ");
          }
  
          printAndAccept(x.getItems(), ", ");
  
          if (x.getMaridbSetForStatement() != null) {
              print0(ucase ? " FOR " : " for ");
              x.getMaridbSetForStatement().accept(this);
          }
          if (x.getHints() != null && x.getHints().size() > 0) {
              print(' ');
              printAndAccept(x.getHints(), " ");
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlHintStatement x) {
          List<SQLCommentHint> hints = x.getHints();
  
          for (SQLCommentHint hint : hints) {
              hint.accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlOrderingExpr x) {
          x.getExpr().accept(this);
          if (x.getType() != null) {
              print(' ');
              print0(ucase ? x.getType().name : x.getType().nameLCase);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(SQLBlockStatement x) {
          SQLObject parent = x.getParent();
          String labelName = x.getLabelName();
  
          if (labelName != null && !labelName.equals("")) {
              print0(labelName);
              print0(": ");
          }
  
          List<SQLParameter> parameters = x.getParameters();
          if (!parameters.isEmpty()) {
              this.indentCount++;
              if (parent instanceof SQLCreateProcedureStatement) {
                  printIndent();
              }
              if (!(parent instanceof SQLCreateProcedureStatement)) {
                  print0(ucase ? "DECLARE" : "declare");
                  println();
              }
  
              for (int i = 0, size = parameters.size(); i < size; ++i) {
                  if (i != 0) {
                      println();
                  }
                  SQLParameter param = parameters.get(i);
                  visit(param);
                  print(';');
              }
  
              this.indentCount--;
              println();
          }
  
          print0(ucase ? "BEGIN" : "begin");
          if (!x.isEndOfCommit()) {
              this.indentCount++;
          } else {
              print(';');
          }
          println();
          List<SQLStatement> statementList = x.getStatementList();
          for (int i = 0, size = statementList.size(); i < size; ++i) {
              if (i != 0) {
                  println();
              }
              SQLStatement stmt = statementList.get(i);
              stmt.accept(this);
          }
  
          if (!x.isEndOfCommit()) {
              this.indentCount--;
              println();
              print0(ucase ? "END" : "end");
              if (labelName != null && !labelName.equals("")) {
                  print(' ');
                  print0(labelName);
              }
          }
  
          return false;
      }
  
      @Override
      public boolean visit(SQLIfStatement x) {
          print0(ucase ? "IF " : "if ");
          x.getCondition().accept(this);
          print0(ucase ? " THEN" : " then");
          this.indentCount++;
          println();
          List<SQLStatement> statements = x.getStatements();
          for (int i = 0, size = statements.size(); i < size; ++i) {
              SQLStatement item = statements.get(i);
              item.accept(this);
              if (i != size - 1) {
                  println();
              }
          }
          this.indentCount--;
          println();
          for (SQLIfStatement.ElseIf iterable_element : x.getElseIfList()) {
              iterable_element.accept(this);
          }
  
          if (x.getElseItem() != null) {
              x.getElseItem().accept(this);
          }
  
          print0(ucase ? "END IF" : "end if");
          return false;
      }
  
      @Override
      public boolean visit(SQLIfStatement.ElseIf x) {
          print0(ucase ? "ELSEIF " : "elseif ");
          x.getCondition().accept(this);
          print0(ucase ? " THEN" : " then");
          this.indentCount++;
          println();
          for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
              SQLStatement item = x.getStatements().get(i);
              item.accept(this);
              if (i != size - 1) {
                  println();
              }
          }
          this.indentCount--;
          println();
          return false;
      }
  
      @Override
      public boolean visit(SQLIfStatement.Else x) {
          print0(ucase ? "ELSE " : "else ");
          this.indentCount++;
          println();
          for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
              SQLStatement item = x.getStatements().get(i);
              item.accept(this);
              if (i != size - 1) {
                  println();
              }
          }
          this.indentCount--;
          println();
          return false;
      }
  
      @Override
      public boolean visit(MySqlCaseStatement x) {
          print0(ucase ? "CASE " : "case ");
          if (x.getCondition() != null) {
              x.getCondition().accept(this);
          }
          println();
          for (int i = 0; i < x.getWhenList().size(); i++) {
              x.getWhenList().get(i).accept(this);
          }
          if (x.getElseItem() != null) {
              x.getElseItem().accept(this);
          }
          print0(ucase ? "END CASE" : "end case");
          return false;
      }
  
      @Override
      public boolean visit(MySqlDeclareStatement x) {
          print0(ucase ? "DECLARE " : "declare ");
          printAndAccept(x.getVarList(), ", ");
          return false;
      }
  
      @Override
      public boolean visit(MySqlSelectIntoStatement x) {
          x.getSelect().accept(this);
          print0(ucase ? " INTO " : " into ");
          for (int i = 0; i < x.getVarList().size(); i++) {
              x.getVarList().get(i).accept(this);
              if (i != x.getVarList().size() - 1) {
                  print0(", ");
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlWhenStatement x) {
          print0(ucase ? "WHEN " : "when ");
          x.getCondition().accept(this);
          print0(" THEN");
          println();
          for (int i = 0; i < x.getStatements().size(); i++) {
              x.getStatements().get(i).accept(this);
              if (i != x.getStatements().size() - 1) {
                  println();
              }
          }
          println();
          return false;
      }
  
      @Override
      public boolean visit(SQLLoopStatement x) {
          if (x.getLabelName() != null && !x.getLabelName().equals("")) {
              print0(x.getLabelName());
              print0(": ");
          }
  
          print0(ucase ? "LOOP " : "loop ");
          this.indentCount++;
          println();
          for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
              SQLStatement item = x.getStatements().get(i);
              item.accept(this);
              if (i != size - 1) {
                  println();
              }
          }
          this.indentCount--;
          println();
          print0(ucase ? "END LOOP" : "end loop");
          if (x.getLabelName() != null && !x.getLabelName().equals("")) {
              print0(" ");
              print0(x.getLabelName());
          }
          return false;
      }
  
      @Override
      public boolean visit(SQLWhileStatement x) {
          String label = x.getLabelName();
  
          if (label != null && !label.isEmpty()) {
              print0(x.getLabelName());
              print0(": ");
          }
          print0(ucase ? "WHILE " : "while ");
          x.getCondition().accept(this);
          print0(ucase ? " DO" : " do");
          println();
          for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
              SQLStatement item = x.getStatements().get(i);
              item.accept(this);
              if (i != size - 1) {
                  println();
              }
          }
          println();
          print0(ucase ? "END WHILE" : "end while");
          if (label != null && !label.isEmpty()) {
              print(' ');
              print0(label);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlLeaveStatement x) {
          print0(ucase ? "LEAVE " : "leave ");
          print0(x.getLabelName());
          return false;
      }
  
      @Override
      public boolean visit(MySqlIterateStatement x) {
          print0(ucase ? "ITERATE " : "iterate ");
          print0(x.getLabelName());
          return false;
      }
  
      @Override
      public boolean visit(MySqlRepeatStatement x) {
          // TODO Auto-generated method stub
          if (x.getLabelName() != null && !x.getLabelName().equals("")) {
              print0(x.getLabelName());
              print0(": ");
          }
  
          print0(ucase ? "REPEAT " : "repeat ");
          this.indentCount++;
          println();
          for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
              SQLStatement item = x.getStatements().get(i);
              item.accept(this);
              if (i != size - 1) {
                  println();
              }
          }
          this.indentCount--;
          println();
          print0(ucase ? "UNTIL " : "until ");
          x.getCondition().accept(this);
          println();
          print0(ucase ? "END REPEAT" : "end repeat");
          if (x.getLabelName() != null && !x.getLabelName().equals("")) {
              print(' ');
              print0(x.getLabelName());
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlCursorDeclareStatement x) {
          print0(ucase ? "DECLARE " : "declare ");
          printExpr(x.getCursorName(), parameterized);
          print0(ucase ? " CURSOR FOR" : " cursor for");
          this.indentCount++;
          println();
          x.getSelect().accept(this);
          this.indentCount--;
          return false;
      }
  
      @Override
      public boolean visit(MySqlUpdateTableSource x) {
          MySqlUpdateStatement update = x.getUpdate();
          if (update != null) {
              update.accept0(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableAlterColumn x) {
          print0(ucase ? "ALTER COLUMN " : "alter column ");
          x.getColumn().accept(this);
          if (x.getDefaultExpr() != null) {
              print0(ucase ? " SET DEFAULT " : " set default ");
              x.getDefaultExpr().accept(this);
          } else if (x.isDropDefault()) {
              print0(ucase ? " DROP DEFAULT" : " drop default");
          } else if (x.getVisibleType() != null) {
              print0(ucase ? " SET " : " set ");
              print0(ucase ? x.getVisibleType().toUpperCase() : x.getVisibleType().toLowerCase());
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableForce x) {
          print0(ucase ? "FORCE" : "force");
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableLock x) {
          print0(ucase ? "LOCK = " : "lock = ");
          printExpr(x.getLockType());
          return false;
      }
  
      public boolean visit(MySqlAlterTableAlgorithm x) {
          print0(ucase ? "ALGORITHM = " : "algorithm = ");
          printExpr(x.getAlgorithmType());
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableOrderBy x) {
          print0(ucase ? "ORDER BY " : "order by ");
          printAndAccept(x.getColumns(), ", ");
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTableValidation x) {
          if (x.isWithValidation()) {
              print0(ucase ? "WITH VALIDATION" : "with validation");
          } else {
              print0(ucase ? "WITHOUT VALIDATION" : "without validation");
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlSubPartitionByKey x) {
          if (x.isLinear()) {
              print0(ucase ? "SUBPARTITION BY LINEAR KEY (" : "subpartition by linear key (");
          } else {
              print0(ucase ? "SUBPARTITION BY KEY (" : "subpartition by key (");
          }
          printAndAccept(x.getColumns(), ", ");
          print(')');
  
          if (x.getSubPartitionsCount() != null) {
              print0(ucase ? " SUBPARTITIONS " : " subpartitions ");
              x.getSubPartitionsCount().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlSubPartitionByValue x) {
          print0(ucase ? "SUBPARTITION BY VALUE (" : "subpartition by value (");
          printAndAccept(x.getColumns(), ", ");
          print(')');
  
          if (x.getLifecycle() != null) {
              print0(ucase ? " LIFECYCLE " : " lifecycle ");
              x.getLifecycle().accept(this);
          }
  
          if (x.getSubPartitionsCount() != null) {
              if ((Boolean) x.getAttribute("adb.partitons")) {
                  print0(ucase ? " PARTITIONS " : " partitions ");
              } else {
                  print0(ucase ? " SUBPARTITIONS " : " subpartitions ");
              }
              x.getSubPartitionsCount().accept(this);
          }
          return false;
      }
  
      @Override
      public boolean visit(SQLAlterTablePartitionCount x) {
          print0(ucase ? "PARTITIONS " : "partitons ");
          x.getCount().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(SQLAlterTableBlockSize x) {
          print0(ucase ? "BLOCK_SIZE " : "block_size ");
          x.getSize().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(SQLAlterTableCompression x) {
          print0(ucase ? "COMPRESSION = " : "compression = ");
          x.getName().accept(this);
          return false;
      }
  
      @Override
      public boolean visit(MySqlSubPartitionByList x) {
          print0(ucase ? "SUBPARTITION BY LIST " : "subpartition by list ");
          if (x.getKeys().size() > 0) {
              if (Boolean.TRUE.equals(x.getAttribute("ads.subPartitionList"))) {
                  print0(ucase ? "KEY (" : "key (");
              } else {
                  print('(');
              }
  
              printAndAccept(x.getKeys(), ",");
              print0(") ");
          } else {
              if (x.getColumns().size() == 1 && Boolean.TRUE.equals(x.getAttribute("ads.subPartitionList"))) {
                  print('(');
              } else {
                  print0(ucase ? "COLUMNS (" : "columns (");
              }
              printAndAccept(x.getColumns(), ", ");
              print(")");
          }
  
          if (!x.getOptions().isEmpty()) {
              println();
              print0(ucase ? "SUBPARTITION OPTIONS (" : "subpartition options (");
              printAndAccept(x.getOptions(), ", ");
              print(')');
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlDeclareHandlerStatement x) {
          String handleType = x.getHandleType().name();
  
          print0(ucase ? "DECLARE " : "declare ");
          print0(ucase ? handleType : handleType.toLowerCase());
          print0(ucase ? " HANDLER FOR " : " handler for ");
          for (int i = 0; i < x.getConditionValues().size(); i++) {
              ConditionValue cv = x.getConditionValues().get(i);
              if (cv.getType() == ConditionType.SQLSTATE) {
                  print0(ucase ? " SQLSTATE " : " sqlstate ");
                  print0(cv.getValue());
              } else if (cv.getType() == ConditionType.MYSQL_ERROR_CODE) {
                  print0(cv.getValue());
              } else if (cv.getType() == ConditionType.SELF) {
                  print0(cv.getValue());
              } else if (cv.getType() == ConditionType.SYSTEM) {
                  print0(ucase ? cv.getValue().toUpperCase() : cv.getValue().toLowerCase());
              }
  
              if (i != x.getConditionValues().size() - 1) {
                  print0(", ");
              }
  
          }
          this.indentCount++;
          println();
          x.getSpStatement().accept(this);
          this.indentCount--;
          return false;
      }
  
      @Override
      public boolean visit(MySqlDeclareConditionStatement x) {
          print0(ucase ? "DECLARE " : "declare ");
          print0(x.getConditionName());
          print0(ucase ? " CONDITION FOR " : " condition for ");
  
          if (x.getConditionValue().getType() == ConditionType.SQLSTATE) {
              print0(ucase ? "SQLSTATE " : "sqlstate ");
              print0(x.getConditionValue().getValue());
          } else {
              print0(x.getConditionValue().getValue());
          }
  
          println();
          return false;
      }
  
      @Override
      public boolean visit(SQLAlterTableDropColumnItem x) {
          for (int i = 0; i < x.getColumns().size(); ++i) {
              if (i != 0) {
                  print0(", ");
              }
  
              SQLName columnn = x.getColumns().get(i);
  
              print0(ucase ? "DROP COLUMN " : "drop column ");
              columnn.accept(this);
  
              if (x.isCascade()) {
                  print0(ucase ? " CASCADE" : " cascade");
              }
          }
          return false;
      }
  
      /**
       * visit procedure create node
       */
      @Override
      public boolean visit(SQLCreateProcedureStatement x) {
          if (x.isOrReplace()) {
              print0(ucase ? "CREATE OR REPLACE PROCEDURE " : "create or replace procedure ");
          } else {
              print0(ucase ? "CREATE PROCEDURE " : "create procedure ");
          }
          x.getName().accept(this);
  
          int paramSize = x.getParameters().size();
  
          print0(" (");
          if (paramSize > 0) {
              this.indentCount++;
              println();
  
              for (int i = 0; i < paramSize; ++i) {
                  if (i != 0) {
                      print0(", ");
                      println();
                  }
                  SQLParameter param = x.getParameters().get(i);
                  param.accept(this);
              }
  
              this.indentCount--;
              println();
          }
          print(')');
  
          if (x.getComment() != null) {
              println();
              print(ucase ? "COMMENT " : "comment ");
              x.getComment().accept(this);
          }
          if (x.isDeterministic()) {
              println();
              print(ucase ? "DETERMINISTIC" : "deterministic");
          }
  
          if (x.isContainsSql()) {
              println();
              print0(ucase ? "CONTAINS SQL" : "contains sql");
          }
  
          if (x.isLanguageSql()) {
              println();
              print0(ucase ? "LANGUAGE SQL" : "language sql");
          }
  
          if (x.isNoSql()) {
              println();
              print(ucase ? "NO SQL" : "no sql");
          }
  
          if (x.isModifiesSqlData()) {
              println();
              print(ucase ? "MODIFIES SQL DATA" : "modifies sql data");
          }
  
          SQLName authid = x.getAuthid();
          if (authid != null) {
              println();
              print(ucase ? "SQL SECURITY " : "sql security ");
              authid.accept(this);
          }
  
          println();
          x.getBlock().accept(this);
          return false;
      }
  
      protected void printCreateFunctionBody(SQLCreateFunctionStatement x) {
          printCreateFunctionReturns(x);
  
          String comment = x.getComment();
          if (comment != null) {
              print(ucase ? " COMMENT " : " comment ");
              print(ucase ? comment.toUpperCase() : comment.toLowerCase());
          }
  
          if (x.isDeterministic()) {
              print(ucase ? " DETERMINISTIC" : " deterministic");
          }
  
          String language = x.getLanguage();
          if (language != null) {
              print(ucase ? " LANGUAGE " : " language ");
              print(ucase ? language.toUpperCase() : language.toLowerCase());
          }
  
          SQLStatement block = x.getBlock();
  
          println();
  
          block.accept(this);
      }
  
      protected void printCreateFunctionReturns(SQLCreateFunctionStatement x) {
          println();
          print(ucase ? "RETURNS " : "returns ");
          x.getReturnDataType().accept(this);
      }
  
      @Override
      public boolean visit(SQLCommentStatement x) {
          SQLCommentStatement.Type type = x.getType();
  
          SQLExprTableSource on = x.getOn();
          if (type == SQLCommentStatement.Type.TABLE) {
              print0(ucase ? "ALTER TABLE " : "alter table ");
              on.accept(this);
              print0(ucase ? " COMMENT = " : " comment = ");
              x.getComment().accept(this);
          } else {
              SQLPropertyExpr propertyExpr = (SQLPropertyExpr) on.getExpr();
  
              SQLExpr table = propertyExpr.getOwner();
              String column = propertyExpr.getName();
  
              print0(ucase ? "ALTER TABLE " : "alter table ");
              printTableSourceExpr(table);
              print0(ucase ? " MODIFY COLUMN " : " modify column ");
              print(column);
              print0(ucase ? " COMMENT " : " comment ");
              x.getComment().accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlFlushStatement x) {
          print0(ucase ? "FLUSH" : "flush");
  
          if (x.isNoWriteToBinlog()) {
              print0(ucase ? " NO_WRITE_TO_BINLOG" : " no_write_to_binlog");
          } else if (x.isLocal()) {
              print0(ucase ? " LOCAL" : " local");
          }
  
          if (x.isBinaryLogs()) {
              print0(ucase ? " BINARY LOGS" : " binary logs");
          }
          if (x.isDesKeyFile()) {
              print0(ucase ? " DES_KEY_FILE" : " des_key_file");
          }
          if (x.isEngineLogs()) {
              print0(ucase ? " ENGINE LOGS" : " engine logs");
          }
          if (x.isErrorLogs()) {
              print0(ucase ? " ERROR LOGS" : " error logs");
          }
          if (x.isGeneralLogs()) {
              print0(ucase ? " GENERAL LOGS" : " general logs");
          }
          if (x.isHots()) {
              print0(ucase ? " HOSTS" : " hosts");
          }
          if (x.isLogs()) {
              print0(ucase ? " LOGS" : " logs");
          }
          if (x.isPrivileges()) {
              print0(ucase ? " PRIVILEGES" : " privileges");
          }
          if (x.isOptimizerCosts()) {
              print0(ucase ? " OPTIMIZER_COSTS" : " optimizer_costs");
          }
          if (x.isQueryCache()) {
              print0(ucase ? " QUERY CACHE" : " query cache");
          }
          if (x.isRelayLogs()) {
              print0(ucase ? " RELAY LOGS" : " relay logs");
              SQLExpr channel = x.getRelayLogsForChannel();
              if (channel != null) {
                  print(' ');
                  channel.accept(this);
              }
          }
          if (x.isSlowLogs()) {
              print0(ucase ? " SLOW LOGS" : " slow logs");
          }
          if (x.isStatus()) {
              print0(ucase ? " STATUS" : " status");
          }
          if (x.isUserResources()) {
              print0(ucase ? " USER_RESOURCES" : " user_resources");
          }
  
          if (x.isTableOption()) {
              print0(ucase ? " TABLES" : " tables");
  
              List<SQLExprTableSource> tables = x.getTables();
              if (tables != null && tables.size() > 0) {
                  print(' ');
                  printAndAccept(tables, ", ");
              }
  
              if (x.isWithReadLock()) {
                  print0(ucase ? " WITH READ LOCK" : " with read lock");
              }
  
              if (x.isForExport()) {
                  print0(ucase ? " FOR EXPORT" : " for export");
              }
  
              if (x.getVersion() != null) {
                  print0(ucase ? " VERSION = " : " version = ");
                  x.getVersion().accept(this);
              }
          }
          return false;
      }
  
      @Override
      public boolean visit(MySqlEventSchedule x) {
          int cnt = 0;
          if (x.getAt() != null) {
              print0(ucase ? "AT " : "at ");
              printExpr(x.getAt(), parameterized);
  
              cnt++;
          }
  
          if (x.getEvery() != null) {
              print0(ucase ? "EVERY " : "every ");
              SQLIntervalExpr interval = (SQLIntervalExpr) x.getEvery();
              printExpr(interval.getValue(), parameterized);
              print(' ');
              print(interval.getUnit().name());
  
              cnt++;
          }
  
          if (x.getStarts() != null) {
              if (cnt > 0) {
                  print(' ');
              }
  
              print0(ucase ? "STARTS " : "starts ");
              printExpr(x.getStarts(), parameterized);
  
              cnt++;
          }
  
          if (x.getEnds() != null) {
              if (cnt > 0) {
                  print(' ');
              }
              print0(ucase ? "ENDS " : "ends ");
              printExpr(x.getEnds(), parameterized);
  
              cnt++;
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlCreateEventStatement x) {
          print0(ucase ? "CREATE " : "create ");
  
          SQLName definer = x.getDefiner();
          if (definer != null) {
              print0(ucase ? "DEFINER = " : "definer = ");
              definer.accept(this);
              print(' ');
          }
  
          print0(ucase ? "EVENT " : "event ");
  
          if (x.isIfNotExists()) {
              print0(ucase ? "IF NOT EXISTS " : "if not exists ");
          }
  
          printExpr(x.getName(), parameterized);
  
          MySqlEventSchedule schedule = x.getSchedule();
          print0(ucase ? " ON SCHEDULE " : " on schedule ");
          schedule.accept(this);
  
          Boolean enable = x.getEnable();
          if (enable != null) {
              if (enable) {
                  print0(ucase ? " ENABLE" : " enable");
              } else {
                  print0(ucase ? " DISABLE" : " disable");
                  if (x.isDisableOnSlave()) {
                      print0(ucase ? " ON SLAVE" : " on slave");
                  }
              }
          }
  
          SQLExpr comment = x.getComment();
          if (comment != null) {
              print0(ucase ? " COMMENT " : " comment ");
              comment.accept(this);
          }
  
          println();
          SQLStatement body = x.getEventBody();
          if (!(body instanceof SQLExprStatement)) {
              print0(ucase ? "DO" : "do");
              println();
          }
          body.accept(this);
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlCreateAddLogFileGroupStatement x) {
          print0(ucase ? "CREATE LOGFILE GROUP " : "create logfile group ");
          x.getName().accept(this);
          print(' ');
          print0(ucase ? "ADD UNDOFILE " : "add undofile ");
          printExpr(x.getAddUndoFile(), false);
  
          SQLExpr initialSize = x.getInitialSize();
          if (initialSize != null) {
              print0(ucase ? " INITIAL_SIZE " : " initial_size ");
              printExpr(initialSize, false);
          }
  
          SQLExpr undoBufferSize = x.getUndoBufferSize();
          if (undoBufferSize != null) {
              print0(ucase ? " UNDO_BUFFER_SIZE " : " undo_buffer_size ");
              printExpr(undoBufferSize, false);
          }
  
          SQLExpr redoBufferSize = x.getRedoBufferSize();
          if (redoBufferSize != null) {
              print0(ucase ? " REDO_BUFFER_SIZE " : " redo_buffer_size ");
              printExpr(redoBufferSize, false);
          }
  
          SQLExpr nodeGroup = x.getNodeGroup();
          if (nodeGroup != null) {
              print0(ucase ? " NODEGROUP " : " nodegroup ");
              printExpr(nodeGroup, false);
          }
  
          if (x.isWait()) {
              print0(ucase ? " WAIT" : " wait");
          }
  
          SQLExpr comment = x.getComment();
          if (comment != null) {
              print0(ucase ? " COMMENT " : " comment ");
              printExpr(comment, parameterized);
          }
  
          SQLExpr engine = x.getEngine();
          if (engine != null) {
              print0(ucase ? " ENGINE " : " engine ");
              printExpr(engine, parameterized);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlCreateServerStatement x) {
          print0(ucase ? "CREATE SERVER " : "create server ");
          x.getName().accept(this);
          print0(ucase ? " FOREIGN DATA WRAPPER " : " foreign data wrapper ");
          printExpr(x.getForeignDataWrapper(), parameterized);
  
          print(" OPTIONS(");
          int cnt = 0;
          SQLExpr host = x.getHost();
          if (host != null) {
              print0(ucase ? "HOST " : "host ");
              printExpr(host, parameterized);
              cnt++;
          }
  
          SQLExpr database = x.getDatabase();
          if (database != null) {
              if (cnt++ > 0) {
                  print(", ");
              }
              print0(ucase ? "DATABASE " : "database ");
              printExpr(database, parameterized);
          }
  
          SQLExpr user = x.getUser();
          if (user != null) {
              if (cnt++ > 0) {
                  print(", ");
              }
              print0(ucase ? "USER " : "user ");
              printExpr(user);
          }
  
          SQLExpr password = x.getPassword();
          if (password != null) {
              if (cnt++ > 0) {
                  print(", ");
              }
              print0(ucase ? "PASSWORD " : "password ");
              printExpr(password);
          }
  
          SQLExpr socket = x.getSocket();
          if (socket != null) {
              if (cnt++ > 0) {
                  print(", ");
              }
              print0(ucase ? "SOCKET " : "socket ");
              printExpr(socket);
          }
  
          SQLExpr owner = x.getOwner();
          if (owner != null) {
              if (cnt++ > 0) {
                  print(", ");
              }
              print0(ucase ? "OWNER " : "owner ");
              printExpr(owner);
          }
  
          SQLExpr port = x.getPort();
          if (port != null) {
              if (cnt++ > 0) {
                  print(", ");
              }
              print0(ucase ? "PORT " : "port ");
              printExpr(port);
          }
          print(')');
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlCreateTableSpaceStatement x) {
          print0(ucase ? "CREATE TABLESPACE " : "create tablespace ");
          x.getName().accept(this);
  
          SQLExpr addDataFile = x.getAddDataFile();
          if (addDataFile != null) {
              print0(ucase ? " ADD DATAFILE " : " add datafile ");
              addDataFile.accept(this);
          }
  
          SQLExpr fileBlockSize = x.getFileBlockSize();
          if (fileBlockSize != null) {
              print0(ucase ? " FILE_BLOCK_SIZE = " : " file_block_size = ");
              fileBlockSize.accept(this);
          }
  
          SQLExpr logFileGroup = x.getLogFileGroup();
          if (logFileGroup != null) {
              print0(ucase ? " USE LOGFILE GROUP " : " use logfile group ");
              logFileGroup.accept(this);
          }
  
          SQLExpr extentSize = x.getExtentSize();
          if (extentSize != null) {
              print0(ucase ? " EXTENT_SIZE = " : " extent_size = ");
              extentSize.accept(this);
          }
  
          SQLExpr initialSize = x.getInitialSize();
          if (initialSize != null) {
              print0(ucase ? " INITIAL_SIZE = " : " initial_size = ");
              initialSize.accept(this);
          }
  
          SQLExpr autoExtentSize = x.getAutoExtentSize();
          if (autoExtentSize != null) {
              print0(ucase ? " AUTOEXTEND_SIZE = " : " autoextend_size = ");
              autoExtentSize.accept(this);
          }
  
          SQLExpr maxSize = x.getMaxSize();
          if (autoExtentSize != null) {
              print0(ucase ? " MAX_SIZE = " : " max_size = ");
              maxSize.accept(this);
          }
  
          SQLExpr nodeGroup = x.getNodeGroup();
          if (nodeGroup != null) {
              print0(ucase ? " NODEGROUP = " : " nodegroup = ");
              nodeGroup.accept(this);
          }
  
          if (x.isWait()) {
              print0(ucase ? " WAIT" : " wait");
          }
  
          SQLExpr comment = x.getComment();
          if (comment != null) {
              print0(ucase ? " COMMENT " : " comment ");
              printExpr(comment);
          }
  
          SQLExpr engine = x.getEngine();
          if (engine != null) {
              print0(ucase ? " ENGINE " : " engine ");
              printExpr(engine);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterEventStatement x) {
          print0(ucase ? "ALTER " : "alter ");
  
          SQLName definer = x.getDefiner();
          if (definer != null) {
              print0(ucase ? "DEFINER = " : "definer = ");
              definer.accept(this);
              print(' ');
          }
  
          print0(ucase ? "EVENT " : "event ");
          printExpr(x.getName());
  
          MySqlEventSchedule schedule = x.getSchedule();
          if (schedule != null) {
              print0(ucase ? " ON SCHEDULE " : " on schedule ");
              schedule.accept(this);
          }
  
          Boolean enable = x.getEnable();
          if (enable != null) {
              if (enable) {
                  print0(ucase ? " ENABLE" : " enable");
              } else {
                  print0(ucase ? " DISABLE" : " disable");
                  if (x.isDisableOnSlave()) {
                      print0(ucase ? " ON SLAVE" : " on slave");
                  }
              }
          }
  
          SQLExpr comment = x.getComment();
          if (comment != null) {
              print0(ucase ? " COMMENT " : " comment ");
              comment.accept(this);
          }
  
          SQLStatement body = x.getEventBody();
          if (body != null) {
              println();
              if (!(body instanceof SQLExprStatement)) {
                  print0(ucase ? "DO" : "do");
                  println();
              }
              body.accept(this);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterLogFileGroupStatement x) {
          print0(ucase ? "ALTER LOGFILE GROUP " : "alter logfile group ");
          x.getName().accept(this);
          print(' ');
          print0(ucase ? "ADD UNDOFILE " : "add undofile ");
          printExpr(x.getAddUndoFile());
  
          SQLExpr initialSize = x.getInitialSize();
          if (initialSize != null) {
              print0(ucase ? " INITIAL_SIZE " : " initial_size ");
              printExpr(initialSize);
          }
  
          if (x.isWait()) {
              print0(ucase ? " WAIT" : " wait");
          }
  
          SQLExpr engine = x.getEngine();
          if (engine != null) {
              print0(ucase ? " ENGINE " : " engine ");
              printExpr(engine);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterServerStatement x) {
          print0(ucase ? "ALTER SERVER " : "alter server ");
          x.getName().accept(this);
  
          print(" OPTIONS(");
          SQLExpr user = x.getUser();
          if (user != null) {
              print0(ucase ? "USER " : "user ");
              printExpr(user);
          }
  
          print(')');
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlAlterTablespaceStatement x) {
          print0(ucase ? "CREATE TABLESPACE " : "create tablespace ");
          x.getName().accept(this);
  
          SQLExpr addDataFile = x.getAddDataFile();
          if (addDataFile != null) {
              print0(ucase ? " ADD DATAFILE " : " add datafile ");
              addDataFile.accept(this);
          }
  
          SQLExpr initialSize = x.getInitialSize();
          if (initialSize != null) {
              print0(ucase ? " INITIAL_SIZE = " : " initial_size = ");
              initialSize.accept(this);
          }
  
          if (x.isWait()) {
              print0(ucase ? " WAIT" : " wait");
          }
  
          SQLExpr engine = x.getEngine();
          if (engine != null) {
              print0(ucase ? " ENGINE " : " engine ");
              printExpr(engine);
          }
  
          return false;
      }
  
      @Override
      public boolean visit(MySqlChecksumTableStatement x) {
          print0(ucase ? "CHECKSUM TABLE " : "checksum table ");
          final List<SQLExprTableSource> tables = x.getTables();
          for (int i = 0; i < tables.size(); i++) {
              if (i != 0) {
                  print0(", ");
              }
              tables.get(i)
                      .accept(this);
          }
          return false;
      }
  
      protected void printQuery(SQLSelectQuery x) {
          Class<?> clazz = x.getClass();
          if (clazz == MySqlSelectQueryBlock.class) {
              visit((MySqlSelectQueryBlock) x);
          } else if (clazz == SQLSelectQueryBlock.class) {
              visit((SQLSelectQueryBlock) x);
          } else if (clazz == SQLUnionQuery.class) {
              visit((SQLUnionQuery) x);
          } else {
              x.accept(this);
          }
      }
  
      public void printInsertColumns(List<SQLExpr> columns) {
          final int size = columns.size();
          if (size > 0) {
              if (size > 5) {
                  this.indentCount++;
                  print(' ');
              }
              print('(');
              for (int i = 0; i < size; ++i) {
                  if (i != 0) {
                      if (i % 5 == 0) {
                          println();
                      }
                      print0(", ");
                  }
  
                  SQLExpr column = columns.get(i);
                  if (column instanceof SQLIdentifierExpr) {
                      visit((SQLIdentifierExpr) column);
                  } else {
                      printExpr(column, parameterized);
                  }
  
                  String dataType = (String) column.getAttribute("dataType");
                  if (dataType != null) {
                      print(' ');
                      print(dataType);
                  }
              }
              print(')');
              if (size > 5) {
                  this.indentCount--;
              }
          }
      }
  
      @Override
      public boolean visit(SQLValuesTableSource x) {
          print('(');
          incrementIndent();
          println();
          print0(ucase ? "VALUES " : "values ");
          List<SQLListExpr> values = x.getValues();
          for (int i = 0; i < values.size(); ++i) {
              if (i != 0) {
                  print(",");
                  println();
              }
              SQLListExpr list = values.get(i);
              visit(list);
          }
          decrementIndent();
          println();
          print0(")");
          if (x.getAlias() != null) {
              print0(" AS ");
              print0(x.getAlias());
  
              if (x.getColumns().size() > 0) {
                  print0(" (");
                  printAndAccept(x.getColumns(), ", ");
                  print(')');
              }
          }
  
          return false;
      }
  
      public boolean visit(SQLExternalRecordFormat x) {
          return hiveVisit(x);
      }
  
      public boolean visit(MySqlJSONTableExpr x) {
          print0(ucase ? "JSON_TABLE(" : "json_table(");
          x.getExpr().accept(this);
          print(',');
          x.getPath().accept(this);
          incrementIndent();
          println();
          print0(ucase ? "COLUMNS (" : "columns (");
          incrementIndent();
          println();
          printlnAndAccept(x.getColumns(), ",");
          decrementIndent();
          println();
          print(')');
          decrementIndent();
          println();
          print(')');
  
          return false;
      }
  
      public boolean visit(TidbSplitTableStatement x) {
          print0(ucase ? "SPLIT " : "split ");
          if (x.isSplitSyntaxOptionRegionFor()) {
              print0(ucase ? "REGION FOR " : "region for ");
          }
          if (x.isSplitSyntaxOptionPartition()) {
              print0(ucase ? "PARTITION " : "partition ");
          }
          print0(ucase ? "TABLE " : "table ");
          x.getTableName().accept(this);
          print(' ');
  
          if (!x.getPartitionNameListOptions().isEmpty()) {
              print0(ucase ? "PARTITION (" : "partition (");
              printAndAccept(x.getPartitionNameListOptions(), ",");
              print(") ");
          }
          if (x.getIndexName() != null) {
              print0(ucase ? "INDEX " : "index ");
              x.getIndexName().accept(this);
              print(' ');
          }
          if (!x.getSplitOptionBys().isEmpty()) {
              print0(ucase ? "BY " : "by ");
              boolean needCommon = false;
              for (List<SQLExpr> list : x.getSplitOptionBys()) {
                  if (!needCommon) {
                      needCommon = true;
                  } else {
                      print0(", ");
                  }
                  print0("(");
                  printlnAndAccept(list, ", ");
                  print0(")");
              }
          }
          if (x.getSplitOptionBetween() != null) {
              print0(ucase ? "BETWEEN (" : " between (");
              printAndAccept(x.getSplitOptionBetween(), ", ");
              print0(ucase ? ") AND (" : ") and (");
              printAndAccept(x.getSplitOptionAnd(), ", ");
              print0(") ");
              print0(ucase ? "REGIONS " : "regions ");
              print(x.getSplitOptionRegions());
          }
  
          return false;
      }
  
      public boolean visit(MySqlJSONTableExpr.Column x) {
          x.getName().accept(this);
  
          if (x.isOrdinality()) {
              print0(ucase ? " FOR ORDINALITY" : " for ordinality");
          }
  
          SQLDataType dataType = x.getDataType();
          if (dataType != null) {
              print(' ');
              dataType.accept(this);
          }
  
          if (x.isExists()) {
              print0(ucase ? " EXISTS" : " exists");
          }
  
          SQLExpr path = x.getPath();
          if (path != null) {
              print0(ucase ? " PATH " : " path ");
              path.accept(this);
          }
  
          List<MySqlJSONTableExpr.Column> nestedColumns = x.getNestedColumns();
          if (nestedColumns.size() > 0) {
              print0(ucase ? " COLUMNS (" : " columns (");
              printAndAccept(nestedColumns, ", ");
              print(')');
          }
  
          SQLExpr onEmpty = x.getOnEmpty();
          if (onEmpty != null) {
              print(' ');
              if (!(onEmpty instanceof SQLNullExpr || onEmpty instanceof SQLIdentifierExpr)) {
                  print0(ucase ? "DEFAULT " : "default ");
              }
              onEmpty.accept(this);
              print0(ucase ? " ON EMPTY" : " on empty");
          }
  
          SQLExpr onError = x.getOnError();
          if (onError != null) {
              print(' ');
              if (!(onEmpty instanceof SQLNullExpr || onEmpty instanceof SQLIdentifierExpr)) {
                  print0(ucase ? "DEFAULT " : "default ");
              }
              onError.accept(this);
              print0(ucase ? " ON ERROR" : " on error");
          }
          return false;
      }
  
  //    public boolean visit(SQLSelectItem x) {
  //        SQLExpr expr = x.getExpr();
  //
  //        if (expr instanceof SQLIdentifierExpr) {
  //            print0(((SQLIdentifierExpr) expr).getName());
  //        } else if (expr instanceof SQLPropertyExpr) {
  //            visit((SQLPropertyExpr) expr);
  //        } else {
  //            printExpr(expr);
  //        }
  //
  //        String alias = x.getAlias();
  //        if (alias != null && alias.length() > 0) {
  //            print0(ucase ? " AS " : " as ");
  //
  //            boolean hasSpecial = false;
  //            for (int i = 1; i < alias.length() - 1; ++i) {
  //                char ch = alias.charAt(i);
  //                if (ch == ' ' || ch == '\"' || ch == '\n') {
  //                    hasSpecial = true;
  //                }
  //            }
  //            char c0 = alias.charAt(0);
  //            if (!hasSpecial) {
  //                print0(alias);
  //            } else {
  //                print('"');
  //
  //                for (int i = 0; i < alias.length(); ++i) {
  //                    char ch = alias.charAt(i);
  //                    if (ch == '\"') {
  //                        print('\\');
  //                        print(ch);
  //                    } else if (ch == '\n') {
  //                        print0("\\n");
  //                    } else {
  //                        print(ch);
  //                    }
  //                }
  //
  //                print('"');
  //            }
  //        }
  //        return false;
  //    }
  
      public boolean visit(MysqlAlterTableAlterCheck x) {
          print0(ucase ? "ALTER CONSTRAINT " : "alter constraint ");
  
          SQLName name = x.getName();
          if (name != null) {
              name.accept(this);
              print(' ');
          }
  
          Boolean enforced = x.getEnforced();
          if (enforced != null) {
              if (enforced) {
                  print0(ucase ? " ENFORCED" : " enforced");
              } else {
                  print0(ucase ? " NOT ENFORCED" : " not enforced");
              }
          }
          return false;
      }
  
      public boolean visit(MySqlXAStatement x) {
          print0(ucase ? "XA " : "xa ");
  
          MySqlXAStatement.XAType type = x.getType();
          print0(ucase ? type.name() : type.name().toLowerCase());
          SQLExpr id = x.getId();
          if (id != null) {
              print(' ');
              printExpr(id);
          }
          return false;
      }
  
      @Override
      protected void printTableOptionsPrefix(SQLCreateTableStatement x) {
      }
  
      @Override
      protected void printTableOptionsPostfix(SQLCreateTableStatement x) {
      }
  
      @Override
      protected void printTableOption(SQLExpr name, SQLExpr value, int index) {
          println();
          name.accept(this);
          print0(" = ");
          value.accept(this);
      }
  
      @Override
      public boolean visit(SQLPartitionSingle x) {
          if (x instanceof MysqlPartitionSingle) {
              return visit((MysqlPartitionSingle) x);
          }
          return super.visit(x);
      }
  
      public boolean visit(MysqlPartitionSingle x) {
          boolean isDbPartiton = false, isTbPartition = false;
          final SQLObject parent = x.getParent();
          if (parent != null) {
              final SQLObject parent2 = parent.getParent();
              if (parent2 instanceof MySqlCreateTableStatement) {
                  MySqlCreateTableStatement stmt = (MySqlCreateTableStatement) parent2;
                  isDbPartiton = parent == stmt.getDbPartitionBy();
                  isTbPartition = parent == stmt.getTablePartitionBy();
              }
          }
  
          if (isDbPartiton) {
              print0(ucase ? "DBPARTITION " : "dbpartition ");
          } else if (isTbPartition) {
              print0(ucase ? "TBPARTITION " : "tbpartition ");
          } else {
              print0(ucase ? "PARTITION " : "partition ");
          }
          x.getName().accept(this);
          if (x.getValues() != null) {
              print(' ');
              x.getValues().accept(this);
          }
  
          if (x.getDataDirectory() != null) {
              this.indentCount++;
              println();
              print0(ucase ? "DATA DIRECTORY " : "data directory ");
              x.getDataDirectory().accept(this);
              this.indentCount--;
          }
  
          if (x.getIndexDirectory() != null) {
              this.indentCount++;
              println();
              print0(ucase ? "INDEX DIRECTORY " : "index directory ");
              x.getIndexDirectory().accept(this);
              this.indentCount--;
          }
  
          this.indentCount++;
          if (x.getTablespace() != null) {
              printTablespace(x.getTablespace());
          }
  
          if (x.getEngine() != null) {
              println();
              print0(ucase ? "STORAGE ENGINE " : "storage engine ");
              x.getEngine().accept(this);
          }
          this.indentCount--;
  
          if (x.getMaxRows() != null) {
              print0(ucase ? " MAX_ROWS " : " max_rows ");
              x.getMaxRows().accept(this);
          }
  
          if (x.getMinRows() != null) {
              print0(ucase ? " MIN_ROWS " : " min_rows ");
              x.getMinRows().accept(this);
          }
  
          if (x.getComment() != null) {
              print0(ucase ? " COMMENT " : " comment ");
              x.getComment().accept(this);
          }
  
          if (x.getSubPartitionsCount() != null) {
              this.indentCount++;
              println();
              print0(ucase ? "SUBPARTITIONS " : "subpartitions ");
              x.getSubPartitionsCount().accept(this);
              this.indentCount--;
          }
  
          if (x.getSubPartitions().size() > 0) {
              print(" (");
              this.indentCount++;
              for (int i = 0; i < x.getSubPartitions().size(); ++i) {
                  if (i != 0) {
                      print(',');
                  }
                  println();
                  x.getSubPartitions().get(i).accept(this);
              }
              this.indentCount--;
              println();
              print(')');
          }
  
          SQLExpr locality = x.getLocality();
          if (locality != null) {
              print(ucase ? " LOCALITY = " : " locality = ");
              locality.accept(this);
          }
  
          return false;
      }
  }
  