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.SQLUtils;
17 
18 import hunt.collection;
19 
20 import hunt.sql.ast;
21 import hunt.sql.ast.expr;
22 // import hunt.sql.ast.statement;
23 // import hunt.sql.dialect.db2.visitor.DB2OutputVisitor;
24 // import hunt.sql.dialect.db2.visitor.DB2SchemaStatVisitor;
25 // import hunt.sql.dialect.h2.visitor.H2OutputVisitor;
26 // import hunt.sql.dialect.h2.visitor.H2SchemaStatVisitor;
27 // import hunt.sql.dialect.hive.visitor.HiveOutputVisitor;
28 // import hunt.sql.dialect.hive.visitor.HiveSchemaStatVisitor;
29 import hunt.sql.dialect.mysql.visitor.MySqlOutputVisitor;
30 import hunt.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
31 // import hunt.sql.dialect.odps.visitor.OdpsOutputVisitor;
32 // import hunt.sql.dialect.odps.visitor.OdpsSchemaStatVisitor;
33 // import hunt.sql.dialect.oracle.visitor.OracleOutputVisitor;
34 // import hunt.sql.dialect.oracle.visitor.OracleSchemaStatVisitor;
35 // import hunt.sql.dialect.oracle.visitor.OracleToMySqlOutputVisitor;
36 import hunt.sql.dialect.postgresql.visitor.PGOutputVisitor;
37 import hunt.sql.dialect.postgresql.visitor.PGSchemaStatVisitor;
38 // import hunt.sql.dialect.sqlserver.visitor.SQLServerOutputVisitor;
39 // import hunt.sql.dialect.sqlserver.visitor.SQLServerSchemaStatVisitor;
40 // import hunt.sql.parser;
41 
42 import hunt.sql.parser.CharTypes;
43 import hunt.sql.parser.EOFParserException;
44 // import hunt.sql.parser.InsertColumnsCache;
45 import hunt.sql.parser.Keywords;
46 import hunt.sql.parser.LayoutCharacters;
47 import hunt.sql.parser.Lexer;
48 import hunt.sql.parser.NotAllowCommentException;
49 import hunt.sql.parser.ParserException;
50 import hunt.sql.parser.SQLCreateTableParser;
51 // import hunt.sql.parser.SQLDDLParser;
52 import hunt.sql.parser.SQLExprParser;
53 import hunt.sql.parser.SQLParseException;
54 import hunt.sql.parser.SQLParser;
55 import hunt.sql.parser.SQLParserFeature;
56 import hunt.sql.parser.SQLParserUtils;
57 import hunt.sql.parser.SQLSelectListCache;
58 import hunt.sql.parser.SQLSelectParser;
59 // import hunt.sql.parser.SQLStatementParser;
60 import hunt.sql.parser.SymbolTable;
61 import hunt.sql.parser.Token;
62 
63 import hunt.sql.visitor.SQLASTOutputVisitor;
64 import hunt.sql.visitor.SchemaStatVisitor;
65 import hunt.sql.visitor.VisitorFeature;
66 import hunt.sql.ast.statement.SQLSelectOrderByItem;
67 import hunt.sql.ast.statement.SQLUpdateSetItem;
68 import hunt.sql.ast.statement.SQLSelectItem;
69 import hunt.sql.ast.statement.SQLSelectQueryBlock;
70 import hunt.sql.ast.statement.SQLSetStatement;
71 import hunt.sql.ast.statement.SQLSelectStatement;
72 import hunt.sql.ast.statement.SQLSelectQuery;
73 import hunt.sql.ast.statement.SQLDeleteStatement;
74 import hunt.sql.ast.statement.SQLUpdateStatement;
75 import hunt.sql.ast.statement.SQLCreateTableStatement;
76 import hunt.util.Common;
77 // import entity.support.logging.Log;
78 // import entity.support.logging.LogFactory;
79 import hunt.sql.util;
80 import hunt.logging;
81 import hunt.collection;
82 import hunt.String;
83 import std.array;
84 import hunt.text;
85 import hunt.util.Appendable;
86 
87 import std.concurrency : initOnce;
88 
89 public class SQLUtils {
90     private  enum SQLParserFeature[] FORMAT_DEFAULT_FEATURES = [
91             SQLParserFeature.KeepComments,
92             SQLParserFeature.EnableSQLBinaryOpExprGroup
93     ];
94 
95     static FormatOption DEFAULT_FORMAT_OPTION() {
96         __gshared FormatOption inst;
97         return initOnce!inst(new FormatOption(true, true));
98      }
99 
100     static FormatOption DEFAULT_LCASE_FORMAT_OPTION() {
101         __gshared FormatOption inst;
102         return initOnce!inst(new FormatOption(false, true));
103      }
104 
105 
106     public static string toSQLString(SQLObject sqlObject, string dbType) {
107         return toSQLString(sqlObject, dbType, null);
108     }
109 
110     public static string toSQLString(SQLObject sqlObject, string dbType, FormatOption option) {
111         StringBuilder _out = new StringBuilder();
112         SQLASTOutputVisitor visitor = createOutputVisitor(_out, dbType);
113 
114         if (option is null) {
115             option = DEFAULT_FORMAT_OPTION;
116         }
117         visitor.setUppCase(option.isUppCase());
118         visitor.setPrettyFormat(option.isPrettyFormat());
119         visitor.setParameterized(option.isParameterized());
120         visitor.setFeatures(option.features);
121 
122         sqlObject.accept(visitor);
123 
124         string sql = _out.toString();
125         return sql;
126     }
127 
128     public static string toSQLString(SQLObject sqlObject) {
129         StringBuilder _out = new StringBuilder();
130         sqlObject.accept(new SQLASTOutputVisitor(_out));
131 
132         string sql = _out.toString();
133         return sql;
134     }
135 
136     // public static string toOdpsString(SQLObject sqlObject) {
137     //     return toOdpsString(sqlObject, null);
138     // }
139 
140     // public static string toOdpsString(SQLObject sqlObject, FormatOption option) {
141     //     return toSQLString(sqlObject, DBType.ODPS, option);
142     // }
143 
144     public static string toMySqlString(SQLObject sqlObject) {
145         return toMySqlString(sqlObject, cast(FormatOption) null);
146     }
147 
148     public static string toMySqlString(SQLObject sqlObject, VisitorFeature[] features...) {
149         return toMySqlString(sqlObject, new FormatOption(features));
150     }
151 
152     public static string toMySqlString(SQLObject sqlObject, FormatOption option) {
153         return toSQLString(sqlObject, DBType.MYSQL.name(), option);
154     }
155 
156     public static SQLExpr toMySqlExpr(string sql) {
157         return toSQLExpr(sql, DBType.MYSQL.name());
158     }
159 
160     public static string formatMySql(string sql) {
161         return format(sql, DBType.MYSQL.name());
162     }
163 
164     public static string formatMySql(string sql, FormatOption option) {
165         return format(sql, DBType.MYSQL.name(), option);
166     }
167 
168     // public static string formatOracle(string sql) {
169     //     return format(sql, DBType.ORACLE);
170     // }
171 
172     // public static string formatOracle(string sql, FormatOption option) {
173     //     return format(sql, DBType.ORACLE, option);
174     // }
175 
176     // public static string formatOdps(string sql) {
177     //     return format(sql, DBType.ODPS);
178     // }
179 
180     // public static string formatHive(string sql) {
181     //     return format(sql, DBType.HIVE);
182     // }
183 
184     // public static string formatOdps(string sql, FormatOption option) {
185     //     return format(sql, DBType.ODPS, option);
186     // }
187 
188     // public static string formatHive(string sql, FormatOption option) {
189     //     return format(sql, DBType.HIVE, option);
190     // }
191 
192     // public static string formatSQLServer(string sql) {
193     //     return format(sql, DBType.SQL_SERVER);
194     // }
195 
196     // public static string toOracleString(SQLObject sqlObject) {
197     //     return toOracleString(sqlObject, null);
198     // }
199 
200     // public static string toOracleString(SQLObject sqlObject, FormatOption option) {
201     //     return toSQLString(sqlObject, DBType.ORACLE, option);
202     // }
203 
204     public static string toPGString(SQLObject sqlObject) {
205         return toPGString(sqlObject, null);
206     }
207 
208     public static string toPGString(SQLObject sqlObject, FormatOption option) {
209         return toSQLString(sqlObject, DBType.POSTGRESQL.name(), option);
210     }
211 
212     // public static string toDB2String(SQLObject sqlObject) {
213     //     return toDB2String(sqlObject, null);
214     // }
215 
216     // public static string toDB2String(SQLObject sqlObject, FormatOption option) {
217     //     return toSQLString(sqlObject, DBType.DB2, option);
218     // }
219 
220     // public static string toSQLServerString(SQLObject sqlObject) {
221     //     return toSQLServerString(sqlObject, null);
222     // }
223 
224     // public static string toSQLServerString(SQLObject sqlObject, FormatOption option) {
225     //     return toSQLString(sqlObject, DBType.SQL_SERVER, option);
226     // }
227 
228     public static string formatPGSql(string sql, FormatOption option) {
229         return format(sql, DBType.POSTGRESQL.name(), option);
230     }
231 
232     public static SQLExpr toSQLExpr(string sql, string dbType) {
233         SQLExprParser parser = SQLParserUtils.createExprParser(sql, dbType);
234         SQLExpr expr = parser.expr();
235 
236         if (parser.getLexer().token() != Token.EOF) {
237             throw new ParserException("illegal sql expr : " ~ sql);
238         }
239         // logDebug("toSQLExpr : ",toSQLString(expr,dbType));
240         return expr;
241     }
242 
243     public static SQLSelectOrderByItem toOrderByItem(string sql, string dbType) {
244         SQLExprParser parser = SQLParserUtils.createExprParser(sql, dbType);
245         SQLSelectOrderByItem orderByItem = parser.parseSelectOrderByItem();
246 
247         if (parser.getLexer().token() != Token.EOF) {
248             throw new ParserException("illegal sql expr : " ~ sql);
249         }
250 
251         return orderByItem;
252     }
253 
254     public static SQLUpdateSetItem toUpdateSetItem(string sql, string dbType) {
255         SQLExprParser parser = SQLParserUtils.createExprParser(sql, dbType);
256         SQLUpdateSetItem updateSetItem = parser.parseUpdateSetItem();
257 
258         if (parser.getLexer().token() != Token.EOF) {
259             throw new ParserException("illegal sql expr : " ~ sql);
260         }
261 
262         return updateSetItem;
263     }
264 
265     public static SQLSelectItem toSelectItem(string sql, string dbType) {
266         SQLExprParser parser = SQLParserUtils.createExprParser(sql, dbType);
267         SQLSelectItem selectItem = parser.parseSelectItem();
268 
269         if (parser.getLexer().token() != Token.EOF) {
270             throw new ParserException("illegal sql expr : " ~ sql);
271         }
272 
273         return selectItem;
274     }
275 
276     public static List!(SQLStatement) toStatementList(string sql, string dbType) {
277         auto parser = SQLParserUtils.createSQLStatementParser(sql, dbType);
278         return parser.parseStatementList();
279     }
280 
281     public static SQLExpr toSQLExpr(string sql) {
282         return toSQLExpr(sql, null);
283     }
284 
285     public static string format(string sql, string dbType) {
286         return format(sql, dbType, null, null);
287     }
288 
289     public static string format(string sql, string dbType, FormatOption option) {
290         return format(sql, dbType, null, option);
291     }
292 
293     public static string format(string sql, string dbType, List!(Object) parameters) {
294         return format(sql, dbType, parameters, null);
295     }
296 
297     public static string format(string sql, string dbType, List!(Object) parameters, FormatOption option) {
298         try {
299             auto parser = SQLParserUtils.createSQLStatementParser(sql, dbType, FORMAT_DEFAULT_FEATURES);
300             List!(SQLStatement) statementList = parser.parseStatementList();
301             return toSQLString(statementList, dbType, parameters, option);
302         } catch (Exception ex) {
303             warningf("format error, dbType: %s, message: %s", dbType, ex.msg);
304             version(HUNT_DEBUG) {
305                 warning(ex);
306             }
307             return sql;
308         }
309         // } catch (ParserException ex) {
310         //     logWarning("format error", ex);
311         //     return sql;
312         // }
313     }
314 
315     public static string toSQLString(List!(SQLStatement) statementList, string dbType) {
316         return toSQLString(statementList, dbType, cast(List!(Object)) null);
317     }
318 
319     public static string toSQLString(List!(SQLStatement) statementList, string dbType, FormatOption option) {
320         return toSQLString(statementList, dbType, null, option);
321     }
322 
323     public static string toSQLString(List!(SQLStatement) statementList, DBType dbType, FormatOption option) {
324         return toSQLString(statementList, dbType.name, null, option);
325     }
326 
327     public static string toSQLString(List!(SQLStatement) statementList, string dbType, List!(Object) parameters) {
328         return toSQLString(statementList, dbType, parameters, null, null);
329     }
330 
331     public static string toSQLString(List!(SQLStatement) statementList, string dbType, List!(Object) parameters, FormatOption option) {
332         return toSQLString(statementList, dbType, parameters, option, null);
333     }
334 
335     public static string toSQLString(List!(SQLStatement) statementList
336             , string dbType
337             , List!(Object) parameters
338             , FormatOption option
339             , Map!(string , string) tableMapping) {
340         StringBuilder _out = new StringBuilder();
341         SQLASTOutputVisitor visitor = createFormatOutputVisitor(_out, statementList, dbType);
342         if (parameters !is null) {
343             visitor.setInputParameters(parameters);
344         }
345 
346         if (option is null) {
347             option = DEFAULT_FORMAT_OPTION;
348         }
349         visitor.setFeatures(option.features);
350 
351         if (tableMapping !is null) {
352             visitor.setTableMapping(tableMapping);
353         }
354 
355         bool printStmtSeperator;
356         if (DBType.SQL_SERVER.opEquals(dbType)) {
357             printStmtSeperator = false;
358         } else {
359             printStmtSeperator = !DBType.ORACLE.opEquals(dbType);
360         }
361 
362         for (int i = 0, size = statementList.size(); i < size; i++) {
363             SQLStatement stmt = statementList.get(i);
364 
365             if (i > 0) {
366                 SQLStatement preStmt = statementList.get(i - 1);
367                 if (printStmtSeperator && !preStmt.isAfterSemi()) {
368                     visitor.print(";");
369                 }
370 
371                 List!(string) comments = preStmt.getAfterCommentsDirect();
372                 if (comments !is null){
373                     for (int j = 0; j < comments.size(); ++j) {
374                         string comment = comments.get(j);
375                         if (j != 0) {
376                             visitor.println();
377                         }
378                         visitor.printComment(comment);
379                     }
380                 }
381 
382                 if (printStmtSeperator) {
383                     visitor.println();
384                 }
385 
386                 if (!(cast(SQLSetStatement)(stmt) !is null)) {
387                     visitor.println();
388                 }
389             }
390             {
391                 List!(string) comments = stmt.getBeforeCommentsDirect();
392                 if (comments !is null){
393                     foreach(string comment ; comments) {
394                         visitor.printComment(comment);
395                         visitor.println();
396                     }
397                 }
398             }
399             stmt.accept(visitor);
400 
401             if (i == size - 1) {
402                 List!(string) comments = stmt.getAfterCommentsDirect();
403                 if (comments !is null){
404                     for (int j = 0; j < comments.size(); ++j) {
405                         string comment = comments.get(j);
406                         if (j != 0) {
407                             visitor.println();
408                         }
409                         visitor.printComment(comment);
410                     }
411                 }
412             }
413         }
414 
415         return _out.toString();
416     }
417 
418     public static SQLASTOutputVisitor createOutputVisitor(Appendable _out, string dbType) {
419         return createFormatOutputVisitor(_out, null, dbType);
420     }
421 
422     public static SQLASTOutputVisitor createFormatOutputVisitor(Appendable _out, //
423                                                                 List!(SQLStatement) statementList, //
424                                                                 string dbType) {
425         // if (DBType.ORACLE.opEquals(dbType) || DBType.ALI_ORACLE.opEquals(dbType)) {
426         //     if (statementList is null || statementList.size() == 1) {
427         //         return new OracleOutputVisitor(_out, false);
428         //     } else {
429         //         return new OracleOutputVisitor(_out, true);
430         //     }
431         // }
432 
433         if (DBType.MYSQL.opEquals(dbType) //
434                 || DBType.MARIADB.opEquals(dbType)) {
435             return new MySqlOutputVisitor(_out);
436         }
437 
438         if (DBType.POSTGRESQL.opEquals(dbType)) {
439             return new PGOutputVisitor(_out);
440         }
441 
442         // if (DBType.SQL_SERVER.opEquals(dbType) || DBType.JTDS.opEquals(dbType)) {
443         //     return new SQLServerOutputVisitor(_out);
444         // }
445 
446         // if (DBType.DB2.opEquals(dbType)) {
447         //     return new DB2OutputVisitor(_out);
448         // }
449 
450         // if (DBType.ODPS.opEquals(dbType)) {
451         //     return new OdpsOutputVisitor(_out);
452         // }
453 
454         // if (DBType.H2.opEquals(dbType)) {
455         //     return new H2OutputVisitor(_out);
456         // }
457 
458         // if (DBType.HIVE.opEquals(dbType)) {
459         //     return new HiveOutputVisitor(_out);
460         // }
461 
462         if (DBType.ELASTIC_SEARCH.opEquals(dbType)) {
463             return new MySqlOutputVisitor(_out);
464         }
465 
466         return new SQLASTOutputVisitor(_out, dbType);
467     }
468 
469     //@Deprecated
470     public static SchemaStatVisitor createSchemaStatVisitor(List!(SQLStatement) statementList, string dbType) {
471         return createSchemaStatVisitor(dbType);
472     }
473 
474     public static SchemaStatVisitor createSchemaStatVisitor(string dbType) {
475         // if (DBType.ORACLE.opEquals(dbType) || DBType.ALI_ORACLE.opEquals(dbType)) {
476         //     return new OracleSchemaStatVisitor();
477         // }
478 
479         if (DBType.MYSQL.opEquals(dbType) || //
480                 DBType.MARIADB.opEquals(dbType)) {
481             return new MySqlSchemaStatVisitor();
482         }
483 
484         if (DBType.POSTGRESQL.opEquals(dbType)) {
485             return new PGSchemaStatVisitor();
486         }
487 
488         // if (DBType.SQL_SERVER.opEquals(dbType) || DBType.JTDS.opEquals(dbType)) {
489         //     return new SQLServerSchemaStatVisitor();
490         // }
491 
492         // if (DBType.DB2.opEquals(dbType)) {
493         //     return new DB2SchemaStatVisitor();
494         // }
495 
496         // if (DBType.ODPS.opEquals(dbType)) {
497         //     return new OdpsSchemaStatVisitor();
498         // }
499 
500         // if (DBType.H2.opEquals(dbType)) {
501         //     return new H2SchemaStatVisitor();
502         // }
503 
504         // if (DBType.HIVE.opEquals(dbType)) {
505         //     return new HiveSchemaStatVisitor();
506         // }
507 
508         if (DBType.ELASTIC_SEARCH.opEquals(dbType)) {
509             return new MySqlSchemaStatVisitor();
510         }
511 
512         return new SchemaStatVisitor();
513     }
514 
515     public static List!(SQLStatement) parseStatements(string sql, string dbType) {
516         auto parser = SQLParserUtils.createSQLStatementParser(sql, dbType);
517         List!(SQLStatement) stmtList = parser.parseStatementList();
518         if (parser.getLexer().token() != Token.EOF) {
519             throw new ParserException("syntax error : " ~ sql);
520         }
521         return stmtList;
522     }
523 
524     public static List!(SQLStatement) parseStatements(string sql, string dbType, bool keepComments) {
525         auto parser = SQLParserUtils.createSQLStatementParser(sql, dbType, keepComments);
526         List!(SQLStatement) stmtList = parser.parseStatementList();
527         if (parser.getLexer().token() != Token.EOF) {
528             throw new ParserException("syntax error. " ~ sql);
529         }
530         return stmtList;
531     }
532 
533     /**
534      * @param columnName
535      * @param tableAlias
536      * @param pattern if pattern is null,it will be set {%Y-%m-%d %H:%i:%s} as mysql default value and set {yyyy-mm-dd
537      * hh24:mi:ss} as oracle default value
538      * @param dbType {@link DBType} if dbType is null ,it will be set the mysql as a default value
539      */
540     public static string buildToDate(string columnName, string tableAlias, string pattern, string dbType) {
541         StringBuilder sql = new StringBuilder();
542         if (columnName.length == 0) return "";
543         if (dbType.length == 0) dbType = DBType.MYSQL.name();
544         string formatMethod = "";
545         if (equalsIgnoreCase(DBType.MYSQL.name(), dbType)) {
546             formatMethod = "STR_TO_DATE";
547             if (pattern.length == 0) pattern = "%Y-%m-%d %H:%i:%s";
548         } else if (equalsIgnoreCase(DBType.ORACLE.name(), dbType)) {
549             formatMethod = "TO_DATE";
550             if (pattern.length == 0) pattern = "yyyy-mm-dd hh24:mi:ss";
551         } else {
552             return "";
553             // expand date's handle method for other database
554         }
555         sql.append(formatMethod).append("(");
556         if (!(tableAlias.length == 0)) sql.append(tableAlias).append(".");
557         sql.append(columnName).append(",");
558         sql.append("'");
559         sql.append(pattern);
560         sql.append("')");
561         return sql.toString();
562     }
563 
564     public static List!(SQLExpr) split(SQLBinaryOpExpr x) {
565         return SQLBinaryOpExpr.split(x);
566     }
567 
568     // public static string translateOracleToMySql(string sql) {
569     //     List!(SQLStatement) stmtList = toStatementList(sql, DBType.ORACLE);
570 
571     //     StringBuilder _out = new StringBuilder();
572     //     OracleToMySqlOutputVisitor visitor = new OracleToMySqlOutputVisitor(_out, false);
573     //     for (int i = 0; i < stmtList.size(); ++i) {
574     //         stmtList.get(i).accept(visitor);
575     //     }
576 
577     //     string mysqlSql = _out.toString();
578     //     return mysqlSql;
579 
580     // }
581 
582     public static string addCondition(string sql, string condition, string dbType) {
583         string result = addCondition(sql, condition, SQLBinaryOperator.BooleanAnd, false, dbType);
584         return result;
585     }
586 
587     public static string addCondition(string sql, string condition, SQLBinaryOperator op, bool left, string dbType) {
588         if (sql is null) {
589             throw new Exception("IllegalArgument : sql is null");
590         }
591 
592         if (condition is null) {
593             return sql;
594         }
595 
596         if (op.getName == string.init) {
597             op = SQLBinaryOperator.BooleanAnd;
598         }
599 
600         if (op != SQLBinaryOperator.BooleanAnd //
601                 && op != SQLBinaryOperator.BooleanOr) {
602             throw new Exception("add condition not support : " ~ op.getName());
603         }
604 
605         List!(SQLStatement) stmtList = parseStatements(sql, dbType);
606 
607         if (stmtList.size() == 0) {
608             throw new Exception("not support empty-statement :" ~ sql);
609         }
610 
611         if (stmtList.size() > 1) {
612             throw new Exception("not support multi-statement :" ~ sql);
613         }
614 
615         SQLStatement stmt = stmtList.get(0);
616 
617         SQLExpr conditionExpr = toSQLExpr(condition, dbType);
618 
619         addCondition(stmt, op, conditionExpr, left);
620 
621         return toSQLString(stmt, dbType);
622     }
623 
624     public static void addCondition(SQLStatement stmt, SQLBinaryOperator op, SQLExpr condition, bool left) {
625         if (cast(SQLSelectStatement)(stmt) !is null) {
626             SQLSelectQuery query = (cast(SQLSelectStatement) stmt).getSelect().getQuery();
627             if (cast(SQLSelectQueryBlock)(query) !is null) {
628                 SQLSelectQueryBlock queryBlock = cast(SQLSelectQueryBlock) query;
629                 SQLExpr newCondition = buildCondition(op, condition, left, queryBlock.getWhere());
630                 queryBlock.setWhere(newCondition);
631             } else {
632                 throw new Exception("add condition not support " ~ typeid(stmt).stringof);
633             }
634 
635             return;
636         }
637 
638         if (cast(SQLDeleteStatement)(stmt) !is null) {
639             SQLDeleteStatement _delete = cast(SQLDeleteStatement) stmt;
640 
641             SQLExpr newCondition = buildCondition(op, condition, left, _delete.getWhere());
642             _delete.setWhere(newCondition);
643 
644             return;
645         }
646 
647         if (cast(SQLUpdateStatement)(stmt) !is null) {
648             SQLUpdateStatement update = cast(SQLUpdateStatement) stmt;
649 
650             SQLExpr newCondition = buildCondition(op, condition, left, update.getWhere());
651             update.setWhere(newCondition);
652 
653             return;
654         }
655 
656         throw new Exception("add condition not support " ~ typeid(stmt).stringof);
657     }
658 
659     public static SQLExpr buildCondition(SQLBinaryOperator op, SQLExpr condition, bool left, SQLExpr where) {
660         if (where is null) {
661             return condition;
662         }
663 
664         SQLBinaryOpExpr newCondition;
665         if (left) {
666             newCondition = new SQLBinaryOpExpr(condition, op, where);
667         } else {
668             newCondition = new SQLBinaryOpExpr(where, op, condition);
669         }
670         return newCondition;
671     }
672 
673     public static string addSelectItem(string selectSql, string expr, string _alias, string dbType) {
674         return addSelectItem(selectSql, expr, _alias, false, dbType);
675     }
676 
677     public static string addSelectItem(string selectSql, string expr, string _alias, bool first, string dbType) {
678         List!(SQLStatement) stmtList = parseStatements(selectSql, dbType);
679 
680         if (stmtList.size() == 0) {
681             throw new Exception("not support empty-statement :" ~ selectSql);
682         }
683 
684         if (stmtList.size() > 1) {
685             throw new Exception("not support multi-statement :" ~ selectSql);
686         }
687 
688         SQLStatement stmt = stmtList.get(0);
689 
690         SQLExpr columnExpr = toSQLExpr(expr, dbType);
691 
692         addSelectItem(stmt, columnExpr, _alias, first);
693 
694         return toSQLString(stmt, dbType);
695     }
696 
697     public static void addSelectItem(SQLStatement stmt, SQLExpr expr, string _alias, bool first) {
698         if (expr is null) {
699             return;
700         }
701 
702         if (cast(SQLSelectStatement)(stmt) !is null) {
703             SQLSelectQuery query = (cast(SQLSelectStatement) stmt).getSelect().getQuery();
704             if (cast(SQLSelectQueryBlock)(query) !is null) {
705                 SQLSelectQueryBlock queryBlock = cast(SQLSelectQueryBlock) query;
706                 addSelectItem(queryBlock, expr, _alias, first);
707             } else {
708                 throw new Exception("add condition not support " ~ typeid(stmt).stringof);
709             }
710 
711             return;
712         }
713 
714         throw new Exception("add selectItem not support " ~ typeid(stmt).stringof);
715     }
716 
717     public static void addSelectItem(SQLSelectQueryBlock queryBlock, SQLExpr expr, string _alias, bool first) {
718         SQLSelectItem selectItem = new SQLSelectItem(expr, _alias);
719         queryBlock.getSelectList().add(selectItem);
720         selectItem.setParent(selectItem);
721     }
722 
723 
724     public static string refactor(string sql, string dbType, Map!(string , string) tableMapping) {
725         List!(SQLStatement) stmtList = parseStatements(sql, dbType);
726         return SQLUtils.toSQLString(stmtList, dbType, null, null, tableMapping);
727     }
728 
729     public static long hash(string sql, string dbType) {
730         Lexer lexer = SQLParserUtils.createLexer(sql, dbType);
731 
732         StringBuilder buf = new StringBuilder(sql.length);
733 
734         for (;;) {
735             lexer.nextToken();
736 
737             Token token = lexer.token();
738             if (token == Token.EOF) {
739                 break;
740             }
741 
742             if (token == Token.ERROR) {
743                 return FnvHash.fnv1a_64(sql);
744             }
745 
746             if (buf.length != 0) {
747 
748             }
749         }
750 
751         return (cast(Object)buf).toHash();
752     }
753 
754     public static SQLExpr not(SQLExpr expr) {
755         if (cast(SQLBinaryOpExpr)(expr) !is null) {
756             SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) expr;
757             SQLBinaryOperator op = binaryOpExpr.getOperator();
758 
759             SQLBinaryOperator notOp;
760 
761             switch (op.getName){
762                 case SQLBinaryOperator.Equality.getName:
763                     notOp = SQLBinaryOperator.LessThanOrGreater;
764                     break;
765                 case SQLBinaryOperator.LessThanOrEqualOrGreaterThan.getName:
766                     notOp = SQLBinaryOperator.Equality;
767                     break;
768                 case SQLBinaryOperator.LessThan.getName:
769                     notOp = SQLBinaryOperator.GreaterThanOrEqual;
770                     break;
771                 case SQLBinaryOperator.LessThanOrEqual.getName:
772                     notOp = SQLBinaryOperator.GreaterThan;
773                     break;
774                 case SQLBinaryOperator.GreaterThan.getName:
775                     notOp = SQLBinaryOperator.LessThanOrEqual;
776                     break;
777                 case SQLBinaryOperator.GreaterThanOrEqual.getName:
778                     notOp = SQLBinaryOperator.LessThan;
779                     break;
780                 case SQLBinaryOperator.Is.getName:
781                     notOp = SQLBinaryOperator.IsNot;
782                     break;
783                 case SQLBinaryOperator.IsNot.getName:
784                     notOp = SQLBinaryOperator.Is;
785                     break;
786                 default:
787                     break;
788             }
789 
790 
791             if (notOp.getName != string.init) {
792                 return new SQLBinaryOpExpr(binaryOpExpr.getLeft(), notOp, binaryOpExpr.getRight());
793             }
794         }
795 
796         if (cast(SQLInListExpr)(expr) !is null) {
797             SQLInListExpr inListExpr = cast(SQLInListExpr) expr;
798 
799             SQLInListExpr newInListExpr = new SQLInListExpr(inListExpr);
800             newInListExpr.getTargetList().addAll(inListExpr.getTargetList());
801             newInListExpr.setNot(!inListExpr.isNot());
802             return newInListExpr;
803         }
804 
805         return new SQLUnaryExpr(SQLUnaryOperator.Not, expr);
806     }
807 
808     public static string normalize(string name) {
809         return normalize(name, null);
810     }
811 
812     public static string normalize(string name, string dbType) {
813         if (name is null) {
814             return null;
815         }
816 
817         if (name.length > 2) {
818             char c0 = charAt(name, 0);
819             char x0 = charAt(name, name.length - 1);
820             if ((c0 == '"' && x0 == '"') || (c0 == '`' && x0 == '`')) {
821                 string normalizeName = name.substring(1, cast(int)name.length - 1);
822                 if (c0 == '`') {
823                     normalizeName = normalizeName.replace("`\\.`", ".");
824                 }
825 
826                 if (DBType.ORACLE.opEquals(dbType)) {
827                     // if (OracleUtils.isKeyword(normalizeName)) {
828                     //     return name;
829                     // }
830                 } else if (DBType.MYSQL.opEquals(dbType)) {
831                     if (MySqlUtils.isKeyword(normalizeName)) {
832                         return name;
833                     }
834                 } else if (DBType.POSTGRESQL.opEquals(dbType)
835                         /* || DBType.ENTERPRISEDB.opEquals(dbType) */) {
836                     if (PGUtils.isKeyword(normalizeName)) {
837                         return name;
838                     }
839                 }
840 
841                 return normalizeName;
842             }
843         }
844 
845         return name;
846     }
847 
848     public static bool nameEquals(SQLName a, SQLName b) {
849         if (a == b) {
850             return true;
851         }
852 
853         if (a is null || b is null) {
854             return false;
855         }
856 
857         return a.nameHashCode64() == b.nameHashCode64();
858     }
859 
860     public static bool nameEquals(string a, string b) {
861         if (a == b) {
862             return true;
863         }
864 
865         if (a is null || b is null) {
866             return false;
867         }
868 
869         if (equalsIgnoreCase(a, b)) {
870             return true;
871         }
872 
873         string normalize_a = normalize(a);
874         string normalize_b = normalize(b);
875 
876         return equalsIgnoreCase(normalize_a, normalize_b);
877     }
878 
879     public static bool isValue(SQLExpr expr) {
880         if (cast(SQLLiteralExpr)(expr) !is null) {
881             return true;
882         }
883 
884         if (cast(SQLVariantRefExpr)(expr) !is null) {
885             return true;
886         }
887 
888         if (cast(SQLBinaryOpExpr)(expr) !is null) {
889             SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) expr;
890             SQLBinaryOperator op = binaryOpExpr.getOperator();
891             if (op == SQLBinaryOperator.Add
892                     || op == SQLBinaryOperator.Subtract
893                     || op == SQLBinaryOperator.Multiply) {
894                 return isValue(binaryOpExpr.getLeft())
895                         && isValue(binaryOpExpr.getRight());
896             }
897         }
898 
899         return false;
900     }
901 
902     public static bool replaceInParent(SQLExpr expr, SQLExpr target) {
903         if (expr is null) {
904             return false;
905         }
906 
907         SQLObject parent = expr.getParent();
908 
909         if (cast(SQLReplaceable)(parent) !is null) {
910             return (cast(SQLReplaceable) parent).replace(expr, target);
911         }
912 
913         return false;
914     }
915 
916     public static string desensitizeTable(string tableName) {
917         if (tableName is null) {
918             return null;
919         }
920 
921         tableName = normalize(tableName);
922         long hash = FnvHash.hashCode64(tableName);
923         return Utils.hex_t(hash);
924     }
925 
926     /**
927      * 
928      * @param sql
929      * @param dbType
930      * @return
931      */
932     public static string sort(string sql, string dbType) {
933         List!(SQLStatement) stmtList = SQLUtils.parseStatements(sql, DBType.ORACLE.name());
934         SQLCreateTableStatement.sort(stmtList);
935         return SQLUtils.toSQLString(stmtList, dbType);
936     }
937 }
938 
939 
940 class FormatOption {
941     private int features = VisitorFeature.of(VisitorFeature.OutputUCase, VisitorFeature.OutputPrettyFormat);
942 
943     this() {
944 
945     }
946 
947     this(VisitorFeature[] features...) {
948         this.features = VisitorFeature.of(features);
949     }
950 
951     this(bool ucase) {
952         this(ucase, true);
953     }
954 
955     this(bool ucase, bool prettyFormat) {
956         this(ucase, prettyFormat, false);
957     }
958 
959     this(bool ucase, bool prettyFormat, bool parameterized) {
960         this.features = VisitorFeature.config(this.features, VisitorFeature.OutputUCase, ucase);
961         this.features = VisitorFeature.config(this.features, VisitorFeature.OutputPrettyFormat, prettyFormat);
962         this.features = VisitorFeature.config(this.features, VisitorFeature.OutputParameterized, parameterized);
963     }
964 
965     bool isDesensitize() {
966         return isEnabled(VisitorFeature.OutputDesensitize);
967     }
968 
969     void setDesensitize(bool val) {
970         config(VisitorFeature.OutputDesensitize, val);
971     }
972 
973     bool isUppCase() {
974         return isEnabled(VisitorFeature.OutputUCase);
975     }
976 
977     void setUppCase(bool val) {
978         config(VisitorFeature.OutputUCase, val);
979     }
980 
981     bool isPrettyFormat() {
982         return isEnabled(VisitorFeature.OutputPrettyFormat);
983     }
984 
985     void setPrettyFormat(bool prettyFormat) {
986         config(VisitorFeature.OutputPrettyFormat, prettyFormat);
987     }
988 
989     bool isParameterized() {
990         return isEnabled(VisitorFeature.OutputParameterized);
991     }
992 
993     void setParameterized(bool parameterized) {
994         config(VisitorFeature.OutputParameterized, parameterized);
995     }
996 
997     void config(VisitorFeature feature, bool state) {
998         features = VisitorFeature.config(features, feature, state);
999     }
1000 
1001     bool isEnabled(VisitorFeature feature) {
1002         return VisitorFeature.isEnabled(this.features, feature);
1003     }
1004 }