1 /*
2  * Copyright 2015-2018 HuntLabs.cn
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 module hunt.sql.visitor.SQLASTOutputVisitor;
17 
18 
19 import std.uni;
20 import hunt.sql.SQLUtils;
21 import hunt.sql.ast;
22 import hunt.sql.ast.expr;
23 import hunt.sql.ast.statement;
24 import hunt.sql.ast.statement.SQLCreateTriggerStatement;
25 // import hunt.sql.ast.statement.SQLCreateTriggerStatement.TriggerEvent;
26 // import hunt.sql.ast.statement.SQLCreateTriggerStatement.TriggerType;
27 // import hunt.sql.ast.statement.ValuesClause;
28 // import hunt.sql.ast.statement.SQLJoinTableSource.JoinType;
29 import hunt.sql.ast.statement.SQLInsertStatement;
30 import hunt.sql.ast.statement.SQLJoinTableSource;
31 // import hunt.sql.ast.statement.SQLMergeStatement.MergeInsertClause;
32 // import hunt.sql.ast.statement.SQLMergeStatement.MergeUpdateClause;
33 import hunt.sql.ast.statement.SQLMergeStatement;
34 import hunt.sql.ast.statement.SQLWhileStatement;
35 // import hunt.sql.dialect.oracle.ast.OracleSegmentAttributes;
36 import hunt.sql.ast.statement.SQLDeclareStatement;
37 // import hunt.sql.dialect.oracle.ast.expr.OracleCursorExpr;
38 // import hunt.sql.dialect.oracle.ast.expr.OracleDatetimeExpr;
39 // import hunt.sql.dialect.oracle.ast.stmt.OracleCreatePackageStatement;
40 // import hunt.sql.dialect.oracle.ast.stmt.OracleForStatement;
41 // import hunt.sql.dialect.oracle.ast.stmt.OracleSelectPivot;
42 // import hunt.sql.dialect.oracle.parser.OracleFunctionDataType;
43 // import hunt.sql.dialect.oracle.parser.OracleProcedureDataType;
44 import hunt.sql.util.DBType;
45 import hunt.sql.visitor.SQLASTVisitorAdapter;
46 import hunt.sql.visitor.ParameterizedVisitor;
47 import hunt.sql.visitor.PrintableVisitor;
48 import hunt.sql.visitor.VisitorFeature;
49 import hunt.sql.ast.expr.SQLCaseStatement;
50 import hunt.sql.visitor.ExportParameterVisitorUtils;
51 
52 import hunt.collection;
53 import hunt.Byte;
54 import hunt.Exceptions;
55 import hunt.logging;
56 import hunt.String;
57 import hunt.Boolean;
58 import hunt.Number;
59 import hunt.Integer;
60 import hunt.Float;
61 import hunt.math;
62 import hunt.util.Common;
63 import hunt.util.Appendable;
64 import hunt.text;
65 import hunt.collection.Collections;
66 
67 import std.array;
68 import std.concurrency : initOnce;
69 import std.conv;
70 import std.datetime;
71 import std.string;
72 
73 
74 
75 class SQLASTOutputVisitor : SQLASTVisitorAdapter , ParameterizedVisitor, PrintableVisitor {
76 
77     alias visit = SQLASTVisitorAdapter.visit;
78     alias endVisit = SQLASTVisitorAdapter.endVisit;
79 
80     static string[] variantValuesCache() {
81         __gshared string[] inst;
82         return initOnce!inst(initializeCache());
83     }
84 
85     private static string[] initializeCache() {
86         string[] v = new string[64];
87         for (int len = 0; len < v.length; ++len) {
88             StringBuilder buf = new StringBuilder();
89             buf.append('(');
90             for (int i = 0; i < len; ++i) {
91                 if (i != 0) {
92                     if (i % 5 == 0) {
93                         buf.append("\n\t\t");
94                     }
95                     buf.append(", ");
96                 }
97                 buf.append('?');
98             }
99             buf.append(')');
100             v[len] = buf.toString();
101         }
102 
103         return v;
104     }
105 
106     protected  Appendable appender;
107     protected int indentCount = 0;
108     protected bool ucase = true;
109     protected int selectListNumberOfLine = 5;
110 
111     protected bool groupItemSingleLine = false;
112 
113     protected List!Object parameters;
114     protected List!Object inputParameters;
115     protected Set!string  tables;
116     protected string       table; // for improved
117 
118     protected bool exportTables = false;
119 
120     protected string dbType;
121 
122     protected Map!(string,string)tableMapping;
123 
124     protected int replaceCount;
125 
126     protected bool parameterizedMergeInList = false;
127     protected bool parameterizedQuesUnMergeInList = false;
128 
129     protected bool parameterized = false;
130     protected bool shardingSupport = false;
131 
132     protected  int lines = 0;
133 
134     protected Boolean printStatementAfterSemi ;
135     private bool _haveQuotes = false;
136     private char _quotes = '"';
137 
138 
139     this() {
140         printStatementAfterSemi = Boolean.FALSE;
141         // features |= VisitorFeature.OutputPrettyFormat.mask;
142         config(VisitorFeature.OutputPrettyFormat, true);
143         initialization();
144     }
145 
146     this(Appendable appender){
147         this.appender = appender;
148         initialization();
149     }
150 
151     this(Appendable appender, string dbType){
152         this.appender = appender;
153         this.dbType = dbType;
154         initialization();
155     }
156 
157     this(Appendable appender, bool parameterized){
158         this.appender = appender;
159         this.config(VisitorFeature.OutputParameterized, parameterized);
160         this.config(VisitorFeature.OutputParameterizedQuesUnMergeInList, parameterizedQuesUnMergeInList);
161         initialization();
162     }
163 
164     private void initialization() {
165         if(dbType == DBType.MYSQL.name) {
166             _quotes = '`';
167         } else if(dbType == DBType.POSTGRESQL.name) {
168             // https://stackoverflow.com/questions/20878932/are-postgresql-column-names-case-sensitive
169             // https://blog.xojo.com/2016/09/28/about-postgresql-case-sensitivity/
170             // https://lerner.co.il/2013/11/30/quoting-postgresql/
171             _quotes = '"';
172         }
173 
174         // config(VisitorFeature.OutputQuotedIdentifier, false);
175     }
176 
177     // bool haveQuotes() {
178     //     return _haveQuotes;
179     // }
180 
181     int getReplaceCount() {
182         return this.replaceCount;
183     }
184 
185     void incrementReplaceCunt() {
186         replaceCount++;
187     }
188 
189     void addTableMapping(string srcTable, string destTable) {
190         if (tableMapping is null) {
191             tableMapping = new HashMap!(string, string)();
192         }
193 
194         if (indexOf(srcTable,'.') >= 0) {
195             SQLExpr expr = SQLUtils.toSQLExpr(srcTable, dbType);
196             if (cast(SQLPropertyExpr)expr !is null){
197                 srcTable = (cast(SQLPropertyExpr) expr).simplify().toString();
198             }
199         } else {
200             srcTable = SQLUtils.normalize(srcTable);
201         }
202         tableMapping.put(srcTable, destTable);
203     }
204 
205     void setTableMapping(Map!(string,string)tableMapping) {
206         this.tableMapping = tableMapping;
207     }
208 
209     List!Object getParameters() {
210         if (parameters is null) {
211             parameters = new ArrayList!Object();
212         }
213 
214         return parameters;
215     }
216 
217     bool isDesensitize() {
218         return isEnabled(VisitorFeature.OutputDesensitize);
219     }
220 
221     void setDesensitize(bool desensitize) {
222         config(VisitorFeature.OutputDesensitize, desensitize);
223     }
224 
225     Set!string getTables() {
226         if (this.table !is null && this.tables is null) {
227             return Collections.singleton!string(this.table);
228         }
229         return this.tables;
230     }
231 
232     //@Deprecated
233     void setParameters(List!Object parameters) {
234         if (parameters !is null && parameters.size() > 0) {
235             this.inputParameters = parameters;
236         } else {
237             this.parameters = parameters;
238         }
239     }
240 
241     void setInputParameters(List!Object parameters) {
242         this.inputParameters = parameters;
243     }
244 
245     /**
246      *
247      * @since 1.1.5
248      */
249     void setOutputParameters(List!Object parameters) {
250         this.parameters = parameters;
251     }
252 
253     int getIndentCount() {
254         return indentCount;
255     }
256 
257     Appendable getAppender() {
258         return appender;
259     }
260 
261     bool isPrettyFormat() {
262         return isEnabled(VisitorFeature.OutputPrettyFormat);
263     }
264 
265     void setPrettyFormat(bool prettyFormat) {
266         config(VisitorFeature.OutputPrettyFormat, prettyFormat);
267     }
268 
269     void decrementIndent() {
270         this.indentCount--;
271     }
272 
273     void incrementIndent() {
274         this.indentCount++;
275     }
276 
277     bool isParameterized() {
278         return isEnabled(VisitorFeature.OutputParameterized);
279     }
280 
281     void setParameterized(bool parameterized) {
282         config(VisitorFeature.OutputParameterized, parameterized);
283     }
284 
285     bool isParameterizedMergeInList() {
286         return parameterizedMergeInList;
287     }
288 
289     void setParameterizedMergeInList(bool parameterizedMergeInList) {
290         this.parameterizedMergeInList = parameterizedMergeInList;
291     }
292 
293     bool isParameterizedQuesUnMergeInList() {
294         return isEnabled(VisitorFeature.OutputParameterizedQuesUnMergeInList);
295     }
296 
297     void setParameterizedQuesUnMergeInList(bool parameterizedQuesUnMergeInList) {
298         config(VisitorFeature.OutputParameterizedQuesUnMergeInList, parameterizedQuesUnMergeInList);
299     }
300 
301     bool isExportTables() {
302         return exportTables;
303     }
304 
305     void setExportTables(bool exportTables) {
306         this.exportTables = exportTables;
307     }
308 
309     void print(char value) {
310         if (this.appender is null) {
311             warning("The appender is null");
312             return;
313         }
314 
315         try {
316             version(HUNT_SQL_DEBUG_MORE) tracef("Appending: %s", value);
317             this.appender.append(value);
318         } catch (Exception e) {
319             warning(e.msg);
320             throw new Exception("print error", e);
321         }
322     }
323 
324     void print(int value) {
325         version(HUNT_SQL_DEBUG_MORE) tracef("Appending: %s", value);
326         if (this.appender is null) {
327             warning("The appender is null");
328             return;
329         }
330 
331         if (cast(StringBuilder)appender !is null) {
332             (cast(StringBuilder) appender).append(value);
333         } else if (cast(StringBuilder)appender !is null) {
334             (cast(StringBuilder) appender).append(value);
335         } else {
336             print0(to!string(value));
337         }
338     }
339 
340     void print(long value) {
341         version(HUNT_SQL_DEBUG_MORE) tracef("Appending: %s", value);
342         if (this.appender is null) {
343             warning("The appender is null");
344             return;
345         }
346 
347         if (cast(StringBuilder)appender !is null) {
348             (cast(StringBuilder) appender).append(cast(int)value);
349         } else if (cast(StringBuilder)appender !is null) {
350             (cast(StringBuilder) appender).append(cast(int)value);
351         } else {
352             print0(to!string(value));
353         }
354     }
355 
356 
357     // void print(Date date) {
358     //     if (this.appender is null) {
359     //         return;
360     //     }
361 
362     //     SimpleDateFormat dateFormat;
363     //     if (cast(java.sql.Timestamp)date !is null) {
364     //         dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
365     //     } else {
366     //         dateFormat = new SimpleDateFormat("yyyy-MM-dd");
367     //     }
368     //     print0("'" ~ dateFormat.format(date) ~ "'");
369     // }
370 
371     void print(String text)
372     {
373         print(text.value());
374     }
375 
376     void print(string text) {
377         if (this.appender is null) {
378             return;
379         }
380         print0(text);
381     }
382      protected void print0(String text)
383      {
384          print0(text.value());
385      }
386 
387     protected void print0(string text) {
388         version(HUNT_SQL_DEBUG_MORE) tracef("Appending: %s", text);
389         if (appender is null) {
390             warning("appender is null");
391             return;
392         }
393 
394         this.appender.append(text);
395     }
396 
397     protected void print0(Bytes data) {        
398         implementationMissing(false);
399     }
400 
401     protected void printAlias(string _alias) {
402         if ((_alias !is null) && (_alias.length > 0)) {
403             print(' ');
404             print0(_alias);
405         }
406     }
407 
408     protected void printAndAccept(T = SQLObject)(List!(T) nodes, string seperator) {
409         for (int i = 0, size = nodes.size(); i < size; ++i) {
410             if (i != 0) {
411                 print0(seperator);
412             }
413             nodes.get(i).accept(this);
414         }
415     }
416 
417     private static int paramCount(SQLExpr x) {
418         if (cast(SQLName)x !is null) {
419             return 1;
420         }
421 
422         if (cast(SQLMethodInvokeExpr)x !is null) {
423             List!SQLExpr params = (cast(SQLMethodInvokeExpr) x).getParameters();
424             int t_paramCount = 1;
425             foreach(SQLExpr param  ;  params) {
426                 t_paramCount += paramCount(param);
427             }
428             return t_paramCount;
429         }
430 
431         if (cast(SQLAggregateExpr)x !is null) {
432             List!SQLExpr params = (cast(SQLAggregateExpr) x).getArguments();
433             int t_paramCount = 1;
434             foreach(SQLExpr param  ;  params) {
435                 t_paramCount += paramCount(param);
436             }
437             return t_paramCount;
438         }
439 
440         if (cast(SQLBinaryOpExpr)x !is null) {
441             return paramCount((cast(SQLBinaryOpExpr) x).getLeft())
442                     + paramCount((cast(SQLBinaryOpExpr) x).getRight());
443         }
444 
445         return 1;
446     }
447 
448     protected void printSelectList(List!SQLSelectItem selectList) {
449         this.indentCount++;
450         for (int i = 0, lineItemCount = 0, size = selectList.size()
451              ; i < size
452                 ; ++i, ++lineItemCount)
453         {
454             SQLSelectItem selectItem = selectList.get(i);
455             SQLExpr selectItemExpr = selectItem.getExpr();
456 
457             int paramCount = paramCount(selectItemExpr);
458 
459             bool methodOrBinary = (!(cast(SQLName)selectItemExpr !is null))
460                     && (cast(SQLMethodInvokeExpr)selectItemExpr !is null
461                     || cast(SQLAggregateExpr)selectItemExpr !is null
462                     || cast(SQLBinaryOpExpr)selectItemExpr !is null);
463 
464             if (methodOrBinary) {
465                 lineItemCount += (paramCount - 1);
466             }
467 
468             if (i != 0) {
469                 SQLSelectItem preSelectItem = selectList.get(i - 1);
470                 if (preSelectItem.getAfterCommentsDirect() !is null) {
471                     lineItemCount = 0;
472                     println();
473                 } else if (methodOrBinary) {
474                     if (lineItemCount >= selectListNumberOfLine) {
475                         lineItemCount = paramCount;
476                         println();
477                     }
478                 } else if (lineItemCount >= selectListNumberOfLine
479                         || cast(SQLQueryExpr)selectItemExpr !is null
480                         || cast(SQLCaseExpr)selectItemExpr !is null) {
481                     lineItemCount = 0;
482                     println();
483                 }
484 
485                 print0(", ");
486             }
487 
488             if (typeid(selectItem) == typeid(SQLSelectItem)) {
489                 this.visit(selectItem);
490             } else {
491                 selectItem.accept(this);
492             }
493 
494             if (selectItem.hasAfterComment()) {
495                 print(' ');
496                 printlnComment(selectItem.getAfterCommentsDirect());
497             }
498         }
499         this.indentCount--;
500     }
501 
502     protected void printlnAndAccept(T = SQLObject)(List!(T) nodes, string seperator) {
503         for (int i = 0, size = nodes.size(); i < size; ++i) {
504             if (i != 0) {
505                 println(seperator);
506             }
507 
508             (cast(T) nodes.get(i)).accept(this);
509         }
510     }
511 
512     protected void printIndent() {
513         if (this.appender is null) {
514             return;
515         }
516 
517         try {
518             for (int i = 0; i < this.indentCount; ++i) {
519                 this.appender.append('\t');
520             }
521         } catch (Exception e) {
522             throw new Exception("print error", e);
523         }
524     }
525 
526     void println() {
527         if (!isPrettyFormat()) {
528             print(' ');
529             return;
530         }
531 
532         print('\n');
533         lines++;
534         printIndent();
535     }
536 
537     void println(string text) {
538         print(text);
539         println();
540     }
541 
542     // ////////////////////
543 
544     override bool visit(SQLBetweenExpr x) {
545          SQLExpr testExpr = x.getTestExpr();
546          SQLExpr beginExpr = x.getBeginExpr();
547          SQLExpr endExpr = x.getEndExpr();
548 
549         bool quote = false;
550         if (cast(SQLBinaryOpExpr)testExpr !is null) {
551             SQLBinaryOperator t_operator = (cast(SQLBinaryOpExpr) testExpr).getOperator();
552             switch (t_operator.name) {
553                 case SQLBinaryOperator.BooleanAnd.name:
554                 case SQLBinaryOperator.BooleanOr.name:
555                 case SQLBinaryOperator.BooleanXor.name:
556                 case SQLBinaryOperator.Assignment.name:
557                     quote = true;
558                     break;
559                 default:
560                     quote = (cast(SQLBinaryOpExpr) testExpr).isBracket();
561                     break;
562             }
563         } else if (cast(SQLNotExpr)testExpr !is null){
564             quote = true;
565         }
566 
567         if (testExpr !is null) {
568             if (quote) {
569                 print('(');
570                 printExpr(testExpr);
571                 print(')');
572             } else {
573                 printExpr(testExpr);
574             }
575         }
576 
577         if (x.isNot()) {
578             print0(ucase ? " NOT BETWEEN " : " not between ");
579         } else {
580             print0(ucase ? " BETWEEN " : " between ");
581         }
582 
583         int lines = this.lines;
584         if (cast(SQLBinaryOpExpr)beginExpr !is null) {
585             SQLBinaryOpExpr binaryOpBegin = cast(SQLBinaryOpExpr) beginExpr;
586             incrementIndent();
587             if (binaryOpBegin.isBracket() || binaryOpBegin.getOperator().isLogical()) {
588                 print('(');
589                 printExpr(beginExpr);
590                 print(')');
591             } else {
592                 printExpr(beginExpr);
593             }
594             decrementIndent();
595         } else {
596             printExpr(beginExpr);
597         }
598 
599         if (lines != this.lines) {
600             println();
601             print0(ucase ? "AND " : "and ");
602         } else {
603             print0(ucase ? " AND " : " and ");
604         }
605 
606         printExpr(endExpr);
607 
608         return false;
609     }
610 
611     override bool visit(SQLBinaryOpExprGroup x) {
612         SQLObject parent = x.getParent();
613         SQLBinaryOperator operator = x.getOperator();
614 
615         bool isRoot = cast(SQLSelectQueryBlock)parent !is null || cast(SQLBinaryOpExprGroup)parent !is null;
616 
617         List!SQLExpr items = x.getItems();
618         if (isRoot) {
619             this.indentCount++;
620         }
621 
622         if (this.parameterized) {
623             SQLExpr firstLeft = null;
624             SQLBinaryOperator firstOp;
625             List!Object parameters = new ArrayList!Object(items.size());
626 
627             List!SQLBinaryOpExpr literalItems = null;
628 
629             if (operator != SQLBinaryOperator.BooleanOr || !isEnabled(VisitorFeature.OutputParameterizedQuesUnMergeOr)) {
630                 for (int i = 0; i < items.size(); i++) {
631                     SQLExpr item = items.get(i);
632                     if (cast(SQLBinaryOpExpr)item !is null) {
633                         SQLBinaryOpExpr binaryItem = cast(SQLBinaryOpExpr) item;
634                         SQLExpr left = binaryItem.getLeft();
635                         SQLExpr right = binaryItem.getRight();
636 
637                         if (cast(SQLLiteralExpr)right !is null && !(cast(SQLNullExpr)right !is null)) {
638                             if (cast(SQLLiteralExpr)left !is null) {
639                                 if (literalItems is null) {
640                                     literalItems = new ArrayList!SQLBinaryOpExpr();
641                                 }
642                                 literalItems.add(binaryItem);
643                                 continue;
644                             }
645 
646                             if (this.parameters !is null) {
647                                 ExportParameterVisitorUtils.exportParameter(parameters, right);
648                             }
649                         } else if (cast(SQLVariantRefExpr)right !is null) {
650                             // skip
651                         } else {
652                             firstLeft = null;
653                             break;
654                         }
655 
656 
657                         if (firstLeft is null) {
658                             firstLeft = binaryItem.getLeft();
659                             firstOp = binaryItem.getOperator();
660                         } else {
661                             if (!SQLExprUtils.opEquals(firstLeft, left)) {
662                                 firstLeft = null;
663                                 break;
664                             }
665                         }
666                     } else {
667                         firstLeft = null;
668                         break;
669                     }
670                 }
671             }
672 
673             if (firstLeft !is null) {
674                 if (literalItems !is null) {
675                     foreach(SQLBinaryOpExpr literalItem  ;  literalItems) {
676                         visit(literalItem);
677                         println();
678                         printOperator(operator);
679                         print(' ');
680 
681                     }
682                 }
683                 printExpr(firstLeft);
684                 print(' ');
685                 printOperator(firstOp);
686                 print0(" ?");
687 
688                 if (this.parameters !is null) {
689                     if (parameters.size() > 0) {
690                         this.parameters.addAll(parameters);
691                     }
692                 }
693 
694                 incrementReplaceCunt();
695                 if (isRoot) {
696                     this.indentCount--;
697                 }
698                 return false;
699             }
700         }
701 
702         for (int i = 0; i < items.size(); i++) {
703             SQLExpr item = items.get(i);
704 
705             if (i != 0) {
706                 println();
707                 printOperator(operator);
708                 print(' ');
709             }
710 
711             if (item.hasBeforeComment()) {
712                 printlnComments(item.getBeforeCommentsDirect());
713             }
714 
715             if (cast(SQLBinaryOpExpr)item !is null) {
716                 SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) item;
717                 SQLExpr binaryOpExprRight = binaryOpExpr.getRight();
718                 SQLBinaryOperator itemOp = binaryOpExpr.getOperator();
719 
720                 bool isLogic = itemOp.isLogical();
721                 if (isLogic) {
722                     indentCount++;
723                 }
724 
725                 bool bracket;
726                 if (itemOp.priority > operator.priority) {
727                     bracket = true;
728                 } else {
729                     bracket = binaryOpExpr.isBracket() & !parameterized;
730                 }
731                 if (bracket) {
732                     print('(');
733                     visit(binaryOpExpr);
734                     print(')');
735                 } else {
736                     visit(binaryOpExpr);
737                 }
738 
739                 if (item.hasAfterComment()) {
740                     print(' ');
741                     printlnComment(item.getAfterCommentsDirect());
742                 }
743 
744                 if (isLogic) {
745                     indentCount--;
746                 }
747             } else if (cast(SQLBinaryOpExprGroup)item !is null) {
748                 print('(');
749                 visit(cast(SQLBinaryOpExprGroup) item);
750                 print(')');
751             } else {
752                 printExpr(item);
753             }
754         }
755         if (isRoot) {
756             this.indentCount--;
757         }
758         return false;
759     }
760 
761     override bool visit(SQLBinaryOpExpr x) {
762         SQLBinaryOperator operator = x.getOperator();
763         if (this.parameterized
764                 && operator == SQLBinaryOperator.BooleanOr
765                 && !isEnabled(VisitorFeature.OutputParameterizedQuesUnMergeOr)) {
766             x = SQLBinaryOpExpr.merge(this, x);
767 
768             operator = x.getOperator();
769         }
770 
771         if (inputParameters !is null
772                 && inputParameters.size() > 0
773                 && operator == SQLBinaryOperator.Equality
774                 && cast(SQLVariantRefExpr)x.getRight() !is null
775                 ) {
776             SQLVariantRefExpr right = cast(SQLVariantRefExpr) x.getRight();
777             int index = right.getIndex();
778             if (index >= 0 && index < inputParameters.size()) {
779                 Object param = inputParameters.get(index);
780                 if (cast(Collection!Object)param !is null) {
781                     x.getLeft().accept(this);
782                     print0(" IN (");
783                     right.accept(this);
784                     print(')');
785                     return false;
786                 }
787             }
788         }
789 
790         SQLObject parent = x.getParent();
791         bool isRoot = cast(SQLSelectQueryBlock)parent !is null;
792         bool relational = operator == SQLBinaryOperator.BooleanAnd
793                              || operator == SQLBinaryOperator.BooleanOr;
794 
795         if (isRoot && relational) {
796             this.indentCount++;
797         }
798 
799         List!SQLExpr groupList = new ArrayList!SQLExpr();
800         SQLExpr left = x.getLeft();
801         SQLExpr right = x.getRight();
802 
803         if (inputParameters !is null
804                 && operator != SQLBinaryOperator.Equality) {
805             int varIndex = -1;
806             if (cast(SQLVariantRefExpr)right !is null) {
807                 varIndex = (cast(SQLVariantRefExpr) right).getIndex();
808             }
809 
810             Object param = null;
811             if (varIndex >= 0 && varIndex < inputParameters.size()) {
812                 param = inputParameters.get(varIndex);
813             }
814 
815             if (cast(Collection!Object)param !is null) {
816                 Collection!Object values  = cast(Collection!Object) param;
817 
818                 if (values.size() > 0) {
819                     print('(');
820                     int valIndex = 0;
821                     foreach(Object value  ;  values) {
822                         if (valIndex++ != 0) {
823                             print0(ucase ? " OR " : " or ");
824                         }
825                         printExpr(left);
826                         print(' ');
827                         if (operator == SQLBinaryOperator.Is) {
828                             print('=');
829                         } else {
830                             printOperator(operator);
831                         }
832                         print(' ');
833                         printParameter(value);
834                     }
835                     print(')');
836                     return false;
837                 }
838             }
839         }
840 
841         if (operator.isRelational()
842                 && cast(SQLIntegerExpr)left !is null
843                 && cast(SQLIntegerExpr)right !is null) {
844             print((cast(SQLIntegerExpr) left).getNumber().longValue());
845             print(' ');
846             printOperator(operator);
847             print(' ');
848             print((cast(SQLIntegerExpr) right).getNumber().longValue());
849             return false;
850         }
851 
852         for (;;) {
853             if (cast(SQLBinaryOpExpr)left !is null && (cast(SQLBinaryOpExpr) left).getOperator() == operator) {
854                 SQLBinaryOpExpr binaryLeft = cast(SQLBinaryOpExpr) left;
855                 groupList.add(binaryLeft.getRight());
856                 left = binaryLeft.getLeft();
857             } else {
858                 groupList.add(left);
859                 break;
860             }
861         }
862 
863         for (int i = groupList.size() - 1; i >= 0; --i) {
864             SQLExpr item = groupList.get(i);
865 
866             if (relational) {
867                 if (isPrettyFormat() && item.hasBeforeComment()) {
868                     printlnComments(item.getBeforeCommentsDirect());
869                 }
870             }
871 
872             if (isPrettyFormat() && item.hasBeforeComment()) {
873                 printlnComments(item.getBeforeCommentsDirect());
874             }
875 
876             visitBinaryLeft(item, operator);
877 
878             if (isPrettyFormat() && item.hasAfterComment()) {
879                 print(' ');
880                 printlnComment(item.getAfterCommentsDirect());
881             }
882 
883             if (i != groupList.size() - 1 && isPrettyFormat() && item.getParent().hasAfterComment()) {
884                 print(' ');
885                 printlnComment(item.getParent().getAfterCommentsDirect());
886             }
887 
888             bool printOpSpace = true;
889             if (relational) {
890                 println();
891             } else {
892                 if (operator == SQLBinaryOperator.Modulus
893                         && DBType.ORACLE.opEquals(dbType)
894                         && cast(SQLIdentifierExpr)left !is null
895                         && cast(SQLIdentifierExpr)right !is null
896                         && (cast(SQLIdentifierExpr) right).getName().equalsIgnoreCase("NOTFOUND")) {
897                     printOpSpace = false;
898                 }
899                 if (printOpSpace) {
900                     print(' ');
901                 }
902             }
903             printOperator(operator);
904             if (printOpSpace) {
905                 print(' ');
906             }
907         }
908 
909         visitorBinaryRight(x);
910 
911         if (isRoot && relational) {
912             this.indentCount--;
913         }
914 
915         return false;
916     }
917 
918     protected void printOperator(SQLBinaryOperator operator) {
919         print0(ucase ? operator.name : operator.name_lcase);
920     }
921 
922     private void visitorBinaryRight(SQLBinaryOpExpr x) {
923         if (isPrettyFormat() && x.getRight().hasBeforeComment()) {
924             printlnComments(x.getRight().getBeforeCommentsDirect());
925         }
926 
927         if ( cast(SQLBinaryOpExpr)x.getRight() !is null) {
928             SQLBinaryOpExpr right = cast(SQLBinaryOpExpr) x.getRight();
929             SQLBinaryOperator rightOp = right.getOperator();
930             SQLBinaryOperator op = x.getOperator();
931             bool rightRational = rightOp == SQLBinaryOperator.BooleanAnd
932                                     || rightOp == SQLBinaryOperator.BooleanOr;
933 
934             if (rightOp.priority >= op.priority
935                     || (right.isBracket()
936                     && rightOp != op
937                     && rightOp.isLogical()
938                     && op.isLogical()
939             )) {
940                 if (rightRational) {
941                     this.indentCount++;
942                 }
943 
944                 print('(');
945                 printExpr(right);
946                 print(')');
947 
948                 if (rightRational) {
949                     this.indentCount--;
950                 }
951             } else {
952                 printExpr(right);
953             }
954         } else {
955             printExpr(x.getRight());
956         }
957 
958         if (x.getRight().hasAfterComment() && isPrettyFormat()) {
959             print(' ');
960             printlnComment(x.getRight().getAfterCommentsDirect());
961         }
962     }
963 
964     private void visitBinaryLeft(SQLExpr left, SQLBinaryOperator op) {
965         if (cast(SQLBinaryOpExpr)left !is null) {
966             SQLBinaryOpExpr binaryLeft = cast(SQLBinaryOpExpr) left;
967             SQLBinaryOperator leftOp = binaryLeft.getOperator();
968             bool leftRational = leftOp == SQLBinaryOperator.BooleanAnd
969                                    || leftOp == SQLBinaryOperator.BooleanOr;
970 
971             if (leftOp.priority > op.priority
972                     || (binaryLeft.isBracket()
973                         && leftOp != op
974                         && leftOp.isLogical()
975                         && op.isLogical()
976             )) {
977                 if (leftRational) {
978                     this.indentCount++;
979                 }
980                 print('(');
981                 printExpr(left);
982                 print(')');
983 
984                 if (leftRational) {
985                     this.indentCount--;
986                 }
987             } else {
988                 printExpr(left);
989             }
990         } else {
991             printExpr(left);
992         }
993     }
994 
995     protected void printTableSource(SQLTableSource x) {
996         auto clazz = typeid(cast(Object)x);
997         if (clazz == typeid(SQLJoinTableSource)) {
998             visit(cast(SQLJoinTableSource) x);
999         } else  if (clazz == typeid(SQLExprTableSource)) {
1000             visit(cast(SQLExprTableSource) x);
1001         } else  if (clazz == typeid(SQLSubqueryTableSource)) {
1002             visit(cast(SQLSubqueryTableSource) x);
1003         } else {
1004             x.accept(this);
1005         }
1006     }
1007 
1008     protected void printQuery(SQLSelectQuery x) {
1009         auto clazz = typeid(cast(Object)x);
1010         if (clazz == typeid(SQLSelectQueryBlock)) {
1011             visit(cast(SQLSelectQueryBlock) x);
1012         } else if (clazz == typeid(SQLUnionQuery)) {
1013             visit(cast(SQLUnionQuery) x);
1014         } else {
1015             x.accept(this);
1016         }
1017     }
1018 
1019     protected  void printExpr(SQLExpr x) {
1020         auto clazz = typeid((cast(Object)x)); //typeid(x);
1021         version(HUNT_DEBUG) {
1022             // tracef("SQLExpr: %s", clazz.name);
1023             // tracef("SQLExpr2: %s", typeid((cast(Object)x)).name);
1024         } 
1025 
1026         if (clazz == typeid(SQLIdentifierExpr)) {
1027             visit(cast(SQLIdentifierExpr) x);
1028         } else if (clazz == typeid(SQLPropertyExpr)) {
1029             visit(cast(SQLPropertyExpr) x);
1030         } else if (clazz == typeid(SQLAllColumnExpr)) {
1031             print('*');
1032         } else if (clazz == typeid(SQLAggregateExpr)) {
1033             visit(cast(SQLAggregateExpr) x);
1034         } else if (clazz == typeid(SQLBinaryOpExpr)) {
1035             visit(cast(SQLBinaryOpExpr) x);
1036         } else if (clazz == typeid(SQLCharExpr)) {
1037             visit(cast(SQLCharExpr) x);
1038         } else if (clazz == typeid(SQLNullExpr)) {
1039             visit(cast(SQLNullExpr) x);
1040         } else if (clazz == typeid(SQLIntegerExpr)) {
1041             visit(cast(SQLIntegerExpr) x);
1042         } else if (clazz == typeid(SQLNumberExpr)) {
1043             visit(cast(SQLNumberExpr) x);
1044         } else if (clazz == typeid(SQLMethodInvokeExpr)) {
1045             visit(cast(SQLMethodInvokeExpr) x);
1046         } else if (clazz == typeid(SQLVariantRefExpr)) {
1047             visit(cast(SQLVariantRefExpr) x);
1048         } else if (clazz == typeid(SQLBinaryOpExprGroup)) {
1049             visit(cast(SQLBinaryOpExprGroup) x);
1050         } else if (clazz == typeid(SQLCaseExpr)) {
1051             visit(cast(SQLCaseExpr) x);
1052         } else if (clazz == typeid(SQLInListExpr)) {
1053             visit(cast(SQLInListExpr) x);
1054         } else if (clazz == typeid(SQLNotExpr)) {
1055             visit(cast(SQLNotExpr) x);
1056         } else {
1057             x.accept(this);
1058         }
1059     }
1060 
1061     override bool visit(SQLCaseExpr x) {
1062         this.indentCount++;
1063         print0(ucase ? "CASE " : "case ");
1064 
1065         SQLExpr valueExpr = x.getValueExpr();
1066         if (valueExpr !is null) {
1067             printExpr(valueExpr);
1068         }
1069 
1070         List!(SQLCaseExpr.Item) items = x.getItems();
1071         for (int i = 0, size = items.size(); i < size; ++i) {
1072             println();
1073             visit(items.get(i));
1074         }
1075 
1076         SQLExpr elExpr = x.getElseExpr();
1077         if (elExpr !is null) {
1078             println();
1079             print0(ucase ? "ELSE " : "else ");
1080             if (cast(SQLCaseExpr)elExpr !is null) {
1081                 this.indentCount++;
1082                 println();
1083                 visit(cast(SQLCaseExpr) elExpr);
1084                 this.indentCount--;
1085             } else {
1086                 printExpr(elExpr);
1087             }
1088         }
1089 
1090         this.indentCount--;
1091         println();
1092         print0(ucase ? "END" : "end");
1093 
1094         return false;
1095     }
1096 
1097     override bool visit(SQLCaseExpr.Item x) {
1098         print0(ucase ? "WHEN " : "when ");
1099         SQLExpr conditionExpr = x.getConditionExpr();
1100         printExpr(conditionExpr);
1101 
1102         print0(ucase ? " THEN " : " then ");
1103         SQLExpr valueExpr = x.getValueExpr();
1104         if (cast(SQLCaseExpr)valueExpr !is null) {
1105             this.indentCount++;
1106             println();
1107             visit(cast(SQLCaseExpr) valueExpr);
1108             this.indentCount--;
1109         } else {
1110             printExpr(valueExpr);
1111         }
1112 
1113         return false;
1114     }
1115 
1116     override bool visit(SQLCaseStatement x) {
1117         print0(ucase ? "CASE" : "case");
1118         SQLExpr valueExpr = x.getValueExpr();
1119         if (valueExpr !is null) {
1120             print(' ');
1121             printExpr(valueExpr);
1122         }
1123         this.indentCount++;
1124         println();
1125         printlnAndAccept!(SQLCaseStatement.Item)((x.getItems()), " ");
1126 
1127         if (x.getElseStatements().size() > 0) {
1128             println();
1129             print0(ucase ? "ELSE " : "else ");
1130             printlnAndAccept!(SQLStatement)((x.getElseStatements()), "");
1131         }
1132 
1133         this.indentCount--;
1134 
1135         println();
1136         print0(ucase ? "END CASE" : "end case");
1137         if (DBType.ORACLE.opEquals(dbType)) {
1138             print(';');
1139         }
1140         return false;
1141     }
1142 
1143     override bool visit(SQLCaseStatement.Item x) {
1144         print0(ucase ? "WHEN " : "when ");
1145         printExpr(x.getConditionExpr());
1146         print0(ucase ? " THEN " : " then ");
1147 
1148         SQLStatement stmt = x.getStatement();
1149         if (stmt !is null) {
1150             stmt.accept(this);
1151             print(';');
1152         }
1153         return false;
1154     }
1155 
1156     override bool visit(SQLCastExpr x) {
1157         print0(ucase ? "CAST(" : "cast(");
1158         x.getExpr().accept(this);
1159         print0(ucase ? " AS " : " as ");
1160         x.getDataType().accept(this);
1161         print0(")");
1162 
1163         return false;
1164     }
1165 
1166     override bool visit(SQLCharExpr x) {
1167         if (this.parameterized) {
1168             print('?');
1169             incrementReplaceCunt();
1170             if (this.parameters !is null) {
1171                 ExportParameterVisitorUtils.exportParameter(this.parameters, x);
1172             }
1173             return false;
1174         }
1175 
1176         printChars(x.getText().value());
1177 
1178         return false;
1179     }
1180 
1181     protected void printChars(string text) {
1182         if (text is null) {
1183             print0(ucase ? "NULL" : "null");
1184         } else {
1185             print('\'');
1186             int index = cast(int)(text.indexOf('\''));
1187             if (index >= 0) {
1188                 text = text.replace("'", "''");//@gxc
1189             }
1190             print0(text);
1191             print('\'');
1192         }
1193     }
1194 
1195     override bool visit(SQLDataType x) {
1196         printDataType(x);
1197 
1198         return false;
1199     }
1200 
1201     protected void printDataType(SQLDataType x) {
1202         bool parameterized = this.parameterized;
1203         this.parameterized = false;
1204 
1205         print0(x.getName());
1206         if (x.getArguments().size() > 0) {
1207             print('(');
1208             printAndAccept!SQLExpr(x.getArguments(), ", ");
1209             print(')');
1210         }
1211 
1212         Boolean withTimeZone = x.getWithTimeZone();
1213         if (withTimeZone !is null)  {
1214             if (withTimeZone.value()) {
1215                 if (x.isWithLocalTimeZone()) {
1216                     print0(ucase ? " WITH LOCAL TIME ZONE" : " with local time zone");
1217                 } else {
1218                     print0(ucase ? " WITH TIME ZONE" : " with time zone");
1219                 }
1220             } else {
1221                 print0(ucase ? " WITHOUT TIME ZONE" : " without time zone");
1222             }
1223         }
1224         
1225         this.parameterized = parameterized;
1226     }
1227 
1228     override bool visit(SQLCharacterDataType x) {
1229         visit(cast(SQLDataType) x);
1230 
1231         List!SQLCommentHint hints = (cast(SQLCharacterDataType) x).hints;
1232         if (hints !is null) {
1233             print(' ');
1234             foreach(SQLCommentHint hint  ;  hints) {
1235                 hint.accept(this);
1236             }
1237         }
1238 
1239         return false;
1240     }
1241 
1242     override bool visit(SQLExistsExpr x) {
1243         if (x.isNot()) {
1244             print0(ucase ? "NOT EXISTS (" : "not exists (");
1245         } else {
1246             print0(ucase ? "EXISTS (" : "exists (");
1247         }
1248         this.indentCount++;
1249         println();
1250         visit(x.getSubQuery());
1251         this.indentCount--;
1252         println();
1253         print(')');
1254         return false;
1255     }
1256 
1257     override bool visit(SQLIdentifierExpr x) {
1258         string name = x.getName();
1259         if(isEnabled(VisitorFeature.OutputQuotedIdentifier)) {
1260             print0(_quotes ~ name ~ _quotes);
1261         } else {
1262             print0(name);
1263         }
1264         return false;
1265     }
1266 
1267     protected bool printName(SQLName x, string name) {
1268         bool shardingSupport = this.shardingSupport
1269                 && this.parameterized;
1270         return printName(x, name, shardingSupport);
1271     }
1272 
1273     string unwrapShardingTable(string name) {
1274         char c0 = charAt(name, 0);
1275         char c_last = charAt(name, name.length - 1);
1276          bool quote = (c0 == '`' && c_last == '`') || (c0 == '"' && c_last == '"');
1277 
1278         int end = cast(int)(name.length);
1279         if (quote) {
1280             end--;
1281         }
1282 
1283         int num_cnt = 0, postfixed_cnt = 0;
1284         for (int i = end - 1; i > 0; --i, postfixed_cnt++) {
1285             char ch = charAt(name, i);
1286             if (ch >= '0' && ch <= '9') {
1287                 num_cnt++;
1288             }
1289 
1290             if (ch != '_' && (ch < '0' || ch > '9')) {
1291                 break;
1292             }
1293         }
1294         if (num_cnt < 1 || postfixed_cnt < 2) {
1295             return name;
1296         }
1297 
1298         int start = end - postfixed_cnt;
1299         if (start < 1) {
1300             return name;
1301         }
1302 
1303         string realName = name.substring(quote ? 1 : 0, start);
1304         return realName;
1305     }
1306 
1307     protected bool printName(SQLName x, string name, bool shardingSupport) {
1308 
1309         if (shardingSupport) {
1310             SQLObject parent = x.getParent();
1311             shardingSupport = cast(SQLExprTableSource)parent !is null || cast(SQLPropertyExpr)parent !is null;
1312 
1313             if (cast(SQLPropertyExpr)parent !is null &&  cast(SQLExprTableSource)parent.getParent() !is null) {
1314                 shardingSupport = false;
1315             }
1316         }
1317 
1318         if (shardingSupport) {
1319              bool quote = charAt(name, 0) == '`' && charAt(name, name.length - 1) == '`';
1320 
1321             string unwrappedName = unwrapShardingTable(name);
1322             if (unwrappedName != name) {
1323                 bool isAlias = false;
1324                 for (SQLObject parent = x.getParent(); parent !is null; parent = parent.getParent()) {
1325                     if (cast(SQLSelectQueryBlock)parent !is null) {
1326                         SQLTableSource from = (cast(SQLSelectQueryBlock) parent).getFrom();
1327                         if (quote) {
1328                             // string name2 = name.substring(1, name.length - 1);
1329                             string name2 = name[1 .. $-1];
1330                             if (isTableSourceAlias(from, name, name2)) {
1331                                 isAlias = true;
1332                             }
1333                         } else {
1334                             if (isTableSourceAlias(from, name)) {
1335                                 isAlias = true;
1336                             }
1337                         }
1338                         break;
1339                     }
1340                 }
1341 
1342                 if (!isAlias) {
1343                     print0(unwrappedName);
1344                     incrementReplaceCunt();
1345                     return false;
1346                 } else {
1347                     print0(name);
1348                     return false;
1349                 }
1350             }
1351         }
1352 
1353         print0(name);
1354         return false;
1355     }
1356 
1357     override bool visit(SQLInListExpr x) {
1358         if (this.parameterized) {
1359             List!SQLExpr targetList = x.getTargetList();
1360 
1361             bool allLiteral = true;
1362             foreach(SQLExpr item  ;  targetList) {
1363                 if (!(cast(SQLLiteralExpr)item !is null || cast(SQLVariantRefExpr)item !is null)) {
1364                     if (cast(SQLListExpr)item !is null) {
1365                         SQLListExpr list = cast(SQLListExpr) item;
1366                         foreach(SQLExpr listItem ;list.getItems()) {
1367                             if (!(cast(SQLLiteralExpr)listItem !is null || cast(SQLVariantRefExpr)listItem !is null)) {
1368                                 allLiteral = false;
1369                                 break;
1370                             }
1371                         }
1372                         if (allLiteral) {
1373                             break;
1374                         }
1375                         continue;
1376                     }
1377                     allLiteral = false;
1378                     break;
1379                 }
1380             }
1381 
1382             if (allLiteral) {
1383                 bool changed = true;
1384                 if (targetList.size() == 1 && cast(SQLVariantRefExpr)targetList.get(0) !is null) {
1385                     changed = false;
1386                 }
1387 
1388                 printExpr(x.getExpr());
1389 
1390                 if (x.isNot()) {
1391                     print(ucase ? " NOT IN" : " not in");
1392                 } else {
1393                     print(ucase ? " IN" : " in");
1394                 }
1395 
1396                 if(!isParameterizedQuesUnMergeInList() || targetList.size() == 1) {
1397                     print(" (?)");
1398                 } else {
1399                     print(" (");
1400                     for (int i = 0; i < targetList.size(); i++) {
1401                         if(i != 0) {
1402                             print(",");
1403                         }
1404                         print(" ?");
1405                     }
1406                     print(")");
1407                 }
1408 
1409                 if (changed) {
1410                     incrementReplaceCunt();
1411                     if (this.parameters !is null) {
1412                         if (parameterizedMergeInList) {
1413                             List!Object subList = new ArrayList!Object(x.getTargetList().size());
1414                             foreach (SQLExpr target ; x.getTargetList()) {
1415                                 ExportParameterVisitorUtils.exportParameter(subList, target);
1416                             }
1417                             if (subList !is null) {
1418                                 parameters.addAll(subList);
1419                             }
1420                         } else {
1421                             foreach (SQLExpr target ; x.getTargetList()) {
1422                                 ExportParameterVisitorUtils.exportParameter(this.parameters, target);
1423                             }
1424                         }
1425                     }
1426                 }
1427 
1428                 return false;
1429             }
1430         }
1431 
1432         printExpr(x.getExpr());
1433 
1434         if (x.isNot()) {
1435             print0(ucase ? " NOT IN (" : " not in (");
1436         } else {
1437             print0(ucase ? " IN (" : " in (");
1438         }
1439 
1440          List!SQLExpr list = x.getTargetList();
1441 
1442         bool _printLn = false;
1443         if (list.size() > 5) {
1444             _printLn = true;
1445             for (int i = 0, size = list.size(); i < size; ++i) {
1446                 if (!(cast(SQLCharExpr)list.get(i) !is null)) {
1447                     _printLn = false;
1448                     break;
1449                 }
1450             }
1451         }
1452 
1453         if (_printLn) {
1454             this.indentCount++;
1455             println();
1456             for (int i = 0, size = list.size(); i < size; ++i) {
1457                 if (i != 0) {
1458                     print0(", ");
1459                     println();
1460                 }
1461                 SQLExpr item = list.get(i);
1462                 printExpr(item);
1463             }
1464             this.indentCount--;
1465             println();
1466         } else {
1467             List!SQLExpr targetList = x.getTargetList();
1468             for (int i = 0; i < targetList.size(); i++) {
1469                 if (i != 0) {
1470                     print0(", ");
1471                 }
1472                 printExpr(targetList.get(i));
1473             }
1474         }
1475 
1476         print(')');
1477         return false;
1478     }
1479 
1480     override bool visit(SQLIntegerExpr x) {
1481         bool parameterized = this.parameterized;
1482         printInteger(x, parameterized);
1483         return false;
1484     }
1485 
1486     protected void printInteger(SQLIntegerExpr x, bool parameterized) {
1487         Number number = x.getNumber();
1488 
1489         if (number == (Integer.valueOf(1))) {
1490             if (DBType.ORACLE.opEquals(dbType)) {
1491                 SQLObject parent = x.getParent();
1492                 if (cast(SQLBinaryOpExpr)parent !is null) {
1493                     SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) parent;
1494                     SQLExpr left = binaryOpExpr.getLeft();
1495                     SQLBinaryOperator op = binaryOpExpr.getOperator();
1496                     if (cast(SQLIdentifierExpr)left !is null
1497                             && op == SQLBinaryOperator.Equality) {
1498                         string name = (cast(SQLIdentifierExpr) left).getName();
1499                         if ("rownum" == (name)) {
1500                             print(1);
1501                             return;
1502                         }
1503                     }
1504                 }
1505             }
1506         }
1507         if (parameterized) {
1508             print('?');
1509             incrementReplaceCunt();
1510 
1511             if(this.parameters !is null){
1512                 ExportParameterVisitorUtils.exportParameter(this.parameters, x);
1513             }
1514             return;
1515         }
1516 
1517         if (cast(BigDecimal)number !is null || cast(BigInteger)number !is null) {
1518             print((cast(Object)(number)).toString());
1519         } else {
1520             print(number.longValue());
1521         }
1522     }
1523 
1524     override bool visit(SQLMethodInvokeExpr x) {
1525         SQLExpr owner = x.getOwner();
1526         if (owner !is null) {
1527             printMethodOwner(owner);
1528         }
1529 
1530         string _function = x.getMethodName();
1531         List!SQLExpr parameters = x.getParameters();
1532 
1533         printFunctionName(_function);
1534         print('(');
1535 
1536         string trimOption = x.getTrimOption();
1537         if (trimOption !is null) {
1538             print0(trimOption);
1539 
1540             if (parameters.size() > 0) {
1541                 print(' ');
1542             }
1543         }
1544 
1545 
1546         for (int i = 0, size = parameters.size(); i < size; ++i) {
1547             if (i != 0) {
1548                 print0(", ");
1549             }
1550             SQLExpr param = parameters.get(i);
1551 
1552             if (this.parameterized) {
1553                 if (size == 2 && i == 1 && cast(SQLCharExpr)param !is null) {
1554                     if (DBType.ORACLE.opEquals(dbType)) {
1555                         if ("TO_CHAR".equalsIgnoreCase(_function)
1556                                 || "TO_DATE".equalsIgnoreCase(_function)) {
1557                             printChars((cast(SQLCharExpr) param).getText().value());
1558                             continue;
1559                         }
1560                     } else if (DBType.MYSQL.opEquals(dbType)) {
1561                         if ("DATE_FORMAT".equalsIgnoreCase(_function)) {
1562                             printChars((cast(SQLCharExpr) param).getText().value());
1563                             continue;
1564                         }
1565                     }
1566                 }
1567 
1568             }
1569 
1570             if (cast(SQLBinaryOpExpr)param !is null) {
1571                 SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) param;
1572                 SQLBinaryOperator op = binaryOpExpr.getOperator();
1573                 if (op == SQLBinaryOperator.BooleanAnd || op == SQLBinaryOperator.BooleanOr) {
1574                     this.indentCount++;
1575                     printExpr(param);
1576                     this.indentCount--;
1577                     continue;
1578                 }
1579             }
1580 
1581             printExpr(param);
1582         }
1583 
1584         SQLExpr from = x.getFrom();
1585         if (from !is null) {
1586             print0(ucase ? " FROM " : " from ");
1587             printExpr(from);
1588 
1589             SQLExpr _for = x.getFor();
1590             if (_for !is null) {
1591                 print0(ucase ? " FOR " : " for ");
1592                 printExpr(_for);
1593             }
1594         }
1595 
1596         SQLExpr using = x.getUsing();
1597         if (using !is null) {
1598             print0(ucase ? " USING " : " using ");
1599             printExpr(using);
1600         }
1601 
1602         print(')');
1603         return false;
1604     }
1605 
1606     protected void printMethodOwner(SQLExpr owner) {
1607         printExpr(owner);
1608         print('.');
1609     }
1610 
1611     protected void printFunctionName(string name) {
1612         print0(name);
1613     }
1614 
1615     override bool visit(SQLAggregateExpr x) {
1616         bool parameterized = this.parameterized;
1617         this.parameterized = false;
1618 
1619         string methodName = x.getMethodName();
1620         print0(ucase ? methodName : toLower(methodName));
1621         print('(');
1622 
1623         SQLAggregateOption option = x.getOption();
1624         if (option.name.length != 0) {
1625             print0(option.name);
1626             print(' ');
1627         }
1628 
1629         List!SQLExpr arguments = x.getArguments();
1630         for (int i = 0, size = arguments.size(); i < size; ++i) {
1631             if (i != 0) {
1632                 print0(", ");
1633             }
1634             printExpr(arguments.get(i));
1635         }
1636 
1637         visitAggreateRest(x);
1638 
1639         print(')');
1640 
1641         if (DBType.POSTGRESQL != dbType) {
1642             SQLOrderBy withGroup = x.getWithinGroup();
1643             if (withGroup !is null) {
1644                 print0(ucase ? " WITHIN GROUP (" : " within group (");
1645                 visit(withGroup);
1646                 print(')');
1647             }
1648         }
1649 
1650         SQLKeep keep = x.getKeep();
1651         if (keep !is null) {
1652             print(' ');
1653             visit(keep);
1654         }
1655 
1656         SQLOver over = x.getOver();
1657         if (over !is null) {
1658             print(' ');
1659             over.accept(this);
1660         }
1661 
1662          SQLExpr filter = x.getFilter();
1663         if (filter !is null) {
1664             print0(ucase ? "FILTER (WHERE " : "filter (where ");
1665             printExpr(filter);
1666             print(')');
1667         }
1668 
1669         this.parameterized = parameterized;
1670         return false;
1671     }
1672 
1673     protected void visitAggreateRest(SQLAggregateExpr aggregateExpr) {
1674 
1675     }
1676 
1677     override bool visit(SQLAllColumnExpr x) {
1678         print('*');
1679         return true;
1680     }
1681 
1682     override bool visit(SQLNCharExpr x) {
1683         if (this.parameterized) {
1684             print('?');
1685             incrementReplaceCunt();
1686 
1687             if(this.parameters !is null){
1688                 ExportParameterVisitorUtils.exportParameter(this.parameters, x);
1689             }
1690             return false;
1691         }
1692 
1693         if ((x.getText() is null) || (x.getText().value.length == 0)) {
1694             print0(ucase ? "NULL" : "null");
1695         } else {
1696             print0(ucase ? "N'" : "n'");
1697             print0(x.getText().value().replace("'", "''"));
1698             print('\'');
1699         }
1700         return false;
1701     }
1702 
1703     override bool visit(SQLNotExpr x) {
1704         print0(ucase ? "NOT " : "not ");
1705         SQLExpr expr = x.getExpr();
1706 
1707         bool needQuote = false;
1708 
1709         if (cast(SQLBinaryOpExpr)expr !is null) {
1710             SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) expr;
1711             needQuote = binaryOpExpr.getOperator().isLogical();
1712         } else if (cast(SQLInListExpr)expr !is null || cast(SQLNotExpr)expr !is null) {
1713             needQuote = true;
1714         }
1715 
1716         if (needQuote) {
1717             print('(');
1718         }
1719         printExpr(expr);
1720 
1721         if (needQuote) {
1722             print(')');
1723         }
1724         return false;
1725     }
1726 
1727     override bool visit(SQLNullExpr x) {
1728         if (this.parameterized
1729                 && cast(ValuesClause)x.getParent() !is null) {
1730             print('?');
1731             incrementReplaceCunt();
1732 
1733             if(this.parameters !is null){
1734                 this.getParameters().add(null);
1735             }
1736             return false;
1737         }
1738 
1739         print0(ucase ? "NULL" : "null");
1740         return false;
1741     }
1742 
1743     override bool visit(SQLNumberExpr x) {
1744         if (this.parameterized) {
1745             print('?');
1746             incrementReplaceCunt();
1747 
1748             if(this.parameters !is null){
1749                 ExportParameterVisitorUtils.exportParameter((this).getParameters(), x);
1750             }
1751             return false;
1752         }
1753 
1754         if (cast(StringBuilder)appender !is null) {
1755             x.output(cast(StringBuilder) appender);
1756         } else if (cast(StringBuilder)appender !is null) {
1757             x.output(cast(StringBuilder) appender);
1758         } else {
1759             print0(x.getNumber().toString());
1760         }
1761         return false;
1762     }
1763 
1764     override bool visit(SQLPropertyExpr x) {
1765         SQLExpr owner = x.getOwner();
1766         SQLIdentifierExpr ownerIdent = cast(SQLIdentifierExpr)owner;
1767 
1768         string mapTableName = null, ownerName = null;
1769         if (ownerIdent !is null) {
1770             ownerName = ownerIdent.getName();
1771             if (tableMapping !is null) {
1772                 mapTableName = tableMapping.get(ownerName);
1773 
1774                 //tracef("mapTableName: %s, ownerName=%s", mapTableName, ownerName);
1775                 if (mapTableName.empty()
1776                         && ownerName.length > 2
1777                         && ownerName[0] == '`'
1778                         && ownerName[$-1] == '`') {
1779                     ownerName = ownerName[1 .. $ - 1];
1780                     mapTableName = tableMapping.get(ownerName);
1781                 }
1782             }
1783         }
1784 
1785 
1786         if (!mapTableName.empty()) {
1787             for (SQLObject parent = x.getParent();parent !is null; parent = parent.getParent()) {
1788                 if (cast(SQLSelectQueryBlock)parent !is null) {
1789                     SQLTableSource from = (cast(SQLSelectQueryBlock) parent).getFrom();
1790                     if (isTableSourceAlias(from, mapTableName, ownerName)) {
1791                         mapTableName = null;
1792                     }
1793                     break;
1794                 }
1795             }
1796         }
1797 
1798         // version(HUNT_SQL_DEBUG_MORE) tracef("mapTableName: %s, ownerName=%s", mapTableName, ownerName);
1799         
1800         if (!mapTableName.empty()) {
1801             print0(mapTableName);
1802             print('.');
1803         } else if (ownerIdent !is null) {
1804             ownerName = ownerIdent.getName();
1805             if(!ownerName.empty()) {
1806                 if(isEnabled(VisitorFeature.OutputQuotedIdentifier)) {
1807                     print(_quotes);
1808                     printName(ownerIdent, ownerName, this.shardingSupport && this.parameterized);
1809                     print(_quotes);
1810                 } else {
1811                     printName(ownerIdent, ownerName, this.shardingSupport && this.parameterized);
1812                 }
1813 
1814                 print('.');
1815             }
1816         } else {
1817             printExpr(owner);
1818             print('.');
1819         }
1820 
1821        string name = x.getName();
1822         if(isEnabled(VisitorFeature.OutputQuotedIdentifier)) {
1823             print0(_quotes ~ name ~ _quotes);
1824         } else {
1825             print0(name);
1826         }
1827 
1828         return false;
1829     }
1830 
1831     protected bool isTableSourceAlias(SQLTableSource from, string[] tableNames...) {
1832         string _alias = from.getAlias();
1833 
1834         if (_alias !is null) {
1835             foreach(string tableName  ;  tableNames) {
1836                 if (equalsIgnoreCase(_alias, tableName)) {
1837                     return true;
1838                 }
1839             }
1840 
1841             if (_alias.length > 2 && charAt(_alias, 0) == '`' && charAt(_alias, _alias.length -1) == '`') {
1842                 _alias = _alias.substring(1, _alias.length -1);
1843                 foreach(string tableName  ;  tableNames) {
1844                     if (equalsIgnoreCase(_alias, tableName)) {
1845                         return true;
1846                     }
1847                 }
1848             }
1849         }
1850         if (cast(SQLJoinTableSource)from !is null) {
1851             SQLJoinTableSource join = cast(SQLJoinTableSource) from;
1852             return isTableSourceAlias(join.getLeft(), tableNames)
1853                     || isTableSourceAlias(join.getRight(), tableNames);
1854         }
1855         return false;
1856     }
1857 
1858     override bool visit(SQLQueryExpr x) {
1859         SQLObject parent = x.getParent();
1860         if (cast(SQLSelect)parent !is null) {
1861             parent = parent.getParent();
1862         }
1863 
1864         SQLSelect subQuery = x.getSubQuery();
1865         if (cast(ValuesClause)parent !is null) {
1866             println();
1867             print('(');
1868             visit(subQuery);
1869             print(')');
1870             println();
1871         // } else if ((cast(SQLStatement)parent !is null
1872         //         && !(cast(OracleForStatement)parent !is null))
1873         //         || cast(OracleSelectPivot.Item)parent !is null) {
1874         //     this.indentCount++;
1875 
1876         //     println();
1877         //     visit(subQuery);
1878 
1879         //     this.indentCount--;
1880         } else if (cast(SQLOpenStatement)parent !is null) {
1881             visit(subQuery);
1882         } else {
1883             print('(');
1884             this.indentCount++;
1885             println();
1886             visit(subQuery);
1887             this.indentCount--;
1888             println();
1889             print(')');
1890         }
1891         return false;
1892     }
1893 
1894     override bool visit(SQLSelectGroupByClause x) {
1895 
1896         bool oracle = DBType.ORACLE.opEquals(dbType);
1897         bool rollup = x.isWithRollUp();
1898         bool cube = x.isWithCube();
1899 
1900         int itemSize = x.getItems().size();
1901         if (itemSize > 0) {
1902             print0(ucase ? "GROUP BY " : "group by ");
1903             if (oracle && rollup) {
1904                 print0(ucase ? "ROLLUP (" : "rollup (");
1905             } else if (oracle && cube) {
1906                 print0(ucase ? "CUBE (" : "cube (");
1907             }
1908             this.indentCount++;
1909             for (int i = 0; i < itemSize; ++i) {
1910                 if (i != 0) {
1911                     if (groupItemSingleLine) {
1912                         println(", ");
1913                     } else {
1914                         print(", ");
1915                     }
1916                 }
1917                 x.getItems().get(i).accept(this);
1918             }
1919             if (oracle && rollup) {
1920                 print(')');
1921             }
1922             this.indentCount--;
1923         }
1924 
1925         if (x.getHaving() !is null) {
1926             println();
1927             print0(ucase ? "HAVING " : "having ");
1928             x.getHaving().accept(this);
1929         }
1930 
1931         if (x.isWithRollUp() && !oracle) {
1932             print0(ucase ? " WITH ROLLUP" : " with rollup");
1933         }
1934 
1935         if (x.isWithCube() && !oracle) {
1936             print0(ucase ? " WITH CUBE" : " with cube");
1937         }
1938 
1939         return false;
1940     }
1941 
1942     override bool visit(SQLSelect x) {
1943         SQLWithSubqueryClause withSubQuery = x.getWithSubQuery();
1944         if (withSubQuery !is null) {
1945             withSubQuery.accept(this);
1946             println();
1947         }
1948 
1949         printQuery(x.getQuery());
1950 
1951         SQLOrderBy orderBy = x.getOrderBy();
1952         if (orderBy !is null) {
1953             println();
1954             orderBy.accept(this);
1955         }
1956 
1957         if (x.getHintsSize() > 0) {
1958             printAndAccept!SQLHint((x.getHints()), "");
1959         }
1960 
1961         return false;
1962     }
1963 
1964     override bool visit(SQLSelectQueryBlock x) {
1965         if (isPrettyFormat() && x.hasBeforeComment()) {
1966             printlnComments(x.getBeforeCommentsDirect());
1967         }
1968 
1969         print0(ucase ? "SELECT " : "select ");
1970 
1971          bool informix =DBType.INFORMIX.opEquals(dbType);
1972         if (informix) {
1973             printFetchFirst(x);
1974         }
1975 
1976          int distinctOption = x.getDistionOption();
1977         if (SQLSetQuantifier.ALL == distinctOption) {
1978             print0(ucase ? "ALL " : "all ");
1979         } else if (SQLSetQuantifier.DISTINCT == distinctOption) {
1980             print0(ucase ? "DISTINCT " : "distinct ");
1981         } else if (SQLSetQuantifier.UNIQUE == distinctOption) {
1982             print0(ucase ? "UNIQUE " : "unique ");
1983         }
1984 
1985         printSelectList(
1986                 x.getSelectList());
1987 
1988         SQLExprTableSource into = x.getInto();
1989         if (into !is null) {
1990             println();
1991             print0(ucase ? "INTO " : "into ");
1992             into.accept(this);
1993         }
1994 
1995         SQLTableSource from = x.getFrom();
1996         if (from !is null) {
1997             println();
1998             print0(ucase ? "FROM " : "from ");
1999             printTableSource(from);
2000         }
2001 
2002         SQLExpr where = x.getWhere();
2003         if (where !is null) {
2004             println();
2005             print0(ucase ? "WHERE " : "where ");
2006             printExpr(where);
2007         }
2008 
2009         printHierarchical(x);
2010 
2011         SQLSelectGroupByClause groupBy = x.getGroupBy();
2012         if (groupBy !is null) {
2013             println();
2014             visit(groupBy);
2015         }
2016 
2017         SQLOrderBy orderBy = x.getOrderBy();
2018         if (orderBy !is null) {
2019             println();
2020             orderBy.accept(this);
2021         }
2022 
2023         if (!informix) {
2024             printFetchFirst(x);
2025         }
2026 
2027         if (x.isForUpdate()) {
2028             println();
2029             print0(ucase ? "FOR UPDATE" : "for update");
2030         }
2031 
2032         return false;
2033     }
2034 
2035     protected void printFetchFirst(SQLSelectQueryBlock x) {
2036         SQLLimit limit = x.getLimit();
2037         if (limit is null) {
2038             return;
2039         }
2040 
2041         SQLExpr offset = limit.getOffset();
2042         SQLExpr first = limit.getRowCount();
2043 
2044         if (limit !is null) {
2045             if (DBType.INFORMIX.opEquals(dbType)) {
2046                 if (offset !is null) {
2047                     print0(ucase ? "SKIP " : "skip ");
2048                     offset.accept(this);
2049                 }
2050 
2051                 print0(ucase ? " FIRST " : " first ");
2052                 first.accept(this);
2053                 print(' ');
2054             } else if (DBType.DB2.opEquals(dbType)
2055                     || DBType.ORACLE.opEquals(dbType)
2056                     || DBType.SQL_SERVER.opEquals(dbType)) {
2057                 //order by 语句必须在FETCH FIRST ROWS ONLY之前
2058                 SQLObject parent = x.getParent();
2059                 if (cast(SQLSelect)parent !is null) {
2060                     SQLOrderBy orderBy = (cast(SQLSelect) parent).getOrderBy();
2061                     if (orderBy !is null && orderBy.getItems().size() > 0) {
2062                         println();
2063                         print0(ucase ? "ORDER BY " : "order by ");
2064                         printAndAccept!SQLSelectOrderByItem((orderBy.getItems()), ", ");
2065                     }
2066                 }
2067 
2068                 println();
2069 
2070                 if (offset !is null) {
2071                     print0(ucase ? "OFFSET " : "offset ");
2072                     offset.accept(this);
2073                     print0(ucase ? " ROWS" : " rows");
2074                 }
2075 
2076                 if (first !is null) {
2077                     if (offset !is null) {
2078                         print(' ');
2079                     }
2080                     if (DBType.SQL_SERVER.opEquals(dbType) && offset !is null) {
2081                         print0(ucase ? "FETCH NEXT " : "fetch next ");
2082                     } else {
2083                         print0(ucase ? "FETCH FIRST " : "fetch first ");
2084                     }
2085                     first.accept(this);
2086                     print0(ucase ? " ROWS ONLY" : " rows only");
2087                 }
2088             } else {
2089                 println();
2090                 limit.accept(this);
2091             }
2092         }
2093     }
2094 
2095     override bool visit(SQLSelectItem x) {
2096         if (x.isConnectByRoot()) {
2097             print0(ucase ? "CONNECT_BY_ROOT " : "connect_by_root ");
2098         }
2099 
2100         SQLExpr expr = x.getExpr();
2101         SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr)expr;
2102 
2103         if (identifierExpr !is null) {
2104             string name = identifierExpr.getName();
2105             print0(name);
2106             // if(isEnabled(VisitorFeature.OutputQuotedIdentifier)) {
2107             //     print0(_quotes ~ name ~ _quotes);
2108             // } else {
2109             //     print0(name);
2110             // }
2111         } else if (cast(SQLPropertyExpr)expr !is null) {
2112             visit(cast(SQLPropertyExpr) expr);
2113         } else {
2114             printExpr(expr);
2115         }
2116 
2117         string _alias = x.getAlias();
2118         if (!_alias.empty) {
2119             print0(ucase ? " AS " : " as ");
2120             _alias = _alias.strip();
2121             char c0 = _alias[0];
2122             if (c0 == '"' || c0 == '\'') { // _alias.indexOf(' ') == -1 || 
2123                 print0(_alias);
2124             } else {
2125                 if(isEnabled(VisitorFeature.OutputQuotedIdentifier)) {
2126                     print0(_quotes ~ _alias ~ _quotes);
2127                 } else {
2128                     print0(_alias);
2129                 }
2130             }
2131         }
2132         return false;
2133     }
2134 
2135     override bool visit(SQLOrderBy x) {
2136         List!SQLSelectOrderByItem items = x.getItems();
2137 
2138         if (items.size() > 0) {
2139             if (x.isSibings()) {
2140                 print0(ucase ? "ORDER SIBLINGS BY " : "order siblings by ");
2141             } else {
2142                 print0(ucase ? "ORDER BY " : "order by ");
2143             }
2144 
2145             for (int i = 0, size = items.size(); i < size; ++i) {
2146                 if (i != 0) {
2147                     print0(", ");
2148                 }
2149                 SQLSelectOrderByItem item = items.get(i);
2150                 visit(item);
2151             }
2152         }
2153         return false;
2154     }
2155 
2156     override bool visit(SQLSelectOrderByItem x) {
2157         SQLExpr expr = x.getExpr();
2158 
2159         if (cast(SQLIntegerExpr)expr !is null) {
2160             print((cast(SQLIntegerExpr) expr).getNumber().longValue());
2161         } else {
2162             printExpr(expr);
2163         }
2164 
2165         SQLOrderingSpecification type = x.getType();
2166         if (type !is null) {
2167             print(' ');
2168             print0(ucase ? type.name : type.name_lcase);
2169         }
2170 
2171         string collate = x.getCollate();
2172         if (collate !is null) {
2173             print0(ucase ? " COLLATE " : " collate ");
2174             print0(collate);
2175         }
2176 
2177         SQLSelectOrderByItem.NullsOrderType nullsOrderType = x.getNullsOrderType();
2178         if (nullsOrderType.name.length != 0) {
2179             print(' ');
2180             print0(nullsOrderType.toFormalString());
2181         }
2182 
2183         return false;
2184     }
2185 
2186     protected void addTable(string table) {
2187         if (tables is null) {
2188             if (this.table is null) {
2189                 this.table = table;
2190                 return;
2191             } else {
2192                 tables = new LinkedHashSet!string();
2193                 tables.add(this.table);
2194             }
2195         }
2196         this.tables.add(table);
2197     }
2198 
2199     protected void printTableSourceExpr(SQLExpr expr) {
2200         if (exportTables) {
2201             addTable((cast(Object)(expr)).toString());
2202         }
2203 
2204         if (isEnabled(VisitorFeature.OutputDesensitize)) {
2205             string ident = null;
2206             if (cast(SQLIdentifierExpr)expr !is null) {
2207                 ident = (cast(SQLIdentifierExpr) expr).getName();
2208             } else if (cast(SQLPropertyExpr)expr !is null) {
2209                 SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) expr;
2210                 propertyExpr.getOwner().accept(this);
2211                 print('.');
2212 
2213                 ident = propertyExpr.getName();
2214             }
2215 
2216             if (ident !is null) {
2217                 string desensitizeTable = SQLUtils.desensitizeTable(ident);
2218                 print0(desensitizeTable);
2219                 return;
2220             }
2221         }
2222 
2223         if (tableMapping !is null && cast(SQLName)expr !is null) {
2224             string tableName;
2225             if (cast(SQLIdentifierExpr)expr !is null) {
2226                 tableName = (cast(SQLIdentifierExpr) expr).normalizedName();
2227             } else if (cast(SQLPropertyExpr)expr !is null) {
2228                 tableName = (cast(SQLPropertyExpr) expr).normalizedName();
2229             } else {
2230                 tableName = (cast(Object)(expr)).toString();
2231             }
2232 
2233             string destTableName = tableMapping.get(tableName);
2234             if (destTableName is null) {
2235                 if (cast(SQLPropertyExpr)expr !is null) {
2236                     SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) expr;
2237                     string propName = propertyExpr.getName();
2238                     destTableName = tableMapping.get(propName);
2239                     if (destTableName is null
2240                             && propName.length > 2 && charAt(propName, 0) == '`' && charAt(propName, propName.length - 1) == '`') {
2241                         destTableName = tableMapping.get(propName.substring(1, propName.length - 1));
2242                     }
2243 
2244                     if (destTableName !is null) {
2245                         propertyExpr.getOwner().accept(this);
2246                         print('.');
2247                         print(destTableName);
2248                         return;
2249                     }
2250                 } else if (cast(SQLIdentifierExpr)expr !is null) {
2251                     bool quote = tableName.length > 2 && charAt(tableName, 0) == '`' && charAt(tableName, tableName.length - 1) == '`';
2252                     if (quote) {
2253                         destTableName = tableMapping.get(tableName.substring(1, tableName.length - 1));
2254                     }
2255                 }
2256             }
2257             if (destTableName !is null) {
2258                 tableName = destTableName;
2259                 print0(tableName);
2260                 return;
2261             }
2262         }
2263 
2264         SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr) expr;
2265         SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) expr;
2266 
2267         if (identifierExpr !is null) {
2268              string name = identifierExpr.getName();
2269             if (!this.parameterized) {
2270                 if(isEnabled(VisitorFeature.OutputQuotedIdentifier)) {
2271                     print0(_quotes ~ name ~ _quotes);
2272                 } else {
2273                     print0(name);
2274                 }
2275                 return;
2276             }
2277 
2278             bool shardingSupport = this.shardingSupport
2279                     && this.parameterized;
2280 
2281             if (shardingSupport) {
2282                 string nameUnwrappe = unwrapShardingTable(name);
2283 
2284                 if (!(name == nameUnwrappe)) {
2285                     incrementReplaceCunt();
2286                 }
2287 
2288                 print0(nameUnwrappe);
2289             } else {
2290                 print0(name);
2291             }
2292         } else if (propertyExpr !is null) {
2293             SQLExpr owner = propertyExpr.getOwner();
2294 
2295             printTableSourceExpr(owner);
2296             print('.');
2297 
2298              string name = propertyExpr.getName();
2299             if (!this.parameterized) {
2300                 print0(propertyExpr.getName());
2301                 return;
2302             }
2303 
2304             bool shardingSupport = this.shardingSupport
2305                     && this.parameterized;
2306 
2307             if (shardingSupport) {
2308                 string nameUnwrappe = unwrapShardingTable(name);
2309 
2310                 if (!(name == nameUnwrappe)) {
2311                     incrementReplaceCunt();
2312                 }
2313 
2314                 print0(nameUnwrappe);
2315             } else {
2316                 print0(name);
2317             }
2318         } else {
2319             expr.accept(this);
2320         }
2321 
2322     }
2323 
2324     override bool visit(SQLExprTableSource x) {
2325         printTableSourceExpr(x.getExpr());
2326 
2327         string _alias = x.getAlias();
2328         if (_alias !is null) {
2329             print(' ');
2330             print0(_alias);
2331         }
2332 
2333         if (isPrettyFormat() && x.hasAfterComment()) {
2334             print(' ');
2335             printlnComment(x.getAfterCommentsDirect());
2336         }
2337 
2338         return false;
2339     }
2340 
2341     override bool visit(SQLSelectStatement stmt) {
2342         List!SQLCommentHint headHints = stmt.getHeadHintsDirect();
2343         if (headHints !is null) {
2344             foreach(SQLCommentHint hint  ;  headHints) {
2345                 hint.accept(this);
2346                 println();
2347             }
2348         }
2349 
2350         SQLSelect select = stmt.getSelect();
2351         this.visit(select);
2352 
2353         return false;
2354     }
2355 
2356     override bool visit(SQLVariantRefExpr x) {
2357         int index = x.getIndex();
2358 
2359         if (index < 0 || inputParameters is null || index >= inputParameters.size()) {
2360             print0(x.getName());
2361             return false;
2362         }
2363 
2364         Object param = inputParameters.get(index);
2365 
2366         SQLObject parent = x.getParent();
2367 
2368         bool _in;
2369         if (cast(SQLInListExpr)parent !is null) {
2370             _in = true;
2371         } else if (cast(SQLBinaryOpExpr)parent !is null) {
2372             SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) parent;
2373             if (binaryOpExpr.getOperator() == SQLBinaryOperator.Equality) {
2374                 _in = true;
2375             } else {
2376                 _in = false;
2377             }
2378         } else {
2379             _in = false;
2380         }
2381 
2382         if (_in && cast(Collection!Object)param !is null) {
2383             bool first = true;
2384             foreach (Object item ; cast(Collection!Object) param) {
2385                 if (!first) {
2386                     print0(", ");
2387                 }
2388                 printParameter(item);
2389                 first = false;
2390             }
2391         } else {
2392             printParameter(param);
2393         }
2394         return false;
2395     }
2396 
2397     void printParameter(Object param) {
2398         if (param is null) {
2399             print0(ucase ? "NULL" : "null");
2400             return;
2401         }
2402 
2403         if (cast(String)param !is null) {
2404             SQLCharExpr charExpr = new SQLCharExpr(cast(String) param);
2405             visit(charExpr);
2406             return;
2407         }
2408 
2409         if (cast(Number)param !is null //
2410             || cast(Boolean)param !is null) {
2411             print0((cast(Object)(param)).toString());
2412             return;
2413         }
2414 
2415         Bytes bytesData = cast(Bytes)param;
2416         if(bytesData !is null) {
2417             print0(bytesData);
2418             return;
2419         }
2420 
2421         //@gxc
2422         // if (cast(Date)param !is null) {
2423         //     print(cast(Date) param);
2424         //     return;
2425         // }
2426 
2427         // if (cast(InputStream)param !is null) {
2428         //     print0("'!(InputStream)");
2429         //     return;
2430         // }
2431 
2432         // if (cast(Reader)param !is null) {
2433         //     print0("'!(Reader)");
2434         //     return;
2435         // }
2436 
2437         // if (cast(Blob)param !is null) {
2438         //     print0("'!(Blob)");
2439         //     return;
2440         // }
2441 
2442         // if (cast(NClob)param !is null) {
2443         //     print0("'!(NClob)");
2444         //     return;
2445         // }
2446 
2447         // if (cast(Clob)param !is null) {
2448         //     print0("'!(Clob)");
2449         //     return;
2450         // }
2451 
2452         // if (cast(byte[])param !is null) {
2453         //     byte[] bytes = cast(byte[]) param;
2454         //     int bytesLen = bytes.length;
2455         //     char[] chars = new char[bytesLen * 2 + 3];
2456         //     chars[0] = 'x';
2457         //     chars[1] = '\'';
2458         //     for (int i = 0; i < bytes.length; i++) {
2459         //         int a = bytes[i] & 0xFF;
2460         //         int b0 = a >> 4;
2461         //         int b1 = a & 0xf;
2462 
2463         //         chars[i * 2 + 2] = cast(char) (b0 + (b0 < 10 ? 48 : 55)); //hexChars[b0];
2464         //         chars[i * 2 + 3] = cast(char) (b1 + (b1 < 10 ? 48 : 55));
2465         //     }
2466         //     chars[chars.length - 1] = '\'';
2467         //     print0(new String(chars));
2468         //     return;
2469         // }
2470         warningf("unhandled parameter: %s", typeid(param).name);
2471         print0("'" ~ typeid(param).name ~ "'");
2472     }
2473 
2474     override bool visit(SQLDropTableStatement x) {
2475         print0(ucase ? "DROP " : "drop ");
2476         List!SQLCommentHint hints = x.getHints();
2477         if (hints !is null) {
2478             printAndAccept!SQLCommentHint(hints, " ");
2479             print(' ');
2480         }
2481 
2482         if (x.isTemporary()) {
2483             print0(ucase ? "TEMPORARY TABLE " : "temporary table ");
2484         } else {
2485             print0(ucase ? "TABLE " : "table ");
2486         }
2487 
2488         if (x.isIfExists()) {
2489             print0(ucase ? "IF EXISTS " : "if exists ");
2490         }
2491 
2492         printAndAccept!SQLExprTableSource((x.getTableSources()), ", ");
2493 
2494         if (x.isCascade()) {
2495             printCascade();
2496         }
2497 
2498         if (x.isRestrict()) {
2499             print0(ucase ? " RESTRICT" : " restrict");
2500         }
2501 
2502         if (x.isPurge()) {
2503             print0(ucase ? " PURGE" : " purge");
2504         }
2505 
2506         return false;
2507     }
2508 
2509     protected void printCascade() {
2510         print0(ucase ? " CASCADE" : " cascade");
2511     }
2512 
2513     override bool visit(SQLDropViewStatement x) {
2514         print0(ucase ? "DROP VIEW " : "drop view ");
2515 
2516         if (x.isIfExists()) {
2517             print0(ucase ? "IF EXISTS " : "if exists ");
2518         }
2519 
2520         printAndAccept!SQLExprTableSource((x.getTableSources()), ", ");
2521 
2522         if (x.isCascade()) {
2523             printCascade();
2524         }
2525         return false;
2526     }
2527 
2528     override bool visit(SQLDropMaterializedViewStatement x) {
2529         print0(ucase ? "DROP VIEW " : "drop view ");
2530 
2531         if (x.isIfExists()) {
2532             print0(ucase ? "IF EXISTS " : "if exists ");
2533         }
2534 
2535         x.getName().accept(this);
2536 
2537         return false;
2538     }
2539 
2540     override bool visit(SQLDropEventStatement x) {
2541         print0(ucase ? "DROP EVENT " : "drop event ");
2542 
2543         if (x.isIfExists()) {
2544             print0(ucase ? "IF EXISTS " : "if exists ");
2545         }
2546 
2547         printExpr(x.getName());
2548         return false;
2549     }
2550 
2551     override bool visit(SQLColumnDefinition x) {
2552         bool parameterized = this.parameterized;
2553         this.parameterized = false;
2554 
2555         x.getName().accept(this);
2556 
2557         if (x.getDataType() !is null) {
2558             print(' ');
2559             x.getDataType().accept(this);
2560         }
2561 
2562         if (x.getDefaultExpr() !is null) {
2563             visitColumnDefault(x);
2564         }
2565 
2566         if (x.isAutoIncrement()) {
2567             print0(ucase ? " AUTO_INCREMENT" : " auto_increment");
2568         }
2569 
2570         foreach (SQLColumnConstraint item ; x.getConstraints()) {
2571             bool newLine = cast(SQLForeignKeyConstraint)item !is null //
2572                               || cast(SQLPrimaryKey)item !is null //
2573                               || cast(SQLColumnCheck)item !is null //
2574                               || cast(SQLColumnCheck)item !is null //
2575                               || item.getName() !is null;
2576             if (newLine) {
2577                 this.indentCount++;
2578                 println();
2579             } else {
2580                 print(' ');
2581             }
2582 
2583             item.accept(this);
2584 
2585             if (newLine) {
2586                 this.indentCount--;
2587             }
2588         }
2589 
2590         SQLExpr generatedAlawsAs = x.getGeneratedAlawsAs();
2591         if (generatedAlawsAs !is null) {
2592             print0(ucase ? " GENERATED ALWAYS AS " : " generated always as ");
2593             printExpr(generatedAlawsAs);
2594         }
2595 
2596         SQLColumnDefinition.Identity identity = x.getIdentity();
2597         if (identity !is null) {
2598             print(' ');
2599             identity.accept(this);
2600         }
2601 
2602         if (x.getEnable() !is null) {
2603             if (x.getEnable().booleanValue()) {
2604                 print0(ucase ? " ENABLE" : " enable");
2605             }
2606         }
2607 
2608         if (x.getComment() !is null) {
2609             print0(ucase ? " COMMENT " : " comment ");
2610             x.getComment().accept(this);
2611         }
2612 
2613         this.parameterized = parameterized;
2614 
2615         return false;
2616     }
2617 
2618     override
2619     bool visit(SQLColumnDefinition.Identity x) {
2620         print0(ucase ? "IDENTITY" : "identity");
2621         if (x.getSeed() !is null) {
2622             print0(" (");
2623             print(x.getSeed().intValue());
2624             print0(", ");
2625             print(x.getIncrement().intValue());
2626             print(')');
2627         }
2628         return false;
2629     }
2630 
2631     protected void visitColumnDefault(SQLColumnDefinition x) {
2632         print0(ucase ? " DEFAULT " : " default ");
2633         x.getDefaultExpr().accept(this);
2634     }
2635 
2636     override bool visit(SQLDeleteStatement x) {
2637         SQLTableSource from = x.getFrom();
2638         string _alias = x.getAlias();
2639 
2640         if (from is null) {
2641             print0(ucase ? "DELETE FROM " : "delete from ");
2642             printTableSourceExpr(x.getTableName());
2643 
2644             if (_alias !is null) {
2645                 print(' ');
2646                 print0(_alias);
2647             }
2648         } else {
2649             print0(ucase ? "DELETE " : "delete ");
2650             printTableSourceExpr(x.getTableName());
2651             print0(ucase ? " FROM " : " from ");
2652             from.accept(this);
2653         }
2654 
2655         SQLExpr where = x.getWhere();
2656         if (where !is null) {
2657             println();
2658             print0(ucase ? "WHERE " : "where ");
2659             this.indentCount++;
2660             where.accept(this);
2661             this.indentCount--;
2662         }
2663 
2664         return false;
2665     }
2666 
2667     override bool visit(SQLCurrentOfCursorExpr x) {
2668         print0(ucase ? "CURRENT OF " : "current of ");
2669         printExpr(x.getCursorName());
2670         return false;
2671     }
2672 
2673     override bool visit(SQLInsertStatement x) {
2674         if (x.isUpsert()) {
2675             print0(ucase ? "UPSERT INTO " : "upsert into ");
2676         } else {
2677             print0(ucase ? "INSERT INTO " : "insert into ");
2678         }
2679 
2680         x.getTableSource().accept(this);
2681 
2682         string columnsString = x.getColumnsString();
2683         if (columnsString !is null) {
2684             print0(columnsString);
2685         } else {
2686             printInsertColumns(x.getColumns());
2687         }
2688 
2689         if (!x.getValuesList().isEmpty()) {
2690             println();
2691             print0(ucase ? "VALUES " : "values ");
2692             printAndAccept!ValuesClause((x.getValuesList()), ", ");
2693         } else {
2694             if (x.getQuery() !is null) {
2695                 println();
2696                 x.getQuery().accept(this);
2697             }
2698         }
2699 
2700         return false;
2701     }
2702 
2703     void printInsertColumns(List!SQLExpr columns) {
2704          int size = columns.size();
2705         if (size > 0) {
2706             if (size > 5) {
2707                 this.indentCount++;
2708                 println();
2709             } else {
2710                 print(' ');
2711             }
2712             print('(');
2713             for (int i = 0; i < size; ++i) {
2714                 if (i != 0) {
2715                     if (i % 5 == 0) {
2716                         println();
2717                     }
2718                     print0(", ");
2719                 }
2720 
2721                 SQLExpr column = columns.get(i);
2722                 if (cast(SQLIdentifierExpr)column !is null) {
2723                     visit(cast(SQLIdentifierExpr) column);
2724                 } else {
2725                     printExpr(column);
2726                 }
2727 
2728                 String dataType = cast(String) column.getAttribute("dataType");
2729                 if (dataType !is null) {
2730                     print(' ');
2731                     print(dataType.value());
2732                 }
2733             }
2734             print(')');
2735             if (size > 5) {
2736                 this.indentCount--;
2737             }
2738         }
2739     }
2740 
2741     override bool visit(SQLUpdateSetItem x) {
2742         printExpr(x.getColumn());
2743         print0(" = ");
2744         printExpr(x.getValue());
2745         return false;
2746     }
2747 
2748     override bool visit(SQLUpdateStatement x) {
2749         print0(ucase ? "UPDATE " : "update ");
2750 
2751         printTableSource(x.getTableSource());
2752 
2753         println();
2754         print0(ucase ? "SET " : "set ");
2755         for (int i = 0, size = x.getItems().size(); i < size; ++i) {
2756             if (i != 0) {
2757                 print0(", ");
2758             }
2759             SQLUpdateSetItem item = x.getItems().get(i);
2760             visit(item);
2761         }
2762 
2763         SQLExpr where = x.getWhere();
2764         if (where !is null) {
2765             println();
2766             indentCount++;
2767             print0(ucase ? "WHERE " : "where ");
2768             printExpr(where);
2769             indentCount--;
2770         }
2771 
2772         return false;
2773     }
2774 
2775     protected void printTableElements(List!SQLTableElement tableElementList) {
2776         int size = tableElementList.size();
2777         if (size == 0) {
2778             return;
2779         }
2780 
2781         print0(" (");
2782 
2783         this.indentCount++;
2784         println();
2785         for (int i = 0; i < size; ++i) {
2786             SQLTableElement element = tableElementList.get(i);
2787             element.accept(this);
2788 
2789             if (i != size - 1) {
2790                 print(',');
2791             }
2792             if (this.isPrettyFormat() && element.hasAfterComment()) {
2793                 print(' ');
2794                 printlnComment(element.getAfterCommentsDirect());
2795             }
2796 
2797             if (i != size - 1) {
2798                 println();
2799             }
2800         }
2801         this.indentCount--;
2802         println();
2803         print(')');
2804     }
2805 
2806     override bool visit(SQLCreateTableStatement x) {
2807         printCreateTable(x, true);
2808 
2809         Map!(string, SQLObject) options = x.getTableOptions();
2810         if (options.size() > 0) {
2811             println();
2812             print0(ucase ? "WITH (" : "with (");
2813             int i = 0;
2814             foreach (string key, SQLObject v ; x.getTableOptions()) {
2815                 if (i > 0) {
2816                     print0(", ");
2817                 }
2818                 // string key = option.getKey();
2819                 print0(key);
2820 
2821                 print0(" = ");
2822 
2823                 v.accept(this);
2824                 ++i;
2825             }
2826             print(')');
2827         }
2828 
2829         return false;
2830     }
2831 
2832     protected void printCreateTable(SQLCreateTableStatement x, bool printSelect) {
2833         print0(ucase ? "CREATE " : "create ");
2834 
2835          SQLCreateTableStatement.Type tableType = x.getType();
2836         if (SQLCreateTableStatement.Type.GLOBAL_TEMPORARY == (tableType)) {
2837             print0(ucase ? "GLOBAL TEMPORARY " : "global temporary ");
2838         } else if (SQLCreateTableStatement.Type.LOCAL_TEMPORARY == (tableType)) {
2839             print0(ucase ? "LOCAL TEMPORARY " : "local temporary ");
2840         }
2841         print0(ucase ? "TABLE " : "table ");
2842 
2843         if (x.isIfNotExiists()) {
2844             print0(ucase ? "IF NOT EXISTS " : "if not exists ");
2845         }
2846 
2847         printTableSourceExpr(x.getName());
2848 
2849         printTableElements(x.getTableElementList());
2850 
2851         SQLExprTableSource inherits = x.getInherits();
2852         if (inherits !is null) {
2853             print0(ucase ? " INHERITS (" : " inherits (");
2854             inherits.accept(this);
2855             print(')');
2856         }
2857 
2858         SQLName storedAs = x.getStoredAs();
2859         if (storedAs !is null) {
2860             print0(ucase ? " STORE AS " : " store as ");
2861             printExpr(storedAs);
2862         }
2863 
2864         SQLSelect select = x.getSelect();
2865         if (printSelect && select !is null) {
2866             println();
2867             print0(ucase ? "AS" : "as");
2868 
2869             println();
2870             visit(select);
2871         }
2872     }
2873 
2874      bool visit(SQLUniqueConstraint x) {
2875         if (x.getName() !is null) {
2876             print0(ucase ? "CONSTRAINT " : "constraint ");
2877             x.getName().accept(this);
2878             print(' ');
2879         }
2880 
2881         print0(ucase ? "UNIQUE (" : "unique (");
2882         List!SQLSelectOrderByItem columns = x.getColumns();
2883         for (int i = 0, size = columns.size(); i < size; ++i) {
2884             if (i != 0) {
2885                 print0(", ");
2886             }
2887             visit(columns.get(i));
2888         }
2889         print(')');
2890         return false;
2891     }
2892 
2893     override bool visit(SQLNotNullConstraint x) {
2894         SQLName name = x.getName();
2895         if (name !is null) {
2896             print0(ucase ? "CONSTRAINT " : "constraint ");
2897             name.accept(this);
2898             print(' ');
2899         }
2900         print0(ucase ? "NOT NULL" : "not null");
2901 
2902         List!SQLCommentHint hints = x.hints;
2903         if (hints !is null) {
2904             print(' ');
2905             foreach (SQLCommentHint hint ; hints) {
2906                 hint.accept(this);
2907             }
2908         }
2909 
2910         return false;
2911     }
2912 
2913     override bool visit(SQLNullConstraint x) {
2914         SQLName name = x.getName();
2915     	if (name !is null) {
2916     		print0(ucase ? "CONSTRAINT " : "constraint ");
2917             name.accept(this);
2918     		print(' ');
2919     	}
2920     	print0(ucase ? "NULL" : "null");
2921     	return false;
2922     }
2923 
2924     override
2925     bool visit(SQLUnionQuery x) {
2926         SQLUnionOperator operator = x.getOperator();
2927         SQLSelectQuery left = x.getLeft();
2928         SQLSelectQuery right = x.getRight();
2929 
2930         bool bracket = x.isBracket() && !( cast(SQLUnionQueryTableSource)x.getParent() !is null);
2931 
2932         SQLOrderBy orderBy = x.getOrderBy();
2933         if ((!bracket)
2934                 && cast(SQLUnionQuery)left !is null
2935                 && (cast(SQLUnionQuery) left).getOperator() == operator
2936                 && !right.isBracket()
2937                 && orderBy is null) {
2938 
2939             SQLUnionQuery leftUnion = cast(SQLUnionQuery) left;
2940 
2941             List!SQLSelectQuery rights = new ArrayList!SQLSelectQuery();
2942             rights.add(right);
2943 
2944             for (;;) {
2945                 SQLSelectQuery leftLeft = leftUnion.getLeft();
2946                 SQLSelectQuery leftRight = leftUnion.getRight();
2947 
2948                 if ((!leftUnion.isBracket())
2949                         && leftUnion.getOrderBy() is null
2950                         && (!leftLeft.isBracket())
2951                         && (!leftRight.isBracket())
2952                         && cast(SQLUnionQuery)leftLeft !is null
2953                         && (cast(SQLUnionQuery) leftLeft).getOperator() == operator) {
2954                     rights.add(leftRight);
2955                     leftUnion =  cast(SQLUnionQuery) leftLeft;
2956                     continue;
2957                 } else {
2958                     rights.add(leftRight);
2959                     rights.add(leftLeft);
2960                 }
2961                 break;
2962             }
2963 
2964             for (int i = rights.size() - 1; i >= 0; i--) {
2965                 SQLSelectQuery item = rights.get(i);
2966                 item.accept(this);
2967 
2968                 if (i > 0) {
2969                     println();
2970                     print0(ucase ? operator.name : operator.name_lcase);
2971                     println();
2972                 }
2973             }
2974             return false;
2975         }
2976 
2977         if (bracket) {
2978             print('(');
2979         }
2980 
2981         if (left !is null) {
2982             for (;;) {
2983                 if (typeid(left) == typeid(SQLUnionQuery)) {
2984                     SQLUnionQuery leftUnion = cast(SQLUnionQuery) left;
2985                     SQLSelectQuery leftLeft = leftUnion.getLeft();
2986                     SQLSelectQuery leftRigt = leftUnion.getRight();
2987                     if ((!leftUnion.isBracket())
2988                             && cast(SQLSelectQueryBlock)leftUnion.getRight() !is null
2989                             && leftUnion.getLeft() !is null
2990                             && leftUnion.getOrderBy() is null)
2991                     {
2992                         if (typeid(leftLeft) == typeid(SQLUnionQuery)) {
2993                             visit(cast(SQLUnionQuery) leftLeft);
2994                         } else {
2995                             printQuery(leftLeft);
2996                         }
2997                         println();
2998                         print0(ucase ? leftUnion.getOperator().name : leftUnion.getOperator().name_lcase);
2999                         println();
3000                         leftRigt.accept(this);
3001                     } else {
3002                         visit(leftUnion);
3003                     }
3004                 } else {
3005                     left.accept(this);
3006                 }
3007                 break;
3008             }
3009         }
3010 
3011         if (right is null) {
3012             return false;
3013         }
3014 
3015         println();
3016         print0(ucase ? operator.name : operator.name_lcase);
3017         println();
3018 
3019         bool needParen = false;
3020         if (orderBy !is null
3021                 && (!right.isBracket()) && cast(SQLSelectQueryBlock)right !is null) {
3022             SQLSelectQueryBlock rightQuery = cast(SQLSelectQueryBlock) right;
3023             if (rightQuery.getOrderBy() !is null || rightQuery.getLimit() !is null) {
3024                 needParen = true;
3025             }
3026         }
3027 
3028         if (needParen) {
3029             print('(');
3030             right.accept(this);
3031             print(')');
3032         } else {
3033             right.accept(this);
3034         }
3035 
3036         if (orderBy !is null) {
3037             println();
3038             orderBy.accept(this);
3039         }
3040 
3041         SQLLimit limit = x.getLimit();
3042         if (limit !is null) {
3043             println();
3044             limit.accept(this);
3045         }
3046 
3047         if (bracket) {
3048             print(')');
3049         }
3050 
3051         return false;
3052     }
3053 
3054     override
3055     bool visit(SQLUnaryExpr x) {
3056         print0(x.getOperator().name);
3057 
3058         SQLExpr expr = x.getExpr();
3059 
3060         switch (x.getOperator().name) {
3061             case SQLUnaryOperator.BINARY.name:
3062             case SQLUnaryOperator.Prior.name:
3063             case SQLUnaryOperator.ConnectByRoot.name:
3064                 print(' ');
3065                 expr.accept(this);
3066                 return false;
3067             default:
3068                 break;
3069         }
3070 
3071         if (cast(SQLBinaryOpExpr)expr !is null) {
3072             print('(');
3073             expr.accept(this);
3074             print(')');
3075         } else if (cast(SQLUnaryExpr)expr !is null) {
3076             print('(');
3077             expr.accept(this);
3078             print(')');
3079         } else {
3080             expr.accept(this);
3081         }
3082         return false;
3083     }
3084 
3085     override
3086     bool visit(SQLHexExpr x) {
3087         if (this.parameterized) {
3088             print('?');
3089             incrementReplaceCunt();
3090 
3091             if(this.parameters !is null){
3092                 ExportParameterVisitorUtils.exportParameter(this.parameters, x);
3093             }
3094             return false;
3095         }
3096 
3097         print0("0x");
3098         print0(x.getHex());
3099 
3100         String charset = cast(String) x.getAttribute("USING");
3101         if (charset !is null) {
3102             print0(ucase ? " USING " : " using ");
3103             print0(charset.value());
3104         }
3105 
3106         return false;
3107     }
3108 
3109 
3110     override
3111     bool visit(SQLBlobExpr x) {
3112         if (this.parameterized) {
3113             print('?');
3114             incrementReplaceCunt();
3115 
3116             if(this.parameters !is null){
3117                 ExportParameterVisitorUtils.exportParameter(this.parameters, x);
3118             }
3119             return false;
3120         }
3121 
3122         print0("0x");
3123         print0(x.getHex());
3124 
3125         String charset = cast(String) x.getAttribute("USING");
3126         if (charset !is null) {
3127             print0(ucase ? " USING " : " using ");
3128             print0(charset.value());
3129         }
3130 
3131         return false;
3132     }    
3133 
3134     override
3135     bool visit(SQLSetStatement x) {
3136         bool printSet = x.getAttribute("parser.set") == Boolean.TRUE || !DBType.ORACLE.opEquals(dbType);
3137         if (printSet) {
3138             print0(ucase ? "SET " : "set ");
3139         }
3140         SQLSetStatement.Option option = x.getOption();
3141         if (option.name.length != 0) {
3142             print(option.name());
3143             print(' ');
3144         }
3145 
3146         printAndAccept!SQLAssignItem((x.getItems()), ", ");
3147 
3148         if (x.getHints() !is null && x.getHints().size() > 0) {
3149             print(' ');
3150             printAndAccept!SQLCommentHint((x.getHints()), " ");
3151         }
3152 
3153         return false;
3154     }
3155 
3156     override
3157     bool visit(SQLAssignItem x) {
3158         x.getTarget().accept(this);
3159         print0(" = ");
3160         x.getValue().accept(this);
3161         return false;
3162     }
3163 
3164     override
3165     bool visit(SQLCallStatement x) {
3166         if (x.isBrace()) {
3167             print('{');
3168         }
3169         if (x.getOutParameter() !is null) {
3170             x.getOutParameter().accept(this);
3171             print0(" = ");
3172         }
3173 
3174         print0(ucase ? "CALL " : "call ");
3175         x.getProcedureName().accept(this);
3176         print('(');
3177 
3178         printAndAccept!SQLExpr((x.getParameters()), ", ");
3179         print(')');
3180         if (x.isBrace()) {
3181             print('}');
3182         }
3183         return false;
3184     }
3185 
3186     override
3187     bool visit(SQLJoinTableSource x) {
3188         SQLTableSource left = x.getLeft();
3189 
3190         if (cast(SQLJoinTableSource)left !is null
3191                 && (cast(SQLJoinTableSource) left).getJoinType() == SQLJoinTableSource.JoinType.COMMA
3192                 && x.getJoinType() != SQLJoinTableSource.JoinType.COMMA
3193                 && !DBType.POSTGRESQL.opEquals(dbType)) {
3194             print('(');
3195             printTableSource(left);
3196             print(')');
3197         } else {
3198             printTableSource(left);
3199         }
3200         this.indentCount++;
3201 
3202         if (x.getJoinType() == SQLJoinTableSource.JoinType.COMMA) {
3203             print(',');
3204         } else {
3205             println();
3206 
3207             if (x.isNatural()) {
3208                 print0(ucase ? "NATURAL " : "natural ");
3209             }
3210 
3211             printJoinType(x.getJoinType());
3212         }
3213         print(' ');
3214 
3215         SQLTableSource right = x.getRight();
3216         if (cast(SQLJoinTableSource)right !is null) {
3217             print('(');
3218             printTableSource(right);
3219             print(')');
3220         } else {
3221             printTableSource(right);
3222         }
3223 
3224         SQLExpr condition = x.getCondition();
3225         if (condition !is null) {
3226             bool newLine = false;
3227 
3228             if(cast(SQLSubqueryTableSource)right !is null) {
3229                 newLine = true;
3230             } else if (cast(SQLBinaryOpExpr)condition !is null) {
3231                 SQLBinaryOperator op = (cast(SQLBinaryOpExpr) condition).getOperator();
3232                 if (op == SQLBinaryOperator.BooleanAnd || op == SQLBinaryOperator.BooleanOr) {
3233                     newLine = true;
3234                 }
3235             } else if (cast(SQLBinaryOpExprGroup)condition !is null) {
3236                 newLine = true;
3237             }
3238             if (newLine) {
3239                 println();
3240             } else {
3241                 print(' ');
3242             }
3243             this.indentCount++;
3244             print0(ucase ? "ON " : "on ");
3245             printExpr(condition);
3246             this.indentCount--;
3247         }
3248 
3249         if (x.getUsing().size() > 0) {
3250             print0(ucase ? " USING (" : " using (");
3251             printAndAccept!SQLExpr((x.getUsing()), ", ");
3252             print(')');
3253         }
3254 
3255         if (x.getAlias() !is null) {
3256             print0(ucase ? " AS " : " as ");
3257             print0(x.getAlias());
3258         }
3259 
3260         this.indentCount--;
3261 
3262         return false;
3263     }
3264 
3265     protected void printJoinType(SQLJoinTableSource.JoinType joinType) {
3266         print0(ucase ? joinType.name : joinType.name_lcase);
3267     }
3268 
3269     // static {
3270     //     for (int len = 0; len < variantValuesCache.length; ++len) {
3271     //         StringBuilder buf = new StringBuilder();
3272     //         buf.append('(');
3273     //         for (int i = 0; i < len; ++i) {
3274     //             if (i != 0) {
3275     //                 if (i % 5 == 0) {
3276     //                     buf.append("\n\t\t");
3277     //                 }
3278     //                 buf.append(", ");
3279     //             }
3280     //             buf.append('?');
3281     //         }
3282     //         buf.append(')');
3283     //         variantValuesCache[len] = buf.toString();
3284     //     }
3285     // }
3286 
3287     override
3288     bool visit(ValuesClause x) {
3289         if ((!this.parameterized)
3290                 && isEnabled(VisitorFeature.OutputUseInsertValueClauseOriginalString)
3291                 && x.getOriginalString() !is null) {
3292             print0(x.getOriginalString());
3293             return false;
3294         }
3295 
3296         int xReplaceCount = x.getReplaceCount();
3297          List!SQLExpr values = x.getValues();
3298 
3299         this.replaceCount += xReplaceCount;
3300 
3301         if (xReplaceCount == values.size() && xReplaceCount < variantValuesCache.length) {
3302             string variantValues = variantValuesCache[xReplaceCount];
3303             print0(variantValues);
3304             return false;
3305         }
3306 
3307         print('(');
3308         this.indentCount++;
3309 
3310 
3311         for (int i = 0, size = values.size(); i < size; ++i) {
3312             if (i != 0) {
3313                 if (i % 5 == 0) {
3314                     println();
3315                 }
3316                 print0(", ");
3317             }
3318 
3319             SQLExpr expr = values.get(i);
3320             if (cast(SQLIntegerExpr)expr !is null) {
3321                 printInteger(cast(SQLIntegerExpr) expr, parameterized);
3322             } else if (cast(SQLCharExpr)expr !is null) {
3323                 visit(cast(SQLCharExpr) expr);
3324             } else if (cast(SQLBooleanExpr)expr !is null) {
3325                 visit(cast(SQLBooleanExpr) expr);
3326             } else if (cast(SQLNumberExpr)expr !is null) {
3327                 visit(cast(SQLNumberExpr) expr);
3328             } else if (cast(SQLNullExpr)expr !is null) {
3329                 visit(cast(SQLNullExpr) expr);
3330             } else if (cast(SQLVariantRefExpr)expr !is null) {
3331                 visit(cast(SQLVariantRefExpr) expr);
3332             } else if (cast(SQLNCharExpr)expr !is null) {
3333                 visit(cast(SQLNCharExpr) expr);
3334             } else {
3335                 expr.accept(this);
3336             }
3337         }
3338 
3339         this.indentCount--;
3340         print(')');
3341         return false;
3342     }
3343 
3344     override
3345     bool visit(SQLSomeExpr x) {
3346         print0(ucase ? "SOME (" : "some (");
3347         this.indentCount++;
3348         println();
3349         x.getSubQuery().accept(this);
3350         this.indentCount--;
3351         println();
3352         print(')');
3353         return false;
3354     }
3355 
3356     override
3357     bool visit(SQLAnyExpr x) {
3358         print0(ucase ? "ANY (" : "any (");
3359         this.indentCount++;
3360         println();
3361         x.getSubQuery().accept(this);
3362         this.indentCount--;
3363         println();
3364         print(')');
3365         return false;
3366     }
3367 
3368     override
3369     bool visit(SQLAllExpr x) {
3370         print0(ucase ? "ALL (" : "all (");
3371         this.indentCount++;
3372         println();
3373         x.getSubQuery().accept(this);
3374         this.indentCount--;
3375         println();
3376         print(')');
3377         return false;
3378     }
3379 
3380     override
3381     bool visit(SQLInSubQueryExpr x) {
3382         x.getExpr().accept(this);
3383         if (x.isNot()) {
3384             print0(ucase ? " NOT IN (" : " not in (");
3385         } else {
3386             print0(ucase ? " IN (" : " in (");
3387         }
3388 
3389         this.indentCount++;
3390         println();
3391         x.getSubQuery().accept(this);
3392         this.indentCount--;
3393         println();
3394         print(')');
3395 
3396         return false;
3397     }
3398 
3399     override
3400     bool visit(SQLListExpr x) {
3401         print('(');
3402         printAndAccept!SQLExpr((x.getItems()), ", ");
3403         print(')');
3404 
3405         return false;
3406     }
3407 
3408     override
3409     bool visit(SQLSubqueryTableSource x) {
3410         print('(');
3411         this.indentCount++;
3412         println();
3413         this.visit(x.getSelect());
3414         this.indentCount--;
3415         println();
3416         print(')');
3417 
3418         if (x.getAlias() !is null) {
3419             print(' ');
3420             print0(x.getAlias());
3421         }
3422 
3423         return false;
3424     }
3425 
3426     override
3427     bool visit(SQLTruncateStatement x) {
3428         print0(ucase ? "TRUNCATE TABLE " : "truncate table ");
3429         printAndAccept!SQLExprTableSource((x.getTableSources()), ", ");
3430         
3431         if (x.isDropStorage()) {
3432             print0(ucase ? " DROP STORAGE" : " drop storage");    
3433         }
3434         
3435         if (x.isReuseStorage()) {
3436             print0(ucase ? " REUSE STORAGE" : " reuse storage");    
3437         }
3438         
3439         if (x.isIgnoreDeleteTriggers()) {
3440             print0(ucase ? " IGNORE DELETE TRIGGERS" : " ignore delete triggers");    
3441         }
3442         
3443         if (x.isRestrictWhenDeleteTriggers()) {
3444             print0(ucase ? " RESTRICT WHEN DELETE TRIGGERS" : " restrict when delete triggers");    
3445         }
3446         
3447         if (x.isContinueIdentity()) {
3448             print0(ucase ? " CONTINUE IDENTITY" : " continue identity");
3449         }
3450         
3451         if (x.isImmediate()) {
3452             print0(ucase ? " IMMEDIATE" : " immediate");    
3453         }
3454         
3455         return false;
3456     }
3457 
3458     override
3459     bool visit(SQLDefaultExpr x) {
3460         print0(ucase ? "DEFAULT" : "default");
3461         return false;
3462     }
3463 
3464     override
3465     void endVisit(SQLCommentStatement x) {
3466 
3467     }
3468 
3469     override
3470     bool visit(SQLCommentStatement x) {
3471         print0(ucase ? "COMMENT ON " : "comment on ");
3472         if (x.getType().name.length != 0) {
3473             print0(x.getType().name);
3474             print(' ');
3475         }
3476         x.getOn().accept(this);
3477 
3478         print0(ucase ? " IS " : " is ");
3479         x.getComment().accept(this);
3480 
3481         return false;
3482     }
3483 
3484     override
3485     bool visit(SQLUseStatement x) {
3486         print0(ucase ? "USE " : "use ");
3487         x.getDatabase().accept(this);
3488         return false;
3489     }
3490 
3491     protected bool isOdps() {
3492         return DBType.ODPS.opEquals(dbType);
3493     }
3494 
3495     override
3496     bool visit(SQLAlterTableAddColumn x) {
3497         bool odps = isOdps();
3498         if (odps) {
3499             print0(ucase ? "ADD COLUMNS (" : "add columns (");
3500         } else {
3501             print0(ucase ? "ADD (" : "add (");
3502         }
3503         printAndAccept!SQLColumnDefinition((x.getColumns()), ", ");
3504         print(')');
3505         return false;
3506     }
3507 
3508     override
3509     bool visit(SQLAlterTableDropColumnItem x) {
3510         print0(ucase ? "DROP COLUMN " : "drop column ");
3511         this.printAndAccept!SQLName((x.getColumns()), ", ");
3512 
3513         if (x.isCascade()) {
3514             print0(ucase ? " CASCADE" : " cascade");
3515         }
3516         return false;
3517     }
3518 
3519     override
3520     void endVisit(SQLAlterTableAddColumn x) {
3521 
3522     }
3523 
3524     override
3525     bool visit(SQLDropIndexStatement x) {
3526         print0(ucase ? "DROP INDEX " : "drop index ");
3527         x.getIndexName().accept(this);
3528 
3529         SQLExprTableSource table = x.getTableName();
3530         if (table !is null) {
3531             print0(ucase ? " ON " : " on ");
3532             table.accept(this);
3533         }
3534 
3535         SQLExpr algorithm = x.getAlgorithm();
3536         if (algorithm !is null) {
3537             print0(ucase ? " ALGORITHM " : " algorithm ");
3538             algorithm.accept(this);
3539         }
3540 
3541         SQLExpr lockOption = x.getLockOption();
3542         if (lockOption !is null) {
3543             print0(ucase ? " LOCK " : " lock ");
3544             lockOption.accept(this);
3545         }
3546 
3547         return false;
3548     }
3549 
3550     override
3551     bool visit(SQLDropLogFileGroupStatement x) {
3552         print0(ucase ? "DROP LOGFILE GROUP " : "drop logfile group ");
3553         x.getName().accept(this);
3554 
3555         return false;
3556     }
3557 
3558     override
3559     bool visit(SQLDropServerStatement x) {
3560         print0(ucase ? "DROP SERVER " : "drop server ");
3561         if (x.isIfExists()) {
3562             print0(ucase ? "IF EXISTS " : "if exists ");
3563         }
3564         x.getName().accept(this);
3565 
3566         return false;
3567     }
3568 
3569     override
3570     bool visit(SQLDropTypeStatement x) {
3571         print0(ucase ? "DROP TYPE " : "drop type ");
3572         if (x.isIfExists()) {
3573             print0(ucase ? "IF EXISTS " : "if exists ");
3574         }
3575         x.getName().accept(this);
3576 
3577         return false;
3578     }
3579 
3580     override
3581     bool visit(SQLDropSynonymStatement x) {
3582         if (x.isPublic()) {
3583             print0(ucase ? "DROP PUBLIC SYNONYM " : "drop synonym ");
3584         } else {
3585             print0(ucase ? "DROP SYNONYM " : "drop synonym ");
3586         }
3587 
3588         if (x.isIfExists()) {
3589             print0(ucase ? "IF EXISTS " : "if exists ");
3590         }
3591 
3592         x.getName().accept(this);
3593 
3594         if (x.isForce()) {
3595             print0(ucase ? " FORCE" : " force");
3596         }
3597 
3598         return false;
3599     }
3600 
3601     override
3602     bool visit(SQLSavePointStatement x) {
3603         print0(ucase ? "SAVEPOINT" : "savepoint");
3604         if (x.getName() !is null) {
3605             print(' ');
3606             x.getName().accept(this);
3607         }
3608         return false;
3609     }
3610 
3611     override
3612     bool visit(SQLReleaseSavePointStatement x) {
3613         print0(ucase ? "RELEASE SAVEPOINT " : "release savepoint ");
3614         x.getName().accept(this);
3615         return false;
3616     }
3617 
3618     override
3619     bool visit(SQLRollbackStatement x) {
3620         print0(ucase ? "ROLLBACK" : "rollback");
3621         if (x.getTo() !is null) {
3622             print0(ucase ? " TO " : " to ");
3623             x.getTo().accept(this);
3624         }
3625         return false;
3626     }
3627 
3628     override bool visit(SQLCommentHint x) {
3629         if (x.hasBeforeComment()) {
3630             printlnComment(x.getBeforeCommentsDirect());
3631             print0(" ");
3632         }
3633 
3634         print0("/*");
3635         print0(x.getText());
3636         print0("*/");
3637         return false;
3638     }
3639 
3640     override
3641     bool visit(SQLCreateDatabaseStatement x) {
3642         print0(ucase ? "CREATE DATABASE " : "create database ");
3643         if (x.isIfNotExists()) {
3644             print0(ucase ? "IF NOT EXISTS " : "if not exists ");
3645         }
3646         x.getName().accept(this);
3647 
3648         if (x.getCharacterSet() !is null) {
3649             print0(ucase ? " CHARACTER SET " : " character set ");
3650             print0(x.getCharacterSet());
3651         }
3652 
3653         if (x.getCollate() !is null) {
3654             print0(ucase ? " COLLATE " : " collate ");
3655             print0(x.getCollate());
3656         }
3657 
3658         return false;
3659     }
3660 
3661     override
3662     bool visit(SQLAlterViewStatement x) {
3663         print0(ucase ? "ALTER " : "atler ");
3664 
3665         this.indentCount++;
3666         string algorithm = x.getAlgorithm();
3667         if (algorithm !is null && algorithm.length > 0) {
3668             print0(ucase ? "ALGORITHM = " : "algorithm = ");
3669             print0(algorithm);
3670             println();
3671         }
3672 
3673         SQLName definer = x.getDefiner();
3674         if (definer !is null) {
3675             print0(ucase ? "DEFINER = " : "definer = ");
3676             definer.accept(this);
3677             println();
3678         }
3679 
3680         string sqlSecurity = x.getSqlSecurity();
3681         if (sqlSecurity !is null && sqlSecurity.length > 0) {
3682             print0(ucase ? "SQL SECURITY = " : "sql security = ");
3683             print0(sqlSecurity);
3684             println();
3685         }
3686 
3687         this.indentCount--;
3688 
3689         print0(ucase ? "VIEW " : "view ");
3690 
3691         if (x.isIfNotExists()) {
3692             print0(ucase ? "IF NOT EXISTS " : "if not exists ");
3693         }
3694 
3695         x.getTableSource().accept(this);
3696 
3697         if (x.getColumns().size() > 0) {
3698             print0(" (");
3699             this.indentCount++;
3700             println();
3701             for (int i = 0; i < x.getColumns().size(); ++i) {
3702                 if (i != 0) {
3703                     print0(", ");
3704                     println();
3705                 }
3706                 x.getColumns().get(i).accept(this);
3707             }
3708             this.indentCount--;
3709             println();
3710             print(')');
3711         }
3712 
3713         if (x.getComment() !is null) {
3714             println();
3715             print0(ucase ? "COMMENT " : "comment ");
3716             x.getComment().accept(this);
3717         }
3718 
3719         println();
3720         print0(ucase ? "AS" : "as");
3721         println();
3722 
3723         x.getSubQuery().accept(this);
3724 
3725         if (x.isWithCheckOption()) {
3726             println();
3727             print0(ucase ? "WITH CHECK OPTION" : "with check option");
3728         }
3729 
3730         return false;
3731     }
3732 
3733     override
3734     bool visit(SQLCreateViewStatement x) {
3735         print0(ucase ? "CREATE " : "create ");
3736         if (x.isOrReplace()) {
3737             print0(ucase ? "OR REPLACE " : "or replace ");
3738         }
3739 
3740         this.indentCount++;
3741         string algorithm = x.getAlgorithm();
3742         if (algorithm !is null && algorithm.length > 0) {
3743             print0(ucase ? "ALGORITHM = " : "algorithm = ");
3744             print0(algorithm);
3745             println();
3746         }
3747 
3748         SQLName definer = x.getDefiner();
3749         if (definer !is null) {
3750             print0(ucase ? "DEFINER = " : "definer = ");
3751             definer.accept(this);
3752             println();
3753         }
3754 
3755         string sqlSecurity = x.getSqlSecurity();
3756         if (sqlSecurity !is null && sqlSecurity.length > 0) {
3757             print0(ucase ? "SQL SECURITY = " : "sql security = ");
3758             print0(sqlSecurity);
3759             println();
3760         }
3761 
3762         this.indentCount--;
3763 
3764         print0(ucase ? "VIEW " : "view ");
3765 
3766         if (x.isIfNotExists()) {
3767             print0(ucase ? "IF NOT EXISTS " : "if not exists ");
3768         }
3769 
3770         x.getTableSource().accept(this);
3771 
3772         if (x.getColumns().size() > 0) {
3773             print0(" (");
3774             this.indentCount++;
3775             println();
3776             for (int i = 0; i < x.getColumns().size(); ++i) {
3777                 if (i != 0) {
3778                     print0(", ");
3779                     println();
3780                 }
3781                 x.getColumns().get(i).accept(this);
3782             }
3783             this.indentCount--;
3784             println();
3785             print(')');
3786         }
3787 
3788         if (x.getComment() !is null) {
3789             println();
3790             print0(ucase ? "COMMENT " : "comment ");
3791             x.getComment().accept(this);
3792         }
3793 
3794         println();
3795         print0(ucase ? "AS" : "as");
3796         println();
3797 
3798         x.getSubQuery().accept(this);
3799 
3800         if (x.isWithCheckOption()) {
3801             println();
3802             print0(ucase ? "WITH CHECK OPTION" : "with check option");
3803         }
3804 
3805         return false;
3806     }
3807 
3808     override bool visit(SQLCreateViewStatement.Column x) {
3809         x.getExpr().accept(this);
3810 
3811         if (x.getComment() !is null) {
3812             print0(ucase ? " COMMENT " : " comment ");
3813             x.getComment().accept(this);
3814         }
3815 
3816         return false;
3817     }
3818 
3819     override
3820     bool visit(SQLAlterTableDropIndex x) {
3821         print0(ucase ? "DROP INDEX " : "drop index ");
3822         x.getIndexName().accept(this);
3823         return false;
3824     }
3825 
3826     override
3827     bool visit(SQLOver x) {
3828         print0(ucase ? "OVER (" : "over (");
3829         if (x.getPartitionBy().size() > 0) {
3830             print0(ucase ? "PARTITION BY " : "partition by ");
3831             printAndAccept!SQLExpr((x.getPartitionBy()), ", ");
3832             print(' ');
3833         }
3834         
3835         if (x.getOrderBy() !is null) {
3836             x.getOrderBy().accept(this);
3837         }
3838         
3839         if (x.getOf() !is null) {
3840             print0(ucase ? " OF " : " of ");
3841             x.getOf().accept(this);
3842         }
3843 
3844         if (x.getWindowing() !is null) {
3845             if (SQLOver.WindowingType.ROWS == (x.getWindowingType())) {
3846                 print0(ucase ? " ROWS " : " rows ");
3847             } else if (SQLOver.WindowingType.RANGE == (x.getWindowingType())) {
3848                 print0(ucase ? " RANGE " : " range ");
3849             }
3850 
3851             printWindowingExpr(x.getWindowing());
3852 
3853             if (x.isWindowingPreceding()) {
3854                 print0(ucase ? " PRECEDING" : " preceding");
3855             } else if (x.isWindowingFollowing()) {
3856                 print0(ucase ? " FOLLOWING" : " following");
3857             }
3858         }
3859 
3860         if (x.getWindowingBetweenBegin() !is null) {
3861             if (SQLOver.WindowingType.ROWS == (x.getWindowingType())) {
3862                 print0(ucase ? " ROWS BETWEEN " : " rows between ");
3863             } else if (SQLOver.WindowingType.RANGE == (x.getWindowingType())) {
3864                 print0(ucase ? " RANGE BETWEEN " : " range between ");
3865             }
3866 
3867             printWindowingExpr(x.getWindowingBetweenBegin());
3868 
3869             if (x.isWindowingBetweenBeginPreceding()) {
3870                 print0(ucase ? " PRECEDING" : " preceding");
3871             } else if (x.isWindowingBetweenBeginFollowing()) {
3872                 print0(ucase ? " FOLLOWING" : " following");
3873             }
3874 
3875             print0(ucase ? " AND " : " and ");
3876 
3877             printWindowingExpr(x.getWindowingBetweenEnd());
3878 
3879             if (x.isWindowingBetweenEndPreceding()) {
3880                 print0(ucase ? " PRECEDING" : " preceding");
3881             } else if (x.isWindowingBetweenEndFollowing()) {
3882                 print0(ucase ? " FOLLOWING" : " following");
3883             }
3884         }
3885         
3886         print(')');
3887         return false;
3888     }
3889 
3890     void printWindowingExpr(SQLExpr expr) {
3891         if (cast(SQLIdentifierExpr)expr !is null) {
3892             string ident = (cast(SQLIdentifierExpr) expr).getName();
3893             print0(ucase ? ident : toLower(ident));
3894         } else {
3895             expr.accept(this);
3896         }
3897     }
3898     
3899     override
3900     bool visit(SQLKeep x) {
3901         if (x.getDenseRank() == SQLKeep.DenseRank.FIRST) {
3902             print0(ucase ? "KEEP (DENSE_RANK FIRST " : "keep (dense_rank first ");    
3903         } else {
3904             print0(ucase ? "KEEP (DENSE_RANK LAST " : "keep (dense_rank last ");
3905         }
3906         
3907         x.getOrderBy().accept(this);
3908         print(')');
3909         
3910         return false;
3911     }
3912 
3913     override
3914     bool visit(SQLColumnPrimaryKey x) {
3915         if (x.getName() !is null) {
3916             print0(ucase ? "CONSTRAINT " : "constraint ");
3917             x.getName().accept(this);
3918             print(' ');
3919         }
3920         print0(ucase ? "PRIMARY KEY" : "primary key");
3921         return false;
3922     }
3923 
3924     override
3925     bool visit(SQLColumnUniqueKey x) {
3926         if (x.getName() !is null) {
3927             print0(ucase ? "CONSTRAINT " : "constraint ");
3928             x.getName().accept(this);
3929             print(' ');
3930         }
3931         print0(ucase ? "UNIQUE" : "unique");
3932         return false;
3933     }
3934 
3935     override
3936     bool visit(SQLColumnCheck x) {
3937         if (x.getName() !is null) {
3938             print0(ucase ? "CONSTRAINT " : "constraint ");
3939             x.getName().accept(this);
3940             print(' ');
3941         }
3942         print0(ucase ? "CHECK (" : "check (");
3943         x.getExpr().accept(this);
3944         print(')');
3945 
3946         if (x.getEnable() !is null) {
3947             if (x.getEnable().booleanValue()) {
3948                 print0(ucase ? " ENABLE" : " enable");
3949             } else {
3950                 print0(ucase ? " DISABLE" : " disable");
3951             }
3952         }
3953         return false;
3954     }
3955 
3956     override
3957     bool visit(SQLWithSubqueryClause x) {
3958         print0(ucase ? "WITH " : "with ");
3959         if (x.getRecursive() == true) {
3960             print0(ucase ? "RECURSIVE " : "recursive ");
3961         }
3962         this.indentCount++;
3963         printlnAndAccept!(SQLWithSubqueryClause.Entry)((x.getEntries()), ", ");
3964         this.indentCount--;
3965         return false;
3966     }
3967 
3968     override
3969     bool visit(SQLWithSubqueryClause.Entry x) {
3970         print0(x.getAlias());
3971 
3972         if (x.getColumns().size() > 0) {
3973             print0(" (");
3974             printAndAccept!SQLName((x.getColumns()), ", ");
3975             print(')');
3976         }
3977         print(' ');
3978         print0(ucase ? "AS " : "as ");
3979         print('(');
3980         this.indentCount++;
3981         println();
3982         SQLSelect query = x.getSubQuery();
3983         if (query !is null) {
3984             query.accept(this);
3985         } else {
3986             x.getReturningStatement().accept(this);
3987         }
3988         this.indentCount--;
3989         println();
3990         print(')');
3991 
3992         return false;
3993     }
3994 
3995     override
3996     bool visit(SQLAlterTableAlterColumn x) {
3997         bool odps = isOdps();
3998         if (odps) {
3999             print0(ucase ? "CHANGE COLUMN " : "change column ");
4000         } else {
4001             print0(ucase ? "ALTER COLUMN " : "alter column ");
4002         }
4003         x.getColumn().accept(this);
4004 
4005         if (x.isSetNotNull()) { // postgresql
4006             print0(ucase ? " SET NOT NULL" : " set not null");
4007         }
4008 
4009         if (x.isDropNotNull()) { // postgresql
4010             print0(ucase ? " DROP NOT NULL" : " drop not null");
4011         }
4012 
4013         if (x.getSetDefault() !is null) { // postgresql
4014             print0(ucase ? " SET DEFAULT " : " set default ");
4015             x.getSetDefault().accept(this);
4016         }
4017 
4018          SQLDataType dataType = x.getDataType();
4019         if (dataType !is null) {
4020             print0(ucase ? " SET DATA TYPE " : " set data type ");
4021             dataType.accept(this);
4022         }
4023 
4024         if (x.isDropDefault()) { // postgresql
4025             print0(ucase ? " DROP DEFAULT" : " drop default");
4026         }
4027 
4028         return false;
4029     }
4030 
4031     override
4032     bool visit(SQLCheck x) {
4033         if (x.getName() !is null) {
4034             print0(ucase ? "CONSTRAINT " : "constraint ");
4035             x.getName().accept(this);
4036             print(' ');
4037         }
4038         print0(ucase ? "CHECK (" : "check (");
4039         this.indentCount++;
4040         x.getExpr().accept(this);
4041         this.indentCount--;
4042         print(')');
4043         return false;
4044     }
4045 
4046     override
4047     bool visit(SQLAlterTableDropForeignKey x) {
4048         print0(ucase ? "DROP FOREIGN KEY " : "drop foreign key ");
4049         x.getIndexName().accept(this);
4050         return false;
4051     }
4052 
4053     override
4054     bool visit(SQLAlterTableDropPrimaryKey x) {
4055         print0(ucase ? "DROP PRIMARY KEY" : "drop primary key");
4056         return false;
4057     }
4058 
4059     override
4060     bool visit(SQLAlterTableDropKey x) {
4061         print0(ucase ? "DROP KEY " : "drop key ");
4062         x.getKeyName().accept(this);
4063         return false;
4064     }
4065 
4066     override
4067     bool visit(SQLAlterTableEnableKeys x) {
4068         print0(ucase ? "ENABLE KEYS" : "enable keys");
4069         return false;
4070     }
4071 
4072     override
4073     bool visit(SQLAlterTableDisableKeys x) {
4074         print0(ucase ? "DISABLE KEYS" : "disable keys");
4075         return false;
4076     }
4077 
4078     override bool visit(SQLAlterTableDisableConstraint x) {
4079         print0(ucase ? "DISABLE CONSTRAINT " : "disable constraint ");
4080         x.getConstraintName().accept(this);
4081         return false;
4082     }
4083 
4084     override bool visit(SQLAlterTableEnableConstraint x) {
4085         print0(ucase ? "ENABLE CONSTRAINT " : "enable constraint ");
4086         x.getConstraintName().accept(this);
4087         return false;
4088     }
4089 
4090     override
4091     bool visit(SQLAlterTableDropConstraint x) {
4092         print0(ucase ? "DROP CONSTRAINT " : "drop constraint ");
4093         x.getConstraintName().accept(this);
4094         return false;
4095     }
4096 
4097     override
4098     bool visit(SQLAlterTableStatement x) {
4099         print0(ucase ? "ALTER TABLE " : "alter table ");
4100         printTableSourceExpr(x.getName());
4101         this.indentCount++;
4102         for (int i = 0; i < x.getItems().size(); ++i) {
4103             SQLAlterTableItem item = x.getItems().get(i);
4104             if (i != 0) {
4105                 print(',');
4106             }
4107             println();
4108             item.accept(this);
4109         }
4110         this.indentCount--;
4111 
4112         if (x.isMergeSmallFiles()) {
4113             print0(ucase ? " MERGE SMALLFILES" : " merge smallfiles");
4114         }
4115         return false;
4116     }
4117 
4118     override
4119     bool visit(SQLExprHint x) {
4120         x.getExpr().accept(this);
4121         return false;
4122     }
4123 
4124     override
4125     bool visit(SQLCreateIndexStatement x) {
4126         print0(ucase ? "CREATE " : "create ");
4127         if (x.getType() !is null) {
4128             print0(x.getType());
4129             print(' ');
4130         }
4131 
4132         print0(ucase ? "INDEX " : "index ");
4133 
4134         x.getName().accept(this);
4135         print0(ucase ? " ON " : " on ");
4136         x.getTable().accept(this);
4137         print0(" (");
4138         printAndAccept!SQLSelectOrderByItem((x.getItems()), ", ");
4139         print(')');
4140 
4141         // for mysql
4142         if (x.getUsing() !is null) {
4143             print0(ucase ? " USING " : " using ");
4144             // ;
4145             print0(x.getUsing());
4146         }
4147 
4148         SQLExpr comment = x.getComment();
4149         if (comment !is null) {
4150             print0(ucase ? " COMMENT " : " comment ");
4151             comment.accept(this);
4152         }
4153 
4154         return false;
4155     }
4156 
4157     override
4158     bool visit(SQLUnique x) {
4159         SQLName name = x.getName();
4160         if (name !is null) {
4161             print0(ucase ? "CONSTRAINT " : "constraint ");
4162             name.accept(this);
4163             print(' ');
4164         }
4165 
4166         print0(ucase ? "UNIQUE (" : "unique (");
4167         printAndAccept!SQLSelectOrderByItem((x.getColumns()), ", ");
4168         print(')');
4169         return false;
4170     }
4171 
4172     override
4173     bool visit(SQLPrimaryKeyImpl x) {
4174         SQLName name = x.getName();
4175         if (name !is null) {
4176             print0(ucase ? "CONSTRAINT " : "constraint ");
4177             name.accept(this);
4178             print(' ');
4179         }
4180 
4181         print0(ucase ? "PRIMARY KEY " : "primary key ");
4182 
4183         if (x.isClustered()) {
4184             print0(ucase ? "CLUSTERED " : "clustered ");
4185         }
4186 
4187         print('(');
4188         printAndAccept!SQLSelectOrderByItem((x.getColumns()), ", ");
4189         print(')');
4190 
4191         return false;
4192     }
4193 
4194     override
4195     bool visit(SQLAlterTableRenameColumn x) {
4196         print0(ucase ? "RENAME COLUMN " : "rename column ");
4197         x.getColumn().accept(this);
4198         print0(ucase ? " TO " : " to ");
4199         x.getTo().accept(this);
4200         return false;
4201     }
4202 
4203     override
4204     bool visit(SQLColumnReference x) {
4205         SQLName name = x.getName();
4206         if (name !is null) {
4207             print0(ucase ? "CONSTRAINT " : "constraint ");
4208             name.accept(this);
4209             print(' ');
4210         }
4211 
4212         print0(ucase ? "REFERENCES " : "references ");
4213         x.getTable().accept(this);
4214         print0(" (");
4215         printAndAccept!SQLName((x.getColumns()), ", ");
4216         print(')');
4217 
4218         SQLForeignKeyImpl.Match match = x.getReferenceMatch();
4219         if (match.name.length != 0) {
4220             print0(ucase ? " MATCH " : " match ");
4221             print0(ucase ? match.name : match.name_lcase);
4222         }
4223 
4224         if (x.getOnDelete().name.length != 0) {
4225             print0(ucase ? " ON DELETE " : " on delete ");
4226             print0(ucase ? x.getOnDelete().name : x.getOnDelete().name_lcase);
4227         }
4228 
4229         if (x.getOnUpdate().name.length != 0) {
4230             print0(ucase ? " ON UPDATE " : " on update ");
4231             print0(ucase ? x.getOnUpdate().name : x.getOnUpdate().name_lcase);
4232         }
4233 
4234         return false;
4235     }
4236 
4237     override
4238     bool visit(SQLForeignKeyImpl x) {
4239         if (x.getName() !is null) {
4240             print0(ucase ? "CONSTRAINT " : "constraint ");
4241             x.getName().accept(this);
4242             print(' ');
4243         }
4244 
4245         print0(ucase ? "FOREIGN KEY (" : "foreign key (");
4246         printAndAccept!SQLName((x.getReferencingColumns()), ", ");
4247         print(')');
4248 
4249         this.indentCount++;
4250         println();
4251         print0(ucase ? "REFERENCES " : "references ");
4252         x.getReferencedTableName().accept(this);
4253 
4254         if (x.getReferencedColumns().size() > 0) {
4255             print0(" (");
4256             printAndAccept!SQLName((x.getReferencedColumns()), ", ");
4257             print(')');
4258         }
4259 
4260         if (x.isOnDeleteCascade()) {
4261             println();
4262             print0(ucase ? "ON DELETE CASCADE" : "on delete cascade");
4263         } else if (x.isOnDeleteSetNull()) {
4264             print0(ucase ? "ON DELETE SET NULL" : "on delete set null");
4265         }
4266         this.indentCount--;
4267         return false;
4268     }
4269 
4270     override
4271     bool visit(SQLDropSequenceStatement x) {
4272         print0(ucase ? "DROP SEQUENCE " : "drop sequence ");
4273         if (x.isIfExists()) {
4274             print0(ucase ? "IF EXISTS " : "if exists ");
4275         }
4276         x.getName().accept(this);
4277         return false;
4278     }
4279 
4280     override
4281     void endVisit(SQLDropSequenceStatement x) {
4282 
4283     }
4284 
4285     override
4286     bool visit(SQLDropTriggerStatement x) {
4287         print0(ucase ? "DROP TRIGGER " : "drop trigger ");
4288         if (x.isIfExists()) {
4289             print0(ucase ? "IF EXISTS " : "if exists ");
4290         }
4291 
4292         x.getName().accept(this);
4293         return false;
4294     }
4295 
4296     override
4297     void endVisit(SQLDropUserStatement x) {
4298 
4299     }
4300 
4301     override
4302     bool visit(SQLDropUserStatement x) {
4303         print0(ucase ? "DROP USER " : "drop user ");
4304         printAndAccept!SQLExpr((x.getUsers()), ", ");
4305         return false;
4306     }
4307 
4308     override
4309     bool visit(SQLExplainStatement x) {
4310         print0(ucase ? "EXPLAIN" : "explain");
4311         if (x.getHints() !is null && x.getHints().size() > 0) {
4312             print(' ');
4313             printAndAccept!SQLCommentHint((x.getHints()), " ");
4314         }
4315 
4316         if (x.getType() !is null) {
4317             print(' ');
4318             print0(x.getType());
4319         }
4320         println();
4321         x.getStatement().accept(this);
4322         return false;
4323     }
4324 
4325     protected void printGrantPrivileges(SQLGrantStatement x) {
4326 
4327     }
4328 
4329     override
4330     bool visit(SQLGrantStatement x) {
4331         print0(ucase ? "GRANT " : "grant ");
4332         printAndAccept!SQLExpr((x.getPrivileges()), ", ");
4333 
4334         printGrantOn(x);
4335 
4336         if (x.getTo() !is null) {
4337             print0(ucase ? " TO " : " to ");
4338             x.getTo().accept(this);
4339         }
4340 
4341         bool _with = false;
4342         if (x.getMaxQueriesPerHour() !is null) {
4343             if (!_with) {
4344                 print0(ucase ? " WITH" : " with");
4345                 _with = true;
4346             }
4347             print0(ucase ? " MAX_QUERIES_PER_HOUR " : " max_queries_per_hour ");
4348             x.getMaxQueriesPerHour().accept(this);
4349         }
4350 
4351         if (x.getMaxUpdatesPerHour() !is null) {
4352             if (!_with) {
4353                 print0(ucase ? " WITH" : " with");
4354                 _with = true;
4355             }
4356             print0(ucase ? " MAX_UPDATES_PER_HOUR " : " max_updates_per_hour ");
4357             x.getMaxUpdatesPerHour().accept(this);
4358         }
4359 
4360         if (x.getMaxConnectionsPerHour() !is null) {
4361             if (!_with) {
4362                 print0(ucase ? " WITH" : " with");
4363                 _with = true;
4364             }
4365             print0(ucase ? " MAX_CONNECTIONS_PER_HOUR " : " max_connections_per_hour ");
4366             x.getMaxConnectionsPerHour().accept(this);
4367         }
4368 
4369         if (x.getMaxUserConnections() !is null) {
4370             if (!_with) {
4371                 print0(ucase ? " WITH" : " with");
4372                 _with = true;
4373             }
4374             print0(ucase ? " MAX_USER_CONNECTIONS " : " max_user_connections ");
4375             x.getMaxUserConnections().accept(this);
4376         }
4377 
4378         if (x.isAdminOption()) {
4379             if (!_with) {
4380                 print0(ucase ? " WITH" : " with");
4381                 _with = true;
4382             }
4383             print0(ucase ? " ADMIN OPTION" : " admin option");
4384         }
4385 
4386         if (x.getIdentifiedBy() !is null) {
4387             print0(ucase ? " IDENTIFIED BY " : " identified by ");
4388             x.getIdentifiedBy().accept(this);
4389         }
4390 
4391         return false;
4392     }
4393 
4394     protected void printGrantOn(SQLGrantStatement x) {
4395         if (x.getOn() !is null) {
4396             print0(ucase ? " ON " : " on ");
4397 
4398             SQLObjectType objectType = x.getObjectType();
4399             if (objectType.name.length != 0) {
4400                 print0(ucase ? objectType.name : objectType.name_lcase);
4401                 print(' ');
4402             }
4403 
4404             x.getOn().accept(this);
4405         }
4406     }
4407 
4408     override
4409     bool visit(SQLRevokeStatement x) {
4410         print0(ucase ? "REVOKE " : "revoke ");
4411         printAndAccept!SQLExpr((x.getPrivileges()), ", ");
4412 
4413         if (x.getOn() !is null) {
4414             print0(ucase ? " ON " : " on ");
4415 
4416             if (x.getObjectType().name.length != 0) {
4417                 print0(x.getObjectType().name);
4418                 print(' ');
4419             }
4420 
4421             x.getOn().accept(this);
4422         }
4423 
4424         if (x.getFrom() !is null) {
4425             print0(ucase ? " FROM " : " from ");
4426             x.getFrom().accept(this);
4427         }
4428 
4429         return false;
4430     }
4431 
4432     override
4433     bool visit(SQLDropDatabaseStatement x) {
4434         print0(ucase ? "DROP DATABASE " : "drop databasE ");
4435 
4436         if (x.isIfExists()) {
4437             print0(ucase ? "IF EXISTS " : "if exists ");
4438         }
4439 
4440         x.getDatabase().accept(this);
4441 
4442         return false;
4443     }
4444 
4445     override
4446     bool visit(SQLDropFunctionStatement x) {
4447         print0(ucase ? "DROP FUNCTION " : "drop function ");
4448 
4449         if (x.isIfExists()) {
4450             print0(ucase ? "IF EXISTS " : "if exists ");
4451         }
4452 
4453         x.getName().accept(this);
4454 
4455         return false;
4456     }
4457 
4458     override
4459     bool visit(SQLDropTableSpaceStatement x) {
4460         print0(ucase ? "DROP TABLESPACE " : "drop tablespace ");
4461 
4462         if (x.isIfExists()) {
4463             print0(ucase ? "IF EXISTS " : "if exists ");
4464         }
4465 
4466         x.getName().accept(this);
4467 
4468         SQLExpr engine = x.getEngine();
4469         if (engine !is null) {
4470             print0(ucase ? " ENGINE " : " engine ");
4471             engine.accept(this);
4472         }
4473 
4474         return false;
4475     }
4476 
4477     override
4478     bool visit(SQLDropProcedureStatement x) {
4479         print0(ucase ? "DROP PROCEDURE " : "drop procedure ");
4480 
4481         if (x.isIfExists()) {
4482             print0(ucase ? "IF EXISTS " : "if exists ");
4483         }
4484 
4485         x.getName().accept(this);
4486 
4487         return false;
4488     }
4489 
4490     override
4491     bool visit(SQLAlterTableAddIndex x) {
4492         print0(ucase ? "ADD " : "add ");
4493         string type = x.getType();
4494 
4495         bool mysql = DBType.MYSQL.opEquals(dbType);
4496 
4497         if (type !is null && !mysql) {
4498             print0(type);
4499             print(' ');
4500         }
4501 
4502         if (x.isUnique()) {
4503             print0(ucase ? "UNIQUE " : "unique ");
4504         }
4505 
4506         if (x.isKey()) {
4507             print0(ucase ? "KEY " : "key ");    
4508         } else {
4509             print0(ucase ? "INDEX " : "index ");
4510         }
4511         
4512         if (x.getName() !is null) {
4513             x.getName().accept(this);
4514             print(' ');
4515         }
4516 
4517         if (type !is null && mysql) {
4518             print0(ucase ? "USING " : "using ");
4519             print0(type);
4520             print(' ');
4521         }
4522 
4523         print('(');
4524         printAndAccept!SQLSelectOrderByItem((x.getItems()), ", ");
4525         print(')');
4526 
4527         if (x.getUsing() !is null) {
4528             print0(ucase ? " USING " : " using ");
4529             print0(x.getUsing());
4530         }
4531 
4532         SQLExpr comment = x.getComment();
4533         if (comment !is null) {
4534             print0(ucase ? " COMMENT " : " comment ");
4535             printExpr(comment);
4536         }
4537         return false;
4538     }
4539 
4540     override
4541     bool visit(SQLAlterTableAddConstraint x) {
4542         if (x.isWithNoCheck()) {
4543             print0(ucase ? "WITH NOCHECK " : "with nocheck ");
4544         }
4545 
4546         print0(ucase ? "ADD " : "add ");
4547 
4548         x.getConstraint().accept(this);
4549         return false;
4550     }
4551 
4552     override bool visit(SQLCreateTriggerStatement x) {
4553         print0(ucase ? "CREATE " : "create ");
4554 
4555         if (x.isOrReplace()) {
4556             print0(ucase ? "OR REPLACE " : "or replace ");
4557         }
4558 
4559         print0(ucase ? "TRIGGER " : "trigger ");
4560 
4561         x.getName().accept(this);
4562 
4563         this.indentCount++;
4564         println();
4565         if (SQLCreateTriggerStatement.TriggerType.INSTEAD_OF == (x.getTriggerType())) {
4566             print0(ucase ? "INSTEAD OF" : "instead of");
4567         } else {
4568             string triggerTypeName = x.getTriggerType().name;
4569             print0(ucase ? triggerTypeName : toLower(triggerTypeName));
4570         }
4571 
4572         if (x.isInsert()) {
4573             print0(ucase ? " INSERT" : " insert");
4574         }
4575 
4576         if (x.isDelete()) {
4577             if (x.isInsert()) {
4578                 print0(ucase ? " OR" : " or");
4579             }
4580             print0(ucase ? " DELETE" : " delete");
4581         }
4582 
4583         if (x.isUpdate()) {
4584             if (x.isInsert() || x.isDelete()) {
4585                 print0(ucase ? " OR" : " or");
4586             }
4587             print0(ucase ? " UPDATE" : " update");
4588 
4589             List!SQLName colums = x.getUpdateOfColumns();
4590             foreach(SQLName colum  ;  colums) {
4591                 print(' ');
4592                 colum.accept(this);
4593             }
4594         }
4595 
4596         println();
4597         print0(ucase ? "ON " : "on ");
4598         x.getOn().accept(this);
4599 
4600         if (x.isForEachRow()) {
4601             println();
4602             print0(ucase ? "FOR EACH ROW" : "for each row");
4603         }
4604 
4605         SQLExpr when = x.getWhen();
4606         if (when !is null) {
4607             println();
4608             print0(ucase ? "WHEN " : "when ");
4609             when.accept(this);
4610         }
4611         this.indentCount--;
4612         println();
4613         x.getBody().accept(this);
4614         return false;
4615     }
4616 
4617     override bool visit(SQLBooleanExpr x) {
4618         print0(x.getBooleanValue().booleanValue ? "true" : "false");
4619         return false;
4620     }
4621 
4622     override void endVisit(SQLBooleanExpr x) {
4623     }
4624 
4625     override
4626     bool visit(SQLUnionQueryTableSource x) {
4627         print('(');
4628         this.indentCount++;
4629         println();
4630         x.getUnion().accept(this);
4631         this.indentCount--;
4632         println();
4633         print(')');
4634 
4635         if (x.getAlias() !is null) {
4636             print(' ');
4637             print0(x.getAlias());
4638         }
4639 
4640         return false;
4641     }
4642 
4643     override
4644     bool visit(SQLTimestampExpr x) {
4645         if (this.parameterized) {
4646             print('?');
4647             incrementReplaceCunt();
4648 
4649             if(this.parameters !is null){
4650                 ExportParameterVisitorUtils.exportParameter(this.parameters, x);
4651             }
4652             return false;
4653         }
4654 
4655         print0(ucase ? "TIMESTAMP " : "timestamp ");
4656 
4657         if (x.isWithTimeZone()) {
4658             print0(ucase ? " WITH TIME ZONE " : " with time zone ");
4659         }
4660 
4661         print('\'');
4662         print0(x.getLiteral());
4663         print('\'');
4664 
4665         if (x.getTimeZone() !is null) {
4666             print0(ucase ? " AT TIME ZONE '" : " at time zone '");
4667             print0(x.getTimeZone());
4668             print('\'');
4669         }
4670 
4671         return false;
4672     }
4673 
4674     override
4675     bool visit(SQLBinaryExpr x) {
4676         print0("b'");
4677         print0(x.getText());
4678         print('\'');
4679 
4680         return false;
4681     }
4682 
4683     override
4684     bool visit(SQLAlterTableRename x) {
4685         print0(ucase ? "RENAME TO " : "rename to ");
4686         x.getTo().accept(this);
4687         return false;
4688     }
4689 
4690     override
4691     bool visit(SQLShowTablesStatement x) {
4692         print0(ucase ? "SHOW TABLES" : "show tables");
4693         if (x.getDatabase() !is null) {
4694             print0(ucase ? " FROM " : " from ");
4695             x.getDatabase().accept(this);
4696         }
4697 
4698         if (x.getLike() !is null) {
4699             print0(ucase ? " LIKE " : " like ");
4700             x.getLike().accept(this);
4701         }
4702         return false;
4703     }
4704 
4705     protected void printlnComment(List!string comments) {
4706         if (comments !is null) {
4707             for (int i = 0; i < comments.size(); ++i) {
4708                 string comment = comments.get(i);
4709                 if (i != 0 && comment.startsWith("--")) {
4710                     println();
4711                 }
4712 
4713                 printComment(comment);
4714             }
4715         }
4716     }
4717 
4718     void printComment(string comment) {
4719         if (comment is null) {
4720             return;
4721         }
4722 
4723         if (comment.startsWith("--") && comment.length > 2 && charAt(comment, 2) != ' ') {
4724             print0("-- ");
4725             print0(comment.substring(2));
4726         } else {
4727             print0(comment);
4728         }
4729     }
4730 
4731     protected void printlnComments(List!string comments) {
4732         if (comments !is null) {
4733             for (int i = 0; i < comments.size(); ++i) {
4734                 string comment = comments.get(i);
4735                 printComment(comment);
4736                 println();
4737             }
4738         }
4739     }
4740 
4741     override
4742     bool visit(SQLAlterViewRenameStatement x) {
4743         print0(ucase ? "ALTER VIEW " : "alter view ");
4744         x.getName().accept(this);
4745         print0(ucase ? " RENAME TO " : " rename to ");
4746         x.getTo().accept(this);
4747         return false;
4748     }
4749 
4750     override
4751     bool visit(SQLAlterTableAddPartition x) {
4752         print0(ucase ? "ADD " : "add ");
4753         if (x.isIfNotExists()) {
4754             print0(ucase ? "IF NOT EXISTS " : "if not exists ");
4755         }
4756         
4757         if (x.getPartitionCount() !is null) {
4758             print0(ucase ? "PARTITION PARTITIONS " : "partition partitions ");
4759             x.getPartitionCount().accept(this);
4760         }
4761 
4762         if (x.getPartitions().size() > 0) {
4763             print0(ucase ? "PARTITION (" : "partition (");
4764             printAndAccept((x.getPartitions()), ", ");
4765             print(')');
4766         }
4767         
4768         return false;
4769     }
4770 
4771     override
4772     bool visit(SQLAlterTableReOrganizePartition x) {
4773         print0(ucase ? "REORGANIZE " : "reorganize ");
4774 
4775         printAndAccept!SQLName((x.getNames()), ", ");
4776 
4777         print0(ucase ? " INTO (" : " into (");
4778         printAndAccept((x.getPartitions()), ", ");
4779         print(')');
4780         return false;
4781     }
4782 
4783     override
4784     bool visit(SQLAlterTableDropPartition x) {
4785         print0(ucase ? "DROP " : "drop ");
4786         if (x.isIfExists()) {
4787             print0(ucase ? "IF EXISTS " : "if exists ");
4788         }
4789         print0(ucase ? "PARTITION " : "partition ");
4790 
4791         if (x.getPartitions().size() == 1 &&  cast(SQLName)x.getPartitions().get(0) !is null) {
4792             x.getPartitions().get(0).accept(this);
4793         } else {
4794             print('(');
4795             printAndAccept((x.getPartitions()), ", ");
4796             print(')');
4797         }
4798 
4799         if (x.isPurge()) {
4800             print0(ucase ? " PURGE" : " purge");
4801         }
4802         return false;
4803     }
4804 
4805     override
4806     bool visit(SQLAlterTableRenamePartition x) {
4807         print0(ucase ? "PARTITION (" : "partition (");
4808         printAndAccept!SQLAssignItem((x.getPartition()), ", ");
4809         print0(ucase ? ") RENAME TO PARTITION(" : ") rename to partition(");
4810         printAndAccept!SQLAssignItem((x.getTo()), ", ");
4811         print(')');
4812         return false;
4813     }
4814 
4815     override
4816     bool visit(SQLAlterTableSetComment x) {
4817         print0(ucase ? "SET COMMENT " : "set comment ");
4818         x.getComment().accept(this);
4819         return false;
4820     }
4821 
4822     override
4823     bool visit(SQLAlterTableSetLifecycle x) {
4824         print0(ucase ? "SET LIFECYCLE " : "set lifecycle ");
4825         x.getLifecycle().accept(this);
4826         return false;
4827     }
4828 
4829     override
4830     bool visit(SQLAlterTableEnableLifecycle x) {
4831         if (x.getPartition().size() != 0) {
4832             print0(ucase ? "PARTITION (" : "partition (");
4833             printAndAccept!SQLAssignItem((x.getPartition()), ", ");
4834             print0(") ");
4835         }
4836 
4837         print0(ucase ? "ENABLE LIFECYCLE" : "enable lifecycle");
4838         return false;
4839     }
4840 
4841     override
4842     bool visit(SQLAlterTableDisableLifecycle x) {
4843         if (x.getPartition().size() != 0) {
4844             print0(ucase ? "PARTITION (" : "partition (");
4845             printAndAccept!SQLAssignItem((x.getPartition()), ", ");
4846             print0(") ");
4847         }
4848 
4849         print0(ucase ? "DISABLE LIFECYCLE" : "disable lifecycle");
4850         return false;
4851     }
4852 
4853     override
4854     bool visit(SQLAlterTableTouch x) {
4855         print0(ucase ? "TOUCH" : "touch");
4856         if (x.getPartition().size() != 0) {
4857             print0(ucase ? " PARTITION (" : " partition (");
4858             printAndAccept!SQLAssignItem((x.getPartition()), ", ");
4859             print(')');
4860         }
4861         return false;
4862     }
4863 
4864     override
4865     bool visit(SQLArrayExpr x) {
4866         x.getExpr().accept(this);
4867         print('[');
4868         printAndAccept!SQLExpr((x.getValues()), ", ");
4869         print(']');
4870         return false;
4871     }
4872 
4873     override
4874     bool visit(SQLOpenStatement x) {
4875         print0(ucase ? "OPEN " : "open ");
4876         printExpr(x.getCursorName());
4877 
4878         List!SQLName columns = x.getColumns();
4879         if (columns.size() > 0) {
4880             print('(');
4881             printAndAccept!SQLName((columns), ", ");
4882             print(')');
4883         }
4884 
4885         SQLExpr forExpr = x.getFor();
4886         if (forExpr !is null) {
4887             print0(ucase ? " FOR " : "for ");
4888             forExpr.accept(this);
4889         }
4890         return false;
4891     }
4892 
4893     override
4894     bool visit(SQLFetchStatement x) {
4895         print0(ucase ? "FETCH " : "fetch ");
4896         x.getCursorName().accept(this);
4897         if (x.isBulkCollect()) {
4898             print0(ucase ? " BULK COLLECT INTO " : " bulk collect into ");
4899         } else {
4900             print0(ucase ? " INTO " : " into ");
4901         }
4902         printAndAccept!SQLExpr((x.getInto()), ", ");
4903         return false;
4904     }
4905 
4906     override
4907     bool visit(SQLCloseStatement x) {
4908         print0(ucase ? "CLOSE " : "close ");
4909         printExpr(x.getCursorName());
4910         return false;
4911     }
4912 
4913     override
4914     bool visit(SQLGroupingSetExpr x) {
4915         print0(ucase ? "GROUPING SETS" : "grouping sets");
4916         print0(" (");
4917         printAndAccept!SQLExpr((x.getParameters()), ", ");
4918         print(')');
4919         return false;
4920     }
4921 
4922     override
4923     bool visit(SQLIfStatement x) {
4924         print0(ucase ? "IF " : "if ");
4925         x.getCondition().accept(this);
4926         this.indentCount++;
4927         println();
4928         for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
4929             SQLStatement item = x.getStatements().get(i);
4930             item.accept(this);
4931             if (i != size - 1) {
4932                 println();
4933             }
4934         }
4935         this.indentCount--;
4936 
4937         foreach (SQLIfStatement.ElseIf elseIf ; x.getElseIfList()) {
4938             println();
4939             elseIf.accept(this);
4940         }
4941 
4942         if (x.getElseItem() !is null) {
4943             println();
4944             x.getElseItem().accept(this);
4945         }
4946         return false;
4947     }
4948 
4949     override
4950     bool visit(SQLIfStatement.Else x) {
4951         print0(ucase ? "ELSE" : "else");
4952         this.indentCount++;
4953         println();
4954 
4955         for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
4956             if (i != 0) {
4957                 println();
4958             }
4959             SQLStatement item = x.getStatements().get(i);
4960             item.accept(this);
4961         }
4962 
4963         this.indentCount--;
4964         return false;
4965     }
4966 
4967     override
4968     bool visit(SQLIfStatement.ElseIf x) {
4969         print0(ucase ? "ELSE IF" : "else if");
4970         x.getCondition().accept(this);
4971         print0(ucase ? " THEN" : " then");
4972         this.indentCount++;
4973         println();
4974 
4975         for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
4976             if (i != 0) {
4977                 println();
4978             }
4979             SQLStatement item = x.getStatements().get(i);
4980             item.accept(this);
4981         }
4982 
4983         this.indentCount--;
4984         return false;
4985     }
4986 
4987     override
4988     bool visit(SQLLoopStatement x) {
4989         print0(ucase ? "LOOP" : "loop");
4990         this.indentCount++;
4991         println();
4992 
4993 
4994         for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
4995             SQLStatement item = x.getStatements().get(i);
4996             item.accept(this);
4997 
4998             if (i != size - 1) {
4999                 println();
5000             }
5001         }
5002 
5003         this.indentCount--;
5004         println();
5005         print0(ucase ? "END LOOP" : "end loop");
5006         if (x.getLabelName() !is null) {
5007             print(' ');
5008             print0(x.getLabelName());
5009         }
5010         return false;
5011     }
5012 
5013     // bool visit(OracleFunctionDataType x) {
5014     //     if (x.isStatic()) {
5015     //         print0(ucase ? "STATIC " : "static ");
5016     //     }
5017 
5018     //     print0(ucase ? "FUNCTION " : "function ");
5019 
5020     //     print0(x.getName());
5021 
5022     //     print(" (");
5023     //     printAndAccept(x.getParameters(), ", ");
5024     //     print(")");
5025     //     print0(ucase ? " RETURN " : " return ");
5026     //     x.getReturnDataType().accept(this);
5027 
5028     //     SQLStatement block = x.getBlock();
5029     //     if (block !is null) {
5030     //         println();
5031     //         print0(ucase ? "IS" : "is");
5032     //         println();
5033     //         block.accept(this);
5034     //     }
5035 
5036     //     return false;
5037     // }
5038 
5039     // bool visit(OracleProcedureDataType x) {
5040     //     if (x.isStatic()) {
5041     //         print0(ucase ? "STATIC " : "static ");
5042     //     }
5043 
5044     //     print0(ucase ? "PROCEDURE " : "procedure ");
5045 
5046     //     print0(x.getName());
5047 
5048     //     if (x.getParameters().size() > 0) {
5049     //         print(" (");
5050     //         printAndAccept(x.getParameters(), ", ");
5051     //         print(")");
5052     //     }
5053 
5054     //     SQLStatement block = x.getBlock();
5055     //     if (block !is null) {
5056     //         println();
5057     //         print0(ucase ? "IS" : "is");
5058     //         println();
5059     //         block.accept(this);
5060     //     }
5061 
5062     //     return false;
5063     // }
5064 
5065     override
5066     bool visit(SQLParameter x) {
5067         SQLName name = x.getName();
5068         if (x.getDataType().getName().equalsIgnoreCase("CURSOR")) {
5069             print0(ucase ? "CURSOR " : "cursor ");
5070             x.getName().accept(this);
5071             print0(ucase ? " IS" : " is");
5072             this.indentCount++;
5073             println();
5074             SQLSelect select = (cast(SQLQueryExpr) x.getDefaultValue()).getSubQuery();
5075             select.accept(this);
5076             this.indentCount--;
5077 
5078         } else {
5079             if (x.isMap()) {
5080                 print0(ucase ? "MAP MEMBER " : "map member ");
5081             } else if (x.isOrder()) {
5082                 print0(ucase ? "ORDER MEMBER " : "order member ");
5083             } else if (x.isMember()) {
5084                 print0(ucase ? "MEMBER " : "member ");
5085             }
5086             SQLDataType dataType = x.getDataType();
5087 
5088             /*if (DBType.ORACLE.opEquals(dbType)
5089                     || cast(OracleFunctionDataType)dataType !is null
5090                     || cast(OracleProcedureDataType)dataType !is null) {
5091                 if (cast(OracleFunctionDataType)dataType !is null) {
5092                     OracleFunctionDataType functionDataType = cast(OracleFunctionDataType) dataType;
5093                     visit(functionDataType);
5094                     return false;
5095                 }
5096 
5097                 if (cast(OracleProcedureDataType)dataType !is null) {
5098                     OracleProcedureDataType procedureDataType = cast(OracleProcedureDataType) dataType;
5099                     visit(procedureDataType);
5100                     return false;
5101                 }
5102 
5103                 string dataTypeName = dataType.getName();
5104                 bool printType = (dataTypeName.startsWith("TABLE OF") && x.getDefaultValue() is null)
5105                         || equalsIgnoreCase(dataTypeName, "REF CURSOR")
5106                         || dataTypeName.startsWith("VARRAY(");
5107                 if (printType) {
5108                     print0(ucase ? "TYPE " : "type ");
5109                 }
5110 
5111                 name.accept(this);
5112                 if (x.getParamType() == SQLParameter.ParameterType.IN) {
5113                     print0(ucase ? " IN " : " in ");
5114                 } else if (x.getParamType() == SQLParameter.ParameterType.OUT) {
5115                     print0(ucase ? " OUT " : " out ");
5116                 } else if (x.getParamType() == SQLParameter.ParameterType.INOUT) {
5117                     print0(ucase ? " IN OUT " : " in out ");
5118                 } else {
5119                     print(' ');
5120                 }
5121 
5122                 if (x.isNoCopy()) {
5123                     print0(ucase ? "NOCOPY " : "nocopy ");
5124                 }
5125 
5126                 if (x.isConstant()) {
5127                     print0(ucase ? "CONSTANT " : "constant ");
5128                 }
5129 
5130                 if (printType) {
5131                     print0(ucase ? "IS " : "is ");
5132                 }
5133             } else */{
5134                 if (x.getParamType() == SQLParameter.ParameterType.IN) {
5135                     bool skip = DBType.MYSQL.opEquals(dbType)
5136                             && cast(SQLCreateFunctionStatement)x.getParent() !is null;
5137 
5138                     if (!skip) {
5139                         print0(ucase ? "IN " : "in ");
5140                     }
5141                 } else if (x.getParamType() == SQLParameter.ParameterType.OUT) {
5142                     print0(ucase ? "OUT " : "out ");
5143                 } else if (x.getParamType() == SQLParameter.ParameterType.INOUT) {
5144                     print0(ucase ? "INOUT " : "inout ");
5145                 }
5146                 x.getName().accept(this);
5147                 print(' ');
5148             }
5149 
5150             dataType.accept(this);
5151 
5152             printParamDefaultValue(x);
5153         }
5154 
5155         return false;
5156     }
5157 
5158     protected void printParamDefaultValue(SQLParameter x) {
5159         if (x.getDefaultValue() !is null) {
5160             print0(" := ");
5161             x.getDefaultValue().accept(this);
5162         }
5163     }
5164 
5165     override
5166     bool visit(SQLDeclareItem x) {
5167         SQLDataType dataType = x.getDataType();
5168 
5169         if (cast(SQLRecordDataType)dataType !is null) {
5170             print0(ucase ? "TYPE " : "type ");
5171         }
5172 
5173         x.getName().accept(this);
5174 
5175 
5176         if (x.getType() == SQLDeclareItem.Type.TABLE) {
5177             print0(ucase ? " TABLE" : " table");
5178             int size = x.getTableElementList().size();
5179 
5180             if (size > 0) {
5181                 print0(" (");
5182                 this.indentCount++;
5183                 println();
5184                 for (int i = 0; i < size; ++i) {
5185                     if (i != 0) {
5186                         print(',');
5187                         println();
5188                     }
5189                     x.getTableElementList().get(i).accept(this);
5190                 }
5191                 this.indentCount--;
5192                 println();
5193                 print(')');
5194             }
5195         } else if (x.getType() == SQLDeclareItem.Type.CURSOR) {
5196             print0(ucase ? " CURSOR" : " cursor");
5197         } else {
5198 
5199             if (dataType !is null) {
5200                 if (cast(SQLRecordDataType)dataType !is null) {
5201                     print0(ucase ? " IS " : " is ");
5202                 } else {
5203                     print(' ');
5204                 }
5205                 dataType.accept(this);
5206             }
5207             if (x.getValue() !is null) {
5208                 if (DBType.MYSQL.opEquals(getDbType())) {
5209                     print0(ucase ? " DEFAULT " : " default ");
5210                 } else {
5211                     print0(" = ");
5212                 }
5213                 x.getValue().accept(this);
5214             }
5215         }
5216 
5217         return false;
5218     }
5219 
5220     override
5221     bool visit(SQLPartitionValue x) {
5222         if (x.getOperator() == SQLPartitionValue.Operator.LessThan //
5223             && (!DBType.ORACLE.opEquals(getDbType())) && x.getItems().size() == 1 //
5224             && cast(SQLIdentifierExpr)x.getItems().get(0) !is null)  {
5225             SQLIdentifierExpr ident = cast(SQLIdentifierExpr) x.getItems().get(0);
5226             if ("MAXVALUE".equalsIgnoreCase(ident.getName())) {
5227                 print0(ucase ? "VALUES LESS THAN MAXVALUE" : "values less than maxvalue");
5228                 return false;
5229             }
5230         }
5231 
5232         if (x.getOperator() == SQLPartitionValue.Operator.LessThan) {
5233             print0(ucase ? "VALUES LESS THAN (" : "values less than (");
5234         } else if (x.getOperator() == SQLPartitionValue.Operator.In) {
5235             print0(ucase ? "VALUES IN (" : "values in (");
5236         } else {
5237             print(ucase ? "VALUES (" : "values (");
5238         }
5239         printAndAccept!SQLExpr((x.getItems()), ", ");
5240         print(')');
5241         return false;
5242     }
5243 
5244     string getDbType() {
5245         return dbType;
5246     }
5247 
5248     bool isUppCase() {
5249         return ucase;
5250     }
5251 
5252     void setUppCase(bool val) {
5253         this.config(VisitorFeature.OutputUCase, true);
5254     }
5255 
5256     override
5257     bool visit(SQLPartition x) {
5258         print0(ucase ? "PARTITION " : "partition ");
5259         x.getName().accept(this);
5260         if (x.getValues() !is null) {
5261             print(' ');
5262             x.getValues().accept(this);
5263         }
5264 
5265         if (x.getDataDirectory() !is null) {
5266             this.indentCount++;
5267             println();
5268             print0(ucase ? "DATA DIRECTORY " : "data directory ");
5269             x.getDataDirectory().accept(this);
5270             this.indentCount--;
5271         }
5272 
5273         if (x.getIndexDirectory() !is null) {
5274             this.indentCount++;
5275             println();
5276             print0(ucase ? "INDEX DIRECTORY " : "index directory ");
5277             x.getIndexDirectory().accept(this);
5278             this.indentCount--;
5279         }
5280 
5281         this.indentCount++;
5282         // printOracleSegmentAttributes(x);//@gxc
5283 
5284 
5285         if (x.getEngine() !is null) {
5286             println();
5287             print0(ucase ? "STORAGE ENGINE " : "storage engine ");
5288             x.getEngine().accept(this);
5289         }
5290         this.indentCount--;
5291 
5292         if (x.getMaxRows() !is null) {
5293             print0(ucase ? " MAX_ROWS " : " max_rows ");
5294             x.getMaxRows().accept(this);
5295         }
5296 
5297         if (x.getMinRows() !is null) {
5298             print0(ucase ? " MIN_ROWS " : " min_rows ");
5299             x.getMinRows().accept(this);
5300         }
5301 
5302         if (x.getComment() !is null) {
5303             print0(ucase ? " COMMENT " : " comment ");
5304             x.getComment().accept(this);
5305         }
5306 
5307         if (x.getSubPartitionsCount() !is null) {
5308             this.indentCount++;
5309             println();
5310             print0(ucase ? "SUBPARTITIONS " : "subpartitions ");
5311             x.getSubPartitionsCount().accept(this);
5312             this.indentCount--;
5313         }
5314 
5315         if (x.getSubPartitions().size() > 0) {
5316             print(" (");
5317             this.indentCount++;
5318             for (int i = 0; i < x.getSubPartitions().size(); ++i) {
5319                 if (i != 0) {
5320                     print(',');
5321                 }
5322                 println();
5323                 x.getSubPartitions().get(i).accept(this);
5324             }
5325             this.indentCount--;
5326             println();
5327             print(')');
5328         }
5329 
5330         return false;
5331     }
5332 
5333     override
5334     bool visit(SQLPartitionByRange x) {
5335         print0(ucase ? "RANGE" : "range");
5336         if (x.getColumns().size() == 1) {
5337             print0(" (");
5338             x.getColumns().get(0).accept(this);
5339             print(')');
5340         } else {
5341             if (DBType.MYSQL.opEquals(getDbType())) {
5342                 print0(ucase ? " COLUMNS (" : " columns (");
5343             } else {
5344                 print0(" (");
5345             }
5346             printAndAccept!SQLExpr((x.getColumns()), ", ");
5347             print(')');
5348         }
5349 
5350         SQLExpr interval = x.getInterval();
5351         if (interval !is null) {
5352             print0(ucase ? " INTERVAL (" : " interval (");
5353             interval.accept(this);
5354             print(')');
5355         }
5356 
5357         printPartitionsCountAndSubPartitions(x);
5358 
5359         print(" (");
5360         this.indentCount++;
5361         for (int i = 0, size = x.getPartitions().size(); i < size; ++i) {
5362             if (i != 0) {
5363                 print(',');
5364             }
5365             println();
5366             x.getPartitions().get(i).accept(this);
5367         }
5368         this.indentCount--;
5369         println();
5370         print(')');
5371 
5372         return false;
5373     }
5374 
5375     override
5376     bool visit(SQLPartitionByList x) {
5377         print0(ucase ? "LIST " : "list ");
5378         if (x.getColumns().size() == 1) {
5379             print('(');
5380             x.getColumns().get(0).accept(this);
5381             print0(")");
5382         } else {
5383             print0(ucase ? "COLUMNS (" : "columns (");
5384             printAndAccept!SQLExpr((x.getColumns()), ", ");
5385             print0(")");
5386         }
5387 
5388         printPartitionsCountAndSubPartitions(x);
5389 
5390         printSQLPartitions(x.getPartitions());
5391         return false;
5392     }
5393 
5394     override
5395     bool visit(SQLPartitionByHash x) {
5396         if (x.isLinear()) {
5397             print0(ucase ? "LINEAR HASH " : "linear hash ");
5398         } else {
5399             print0(ucase ? "HASH " : "hash ");
5400         }
5401 
5402         if (x.isKey()) {
5403             print0(ucase ? "KEY" : "key");
5404         }
5405 
5406         print('(');
5407         printAndAccept!SQLExpr((x.getColumns()), ", ");
5408         print(')');
5409 
5410         printPartitionsCountAndSubPartitions(x);
5411 
5412         printSQLPartitions(x.getPartitions());
5413 
5414         return false;
5415     }
5416 
5417     private void printSQLPartitions(List!SQLPartition partitions) {
5418         int partitionsSize = partitions.size();
5419         if (partitionsSize > 0) {
5420             print0(" (");
5421             this.indentCount++;
5422             for (int i = 0; i < partitionsSize; ++i) {
5423                 println();
5424                 partitions.get(i).accept(this);
5425                 if (i != partitionsSize - 1) {
5426                     print0(", ");
5427                 }
5428             }
5429             this.indentCount--;
5430             println();
5431             print(')');
5432         }
5433     }
5434 
5435     protected void printPartitionsCountAndSubPartitions(SQLPartitionBy x) {
5436         if (x.getPartitionsCount() !is null) {
5437 
5438             if (Boolean.TRUE.opEquals(x.getAttribute("ads.partition"))) {
5439                 print0(ucase ? " PARTITION NUM " : " partition num ");
5440             } else {
5441                 print0(ucase ? " PARTITIONS " : " partitions ");
5442             }
5443 
5444             x.getPartitionsCount().accept(this);
5445         }
5446 
5447         if (x.getSubPartitionBy() !is null) {
5448             println();
5449             x.getSubPartitionBy().accept(this);
5450         }
5451 
5452         if (x.getStoreIn().size() > 0) {
5453             println();
5454             print0(ucase ? "STORE IN (" : "store in (");
5455             printAndAccept!SQLName((x.getStoreIn()), ", ");
5456             print(')');
5457         }
5458     }
5459 
5460     override
5461     bool visit(SQLSubPartitionByHash x) {
5462         if (x.isLinear()) {
5463             print0(ucase ? "SUBPARTITION BY LINEAR HASH " : "subpartition by linear hash ");
5464         } else {
5465             print0(ucase ? "SUBPARTITION BY HASH " : "subpartition by hash ");
5466         }
5467 
5468         if (x.isKey()) {
5469             print0(ucase ? "KEY" : "key");
5470         }
5471 
5472         print('(');
5473         x.getExpr().accept(this);
5474         print(')');
5475 
5476         if (x.getSubPartitionsCount() !is null) {
5477             print0(ucase ? " SUBPARTITIONS " : " subpartitions ");
5478             x.getSubPartitionsCount().accept(this);
5479         }
5480 
5481         return false;
5482     }
5483 
5484     override
5485     bool visit(SQLSubPartitionByList x) {
5486         if (x.isLinear()) {
5487             print0(ucase ? "SUBPARTITION BY LINEAR HASH " : "subpartition by linear hash ");
5488         } else {
5489             print0(ucase ? "SUBPARTITION BY HASH " : "subpartition by hash ");
5490         }
5491 
5492         print('(');
5493         x.getColumn().accept(this);
5494         print(')');
5495 
5496         if (x.getSubPartitionsCount() !is null) {
5497             print0(ucase ? " SUBPARTITIONS " : " subpartitions ");
5498             x.getSubPartitionsCount().accept(this);
5499         }
5500 
5501         if (x.getSubPartitionTemplate().size() > 0) {
5502             this.indentCount++;
5503             println();
5504             print0(ucase ? "SUBPARTITION TEMPLATE (" : "subpartition template (");
5505             this.indentCount++;
5506             println();
5507             printlnAndAccept!(SQLSubPartition)((x.getSubPartitionTemplate()), ",");
5508             this.indentCount--;
5509             println();
5510             print(')');
5511             this.indentCount--;
5512         }
5513 
5514         return false;
5515     }
5516 
5517     override
5518     bool visit(SQLSubPartition x) {
5519         print0(ucase ? "SUBPARTITION " : "subpartition ");
5520         x.getName().accept(this);
5521 
5522         if (x.getValues() !is null) {
5523             print(' ');
5524             x.getValues().accept(this);
5525         }
5526 
5527         SQLName tableSpace = x.getTableSpace();
5528         if (tableSpace !is null) {
5529             print0(ucase ? " TABLESPACE " : " tablespace ");
5530             tableSpace.accept(this);
5531         }
5532 
5533         return false;
5534     }
5535 
5536     override
5537     bool visit(SQLAlterDatabaseStatement x) {
5538         print0(ucase ? "ALTER DATABASE " : "alter database ");
5539         x.getName().accept(this);
5540         if (x.isUpgradeDataDirectoryName()) {
5541             print0(ucase ? " UPGRADE DATA DIRECTORY NAME" : " upgrade data directory name");
5542         }
5543 
5544         SQLAlterCharacter character = x.getCharacter();
5545         if (character !is null) {
5546             print(' ');
5547             character.accept(this);
5548         }
5549         return false;
5550     }
5551 
5552     override
5553     bool visit(SQLAlterTableConvertCharSet x) {
5554         print0(ucase ? "CONVERT TO CHARACTER SET " : "convert to character set ");
5555         x.getCharset().accept(this);
5556 
5557         if (x.getCollate() !is null) {
5558             print0(ucase ? "COLLATE " : "collate ");
5559             x.getCollate().accept(this);
5560         }
5561         return false;
5562     }
5563 
5564     override
5565     bool visit(SQLAlterTableCoalescePartition x) {
5566         print0(ucase ? "COALESCE PARTITION " : "coalesce partition ");
5567         x.getCount().accept(this);
5568         return false;
5569     }
5570     
5571     override
5572     bool visit(SQLAlterTableTruncatePartition x) {
5573         print0(ucase ? "TRUNCATE PARTITION " : "truncate partition ");
5574         printPartitions(x.getPartitions());
5575         return false;
5576     }
5577     
5578     override
5579     bool visit(SQLAlterTableDiscardPartition x) {
5580         print0(ucase ? "DISCARD PARTITION " : "discard partition ");
5581         printPartitions(x.getPartitions());
5582 
5583         if (x.isTablespace()) {
5584             print0(ucase ? " TABLESPACE" : " tablespace");
5585         }
5586 
5587         return false;
5588     }
5589     
5590     override
5591     bool visit(SQLAlterTableImportPartition x) {
5592         print0(ucase ? "IMPORT PARTITION " : "import partition ");
5593         printPartitions(x.getPartitions());
5594         return false;
5595     }
5596     
5597     override
5598     bool visit(SQLAlterTableAnalyzePartition x) {
5599         print0(ucase ? "ANALYZE PARTITION " : "analyze partition ");
5600         
5601         printPartitions(x.getPartitions());
5602         return false;
5603     }
5604     
5605     protected void printPartitions(List!SQLName partitions) {
5606         if (partitions.size() == 1 && "ALL".equalsIgnoreCase(partitions.get(0).getSimpleName())) {
5607             print0(ucase ? "ALL" : "all");    
5608         } else {
5609             printAndAccept!SQLName((partitions), ", ");
5610         }
5611     }
5612     
5613     override
5614     bool visit(SQLAlterTableCheckPartition x) {
5615         print0(ucase ? "CHECK PARTITION " : "check partition ");
5616         printPartitions(x.getPartitions());
5617         return false;
5618     }
5619     
5620     override
5621     bool visit(SQLAlterTableOptimizePartition x) {
5622         print0(ucase ? "OPTIMIZE PARTITION " : "optimize partition ");
5623         printPartitions(x.getPartitions());
5624         return false;
5625     }
5626     
5627     override
5628     bool visit(SQLAlterTableRebuildPartition x) {
5629         print0(ucase ? "REBUILD PARTITION " : "rebuild partition ");
5630         printPartitions(x.getPartitions());
5631         return false;
5632     }
5633     
5634     override
5635     bool visit(SQLAlterTableRepairPartition x) {
5636         print0(ucase ? "REPAIR PARTITION " : "repair partition ");
5637         printPartitions(x.getPartitions());
5638         return false;
5639     }
5640     
5641     override
5642     bool visit(SQLSequenceExpr x) {
5643         x.getSequence().accept(this);
5644         print('.');
5645         print0(ucase ? x.getFunction().name : x.getFunction().name_lcase);
5646         return false;
5647     }
5648     
5649     override
5650     bool visit(SQLMergeStatement x) {
5651         print0(ucase ? "MERGE " : "merge ");
5652         if (x.getHints().size() > 0) {
5653             printAndAccept!SQLHint((x.getHints()), ", ");
5654             print(' ');
5655         }
5656 
5657         print0(ucase ? "INTO " : "into ");
5658         x.getInto().accept(this);
5659 
5660         println();
5661         print0(ucase ? "USING " : "using ");
5662         x.getUsing().accept(this);
5663 
5664         print0(ucase ? " ON (" : " on (");
5665         x.getOn().accept(this);
5666         print0(") ");
5667 
5668         if (x.getUpdateClause() !is null) {
5669             println();
5670             x.getUpdateClause().accept(this);
5671         }
5672 
5673         if (x.getInsertClause() !is null) {
5674             println();
5675             x.getInsertClause().accept(this);
5676         }
5677 
5678         if (x.getErrorLoggingClause() !is null) {
5679             println();
5680             x.getErrorLoggingClause().accept(this);
5681         }
5682 
5683         return false;
5684     }
5685 
5686     override
5687     bool visit(SQLMergeStatement.MergeUpdateClause x) {
5688         print0(ucase ? "WHEN MATCHED THEN UPDATE SET " : "when matched then update set ");
5689         printAndAccept!SQLUpdateSetItem((x.getItems()), ", ");
5690 
5691         SQLExpr where = x.getWhere();
5692         if (where !is null) {
5693             this.indentCount++;
5694             println();
5695             print0(ucase ? "WHERE " : "where ");
5696             printExpr(where);
5697             this.indentCount--;
5698         }
5699 
5700         SQLExpr deleteWhere = x.getDeleteWhere();
5701         if (deleteWhere !is null) {
5702             this.indentCount++;
5703             println();
5704             print0(ucase ? "DELETE WHERE " : "delete where ");
5705             printExpr(deleteWhere);
5706             this.indentCount--;
5707         }
5708 
5709         return false;
5710     }
5711 
5712     override
5713     bool visit(SQLMergeStatement.MergeInsertClause x) {
5714         print0(ucase ? "WHEN NOT MATCHED THEN INSERT" : "when not matched then insert");
5715         if (x.getColumns().size() > 0) {
5716             print(" (");
5717             printAndAccept!SQLExpr((x.getColumns()), ", ");
5718             print(')');
5719         }
5720         print0(ucase ? " VALUES (" : " values (");
5721         printAndAccept!SQLExpr((x.getValues()), ", ");
5722         print(')');
5723         if (x.getWhere() !is null) {
5724             this.indentCount++;
5725             println();
5726             print0(ucase ? "WHERE " : "where ");
5727             x.getWhere().accept(this);
5728             this.indentCount--;
5729         }
5730 
5731         return false;
5732     }
5733 
5734     override
5735     bool visit(SQLErrorLoggingClause x) {
5736         print0(ucase ? "LOG ERRORS " : "log errors ");
5737         if (x.getInto() !is null) {
5738             print0(ucase ? "INTO " : "into ");
5739             x.getInto().accept(this);
5740             print(' ');
5741         }
5742 
5743         if (x.getSimpleExpression() !is null) {
5744             print('(');
5745             x.getSimpleExpression().accept(this);
5746             print(')');
5747         }
5748 
5749         if (x.getLimit() !is null) {
5750             print0(ucase ? " REJECT LIMIT " : " reject limit ");
5751             x.getLimit().accept(this);
5752         }
5753 
5754         return false;
5755     }
5756 
5757     override
5758     bool visit(SQLCreateSequenceStatement x) {
5759         print0(ucase ? "CREATE SEQUENCE " : "create sequence ");
5760         x.getName().accept(this);
5761 
5762         if (x.getStartWith() !is null) {
5763             print0(ucase ? " START WITH " : " start with ");
5764             x.getStartWith().accept(this);
5765         }
5766 
5767         if (x.getIncrementBy() !is null) {
5768             print0(ucase ? " INCREMENT BY " : " increment by ");
5769             x.getIncrementBy().accept(this);
5770         }
5771 
5772         if (x.getMaxValue() !is null) {
5773             print0(ucase ? " MAXVALUE " : " maxvalue ");
5774             x.getMaxValue().accept(this);
5775         }
5776 
5777         if (x.isNoMaxValue()) {
5778             if (DBType.POSTGRESQL.opEquals(dbType)) {
5779                 print0(ucase ? " NO MAXVALUE" : " no maxvalue");
5780             } else {
5781                 print0(ucase ? " NOMAXVALUE" : " nomaxvalue");
5782             }
5783         }
5784 
5785         if (x.getMinValue() !is null) {
5786             print0(ucase ? " MINVALUE " : " minvalue ");
5787             x.getMinValue().accept(this);
5788         }
5789 
5790         if (x.isNoMinValue()) {
5791             if (DBType.POSTGRESQL.opEquals(dbType)) {
5792                 print0(ucase ? " NO MINVALUE" : " no minvalue");
5793             } else {
5794                 print0(ucase ? " NOMINVALUE" : " nominvalue");
5795             }
5796         }
5797 
5798         if (x.getCycle() !is null) {
5799             if (x.getCycle().booleanValue()) {
5800                 print0(ucase ? " CYCLE" : " cycle");
5801             } else {
5802                 if (DBType.POSTGRESQL.opEquals(dbType)) {
5803                     print0(ucase ? " NO CYCLE" : " no cycle");
5804                 } else {
5805                     print0(ucase ? " NOCYCLE" : " nocycle");
5806                 }
5807             }
5808         }
5809 
5810         Boolean cache = x.getCache();
5811         if (cache !is null) {
5812             if (cache.booleanValue()) {
5813                 print0(ucase ? " CACHE" : " cache");
5814 
5815                 SQLExpr cacheValue = x.getCacheValue();
5816                 if (cacheValue !is null) {
5817                     print(' ');
5818                     cacheValue.accept(this);
5819                 }
5820             } else {
5821                 print0(ucase ? " NOCACHE" : " nocache");
5822             }
5823         }
5824 
5825         Boolean order = x.getOrder();
5826         if (order !is null) {
5827             if (order.booleanValue()) {
5828                 print0(ucase ? " ORDER" : " order");
5829             } else {
5830                 print0(ucase ? " NOORDER" : " noorder");
5831             }
5832         }
5833 
5834         return false;
5835     }
5836 
5837     override
5838     bool visit(SQLAlterSequenceStatement x) {
5839         print0(ucase ? "ALTER SEQUENCE " : "alter sequence ");
5840         x.getName().accept(this);
5841 
5842         if (x.getStartWith() !is null) {
5843             print0(ucase ? " START WITH " : " start with ");
5844             x.getStartWith().accept(this);
5845         }
5846 
5847         if (x.getIncrementBy() !is null) {
5848             print0(ucase ? " INCREMENT BY " : " increment by ");
5849             x.getIncrementBy().accept(this);
5850         }
5851 
5852         if (x.getMaxValue() !is null) {
5853             print0(ucase ? " MAXVALUE " : " maxvalue ");
5854             x.getMaxValue().accept(this);
5855         }
5856 
5857         if (x.isNoMaxValue()) {
5858             if (DBType.POSTGRESQL.opEquals(dbType)) {
5859                 print0(ucase ? " NO MAXVALUE" : " no maxvalue");
5860             } else {
5861                 print0(ucase ? " NOMAXVALUE" : " nomaxvalue");
5862             }
5863         }
5864 
5865         if (x.getMinValue() !is null) {
5866             print0(ucase ? " MINVALUE " : " minvalue ");
5867             x.getMinValue().accept(this);
5868         }
5869 
5870         if (x.isNoMinValue()) {
5871             if (DBType.POSTGRESQL.opEquals(dbType)) {
5872                 print0(ucase ? " NO MINVALUE" : " no minvalue");
5873             } else {
5874                 print0(ucase ? " NOMINVALUE" : " nominvalue");
5875             }
5876         }
5877 
5878         if (x.getCycle() !is null) {
5879             if (x.getCycle().booleanValue()) {
5880                 print0(ucase ? " CYCLE" : " cycle");
5881             } else {
5882                 if (DBType.POSTGRESQL.opEquals(dbType)) {
5883                     print0(ucase ? " NO CYCLE" : " no cycle");
5884                 } else {
5885                     print0(ucase ? " NOCYCLE" : " nocycle");
5886                 }
5887             }
5888         }
5889 
5890         Boolean cache = x.getCache();
5891         if (cache !is null) {
5892             if (cache.booleanValue()) {
5893                 print0(ucase ? " CACHE" : " cache");
5894 
5895                 SQLExpr cacheValue = x.getCacheValue();
5896                 if (cacheValue !is null) {
5897                     print(' ');
5898                     cacheValue.accept(this);
5899                 }
5900             } else {
5901                 print0(ucase ? " NOCACHE" : " nocache");
5902             }
5903         }
5904 
5905         Boolean order = x.getOrder();
5906         if (order !is null) {
5907             if (order.booleanValue()) {
5908                 print0(ucase ? " ORDER" : " order");
5909             } else {
5910                 print0(ucase ? " NOORDER" : " noorder");
5911             }
5912         }
5913 
5914         return false;
5915     }
5916 
5917     override bool visit(SQLDateExpr x) {
5918         if (this.parameterized) {
5919             print('?');
5920             incrementReplaceCunt();
5921 
5922             if(this.parameters !is null){
5923                 ExportParameterVisitorUtils.exportParameter(this.parameters, x);
5924             }
5925             return false;
5926         }
5927 
5928         SQLExpr literal = x.getLiteral();
5929         print0(ucase ? "DATE " : "date ");
5930         printExpr(literal);
5931 
5932         return false;
5933     }
5934 
5935     override bool visit(SQLLimit x) {
5936         print0(ucase ? "LIMIT " : "limit ");
5937         SQLExpr offset = x.getOffset();
5938         if (offset !is null) {
5939             printExpr(offset);
5940             print0(", ");
5941         }
5942 
5943         SQLExpr rowCount = x.getRowCount();
5944         printExpr(rowCount);
5945 
5946         return false;
5947     }
5948 
5949     override bool visit(SQLDescribeStatement x) {
5950         print0(ucase ? "DESC " : "desc ");
5951         if (x.getObjectType().name.length != 0) {
5952             print0(x.getObjectType().name);
5953             print(' ');
5954         }
5955 
5956         if(x.getObject() !is null) {
5957             x.getObject().accept(this);
5958         }
5959 
5960         if (x.getPartition().size() > 0) {
5961             print0(ucase ? " PARTITION (" : " partition (");
5962             printAndAccept!SQLExpr((x.getPartition()), ", ");
5963             print(')');
5964         }
5965         return false;
5966     }
5967 
5968     protected void printHierarchical(SQLSelectQueryBlock x) {
5969         SQLExpr startWith = x.getStartWith(), connectBy = x.getConnectBy();
5970         if (startWith !is null || connectBy !is null){
5971             println();
5972             if (x.getStartWith() !is null) {
5973                 print0(ucase ? "START WITH " : "start with ");
5974                 x.getStartWith().accept(this);
5975                 println();
5976             }
5977 
5978             print0(ucase ? "CONNECT BY " : "connect by ");
5979 
5980             if (x.isNoCycle()) {
5981                 print0(ucase ? "NOCYCLE " : "nocycle ");
5982             }
5983 
5984             if (x.isPrior()) {
5985                 print0(ucase ? "PRIOR " : "prior ");
5986             }
5987 
5988             x.getConnectBy().accept(this);
5989         }
5990     }
5991 
5992     // void printOracleSegmentAttributes(OracleSegmentAttributes x) {
5993 
5994     //     if (x.getPctfree() !is null) {
5995     //         println();
5996     //         print0(ucase ? "PCTFREE " : "pctfree ");
5997     //         print(x.getPctfree());
5998     //     }
5999 
6000     //     if (x.getPctused() !is null) {
6001     //         println();
6002     //         print0(ucase ? "PCTUSED " : "pctused ");
6003     //         print(x.getPctused());
6004     //     }
6005 
6006     //     if (x.getInitrans() !is null) {
6007     //         println();
6008     //         print0(ucase ? "INITRANS " : "initrans ");
6009     //         print(x.getInitrans());
6010     //     }
6011 
6012     //     if (x.getMaxtrans() !is null) {
6013     //         println();
6014     //         print0(ucase ? "MAXTRANS " : "maxtrans ");
6015     //         print(x.getMaxtrans());
6016     //     }
6017 
6018     //     if (x.getCompress() == bool.FALSE) {
6019     //         println();
6020     //         print0(ucase ? "NOCOMPRESS" : "nocompress");
6021     //     } else if (x.getCompress() == bool.TRUE) {
6022     //         println();
6023     //         print0(ucase ? "COMPRESS" : "compress");
6024 
6025     //         if (x.getCompressLevel() !is null) {
6026     //             print(' ');
6027     //             print(x.getCompressLevel());
6028     //         }
6029     //     }
6030 
6031     //     if (x.getLogging() == bool.TRUE) {
6032     //         println();
6033     //         print0(ucase ? "LOGGING" : "logging");
6034     //     } else if (x.getLogging() == bool.FALSE) {
6035     //         println();
6036     //         print0(ucase ? "NOLOGGING" : "nologging");
6037     //     }
6038 
6039     //     if (x.getTablespace() !is null) {
6040     //         println();
6041     //         print0(ucase ? "TABLESPACE " : "tablespace ");
6042     //         x.getTablespace().accept(this);
6043     //     }
6044 
6045     //     if (x.getStorage() !is null) {
6046     //         println();
6047     //         x.getStorage().accept(this);
6048     //     }
6049     // }
6050 
6051     override
6052     bool visit(SQLWhileStatement x) {
6053         string label = x.getLabelName();
6054 
6055         if (label !is null && label.length != 0) {
6056             print0(x.getLabelName());
6057             print0(": ");
6058         }
6059         print0(ucase ? "WHILE " : "while ");
6060         x.getCondition().accept(this);
6061         print0(ucase ? " DO" : " do");
6062         println();
6063         for (int i = 0, size = x.getStatements().size(); i < size; ++i) {
6064             SQLStatement item = x.getStatements().get(i);
6065             item.accept(this);
6066             if (i != size - 1) {
6067                 println();
6068             }
6069         }
6070         println();
6071         print0(ucase ? "END WHILE" : "end while");
6072         if (label !is null && label.length != 0) {
6073             print(' ');
6074             print0(label);
6075         }
6076         return false;
6077     }
6078 
6079     override
6080     bool visit(SQLDeclareStatement x) {
6081         // bool printDeclare = !(cast(OracleCreatePackageStatement)x.getParent() !is null);
6082         // if (printDeclare) {
6083         //     print0(ucase ? "DECLARE " : "declare ");
6084         // }  //@gxc
6085         this.printAndAccept!SQLDeclareItem((x.getItems()), ", ");
6086         return false;
6087     }
6088 
6089     override
6090     bool visit(SQLReturnStatement x) {
6091         print0(ucase ? "RETURN" : "return");
6092 
6093         if (x.getExpr() !is null) {
6094             print(' ');
6095             x.getExpr().accept(this);
6096         }
6097         return false;
6098     }
6099 
6100     override void postVisit(SQLObject x) {
6101         if (cast(SQLStatement)x !is null) {
6102             SQLStatement stmt = cast(SQLStatement) x;
6103             bool printSemi = printStatementAfterSemi is null
6104                     ? stmt.isAfterSemi()
6105                     : printStatementAfterSemi.booleanValue();
6106             if (printSemi) {
6107                 print(';');
6108             }
6109         }
6110     }
6111 
6112     override
6113     bool visit(SQLArgument x) {
6114         SQLParameter.ParameterType type = x.getType();
6115         if (type.name.length != 0) {
6116             print0(type.name);
6117             print(' ');
6118         }
6119 
6120         x.getExpr().accept(this);
6121         return false;
6122     }
6123 
6124     override
6125     bool visit(SQLCommitStatement x) {
6126         print0(ucase ? "COMMIT" : "commit");
6127 
6128         if (x.isWrite()) {
6129             print0(ucase ? " WRITE" : " write");
6130             if (x.getWait() !is null) {
6131                 if (x.getWait().booleanValue()) {
6132                     print0(ucase ? " WAIT" : " wait");
6133                 } else {
6134                     print0(ucase ? " NOWAIT" : " nowait");
6135                 }
6136             }
6137 
6138             if (x.getImmediate() !is null) {
6139                 if (x.getImmediate().booleanValue()) {
6140                     print0(ucase ? " IMMEDIATE" : " immediate");
6141                 } else {
6142                     print0(ucase ? " BATCH" : " batch");
6143                 }
6144             }
6145         }
6146 
6147         if (x.isWork()) {
6148             print0(ucase ? " WORK" : " work");
6149         }
6150 
6151         if (x.getChain() !is null) {
6152             if (x.getChain().booleanValue()) {
6153                 print0(ucase ? " AND CHAIN" : " and chain");
6154             } else {
6155                 print0(ucase ? " AND NO CHAIN" : " and no chain");
6156             }
6157         }
6158 
6159         if (x.getRelease() !is null) {
6160             if (x.getRelease().booleanValue()) {
6161                 print0(ucase ? " AND RELEASE" : " and release");
6162             } else {
6163                 print0(ucase ? " AND NO RELEASE" : " and no release");
6164             }
6165         }
6166 
6167         return false;
6168     }
6169 
6170     override bool visit(SQLFlashbackExpr x) {
6171         print0(x.getType().name);
6172         print(' ');
6173         SQLExpr expr = x.getExpr();
6174         if (cast(SQLBinaryOpExpr)expr !is null) {
6175             print('(');
6176             expr.accept(this);
6177             print(')');
6178         } else {
6179             expr.accept(this);
6180         }
6181         return false;
6182     }
6183 
6184     override bool visit(SQLCreateMaterializedViewStatement x) {
6185         print0(ucase ? "CREATE MATERIALIZED VIEW " : "create materialized view ");
6186         x.getName().accept(this);
6187 
6188         SQLPartitionBy partitionBy = x.getPartitionBy();
6189         if (partitionBy !is null) {
6190             println();
6191             print0(ucase ? "PARTITION BY " : "partition by ");
6192             partitionBy.accept(this);
6193         }
6194 
6195         // this.printOracleSegmentAttributes(x);//@gxc
6196         println();
6197 
6198         Boolean cache = x.getCache();
6199         if (cache !is null) {
6200             print(cache.booleanValue ? "CACHE" : "NOCACHE");
6201             println();
6202         }
6203 
6204         auto parallel = x.getParallel();
6205         if (parallel !is null) {
6206             if (parallel.booleanValue) {
6207                 print(ucase ? "PARALLEL" : "parallel");
6208                 Integer parallelValue = x.getParallelValue();
6209                 if (parallelValue !is null) {
6210                     print(' ');
6211                     print(parallelValue.intValue());
6212                 }
6213             } else {
6214                 print(ucase ? "NOPARALLEL" : "noparallel");
6215             }
6216             println();
6217         }
6218 
6219         if (x.isBuildImmediate()) {
6220             println(ucase ? "BUILD IMMEDIATE" : "build immediate");
6221         }
6222 
6223         if (x.isRefresh()) {
6224             print(ucase ? "REFRESH" : "refresh");
6225 
6226             if (x.isRefreshFast()) {
6227                 print(ucase ? " FAST" : " fast");
6228             } else if (x.isRefreshComlete()) {
6229                 print(ucase ? " COMPLETE" : " complete");
6230             } else if (x.isRefreshForce()) {
6231                 print(ucase ? " FORCE" : " force");
6232             }
6233 
6234             if (x.isRefreshOnCommit()) {
6235                 print(ucase ? " ON COMMIT" : " on commit");
6236             } else if (x.isRefreshOnDemand()) {
6237                 print(ucase ? " ON DEMAND" : " on demand");
6238             }
6239 
6240             println();
6241         }
6242 
6243         Boolean enableQueryRewrite = x.getEnableQueryRewrite();
6244         if (enableQueryRewrite !is null) {
6245             if (enableQueryRewrite.booleanValue) {
6246                 print(ucase ? "ENABLE QUERY REWRITE" : "enable query rewrite");
6247             } else {
6248                 print(ucase ? "DISABLE QUERY REWRITE" : "disable query rewrite");
6249             }
6250             println();
6251         }
6252 
6253         println(ucase ? "AS" : "as");
6254         x.getQuery().accept(this);
6255         return false;
6256     }
6257 
6258     override bool visit(SQLCreateUserStatement x) {
6259         print0(ucase ? "CREATE USER " : "create user ");
6260         x.getUser().accept(this);
6261         print0(ucase ? " IDENTIFIED BY " : " identified by ");
6262         x.getPassword().accept(this);
6263         return false;
6264     }
6265 
6266     override bool visit(SQLAlterFunctionStatement x) {
6267         print0(ucase ? "ALTER FUNCTION " : "alter function ");
6268         x.getName().accept(this);
6269 
6270         if (x.isDebug()) {
6271             print0(ucase ? " DEBUG" : " debug");
6272         }
6273 
6274         if (x.isReuseSettings()) {
6275             print0(ucase ? " REUSE SETTINGS" : " reuse settings");
6276         }
6277 
6278         return false;
6279     }
6280 
6281     override bool visit(SQLAlterTypeStatement x) {
6282         print0(ucase ? "ALTER TYPE " : "alter type ");
6283         x.getName().accept(this);
6284 
6285         if (x.isCompile()) {
6286             print0(ucase ? " COMPILE" : " compile");
6287         }
6288 
6289         if (x.isBody()) {
6290             print0(ucase ? " BODY" : " body");
6291         }
6292 
6293         if (x.isDebug()) {
6294             print0(ucase ? " DEBUG" : " debug");
6295         }
6296 
6297         if (x.isReuseSettings()) {
6298             print0(ucase ? " REUSE SETTINGS" : " reuse settings");
6299         }
6300 
6301         return false;
6302     }
6303 
6304     override
6305     bool visit(SQLIntervalExpr x) {
6306         print0(ucase ? "INTERVAL " : "interval ");
6307         SQLExpr value = x.getValue();
6308         value.accept(this);
6309 
6310         SQLIntervalUnit unit = x.getUnit();
6311         if (unit.name.length != 0) {
6312             print(' ');
6313             print0(ucase ? unit.name : unit.name_lcase);
6314         }
6315         return false;
6316     }
6317 
6318     Boolean getPrintStatementAfterSemi() {
6319         return printStatementAfterSemi;
6320     }
6321 
6322     void setPrintStatementAfterSemi(Boolean printStatementAfterSemi) {
6323         this.printStatementAfterSemi = printStatementAfterSemi;
6324     }
6325 
6326     override void config(VisitorFeature feature, bool state) {
6327         super.config(feature, state);
6328         if (feature == VisitorFeature.OutputUCase) {
6329             this.ucase = state;
6330         } else if (feature == VisitorFeature.OutputParameterized) {
6331             this.parameterized = state;
6332         }
6333     }
6334 
6335     override void setFeatures(int features) {
6336         super.setFeatures(features);
6337         this.ucase = isEnabled(VisitorFeature.OutputUCase);
6338         this.parameterized = isEnabled(VisitorFeature.OutputParameterized);
6339         this.parameterizedQuesUnMergeInList = isEnabled(VisitorFeature.OutputParameterizedQuesUnMergeInList);
6340     }
6341 
6342     /////////////// for oracle
6343     // bool visit(OracleCursorExpr x) {
6344     //     print0(ucase ? "CURSOR(" : "cursor(");
6345     //     this.indentCount++;
6346     //     println();
6347     //     x.getQuery().accept(this);
6348     //     this.indentCount--;
6349     //     println();
6350     //     print(')');
6351     //     return false;
6352     // }
6353 
6354     // bool visit(OracleDatetimeExpr x) {
6355     //     x.getExpr().accept(this);
6356     //     SQLExpr timeZone = x.getTimeZone();
6357 
6358     //     if (cast(SQLIdentifierExpr)timeZone !is null) {
6359     //         if ((cast(SQLIdentifierExpr) timeZone).getName().equalsIgnoreCase("LOCAL")) {
6360     //             print0(ucase ? " AT LOCAL" : "alter session set ");
6361     //             return false;
6362     //         }
6363     //     }
6364 
6365     //     print0(ucase ? " AT TIME ZONE " : " at time zone ");
6366     //     timeZone.accept(this);
6367 
6368     //     return false;
6369     // }
6370 
6371     ///////////// for odps & hive
6372     override
6373     bool visit(SQLLateralViewTableSource x) {
6374         x.getTableSource().accept(this);
6375         this.indentCount++;
6376         println();
6377         print0(ucase ? "LATERAL VIEW " : "lateral view ");
6378         x.getMethod().accept(this);
6379         print(' ');
6380         print0(x.getAlias());
6381         print0(ucase ? " AS " : " as ");
6382         printAndAccept!SQLName((x.getColumns()), ", ");
6383         this.indentCount--;
6384         return false;
6385     }
6386 
6387     override
6388     bool visit(SQLShowErrorsStatement x) {
6389         print0(ucase ? "SHOW ERRORS" : "show errors");
6390         return true;
6391     }
6392 
6393     override
6394     bool visit(SQLAlterCharacter x) {
6395         print0(ucase ? "CHARACTER SET = " : "character set = ");
6396         x.getCharacterSet().accept(this);
6397 
6398         if (x.getCollate() !is null) {
6399             print0(ucase ? ", COLLATE = " : ", collate = ");
6400             x.getCollate().accept(this);
6401         }
6402 
6403         return false;
6404     }
6405 
6406     override
6407     bool visit(SQLRecordDataType x) {
6408         print0(ucase ? "RECORD (" : "record (");
6409         indentCount++;
6410         println();
6411         List!SQLColumnDefinition columns = x.getColumns();
6412         for (int i = 0; i < columns.size(); i++) {
6413             if (i != 0) {
6414                 println();
6415             }
6416             columns.get(i).accept(this);
6417             if (i != columns.size() - 1) {
6418                 print0(", ");
6419             }
6420         }
6421         indentCount--;
6422         println();
6423         print(')');
6424 
6425         return false;
6426     }
6427 
6428     override
6429     bool visit(SQLExprStatement x) {
6430         x.getExpr().accept(this);
6431         return false;
6432     }
6433 
6434     override
6435     bool visit(SQLBlockStatement x) {
6436         if (x.getParameters().size() != 0) {
6437             this.indentCount++;
6438             if (cast(SQLCreateProcedureStatement)x.getParent() !is null) {
6439                 SQLCreateProcedureStatement procedureStatement = cast(SQLCreateProcedureStatement) x.getParent();
6440                 if (procedureStatement.isCreate()) {
6441                     printIndent();
6442                 }
6443             }
6444             if (!( cast(SQLCreateProcedureStatement)x.getParent() !is null
6445                     || cast(SQLCreateFunctionStatement)x.getParent() !is null
6446                     /*|| cast(OracleFunctionDataType)x.getParent() !is null
6447                     || cast(OracleProcedureDataType)x.getParent() !is null*/)
6448                     ) {
6449                 print0(ucase ? "DECLARE" : "declare");
6450                 println();
6451             }
6452 
6453             for (int i = 0, size = x.getParameters().size(); i < size; ++i) {
6454                 if (i != 0) {
6455                     println();
6456                 }
6457                 SQLParameter param = x.getParameters().get(i);
6458                 param.accept(this);
6459                 print(';');
6460             }
6461 
6462             this.indentCount--;
6463             println();
6464         }
6465         print0(ucase ? "BEGIN" : "begin");
6466         this.indentCount++;
6467 
6468         for (int i = 0, size = x.getStatementList().size(); i < size; ++i) {
6469             println();
6470             SQLStatement stmt = x.getStatementList().get(i);
6471             stmt.accept(this);
6472         }
6473         this.indentCount--;
6474 
6475         SQLStatement exception = x.getException();
6476         if (exception !is null) {
6477             println();
6478             exception.accept(this);
6479         }
6480 
6481         println();
6482         print0(ucase ? "END;" : "end;");
6483         return false;
6484     }
6485 
6486     override
6487     bool visit(SQLCreateProcedureStatement x) {
6488         bool create = x.isCreate();
6489         if (!create) {
6490             print0(ucase ? "PROCEDURE " : "procedure ");
6491         } else if (x.isOrReplace()) {
6492             print0(ucase ? "CREATE OR REPLACE PROCEDURE " : "create or replace procedure ");
6493         } else {
6494             print0(ucase ? "CREATE PROCEDURE " : "create procedure ");
6495         }
6496         x.getName().accept(this);
6497 
6498         int paramSize = x.getParameters().size();
6499 
6500         if (paramSize > 0) {
6501             print0(" (");
6502             this.indentCount++;
6503             println();
6504 
6505             for (int i = 0; i < paramSize; ++i) {
6506                 if (i != 0) {
6507                     print0(", ");
6508                     println();
6509                 }
6510                 SQLParameter param = x.getParameters().get(i);
6511                 param.accept(this);
6512             }
6513 
6514             this.indentCount--;
6515             println();
6516             print(')');
6517         }
6518 
6519         SQLName authid = x.getAuthid();
6520         if (authid !is null) {
6521             print(ucase ? " AUTHID " : " authid ");
6522             authid.accept(this);
6523         }
6524 
6525         SQLStatement block = x.getBlock();
6526         string wrappedSource = x.getWrappedSource();
6527         if (wrappedSource !is null) {
6528             print0(ucase ? " WRAPPED " : " wrapped ");
6529             print0(wrappedSource);
6530         } else {
6531             if (block !is null && !create) {
6532                 println();
6533                 print("IS");
6534                 println();
6535             } else {
6536                 println();
6537                 if (cast(SQLBlockStatement)block !is null) {
6538                     SQLBlockStatement blockStatement = cast(SQLBlockStatement) block;
6539                     if (blockStatement.getParameters().size() > 0 || authid !is null) {
6540                         println(ucase ? "AS" : "as");
6541                     } else {
6542                         println(ucase ? "IS" : "is");
6543                     }
6544                 }
6545             }
6546 
6547             string javaCallSpec = x.getJavaCallSpec();
6548             if (javaCallSpec !is null) {
6549                 print0(ucase ? "LANGUAGE JAVA NAME '" : "language java name '");
6550                 print0(javaCallSpec);
6551                 print('\'');
6552                 return false;
6553             }
6554         }
6555 
6556         bool afterSemi = false;
6557         if (block !is null) {
6558             block.accept(this);
6559 
6560             if (cast(SQLBlockStatement)block !is null
6561                     && (cast(SQLBlockStatement) block).getStatementList().size() > 0) {
6562                 afterSemi = (cast(SQLBlockStatement) block).getStatementList().get(0).isAfterSemi();
6563             }
6564         }
6565 
6566         // if ((!afterSemi) && cast(OracleCreatePackageStatement)x.getParent() !is null) {
6567         //     print(';');
6568         // }
6569         return false;
6570     }
6571 
6572     override bool visit(SQLExternalRecordFormat x) {
6573         if (x.getDelimitedBy() !is null) {
6574             println();
6575             print0(ucase ? "RECORDS DELIMITED BY " : "records delimited by ");
6576             x.getDelimitedBy().accept(this);
6577         }
6578 
6579         if (x.getTerminatedBy() !is null) {
6580             println();
6581             print0(ucase ? "FIELDS TERMINATED BY " : "fields terminated by ");
6582             x.getTerminatedBy().accept(this);
6583         }
6584 
6585         return false;
6586     }
6587 
6588     override
6589     bool visit(SQLArrayDataType x) {
6590         print0(ucase ? "ARRAY<" : "array<");
6591         x.getComponentType().accept(this);
6592         print('>');
6593         return false;
6594     }
6595 
6596     override
6597     bool visit(SQLMapDataType x) {
6598         print0(ucase ? "MAP<" : "map<");
6599         x.getKeyType().accept(this);
6600         print0(", ");
6601         x.getValueType().accept(this);
6602         print('>');
6603         return false;
6604     }
6605 
6606     override
6607     bool visit(SQLStructDataType x) {
6608         print0(ucase ? "STRUCT<" : "struct<");
6609         printAndAccept!(SQLStructDataType.Field)((x.getFields()), ", ");
6610         print('>');
6611         return false;
6612     }
6613 
6614     override
6615     bool visit(SQLStructDataType.Field x) {
6616         x.getName().accept(this);
6617         print(':');
6618         x.getDataType().accept(this);
6619         print('>');
6620         return false;
6621     }
6622 
6623     override bool visit(SQLAlterTableRenameIndex x) {
6624         print0(ucase ? "RENAME INDEX " : "rename index ");
6625         x.getName().accept(this);
6626         print0(ucase ? " TO " : " to ");
6627         x.getTo().accept(this);
6628         return false;
6629     }
6630 
6631     override
6632     bool visit(SQLAlterTableExchangePartition x) {
6633         print0(ucase ? "EXCHANGE PARTITION " : "exchange partition ");
6634         x.getPartition().accept(this);
6635         print0(ucase ? " WITH TABLE " : " with table ");
6636         x.getTable().accept(this);
6637 
6638         auto validation = x.getValidation();
6639         if (validation !is null) {
6640             if (validation.booleanValue) {
6641                 print0(ucase ? " WITH VALIDATION" : " with validation");
6642             } else {
6643                 print0(ucase ? " WITHOUT VALIDATION" : " without validation");
6644             }
6645         }
6646 
6647         return false;
6648     }
6649 
6650     override
6651     bool visit(SQLValuesExpr x) {
6652         print0(ucase ? "VALUES (" : "values (");
6653         printAndAccept!SQLListExpr((x.getValues()), ", ");
6654         return false;
6655     }
6656 
6657     override
6658     bool visit(SQLValuesTableSource x) {
6659         List!SQLName columns = x.getColumns();
6660 
6661         if (columns.size() > 0) {
6662             print('(');
6663         }
6664         print0(ucase ? "VALUES " : "values ");
6665         printAndAccept!SQLListExpr((x.getValues()), ", ");
6666 
6667         if (columns.size() > 0) {
6668             print(") ");
6669         }
6670 
6671         print0(ucase ? "AS " : "as ");
6672         print0(x.getAlias());
6673         print0(" (");
6674         printAndAccept!SQLName(columns, ", ");
6675         print(')');
6676 
6677         return false;
6678     }
6679 
6680     override bool visit(SQLContainsExpr x) {
6681         SQLExpr expr = x.getExpr();
6682         if (expr !is null) {
6683             printExpr(expr);
6684             print(' ');
6685         }
6686 
6687         if (x.isNot()) {
6688             print0(ucase ? "NOT CONTAINS (" : " not contains (");
6689         } else {
6690             print0(ucase ? "CONTAINS (" : " contains (");
6691         }
6692 
6693          List!SQLExpr list = x.getTargetList();
6694 
6695         bool printLn = false;
6696         if (list.size() > 5) {
6697             printLn = true;
6698             for (int i = 0, size = list.size(); i < size; ++i) {
6699                 if (!(cast(SQLCharExpr)list.get(i) !is null)) {
6700                     printLn = false;
6701                     break;
6702                 }
6703             }
6704         }
6705 
6706         if (printLn) {
6707             this.indentCount++;
6708             println();
6709             for (int i = 0, size = list.size(); i < size; ++i) {
6710                 if (i != 0) {
6711                     print0(", ");
6712                     println();
6713                 }
6714                 SQLExpr item = list.get(i);
6715                 printExpr(item);
6716             }
6717             this.indentCount--;
6718             println();
6719         } else {
6720             List!SQLExpr targetList = x.getTargetList();
6721             for (int i = 0; i < targetList.size(); i++) {
6722                 if (i != 0) {
6723                     print0(", ");
6724                 }
6725                 printExpr(targetList.get(i));
6726             }
6727         }
6728 
6729         print(')');
6730         return false;
6731     }
6732 
6733     override bool visit(SQLRealExpr x) {
6734         float value = (cast(Float)(x.getValue())).floatValue;
6735         print0(ucase ? "REAL '" : "real '");
6736         print(value);
6737         print('\'');
6738 
6739         return false;
6740     }
6741 
6742     override
6743     bool visit(SQLWindow x) {
6744         x.getName().accept(this);
6745         print0(ucase ? " AS " : " as ");
6746         x.getOver().accept(this);
6747         return false;
6748     }
6749 
6750     override
6751     bool visit(SQLDumpStatement x) {
6752         List!SQLCommentHint headHints = x.getHeadHintsDirect();
6753         if (headHints !is null) {
6754             foreach(SQLCommentHint hint  ;  headHints) {
6755                 hint.accept(this);
6756                 println();
6757             }
6758         }
6759 
6760         print0(ucase ? "DUMP DATA " : "dump data ");
6761 
6762 
6763         if (x.isOverwrite()) {
6764             print0(ucase ? "OVERWRITE " : "overwrite ");
6765         }
6766 
6767         SQLExprTableSource into = x.getInto();
6768         if (into !is null) {
6769             into.accept(this);
6770         }
6771 
6772         x.getSelect().accept(this);
6773         return false;
6774     }
6775 
6776     void print(float value) {
6777         if (this.appender is null) {
6778             return;
6779         }
6780 
6781         if (cast(StringBuilder)appender !is null) {
6782             (cast(StringBuilder) appender).append(value);
6783         } else if (cast(StringBuilder)appender !is null) {
6784             (cast(StringBuilder) appender).append(value);
6785         } else {
6786             print0(to!string(value));
6787         }
6788     }
6789 }