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.SchemaStatVisitor;
17 
18 
19 import std.string;
20 import std.uni;
21 import hunt.collection;
22 import std.array;
23 import hunt.String;
24 import hunt.sql.SQLUtils;
25 import hunt.sql.ast;
26 import hunt.sql.ast.expr;
27 import hunt.sql.ast.statement;
28 import hunt.sql.dialect.mysql.ast.expr.MySqlExpr;
29 import hunt.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
30 import hunt.sql.ast.statement.SQLLateralViewTableSource;
31 // import hunt.sql.dialect.odps.ast.OdpsValuesTableSource;
32 // import hunt.sql.dialect.oracle.ast.expr.OracleDbLinkExpr;
33 // import hunt.sql.dialect.oracle.ast.expr.OracleExpr;
34 import hunt.sql.dialect.postgresql.visitor.PGASTVisitorAdapter;
35 import hunt.sql.repository.SchemaObject;
36 import hunt.sql.repository.SchemaRepository;
37 import hunt.sql.stat.TableStat;
38 // import hunt.sql.stat.TableStat.Column;
39 // import hunt.sql.stat.TableStat.Condition;
40 // import hunt.sql.stat.TableStat.Mode;
41 // import hunt.sql.stat.TableStat.Relationship;
42 import hunt.sql.util.FnvHash;
43 import hunt.sql.util.DBType;
44 import hunt.sql.visitor.SQLASTVisitorAdapter;
45 import hunt.Long;
46 import hunt.Boolean;
47 import hunt.sql.visitor.SQLASTVisitor;
48 import hunt.sql.visitor.SQLEvalVisitorUtils;
49 import hunt.sql.visitor.SQLEvalVisitor;
50 import hunt.text;
51 
52 public class SchemaStatVisitor : SQLASTVisitorAdapter {
53 
54     alias visit = SQLASTVisitorAdapter.visit;
55     alias endVisit = SQLASTVisitorAdapter.endVisit;
56 
57     protected SchemaRepository repository;
58 
59     protected  HashMap!(TableStat.Name, TableStat) tableStats  ;
60     protected  Map!(Long, TableStat.Column)                  columns       ;
61     protected  List!(TableStat.Condition)                    conditions    ;
62     protected  Set!(TableStat.Relationship)                  relationships ;
63     protected  List!(TableStat.Column)                       orderByColumns;
64     protected  Set!(TableStat.Column)                        groupByColumns;
65     protected  List!(SQLAggregateExpr)             aggregateFunctions;
66     protected  List!(SQLMethodInvokeExpr)          functions         ;
67 
68     private List!(Object) parameters;
69 
70     private TableStat.Mode mode;
71 
72     protected string dbType;
73 
74     public this(){
75         this(cast(string) null);
76     }
77 
78     public this(string dbType){
79         this(new SchemaRepository(dbType), new ArrayList!(Object)());
80         this.dbType = dbType;
81     }
82 
83     public SchemaRepository getRepository() {
84         return repository;
85     }
86 
87     public void setRepository(SchemaRepository repository) {
88         this.repository = repository;
89     }
90 
91     public this(List!(Object) parameters){
92         this(cast(string) null, parameters);
93     }
94 
95     public this(string dbType, List!(Object) parameters){
96         this(new SchemaRepository(dbType), parameters);
97         this.parameters = parameters;
98     }
99 
100     public this(SchemaRepository repository, List!(Object) parameters){
101         tableStats     = new LinkedHashMap!(TableStat.Name, TableStat);
102         columns        = new LinkedHashMap!(Long, TableStat.Column)();
103         conditions     = new ArrayList!(TableStat.Condition)();
104         relationships  = new LinkedHashSet!(TableStat.Relationship)();
105         orderByColumns = new ArrayList!(TableStat.Column)();
106         groupByColumns = new LinkedHashSet!(TableStat.Column)();
107         aggregateFunctions = new ArrayList!(SQLAggregateExpr)();
108         functions          = new ArrayList!(SQLMethodInvokeExpr)(2);
109 
110         this.repository = repository;
111         this.parameters = parameters;
112         if (repository !is null) {
113             string dbType = repository.getDbType();
114             if (dbType !is null && this.dbType is null) {
115                 this.dbType = dbType;
116             }
117         }
118     }
119 
120     public List!(Object) getParameters() {
121         return parameters;
122     }
123 
124     public void setParameters(List!(Object) parameters) {
125         this.parameters = parameters;
126     }
127 
128     public TableStat getTableStat(string tableName) {
129         tableName = handleName(tableName);
130 
131         TableStat.Name tableNameObj = new TableStat.Name(tableName);
132         TableStat stat = tableStats.get(tableNameObj);
133         if (stat is null) {
134             stat = new TableStat();
135             tableStats.put(new TableStat.Name(tableName), stat);
136         }
137         return stat;
138     }
139 
140     public TableStat getTableStat(SQLName tableName) {
141         string strName = (cast(Object)(tableName)).toString();
142         long hashCode64 = tableName.hashCode64();
143 
144         if (hashCode64 == FnvHash.Constants.DUAL) {
145             return null;
146         }
147 
148         TableStat.Name tableNameObj = new TableStat.Name(strName, hashCode64);
149         TableStat stat = tableStats.get(tableNameObj);
150         if (stat is null) {
151             stat = new TableStat();
152             tableStats.put(new TableStat.Name(strName, hashCode64), stat);
153         }
154         return stat;
155     }
156 
157     protected TableStat.Column addColumn(string tableName, string columnName) {
158         TableStat.Column column = this.getColumn(tableName, columnName);
159         if (column is null && columnName !is null) {
160             column = new TableStat.Column(tableName, columnName);
161             columns.put(new Long(column.hashCode64()), column);
162         }
163         return column;
164     }
165 
166     protected TableStat.Column addColumn(SQLName table, string columnName) {
167         string tableName = (cast(Object)(table)).toString();
168         long tableHashCode64 = table.hashCode64();
169 
170         long basic = tableHashCode64;
171         basic ^= '.';
172         basic *= FnvHash.PRIME;
173         long columnHashCode64 = FnvHash.hashCode64(basic, columnName);
174 
175         TableStat.Column column = this.columns.get(new Long(columnHashCode64));
176         if (column is null && columnName !is null) {
177             column = new TableStat.Column(tableName, columnName, columnHashCode64);
178             columns.put(new Long(columnHashCode64), column);
179         }
180         return column;
181     }
182 
183     private string handleName(string ident) {
184         int len = cast(int)(ident.length);
185         if (charAt(ident, 0) == '[' && charAt(ident, len - 1) == ']') {
186             ident = ident.substring(1, len - 1);
187         } else {
188             bool flag0 = false;
189             bool flag1 = false;
190             bool flag2 = false;
191             bool flag3 = false;
192             for (int i = 0; i < len; ++i) {
193                  char ch = charAt(ident, i);
194                 if (ch == '\"') {
195                     flag0 = true;
196                 } else if (ch == '`') {
197                     flag1 = true;
198                 } else if (ch == ' ') {
199                     flag2 = true;
200                 } else if (ch == '\'') {
201                     flag3 = true;
202                 }
203             }
204             if (flag0) {
205                 ident = ident.replace("\"", "");
206             }
207 
208             if (flag1) {
209                 ident = ident.replace("`", "");
210             }
211 
212             if (flag2) {
213                 ident = ident.replace(" ", "");
214             }
215 
216             if (flag3) {
217                 ident = ident.replace("'", "");
218             }
219         }
220         return ident;
221     }
222 
223     protected TableStat.Mode getMode() {
224         return mode;
225     }
226 
227     protected void setModeOrigin(SQLObject x) {
228         TableStat.Mode originalMode = cast(TableStat.Mode) x.getAttribute("_original_use_mode");
229         mode = originalMode;
230     }
231 
232     protected TableStat.Mode setMode(SQLObject x, const TableStat.Mode mode) {
233         TableStat.Mode oldMode = this.mode;
234         x.putAttribute("_original_use_mode", oldMode);
235         this.mode = cast(TableStat.Mode)mode;
236         return oldMode;
237     }
238 
239     private bool visitOrderBy(SQLIdentifierExpr x) {
240         SQLTableSource tableSource = x.getResolvedTableSource();
241 
242         string tableName = null;
243         if (cast(SQLExprTableSource)(tableSource) !is null) {
244             SQLExpr expr = (cast(SQLExprTableSource) tableSource).getExpr();
245             if (cast(SQLIdentifierExpr)(expr) !is null) {
246                 SQLIdentifierExpr table = cast(SQLIdentifierExpr) expr;
247                 tableName = table.getName();
248             } else if (cast(SQLPropertyExpr)(expr) !is null) {
249                 SQLPropertyExpr table = cast(SQLPropertyExpr) expr;
250                 tableName = (cast(Object)(table)).toString();
251             } else if (cast(SQLMethodInvokeExpr)(expr) !is null) {
252                 SQLMethodInvokeExpr methodInvokeExpr = cast(SQLMethodInvokeExpr) expr;
253                 if ("table".equalsIgnoreCase(methodInvokeExpr.getMethodName())
254                         && methodInvokeExpr.getParameters().size() == 1
255                         && cast(SQLName)methodInvokeExpr.getParameters().get(0) !is null) {
256                     SQLName table = cast(SQLName) methodInvokeExpr.getParameters().get(0);
257 
258                     if (cast(SQLPropertyExpr)table !is null) {
259                         SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) table;
260                         SQLIdentifierExpr owner = cast(SQLIdentifierExpr) propertyExpr.getOwner();
261                         if (propertyExpr.getResolvedTableSource() !is null
262                                 && (cast(SQLExprTableSource) propertyExpr.getResolvedTableSource()) !is null) {
263                             SQLExpr resolveExpr = (cast(SQLExprTableSource) propertyExpr.getResolvedTableSource()).getExpr();
264                             if (cast(SQLName)(resolveExpr) !is null) {
265                                 tableName = (cast(Object)(resolveExpr)).toString() ~ "." ~ propertyExpr.getName();
266                             }
267                         }
268                     }
269 
270                     if (tableName is null) {
271                         tableName = (cast(Object)(table)).toString();
272                     }
273                 }
274             }
275         } else if (cast(SQLWithSubqueryClause.Entry)(tableSource) !is null) {
276             return false;
277         } else if (cast(SQLSubqueryTableSource)(tableSource) !is null) {
278             SQLSelectQueryBlock queryBlock = (cast(SQLSubqueryTableSource) tableSource).getSelect().getQueryBlock();
279             if (queryBlock is null) {
280                 return false;
281             }
282 
283             SQLSelectItem selectItem = queryBlock.findSelectItem(x.nameHashCode64());
284             if (selectItem is null) {
285                 return false;
286             }
287 
288             SQLExpr selectItemExpr = selectItem.getExpr();
289             SQLTableSource columnTableSource = null;
290             if (cast(SQLIdentifierExpr)(selectItemExpr) !is null) {
291                 columnTableSource = (cast(SQLIdentifierExpr) selectItemExpr).getResolvedTableSource();
292             } else if (cast(SQLPropertyExpr)(selectItemExpr) !is null) {
293                 columnTableSource = (cast(SQLPropertyExpr) selectItemExpr).getResolvedTableSource();
294             }
295 
296             if (cast(SQLExprTableSource)(columnTableSource) !is null && cast(SQLName) (cast(SQLExprTableSource) columnTableSource).getExpr() !is null) {
297                 SQLName tableExpr = cast(SQLName) (cast(SQLExprTableSource) columnTableSource).getExpr();
298                 if (cast(SQLIdentifierExpr)(tableExpr) !is null) {
299                     tableName = (cast(SQLIdentifierExpr) tableExpr).normalizedName();
300                 } else if (cast(SQLPropertyExpr)(tableExpr) !is null) {
301                     tableName = (cast(SQLPropertyExpr) tableExpr).normalizedName();
302                 }
303             }
304         } else {
305             bool skip = false;
306             for (SQLObject parent = x.getParent();parent !is null;parent = parent.getParent()) {
307                 if (cast(SQLSelectQueryBlock)(parent) !is null) {
308                     SQLTableSource from = (cast(SQLSelectQueryBlock) parent).getFrom();
309 
310                     // if (cast(OdpsValuesTableSource)(from) !is null) {
311                     //     skip = true;
312                     //     break;
313                     // }//@gxc
314                 } else if (cast(SQLSelectQuery)(parent) !is null) {
315                     break;
316                 }
317             }
318         }
319 
320         string identName = x.getName();
321         if (tableName !is null) {
322             orderByAddColumn(tableName, identName, x);
323         } else {
324             orderByAddColumn("UNKOWN", identName, x);
325         }
326         return false;
327     }
328 
329     private bool visitOrderBy(SQLPropertyExpr x) {
330         if (isSubQueryOrParamOrVariant(x)) {
331             return false;
332         }
333 
334         string owner = null;
335 
336         SQLTableSource tableSource = x.getResolvedTableSource();
337         if (cast(SQLExprTableSource)(tableSource) !is null) {
338             SQLExpr tableSourceExpr = (cast(SQLExprTableSource) tableSource).getExpr();
339             if (cast(SQLName)(tableSourceExpr) !is null) {
340                 owner = (cast(Object)(tableSourceExpr)).toString();
341             }
342         }
343 
344         if (owner is null && (cast(SQLIdentifierExpr) x.getOwner()).getName() !is null) {
345             owner = (cast(SQLIdentifierExpr) x.getOwner()).getName();
346         }
347 
348         if (owner is null) {
349             return false;
350         }
351 
352         if (owner !is null) {
353             orderByAddColumn(owner, x.getName(), x);
354         }
355 
356         return false;
357     }
358 
359     private void orderByAddColumn(string table, string columnName, SQLObject expr) {
360         TableStat.Column column = new TableStat.Column(table, columnName);
361 
362         SQLObject parent = expr.getParent();
363         if (cast(SQLSelectOrderByItem)(parent) !is null) {
364             SQLOrderingSpecification type = (cast(SQLSelectOrderByItem) parent).getType();
365             column.getAttributes().put("orderBy.type", cast(Object)type);
366         }
367 
368         orderByColumns.add(column);
369     }
370 
371     protected class OrderByStatVisitor : SQLASTVisitorAdapter {
372 
373         alias visit = SQLASTVisitorAdapter.visit;
374         alias endVisit = SQLASTVisitorAdapter.endVisit;
375 
376         private  SQLOrderBy orderBy;
377 
378         public this(SQLOrderBy orderBy){
379             this.orderBy = orderBy;
380             foreach(SQLSelectOrderByItem item ; orderBy.getItems()) {
381                 item.getExpr().setParent(item);
382             }
383         }
384 
385         public SQLOrderBy getOrderBy() {
386             return orderBy;
387         }
388 
389         override public bool visit(SQLIdentifierExpr x) {
390             return visitOrderBy(x);
391         }
392 
393         override public bool visit(SQLPropertyExpr x) {
394             return visitOrderBy(x);
395         }
396     }
397 
398     protected class MySqlOrderByStatVisitor : MySqlASTVisitorAdapter {
399 
400         alias visit = MySqlASTVisitorAdapter.visit;
401         alias endVisit = MySqlASTVisitorAdapter.endVisit;
402 
403         private  SQLOrderBy orderBy;
404 
405         public this(SQLOrderBy orderBy){
406             this.orderBy = orderBy;
407             foreach(SQLSelectOrderByItem item ; orderBy.getItems()) {
408                 item.getExpr().setParent(item);
409             }
410         }
411 
412         public SQLOrderBy getOrderBy() {
413             return orderBy;
414         }
415 
416         override public bool visit(SQLIdentifierExpr x) {
417             return visitOrderBy(x);
418         }
419 
420         override public bool visit(SQLPropertyExpr x) {
421             return visitOrderBy(x);
422         }
423     }
424 
425     protected class PGOrderByStatVisitor : PGASTVisitorAdapter {
426 
427         alias visit = PGASTVisitorAdapter.visit;
428         alias endVisit = PGASTVisitorAdapter.endVisit;
429 
430         private  SQLOrderBy orderBy;
431 
432         public this(SQLOrderBy orderBy){
433             this.orderBy = orderBy;
434             foreach(SQLSelectOrderByItem item ; orderBy.getItems()) {
435                 item.getExpr().setParent(item);
436             }
437         }
438 
439         public SQLOrderBy getOrderBy() {
440             return orderBy;
441         }
442 
443         override public bool visit(SQLIdentifierExpr x) {
444             return visitOrderBy(x);
445         }
446 
447         override public bool visit(SQLPropertyExpr x) {
448             return visitOrderBy(x);
449         }
450     }
451 
452     protected class OracleOrderByStatVisitor : PGASTVisitorAdapter {
453 
454         alias visit = PGASTVisitorAdapter.visit;
455         alias endVisit = PGASTVisitorAdapter.endVisit;
456         
457         private  SQLOrderBy orderBy;
458 
459         public this(SQLOrderBy orderBy){
460             this.orderBy = orderBy;
461             foreach(SQLSelectOrderByItem item ; orderBy.getItems()) {
462                 item.getExpr().setParent(item);
463             }
464         }
465 
466         public SQLOrderBy getOrderBy() {
467             return orderBy;
468         }
469 
470         override public bool visit(SQLIdentifierExpr x) {
471             return visitOrderBy(x);
472         }
473 
474         override public bool visit(SQLPropertyExpr x) {
475             SQLExpr unwrapped = unwrapExpr(x);
476             if (cast(SQLPropertyExpr)(unwrapped) !is null) {
477                 visitOrderBy(cast(SQLPropertyExpr) unwrapped);
478             } else if (cast(SQLIdentifierExpr)(unwrapped) !is null) {
479                 visitOrderBy(cast(SQLIdentifierExpr) unwrapped);
480             }
481             return false;
482         }
483     }
484 
485     override public bool visit(SQLOrderBy x) {
486          SQLASTVisitor orderByVisitor = createOrderByVisitor(x);
487 
488         SQLSelectQueryBlock query = null;
489         if ( cast(SQLSelectQueryBlock) x.getParent() !is null) {
490             query = cast(SQLSelectQueryBlock) x.getParent();
491         }
492         if (query !is null) {
493             foreach(SQLSelectOrderByItem item ; x.getItems()) {
494                 SQLExpr expr = item.getExpr();
495                 if (cast(SQLIntegerExpr)(expr) !is null) {
496                     int intValue = (cast(SQLIntegerExpr) expr).getNumber().intValue() - 1;
497                     if (intValue < query.getSelectList().size()) {
498                         SQLSelectItem selectItem = query.getSelectList().get(intValue);
499                         selectItem.getExpr().accept(orderByVisitor);
500                     }
501                 } else if (cast(MySqlExpr)(expr) !is null /* || cast(OracleExpr)(expr) !is null */) {
502                     continue;
503                 }
504             }
505         }
506         x.accept(orderByVisitor);
507 
508         foreach(SQLSelectOrderByItem orderByItem ; x.getItems()) {
509             statExpr(
510                     orderByItem.getExpr());
511         }
512 
513         return false;
514     }
515 
516     override public bool visit(SQLOver x) {
517         SQLName of = x.getOf();
518         SQLOrderBy orderBy = x.getOrderBy();
519         List!(SQLExpr) partitionBy = x.getPartitionBy();
520 
521 
522         if (of is null // skip if of is not null
523                 && orderBy !is null) {
524             orderBy.accept(this);
525         }
526 
527         if (partitionBy !is null) {
528             foreach(SQLExpr expr ; partitionBy) {
529                 expr.accept(this);
530             }
531         }
532 
533         return false;
534     }
535 
536     protected SQLASTVisitor createOrderByVisitor(SQLOrderBy x) {
537          SQLASTVisitor orderByVisitor;
538         if (DBType.MYSQL.opEquals(dbType)) {
539             orderByVisitor = new MySqlOrderByStatVisitor(x);
540         } else if (DBType.POSTGRESQL.opEquals(dbType)) {
541             orderByVisitor = new PGOrderByStatVisitor(x);
542         } else if (DBType.ORACLE.opEquals(dbType)) {
543             orderByVisitor = new OracleOrderByStatVisitor(x);
544         } else {
545             orderByVisitor = new OrderByStatVisitor(x);
546         }
547         return orderByVisitor;
548     }
549 
550     public Set!(TableStat.Relationship) getRelationships() {
551         return relationships;
552     }
553 
554     public List!(TableStat.Column) getOrderByColumns() {
555         return orderByColumns;
556     }
557 
558     public Set!(TableStat.Column) getGroupByColumns() {
559         return groupByColumns;
560     }
561 
562     public List!(TableStat.Condition) getConditions() {
563         return conditions;
564     }
565     
566     public List!(SQLAggregateExpr) getAggregateFunctions() {
567         return aggregateFunctions;
568     }
569 
570     override public bool visit(SQLBetweenExpr x) {
571         SQLObject parent = x.getParent();
572 
573         SQLExpr test = x.getTestExpr();
574         SQLExpr begin = x.getBeginExpr();
575         SQLExpr end = x.getEndExpr();
576 
577         statExpr(test);
578         statExpr(begin);
579         statExpr(end);
580 
581         handleCondition(test, "BETWEEN", begin, end);
582 
583         return false;
584     }
585 
586     override public bool visit(SQLBinaryOpExpr x) {
587         SQLObject parent = x.getParent();
588 
589         if (cast(SQLIfStatement)(parent) !is null) {
590             return true;
591         }
592 
593          SQLBinaryOperator op = x.getOperator();
594          SQLExpr left = x.getLeft();
595          SQLExpr right = x.getRight();
596 
597         switch (op.name) {
598             case SQLBinaryOperator.Equality.name:
599             case SQLBinaryOperator.NotEqual.name:
600             case SQLBinaryOperator.GreaterThan.name:
601             case SQLBinaryOperator.GreaterThanOrEqual.name:
602             case SQLBinaryOperator.LessThan.name:
603             case SQLBinaryOperator.LessThanOrGreater.name:
604             case SQLBinaryOperator.LessThanOrEqual.name:
605             case SQLBinaryOperator.LessThanOrEqualOrGreaterThan.name:
606             case SQLBinaryOperator.Like.name:
607             case SQLBinaryOperator.NotLike.name:
608             case SQLBinaryOperator.Is.name:
609             case SQLBinaryOperator.IsNot.name:
610                 handleCondition(left, x.getOperator().name, right);
611                 handleCondition(right, x.getOperator().name, left);
612 
613                 handleRelationship(left, x.getOperator().name, right);
614                 break;
615             case SQLBinaryOperator.BooleanOr.name: {
616                 List!(SQLExpr) list = SQLBinaryOpExpr.split(x, op);
617 
618                 foreach(SQLExpr item ; list) {
619                     if (cast(SQLBinaryOpExpr)(item) !is null) {
620                         visit(cast(SQLBinaryOpExpr) item);
621                     } else {
622                         item.accept(this);
623                     }
624                 }
625 
626                 return false;
627             }
628             case SQLBinaryOperator.Modulus.name:
629                 if (cast(SQLIdentifierExpr)(right) !is null) {
630                     long hashCode64 = (cast(SQLIdentifierExpr) right).hashCode64();
631                     if (hashCode64 == FnvHash.Constants.ISOPEN) {
632                         left.accept(this);
633                         return false;
634                     }
635                 }
636                 break;
637             default:
638                 break;
639         }
640 
641         statExpr(left);
642         statExpr(right);
643 
644         return false;
645     }
646 
647     protected void handleRelationship(SQLExpr left, string operator, SQLExpr right) {
648         TableStat.Column leftColumn = getColumn(left);
649         if (leftColumn is null) {
650             return;
651         }
652 
653         TableStat.Column rightColumn = getColumn(right);
654         if (rightColumn is null) {
655             return;
656         }
657 
658         TableStat.Relationship relationship = new TableStat.Relationship(leftColumn, rightColumn, operator);
659         this.relationships.add(relationship);
660     }
661 
662     protected void handleCondition(SQLExpr expr, string operator, List!(SQLExpr) values) {
663         handleCondition(expr, operator, values.toArray());
664     }
665 
666     protected void handleCondition(SQLExpr expr, string operator, SQLExpr[] valueExprs...) {
667         if (cast(SQLCastExpr)(expr) !is null) {
668             expr = (cast(SQLCastExpr) expr).getExpr();
669         }
670         
671         TableStat.Column column = getColumn(expr);
672         if (column is null) {
673             return;
674         }
675         
676         TableStat.Condition condition = null;
677         foreach(TableStat.Condition item ; this.getConditions()) {
678             if (item.getColumn().opEquals(column) && item.getOperator() == (operator)) {
679                 condition = item;
680                 break;
681             }
682         }
683 
684         if (condition is null) {
685             condition = new TableStat.Condition(column, operator);
686             this.conditions.add(condition);
687         }
688 
689         foreach(SQLExpr item ; valueExprs) {
690             TableStat.Column valueColumn = getColumn(item);
691             if (valueColumn !is null) {
692                 continue;
693             }
694 
695             Object value;
696             if (cast(SQLMethodInvokeExpr)(item) !is null) {
697                 value = (cast(Object)(item));
698             } else {
699                 value = SQLEvalVisitorUtils.eval(dbType, item, parameters, false);
700                 if (value == SQLEvalVisitor.EVAL_VALUE_NULL) {
701                     value = null;
702                 }
703             }
704 
705             condition.addValue(value);
706         }
707     }
708 
709     public string getDbType() {
710         return dbType;
711     }
712 
713     protected TableStat.Column getColumn(SQLExpr expr) {
714          SQLExpr original = expr;
715 
716         // unwrap
717         expr = unwrapExpr(expr);
718 
719         if (cast(SQLPropertyExpr)(expr) !is null) {
720             SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) expr;
721 
722             SQLExpr owner = propertyExpr.getOwner();
723             string column = propertyExpr.getName();
724 
725             if (cast(SQLName)(owner) !is null) {
726                 SQLName table = cast(SQLName) owner;
727 
728                 SQLObject resolvedOwnerObject = propertyExpr.getResolvedOwnerObject();
729                 if (cast(SQLSubqueryTableSource)(resolvedOwnerObject) !is null
730                         || cast(SQLCreateProcedureStatement)(resolvedOwnerObject) !is null
731                         || cast(SQLCreateFunctionStatement)(resolvedOwnerObject) !is null) {
732                     table = null;
733                 }
734 
735                 if (cast(SQLExprTableSource)(resolvedOwnerObject) !is null) {
736                     SQLExpr tableSourceExpr = (cast(SQLExprTableSource) resolvedOwnerObject).getExpr();
737                     if (cast(SQLName)(tableSourceExpr) !is null) {
738                         table = cast(SQLName) tableSourceExpr;
739                     }
740                 }
741 
742                 if (table !is null) {
743                     long tableHashCode64 = table.hashCode64();
744 
745                     long basic = tableHashCode64;
746                     basic ^= '.';
747                     basic *= FnvHash.PRIME;
748                     long columnHashCode64 = FnvHash.hashCode64(basic, column);
749 
750                     return new TableStat.Column((cast(Object)(table)).toString(), column, columnHashCode64);
751                 }
752             }
753 
754             return null;
755         }
756 
757         if (cast(SQLIdentifierExpr)(expr) !is null) {
758             SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr) expr;
759             if (identifierExpr.getResolvedParameter() !is null) {
760                 return null;
761             }
762 
763             if (cast(SQLSubqueryTableSource)identifierExpr.getResolvedTableSource() !is null) {
764                 return null;
765             }
766 
767             if (identifierExpr.getResolvedDeclareItem() !is null || identifierExpr.getResolvedParameter() !is null) {
768                 return null;
769             }
770 
771             string column = identifierExpr.getName();
772 
773             SQLName table = null;
774             SQLTableSource tableSource = identifierExpr.getResolvedTableSource();
775             if (cast(SQLExprTableSource)(tableSource) !is null) {
776                 SQLExpr tableSourceExpr = (cast(SQLExprTableSource) tableSource).getExpr();
777 
778                 if (tableSourceExpr !is null && !(cast(SQLName)(tableSourceExpr) !is null)) {
779                     tableSourceExpr = unwrapExpr(tableSourceExpr);
780                 }
781 
782                 if (cast(SQLName)(tableSourceExpr) !is null) {
783                     table = cast(SQLName) tableSourceExpr;
784                 }
785             }
786 
787             if (table !is null) {
788                 long tableHashCode64 = table.hashCode64();
789                 long basic = tableHashCode64;
790                 basic ^= '.';
791                 basic *= FnvHash.PRIME;
792                 long columnHashCode64 = FnvHash.hashCode64(basic, column);
793 
794                 return new TableStat.Column((cast(Object)(table)).toString(), column, columnHashCode64);
795             }
796 
797             return new TableStat.Column("UNKNOWN", column);
798         }
799 
800         if (cast(SQLMethodInvokeExpr)(expr) !is null) {
801             SQLMethodInvokeExpr methodInvokeExpr = cast(SQLMethodInvokeExpr) expr;
802             List!(SQLExpr) arguments = methodInvokeExpr.getParameters();
803             long nameHash = methodInvokeExpr.methodNameHashCode64();
804             if (nameHash == FnvHash.Constants.DATE_FORMAT) {
805                 if (arguments.size() == 2
806                         && cast(SQLName)arguments.get(0) !is null
807                         && cast(SQLCharExpr)arguments.get(1) !is null) {
808                     return getColumn(arguments.get(0));
809                 }
810             }
811         }
812 
813         return null;
814     }
815 
816     private SQLExpr unwrapExpr(SQLExpr expr) {
817         SQLExpr original = expr;
818 
819         for (;;) {
820             if (cast(SQLMethodInvokeExpr)(expr) !is null) {
821                 SQLMethodInvokeExpr methodInvokeExp = cast(SQLMethodInvokeExpr) expr;
822                 if (methodInvokeExp.getParameters().size() == 1) {
823                     SQLExpr firstExpr = methodInvokeExp.getParameters().get(0);
824                     expr = firstExpr;
825                     continue;
826                 }
827             }
828 
829             if (cast(SQLCastExpr)(expr) !is null) {
830                 expr = (cast(SQLCastExpr) expr).getExpr();
831                 continue;
832             }
833 
834             if (cast(SQLPropertyExpr)(expr) !is null) {
835                 SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) expr;
836 
837                 SQLTableSource resolvedTableSource = propertyExpr.getResolvedTableSource();
838                 if (cast(SQLSubqueryTableSource)(resolvedTableSource) !is null) {
839                     SQLSelect select = (cast(SQLSubqueryTableSource) resolvedTableSource).getSelect();
840                     SQLSelectQueryBlock queryBlock = select.getFirstQueryBlock();
841                     if (queryBlock !is null) {
842                         if (queryBlock.getGroupBy() !is null) {
843                             if (cast(SQLBinaryOpExpr)original.getParent() !is null) {
844                                 SQLExpr other = (cast(SQLBinaryOpExpr) original.getParent()).other(original);
845                                 if (!SQLExprUtils.isLiteralExpr(other)) {
846                                     break;
847                                 }
848                             }
849                         }
850 
851                         SQLSelectItem selectItem = queryBlock.findSelectItem(propertyExpr
852                                 .nameHashCode64());
853                         if (selectItem !is null) {
854                             expr = selectItem.getExpr();
855                             continue;
856 
857                         } else if (queryBlock.selectItemHasAllColumn()) {
858                             SQLTableSource allColumnTableSource = null;
859 
860                             SQLTableSource from = queryBlock.getFrom();
861                             if (cast(SQLJoinTableSource)(from) !is null) {
862                                 SQLSelectItem allColumnSelectItem = queryBlock.findAllColumnSelectItem();
863                                 if (allColumnSelectItem !is null && cast(SQLPropertyExpr) allColumnSelectItem.getExpr() !is null) {
864                                     SQLExpr owner = (cast(SQLPropertyExpr) allColumnSelectItem.getExpr()).getOwner();
865                                     if (cast(SQLName)(owner) !is null) {
866                                         allColumnTableSource = from.findTableSource((cast(SQLName) owner).nameHashCode64());
867                                     }
868                                 }
869                             } else {
870                                 allColumnTableSource = from;
871                             }
872 
873                             if (allColumnTableSource is null) {
874                                 break;
875                             }
876 
877                             propertyExpr = propertyExpr.clone();
878                             propertyExpr.setResolvedTableSource(allColumnTableSource);
879 
880                             if (cast(SQLExprTableSource)(allColumnTableSource) !is null) {
881                                 propertyExpr.setOwner((cast(SQLExprTableSource) allColumnTableSource).getExpr().clone());
882                             }
883                             expr = propertyExpr;
884                             continue;
885                         }
886                     }
887                 } else if (cast(SQLExprTableSource)(resolvedTableSource) !is null) {
888                     SQLExprTableSource exprTableSource = cast(SQLExprTableSource) resolvedTableSource;
889                     if (exprTableSource.getSchemaObject() !is null) {
890                         break;
891                     }
892 
893                     SQLTableSource redirectTableSource = null;
894                     SQLExpr tableSourceExpr = exprTableSource.getExpr();
895                     if (cast(SQLIdentifierExpr)(tableSourceExpr) !is null) {
896                         redirectTableSource = (cast(SQLIdentifierExpr) tableSourceExpr).getResolvedTableSource();
897                     } else if (cast(SQLPropertyExpr)(tableSourceExpr) !is null) {
898                         redirectTableSource = (cast(SQLPropertyExpr) tableSourceExpr).getResolvedTableSource();
899                     }
900 
901                     if (redirectTableSource == resolvedTableSource) {
902                         redirectTableSource = null;
903                     }
904 
905                     if (redirectTableSource !is null) {
906                         propertyExpr = propertyExpr.clone();
907                         if (cast(SQLExprTableSource)(redirectTableSource) !is null) {
908                             propertyExpr.setOwner((cast(SQLExprTableSource) redirectTableSource).getExpr().clone());
909                         }
910                         propertyExpr.setResolvedTableSource(redirectTableSource);
911                         expr = propertyExpr;
912                         continue;
913                     }
914 
915                     propertyExpr = propertyExpr.clone();
916                     propertyExpr.setOwner(tableSourceExpr);
917                     expr = propertyExpr;
918                     break;
919                 }
920             }
921             break;
922         }
923 
924         return expr;
925     }
926 
927     override
928     public bool visit(SQLTruncateStatement x) {
929         setMode(x, TableStat.Mode.Delete);
930 
931         foreach(SQLExprTableSource tableSource ; x.getTableSources()) {
932             SQLName name = cast(SQLName) tableSource.getExpr();
933             TableStat stat = getTableStat(name);
934             stat.incrementDeleteCount();
935         }
936 
937         return false;
938     }
939 
940     override
941     public bool visit(SQLDropViewStatement x) {
942         setMode(x, TableStat.Mode.Drop);
943         return true;
944     }
945 
946     override
947     public bool visit(SQLDropTableStatement x) {
948         setMode(x, TableStat.Mode.Insert);
949 
950         foreach(SQLExprTableSource tableSource ; x.getTableSources()) {
951             SQLName name = cast(SQLName) tableSource.getExpr();
952             TableStat stat = getTableStat(name);
953             stat.incrementDropCount();
954         }
955 
956         return false;
957     }
958 
959     override
960     public bool visit(SQLInsertStatement x) {
961         if (repository !is null
962                 && x.getParent() is null) {
963             repository.resolve(x);
964         }
965 
966         setMode(x, TableStat.Mode.Insert);
967 
968         if ( cast(SQLName)x.getTableName() !is null) {
969             string ident = (cast(Object)((cast(SQLName) x.getTableName()))).toString();
970 
971             TableStat stat = getTableStat(x.getTableName());
972             stat.incrementInsertCount();
973         }
974 
975         accept!SQLExpr(x.getColumns());
976         accept(x.getQuery());
977 
978         return false;
979     }
980     
981     protected static void putAliasMap(Map!(string, string) aliasMap, string name, string value) {
982         if (aliasMap is null || name is null) {
983             return;
984         }
985         aliasMap.put(toLower(name), value);
986     }
987 
988     protected void accept(SQLObject x) {
989         if (x !is null) {
990             x.accept(this);
991         }
992     }
993 
994     protected void accept(T = SQLObject)(List!(T) nodes) {
995         import std.stdio;
996         if(nodes is null ) throw new Exception("object is null");
997         for (int i = 0, size = nodes.size(); i < size; ++i) {
998             accept(nodes.get(i));
999         }
1000     }
1001 
1002     override public bool visit(SQLSelectQueryBlock x) {
1003         if (x.getFrom() is null) {
1004             foreach(SQLSelectItem selectItem ; x.getSelectList()) {
1005                 statExpr(
1006                         selectItem.getExpr());
1007             }
1008             return false;
1009         }
1010 
1011         setMode(x, TableStat.Mode.Select);
1012 
1013 //        if (x.getFrom(cast(SQLSubqueryTableSource)()) !is null) {
1014 //            x.getFrom().accept(this);
1015 //            return false;
1016 //        }
1017 
1018         SQLTableSource from = x.getFrom();
1019         if (from !is null) {
1020             from.accept(this); // 提前执行,获得aliasMap
1021         }
1022 
1023         SQLExprTableSource into = x.getInto();
1024         if (into !is null && cast(SQLName)into.getExpr() !is null) {
1025             SQLName intoExpr = cast(SQLName) into.getExpr();
1026 
1027             bool isParam = cast(SQLIdentifierExpr)intoExpr !is null && isParam(cast(SQLIdentifierExpr) intoExpr);
1028 
1029             if (!isParam) {
1030                 TableStat stat = getTableStat(intoExpr);
1031                 if (stat !is null) {
1032                     stat.incrementInsertCount();
1033                 }
1034             }
1035             into.accept(this);
1036         }
1037 
1038         foreach(SQLSelectItem selectItem ; x.getSelectList()) {
1039             statExpr(
1040                     selectItem.getExpr());
1041         }
1042 
1043         SQLExpr where = x.getWhere();
1044         if (where !is null) {
1045             statExpr(where);
1046         }
1047 
1048         SQLExpr startWith = x.getStartWith();
1049         if (startWith !is null) {
1050             statExpr(startWith);
1051         }
1052 
1053         SQLExpr connectBy = x.getConnectBy();
1054         if (connectBy !is null) {
1055             statExpr(connectBy);
1056         }
1057 
1058         SQLSelectGroupByClause groupBy = x.getGroupBy();
1059         if (groupBy !is null) {
1060             foreach(SQLExpr expr ; groupBy.getItems()) {
1061                 statExpr(expr);
1062             }
1063         }
1064 
1065         SQLOrderBy orderBy = x.getOrderBy();
1066         if (orderBy !is null) {
1067             this.visit(orderBy);
1068         }
1069 
1070         SQLExpr first = x.getFirst();
1071         if(first !is null) {
1072             statExpr(first);
1073         }
1074 
1075         List!(SQLExpr) distributeBy = x.getDistributeBy();
1076         if (distributeBy !is null) {
1077             foreach(SQLExpr expr ; distributeBy) {
1078                 statExpr(expr);
1079             }
1080         }
1081 
1082         List!(SQLSelectOrderByItem) sortBy = x.getSortBy();
1083         if (sortBy !is null) {
1084             foreach(SQLSelectOrderByItem orderByItem ; sortBy) {
1085                 visit(orderBy);
1086             }
1087         }
1088 
1089         foreach(SQLExpr expr ; x.getForUpdateOf()) {
1090             statExpr(expr);
1091         }
1092 
1093         return false;
1094     }
1095 
1096     private static bool isParam(SQLIdentifierExpr x) {
1097         if (x.getResolvedParameter() !is null
1098                 || x.getResolvedDeclareItem() !is null) {
1099             return true;
1100         }
1101         return false;
1102     }
1103 
1104     override public void endVisit(SQLSelectQueryBlock x) {
1105         setModeOrigin(x);
1106     }
1107 
1108     override public bool visit(SQLJoinTableSource x) {
1109         SQLTableSource left = x.getLeft(), right = x.getRight();
1110 
1111         left.accept(this);
1112         right.accept(this);
1113 
1114         SQLExpr condition = x.getCondition();
1115         if (condition !is null) {
1116             condition.accept(this);
1117         }
1118 
1119         if (x.getUsing().size() > 0
1120                 && cast(SQLExprTableSource)(left) !is null && cast(SQLExprTableSource)(right) !is null) {
1121             SQLExpr leftExpr = (cast(SQLExprTableSource) left).getExpr();
1122             SQLExpr rightExpr = (cast(SQLExprTableSource) right).getExpr();
1123 
1124             foreach(SQLExpr expr ; x.getUsing()) {
1125                 if (cast(SQLIdentifierExpr)(expr) !is null) {
1126                     string name = (cast(SQLIdentifierExpr) expr).getName();
1127                     SQLPropertyExpr leftPropExpr = new SQLPropertyExpr(leftExpr, name);
1128                     SQLPropertyExpr rightPropExpr = new SQLPropertyExpr(rightExpr, name);
1129 
1130                     leftPropExpr.setResolvedTableSource(left);
1131                     rightPropExpr.setResolvedTableSource(right);
1132 
1133                     SQLBinaryOpExpr usingCondition = new SQLBinaryOpExpr(leftPropExpr, SQLBinaryOperator.Equality, rightPropExpr);
1134                     usingCondition.accept(this);
1135                 }
1136             }
1137         }
1138 
1139         return false;
1140     }
1141 
1142     override public bool visit(SQLPropertyExpr x) {
1143         TableStat.Column column = null;
1144         string ident = x.getName();
1145 
1146         SQLTableSource tableSource = x.getResolvedTableSource();
1147         if (cast(SQLExprTableSource)(tableSource) !is null) {
1148             SQLExpr expr = (cast(SQLExprTableSource) tableSource).getExpr();
1149 
1150             if (cast(SQLIdentifierExpr)(expr) !is null) {
1151                 SQLIdentifierExpr table = cast(SQLIdentifierExpr) expr;
1152                 SQLTableSource resolvedTableSource = table.getResolvedTableSource();
1153                 if (cast(SQLExprTableSource)(resolvedTableSource) !is null) {
1154                     expr = (cast(SQLExprTableSource) resolvedTableSource).getExpr();
1155                 }
1156             } else if (cast(SQLPropertyExpr)(expr) !is null) {
1157                 SQLPropertyExpr table = cast(SQLPropertyExpr) expr;
1158                 SQLTableSource resolvedTableSource = table.getResolvedTableSource();
1159                 if (cast(SQLExprTableSource)(resolvedTableSource) !is null) {
1160                     expr = (cast(SQLExprTableSource) resolvedTableSource).getExpr();
1161                 }
1162             }
1163 
1164             if (cast(SQLIdentifierExpr)(expr) !is null) {
1165                 SQLIdentifierExpr table = cast(SQLIdentifierExpr) expr;
1166 
1167                 SQLTableSource resolvedTableSource = table.getResolvedTableSource();
1168                 if (cast(SQLWithSubqueryClause.Entry)(resolvedTableSource) !is null) {
1169                     return false;
1170                 }
1171 
1172                 column = addColumn(table.getName(), ident);
1173 
1174                 if (column !is null && isParentGroupBy(x)) {
1175                     this.groupByColumns.add(column);
1176                 }
1177             } else if (cast(SQLPropertyExpr)(expr) !is null) {
1178                 SQLPropertyExpr table = cast(SQLPropertyExpr) expr;
1179                 string tableName = (cast(Object)(table)).toString();
1180                 column = addColumn(tableName, ident);
1181 
1182                 if (column !is null && isParentGroupBy(x)) {
1183                     this.groupByColumns.add(column);
1184                 }
1185             } else if (cast(SQLMethodInvokeExpr)(expr) !is null) {
1186                 SQLMethodInvokeExpr methodInvokeExpr = cast(SQLMethodInvokeExpr) expr;
1187                 if ("table".equalsIgnoreCase(methodInvokeExpr.getMethodName())
1188                         && methodInvokeExpr.getParameters().size() == 1
1189                         &&  cast(SQLName)methodInvokeExpr.getParameters().get(0) !is null) {
1190                     SQLName table = cast(SQLName) methodInvokeExpr.getParameters().get(0);
1191 
1192                     string tableName = null;
1193                     if (cast(SQLPropertyExpr)(table) !is null) {
1194                         SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) table;
1195                         SQLIdentifierExpr owner = cast(SQLIdentifierExpr) propertyExpr.getOwner();
1196                         if (propertyExpr.getResolvedTableSource() !is null
1197                                 && cast(SQLExprTableSource)propertyExpr.getResolvedTableSource() !is null) {
1198                             SQLExpr resolveExpr = (cast(SQLExprTableSource) propertyExpr.getResolvedTableSource()).getExpr();
1199                             if (cast(SQLName)(resolveExpr) !is null) {
1200                                 tableName = (cast(Object)(resolveExpr)).toString() ~ "." ~ propertyExpr.getName();
1201                             }
1202                         }
1203                     }
1204 
1205                     if (tableName is null) {
1206                         tableName = (cast(Object)(table)).toString();
1207                     }
1208 
1209                     column = addColumn(tableName, ident);
1210                 }
1211             }
1212         } else if (cast(SQLWithSubqueryClause.Entry)(tableSource) !is null
1213                 || cast(SQLSubqueryTableSource)(tableSource) !is null
1214                 || cast(SQLUnionQueryTableSource)(tableSource) !is null
1215                 || cast(SQLLateralViewTableSource)(tableSource) !is null
1216                 || cast(SQLValuesTableSource)(tableSource) !is null) {
1217             return false;
1218         } else {
1219             if (x.getResolvedProcudure() !is null) {
1220                 return false;
1221             }
1222 
1223             if (cast(SQLParameter)x.getResolvedOwnerObject() !is null) {
1224                 return false;
1225             }
1226 
1227             bool skip = false;
1228             for (SQLObject parent = x.getParent();parent !is null;parent = parent.getParent()) {
1229                 if (cast(SQLSelectQueryBlock)(parent) !is null) {
1230                     SQLTableSource from = (cast(SQLSelectQueryBlock) parent).getFrom();
1231 
1232                     // if (cast(OdpsValuesTableSource)(from) !is null) {
1233                     //     skip = true;
1234                     //     break;
1235                     // }
1236                 } else if (cast(SQLSelectQuery)(parent) !is null) {
1237                     break;
1238                 }
1239             }
1240             if (!skip) {
1241                 column = handleUnkownColumn(ident);
1242             }
1243         }
1244 
1245         if (column !is null) {
1246             SQLObject parent = x.getParent();
1247             if (cast(SQLSelectOrderByItem)(parent) !is null) {
1248                 parent = parent.getParent();
1249             }
1250             if (cast(SQLPrimaryKey)(parent) !is null) {
1251                 column.setPrimaryKey(true);
1252             } else if (cast(SQLUnique)(parent) !is null) {
1253                 column.setUnique(true);
1254             }
1255 
1256             setColumn(x, column);
1257         }
1258 
1259         return false;
1260     }
1261 
1262     protected bool isPseudoColumn(long hash) {
1263         return false;
1264     }
1265 
1266     override public bool visit(SQLIdentifierExpr x) {
1267         if (isParam(x)) {
1268             return false;
1269         }
1270 
1271         SQLTableSource tableSource = x.getResolvedTableSource();
1272         if (cast(SQLSelectOrderByItem)x.getParent() !is null) {
1273             SQLSelectOrderByItem selectOrderByItem = cast(SQLSelectOrderByItem) x.getParent();
1274             if (selectOrderByItem.getResolvedSelectItem() !is null) {
1275                 return false;
1276             }
1277         }
1278 
1279         if (tableSource is null
1280                 && (x.getResolvedParameter() !is null
1281                     || x.getResolvedDeclareItem() !is null))
1282         {
1283             return false;
1284         }
1285 
1286         long hash = x.nameHashCode64();
1287         if (isPseudoColumn(hash)) {
1288             return false;
1289         }
1290 
1291         if ((hash == FnvHash.Constants.LEVEL
1292                 || hash == FnvHash.Constants.CONNECT_BY_ISCYCLE
1293                 || hash == FnvHash.Constants.ROWNUM)
1294                 && x.getResolvedColumn() is null
1295                 && tableSource is null) {
1296             return false;
1297         }
1298 
1299         TableStat.Column column = null;
1300         string ident = x.getName();
1301 
1302         if (cast(SQLExprTableSource)(tableSource) !is null) {
1303             SQLExpr expr = (cast(SQLExprTableSource) tableSource).getExpr();
1304             if (cast(SQLIdentifierExpr)(expr) !is null) {
1305                 SQLIdentifierExpr table = cast(SQLIdentifierExpr) expr;
1306                 column = addColumn(table, ident);
1307 
1308                 if (column !is null && isParentGroupBy(x)) {
1309                     this.groupByColumns.add(column);
1310                 }
1311             } else if (cast(SQLPropertyExpr)(expr) !is null /* || cast(OracleDbLinkExpr)(expr) !is null */) {
1312                 string tableName = (cast(Object)(expr)).toString();
1313                 column = addColumn(tableName, ident);
1314 
1315                 if (column !is null && isParentGroupBy(x)) {
1316                     this.groupByColumns.add(column);
1317                 }
1318             } else if (cast(SQLMethodInvokeExpr)(expr) !is null) {
1319                 SQLMethodInvokeExpr methodInvokeExpr = cast(SQLMethodInvokeExpr) expr;
1320                 if ("table".equalsIgnoreCase(methodInvokeExpr.getMethodName())
1321                         && methodInvokeExpr.getParameters().size() == 1
1322                         && cast(SQLName)(methodInvokeExpr.getParameters().get(0)) !is null) {
1323                     SQLName table = cast(SQLName) methodInvokeExpr.getParameters().get(0);
1324 
1325                     string tableName = null;
1326                     if (cast(SQLPropertyExpr)(table) !is null) {
1327                         SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr) table;
1328                         SQLIdentifierExpr owner = cast(SQLIdentifierExpr) propertyExpr.getOwner();
1329                         if (propertyExpr.getResolvedTableSource() !is null
1330                                 && cast(SQLExprTableSource) propertyExpr.getResolvedTableSource() !is null) {
1331                             SQLExpr resolveExpr = (cast(SQLExprTableSource) propertyExpr.getResolvedTableSource()).getExpr();
1332                             if (cast(SQLName)(resolveExpr) !is null) {
1333                                 tableName = (cast(Object)(resolveExpr)).toString() ~ "." ~ propertyExpr.getName();
1334                             }
1335                         }
1336                     }
1337 
1338                     if (tableName is null) {
1339                         tableName = (cast(Object)(table)).toString();
1340                     }
1341 
1342                     column = addColumn(tableName, ident);
1343                 }
1344             }
1345         } else if (cast(SQLWithSubqueryClause.Entry)(tableSource) !is null
1346                 || cast(SQLSubqueryTableSource)(tableSource) !is null
1347                 || cast(SQLValuesTableSource)(tableSource) !is null
1348                 || cast(SQLLateralViewTableSource)(tableSource) !is null) {
1349             return false;
1350         } else {
1351             bool skip = false;
1352             for (SQLObject parent = x.getParent();parent !is null;parent = parent.getParent()) {
1353                 if (cast(SQLSelectQueryBlock)(parent) !is null) {
1354                     SQLTableSource from = (cast(SQLSelectQueryBlock) parent).getFrom();
1355 
1356                     // if (cast(OdpsValuesTableSource)(from) !is null) {
1357                     //     skip = true;
1358                     //     break;
1359                     // }
1360                 } else if (cast(SQLSelectQuery)(parent) !is null) {
1361                     break;
1362                 }
1363             }
1364             if (!skip) {
1365                 column = handleUnkownColumn(ident);
1366             }
1367         }
1368 
1369         if (column !is null) {
1370             SQLObject parent = x.getParent();
1371             if (cast(SQLSelectOrderByItem)(parent) !is null) {
1372                 parent = parent.getParent();
1373             }
1374             if (cast(SQLPrimaryKey)(parent) !is null) {
1375                 column.setPrimaryKey(true);
1376             } else if (cast(SQLUnique)(parent) !is null) {
1377                 column.setUnique(true);
1378             }
1379 
1380             setColumn(x, column);
1381         }
1382 
1383         return false;
1384     }
1385 
1386     private bool isParentSelectItem(SQLObject parent) {
1387         for (; parent !is null; parent = parent.getParent()) {
1388             if (cast(SQLSelectItem)(parent) !is null) {
1389                 return true;
1390             }
1391 
1392             if (cast(SQLSelectQueryBlock)(parent) !is null) {
1393                 return false;
1394             }
1395         }
1396         return false;
1397     }
1398     
1399     private bool isParentGroupBy(SQLObject parent) {
1400         for (; parent !is null; parent = parent.getParent()) {
1401             if (cast(SQLSelectItem)(parent) !is null) {
1402                 return false;
1403             }
1404 
1405             if (cast(SQLSelectGroupByClause)(parent) !is null) {
1406                 return true;
1407             }
1408         }
1409         return false;
1410     }
1411 
1412     private void setColumn(SQLExpr x, TableStat.Column column) {
1413         SQLObject current = x;
1414         for (;;) {
1415             SQLObject parent = current.getParent();
1416 
1417             if (parent is null) {
1418                 break;
1419             }
1420 
1421             if (cast(SQLSelectQueryBlock)(parent) !is null) {
1422                 SQLSelectQueryBlock query = cast(SQLSelectQueryBlock) parent;
1423                 if (query.getWhere() == current) {
1424                     column.setWhere(true);
1425                 }
1426                 break;
1427             }
1428 
1429             if (cast(SQLSelectGroupByClause)(parent) !is null) {
1430                 SQLSelectGroupByClause groupBy = cast(SQLSelectGroupByClause) parent;
1431                 if (current == groupBy.getHaving()) {
1432                     column.setHaving(true);
1433                 } else if (groupBy.getItems().contains(cast(SQLExpr)current)) {
1434                     column.setGroupBy(true);
1435                 }
1436                 break;
1437             }
1438 
1439             if (isParentSelectItem(parent)) {
1440                 column.setSelec(true);
1441                 break;
1442             }
1443 
1444             if (cast(SQLJoinTableSource)(parent) !is null) {
1445                 SQLJoinTableSource join = cast(SQLJoinTableSource) parent;
1446                 if (join.getCondition() == current) {
1447                     column.setJoin(true);
1448                 }
1449                 break;
1450             }
1451 
1452             current = parent;
1453         }
1454     }
1455 
1456     protected TableStat.Column handleUnkownColumn(string columnName) {
1457         return addColumn("UNKNOWN", columnName);
1458     }
1459 
1460     override public bool visit(SQLAllColumnExpr x) {
1461         SQLTableSource tableSource = x.getResolvedTableSource();
1462         if (tableSource is null) {
1463             return false;
1464         }
1465 
1466         statAllColumn(x, tableSource);
1467 
1468         return false;
1469     }
1470 
1471     private void statAllColumn(SQLAllColumnExpr x, SQLTableSource tableSource) {
1472         if (cast(SQLExprTableSource)(tableSource) !is null) {
1473             statAllColumn(x, cast(SQLExprTableSource) tableSource);
1474             return;
1475         }
1476 
1477         if (cast(SQLJoinTableSource)(tableSource) !is null) {
1478             SQLJoinTableSource join = cast(SQLJoinTableSource) tableSource;
1479             statAllColumn(x, join.getLeft());
1480             statAllColumn(x, join.getRight());
1481         }
1482     }
1483 
1484     private void statAllColumn(SQLAllColumnExpr x, SQLExprTableSource tableSource) {
1485         SQLExprTableSource exprTableSource = tableSource;
1486         SQLName expr = exprTableSource.getName();
1487 
1488         SQLCreateTableStatement createStmt = null;
1489 
1490         SchemaObject tableObject = exprTableSource.getSchemaObject();
1491         if (tableObject !is null) {
1492             SQLStatement stmt = tableObject.getStatement();
1493             if (cast(SQLCreateTableStatement)(stmt) !is null) {
1494                 createStmt = cast(SQLCreateTableStatement) stmt;
1495             }
1496         }
1497 
1498         if (createStmt !is null
1499                 && createStmt.getTableElementList().size() > 0) {
1500             SQLName tableName = createStmt.getName();
1501             foreach(SQLTableElement e ; createStmt.getTableElementList()) {
1502                 if (cast(SQLColumnDefinition)(e) !is null) {
1503                     SQLColumnDefinition columnDefinition = cast(SQLColumnDefinition) e;
1504                     SQLName columnName = columnDefinition.getName();
1505                     TableStat.Column column = addColumn((cast(Object)(tableName)).toString(), (cast(Object)(columnName)).toString());
1506                     if (isParentSelectItem(x.getParent())) {
1507                         column.setSelec(true);
1508                     }
1509                 }
1510             }
1511         } else if (expr !is null) {
1512             TableStat.Column column = addColumn((cast(Object)(expr)).toString(), "*");
1513             if (isParentSelectItem(x.getParent())) {
1514                 column.setSelec(true);
1515             }
1516         }
1517     }
1518 
1519     public Map!(TableStat.Name, TableStat) getTables() {
1520         return tableStats;
1521     }
1522 
1523     public bool containsTable(string tableName) {
1524         return tableStats.containsKey(new TableStat.Name(tableName));
1525     }
1526 
1527     public bool containsColumn(string tableName, string columnName) {
1528         long hashCode;
1529 
1530         int p = cast(int)(tableName.indexOf('.'));
1531         if (p != -1) {
1532             SQLExpr owner = SQLUtils.toSQLExpr(tableName, dbType);
1533             hashCode = new SQLPropertyExpr(owner, columnName).hashCode64();
1534         } else {
1535             hashCode = FnvHash.hashCode64(tableName, columnName);
1536         }
1537         return columns.containsKey(new Long(hashCode));
1538     }
1539 
1540     public TableStat.Column[] getColumns() {
1541         return columns.values();
1542     }
1543 
1544     public TableStat.Column getColumn(string tableName, string columnName) {
1545         TableStat.Column column = new TableStat.Column(tableName, columnName);
1546         
1547         return this.columns.get(new Long(column.hashCode64()));
1548     }
1549 
1550     override  public bool visit(SQLSelectStatement x) {
1551         if (repository !is null
1552                 && x.getParent() is null) {
1553             repository.resolve(x);
1554         }
1555 
1556         visit(x.getSelect());
1557 
1558         return false;
1559     }
1560 
1561     override public void endVisit(SQLSelectStatement x) {
1562     }
1563 
1564     override
1565     public bool visit(SQLWithSubqueryClause.Entry x) {
1566         string alias_p = x.getAlias();
1567         SQLWithSubqueryClause with_p = cast(SQLWithSubqueryClause) x.getParent();
1568 
1569         if (Boolean.TRUE.booleanValue == with_p.getRecursive()) {
1570             SQLSelect select = x.getSubQuery();
1571             if (select !is null) {
1572                 select.accept(this);
1573             } else {
1574                 x.getReturningStatement().accept(this);
1575             }
1576         } else {
1577             SQLSelect select = x.getSubQuery();
1578             if (select !is null) {
1579                 select.accept(this);
1580             } else {
1581                 x.getReturningStatement().accept(this);
1582             }
1583         }
1584 
1585         return false;
1586     }
1587 
1588     override public bool visit(SQLSubqueryTableSource x) {
1589         x.getSelect().accept(this);
1590         return false;
1591     }
1592 
1593     protected bool isSimpleExprTableSource(SQLExprTableSource x) {
1594         return  cast(SQLName)x.getExpr() !is null;
1595     }
1596 
1597     public TableStat getTableStat(SQLExprTableSource tableSource) {
1598         return getTableStatWithUnwrap(
1599                 tableSource.getExpr());
1600     }
1601 
1602     private TableStat getTableStatWithUnwrap(SQLExpr expr) {
1603         SQLExpr identExpr = null;
1604 
1605         expr = unwrapExpr(expr);
1606 
1607         if (cast(SQLIdentifierExpr)(expr) !is null) {
1608             SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr) expr;
1609 
1610             if (identifierExpr.nameHashCode64() == FnvHash.Constants.DUAL) {
1611                 return null;
1612             }
1613 
1614             if (isSubQueryOrParamOrVariant(identifierExpr)) {
1615                 return null;
1616             }
1617         }
1618 
1619         SQLTableSource tableSource = null;
1620         if (cast(SQLIdentifierExpr)(expr) !is null) {
1621             tableSource = (cast(SQLIdentifierExpr) expr).getResolvedTableSource();
1622         } else if (cast(SQLPropertyExpr)(expr) !is null) {
1623             tableSource = (cast(SQLPropertyExpr) expr).getResolvedTableSource();
1624         }
1625 
1626         if (cast(SQLExprTableSource)(tableSource) !is null) {
1627             SQLExpr tableSourceExpr = (cast(SQLExprTableSource) tableSource).getExpr();
1628             if (cast(SQLName)(tableSourceExpr) !is null) {
1629                 identExpr = tableSourceExpr;
1630             }
1631         }
1632 
1633         if (identExpr is null) {
1634             identExpr = expr;
1635         }
1636 
1637         if (cast(SQLName)(identExpr) !is null) {
1638             return getTableStat(cast(SQLName) identExpr);
1639         }
1640         return getTableStat((cast(Object)(identExpr)).toString());
1641     }
1642 
1643     override public bool visit(SQLExprTableSource x) {
1644         if (isSimpleExprTableSource(x)) {
1645             SQLExpr expr = x.getExpr();
1646             TableStat stat = getTableStatWithUnwrap(expr);
1647             if (stat is null) {
1648                 return false;
1649             }
1650 
1651             TableStat.Mode mode = getMode();
1652             if (mode !is null) {
1653                 if(mode.mark == TableStat.Mode.Delete.mark) {
1654                     stat.incrementDeleteCount();
1655                 } else if(mode.mark == TableStat.Mode.Insert.mark) {
1656                     stat.incrementInsertCount();
1657                 } else if(mode.mark == TableStat.Mode.Update.mark) {
1658                     stat.incrementUpdateCount();
1659                 } else if(mode.mark == TableStat.Mode.Select.mark) {
1660                     stat.incrementSelectCount();
1661                 } else if(mode.mark == TableStat.Mode.Merge.mark) {
1662                     stat.incrementMergeCount();
1663                 } else if(mode.mark == TableStat.Mode.Drop.mark) {
1664                     stat.incrementDropCount();
1665                 }
1666             }
1667         } else {
1668             accept(x.getExpr());
1669         }
1670 
1671         return false;
1672     }
1673 
1674     protected bool isSubQueryOrParamOrVariant(SQLIdentifierExpr identifierExpr) {
1675         SQLObject resolvedColumnObject = identifierExpr.getResolvedColumnObject();
1676         if (cast(SQLWithSubqueryClause.Entry)(resolvedColumnObject) !is null
1677                 || cast(SQLParameter)(resolvedColumnObject) !is null
1678                 || cast(SQLDeclareItem)(resolvedColumnObject) !is null) {
1679             return true;
1680         }
1681 
1682         SQLObject resolvedOwnerObject = identifierExpr.getResolvedOwnerObject();
1683         if (cast(SQLSubqueryTableSource)(resolvedOwnerObject) !is null
1684                 || cast(SQLWithSubqueryClause.Entry)(resolvedOwnerObject) !is null) {
1685             return true;
1686         }
1687 
1688         return false;
1689     }
1690 
1691     protected bool isSubQueryOrParamOrVariant(SQLPropertyExpr x) {
1692         SQLObject resolvedOwnerObject = x.getResolvedOwnerObject();
1693         if (cast(SQLSubqueryTableSource)(resolvedOwnerObject) !is null
1694                 || cast(SQLWithSubqueryClause.Entry)(resolvedOwnerObject) !is null) {
1695             return true;
1696         }
1697 
1698         SQLExpr owner = x.getOwner();
1699         if (cast(SQLIdentifierExpr)(owner) !is null) {
1700             if (isSubQueryOrParamOrVariant(cast(SQLIdentifierExpr) owner)) {
1701                 return true;
1702             }
1703         }
1704 
1705         SQLTableSource tableSource = x.getResolvedTableSource();
1706         if (cast(SQLExprTableSource)(tableSource) !is null) {
1707             SQLExprTableSource exprTableSource = cast(SQLExprTableSource) tableSource;
1708             if (exprTableSource.getSchemaObject() !is null) {
1709                 return false;
1710             }
1711 
1712             SQLExpr expr = exprTableSource.getExpr();
1713 
1714             if (cast(SQLIdentifierExpr)(expr) !is null) {
1715                 return isSubQueryOrParamOrVariant(cast(SQLIdentifierExpr) expr);
1716             }
1717 
1718             if (cast(SQLPropertyExpr)(expr) !is null) {
1719                 return isSubQueryOrParamOrVariant(cast(SQLPropertyExpr) expr);
1720             }
1721         }
1722 
1723         return false;
1724     }
1725 
1726     override public bool visit(SQLSelectItem x) {
1727         statExpr(
1728                 x.getExpr());
1729 
1730         return false;
1731     }
1732 
1733     override public void endVisit(SQLSelect x) {
1734     }
1735 
1736     override public bool visit(SQLSelect x) {
1737         SQLWithSubqueryClause with_p = x.getWithSubQuery();
1738         if (with_p !is null) {
1739             with_p.accept(this);
1740         }
1741 
1742         SQLSelectQuery query = x.getQuery();
1743         if (query !is null) {
1744             query.accept(this);
1745         }
1746 
1747         SQLOrderBy orderBy = x.getOrderBy();
1748         if (orderBy !is null) {
1749             accept(x.getOrderBy());
1750         }
1751 
1752 
1753         return false;
1754     }
1755 
1756     override public bool visit(SQLAggregateExpr x) {
1757         this.aggregateFunctions.add(x);
1758         
1759         accept!SQLExpr(x.getArguments());
1760         accept(x.getWithinGroup());
1761         accept(x.getOver());
1762         return false;
1763     }
1764 
1765     override public bool visit(SQLMethodInvokeExpr x) {
1766         this.functions.add(x);
1767 
1768         accept!SQLExpr(x.getParameters());
1769         return false;
1770     }
1771 
1772     override public bool visit(SQLUpdateStatement x) {
1773         if (repository !is null
1774                 && x.getParent() is null) {
1775             repository.resolve(x);
1776         }
1777 
1778         setMode(x, TableStat.Mode.Update);
1779 
1780         SQLTableSource tableSource = x.getTableSource();
1781         if (cast(SQLExprTableSource)(tableSource) !is null) {
1782             SQLName identName = (cast(SQLExprTableSource) tableSource).getName();
1783             TableStat stat = getTableStat(identName);
1784             stat.incrementUpdateCount();
1785         } else {
1786             tableSource.accept(this);
1787         }
1788 
1789         accept(x.getFrom());
1790 
1791         accept!SQLUpdateSetItem((x.getItems()));
1792         accept(x.getWhere());
1793 
1794         return false;
1795     }
1796 
1797     override public bool visit(SQLDeleteStatement x) {
1798         if (repository !is null
1799                 && x.getParent() is null) {
1800             repository.resolve(x);
1801         }
1802 
1803         setMode(x, TableStat.Mode.Delete);
1804 
1805         if (cast(SQLSubqueryTableSource) x.getTableSource() !is null) {
1806             SQLSelectQuery selectQuery = (cast(SQLSubqueryTableSource) x.getTableSource()).getSelect().getQuery();
1807             if (cast(SQLSelectQueryBlock)(selectQuery) !is null) {
1808                 SQLSelectQueryBlock subQueryBlock = (cast(SQLSelectQueryBlock) selectQuery);
1809                 subQueryBlock.getWhere().accept(this);
1810             }
1811         }
1812 
1813         TableStat stat = getTableStat(x.getTableName());
1814         stat.incrementDeleteCount();
1815 
1816         accept(x.getWhere());
1817 
1818         return false;
1819     }
1820 
1821     override public bool visit(SQLInListExpr x) {
1822         if (x.isNot()) {
1823             handleCondition(x.getExpr(), "NOT IN", x.getTargetList());
1824         } else {
1825             handleCondition(x.getExpr(), "IN", x.getTargetList());
1826         }
1827 
1828         return true;
1829     }
1830 
1831     override
1832     public bool visit(SQLInSubQueryExpr x) {
1833         if (x.isNot()) {
1834             handleCondition(x.getExpr(), "NOT IN");
1835         } else {
1836             handleCondition(x.getExpr(), "IN");
1837         }
1838         return true;
1839     }
1840 
1841     override public bool visit(SQLCreateTableStatement x) {
1842         if (repository !is null
1843                 && x.getParent() is null) {
1844             repository.resolve(x);
1845         }
1846 
1847         foreach(SQLTableElement e ; x.getTableElementList()) {
1848             e.setParent(x);
1849         }
1850 
1851         TableStat stat = getTableStat(x.getName());
1852         stat.incrementCreateCount();
1853 
1854         accept!SQLTableElement(x.getTableElementList());
1855 
1856         if (x.getInherits() !is null) {
1857             x.getInherits().accept(this);
1858         }
1859 
1860         if (x.getSelect() !is null) {
1861             x.getSelect().accept(this);
1862         }
1863 
1864         return false;
1865     }
1866 
1867     override public bool visit(SQLColumnDefinition x) {
1868         string tableName = null;
1869         {
1870             SQLObject parent = x.getParent();
1871             if (cast(SQLCreateTableStatement)(parent) !is null) {
1872                 tableName = (cast(Object)(cast(SQLCreateTableStatement) parent).getName()).toString();
1873             }
1874         }
1875 
1876         if (tableName is null) {
1877             return true;
1878         }
1879 
1880         string columnName = (cast(Object)x.getName()).toString();
1881         TableStat.Column column = addColumn(tableName, columnName);
1882         if (x.getDataType() !is null) {
1883             column.setDataType(x.getDataType().getName());
1884         }
1885 
1886         foreach(SQLColumnConstraint item ; x.getConstraints()) {
1887             if (cast(SQLPrimaryKey)(item) !is null) {
1888                 column.setPrimaryKey(true);
1889             } else if (cast(SQLUnique)(item) !is null) {
1890                 column.setUnique(true);
1891             }
1892         }
1893 
1894         return false;
1895     }
1896 
1897     override
1898     public bool visit(SQLCallStatement x) {
1899         return false;
1900     }
1901 
1902     override
1903     public void endVisit(SQLCommentStatement x) {
1904 
1905     }
1906 
1907     override
1908     public bool visit(SQLCommentStatement x) {
1909         return false;
1910     }
1911 
1912     override public bool visit(SQLCurrentOfCursorExpr x) {
1913         return false;
1914     }
1915 
1916     override
1917     public bool visit(SQLAlterTableAddColumn x) {
1918         SQLAlterTableStatement stmt = cast(SQLAlterTableStatement) x.getParent();
1919         string table = (cast(Object)stmt.getName()).toString();
1920 
1921         foreach(SQLColumnDefinition column ; x.getColumns()) {
1922             string columnName = (cast(Object)column.getName()).toString();
1923             addColumn(table, columnName);
1924         }
1925         return false;
1926     }
1927 
1928     override
1929     public void endVisit(SQLAlterTableAddColumn x) {
1930 
1931     }
1932 
1933     override
1934     public bool visit(SQLRollbackStatement x) {
1935         return false;
1936     }
1937 
1938     override public bool visit(SQLCreateViewStatement x) {
1939         if (repository !is null
1940                 && x.getParent() is null) {
1941             repository.resolve(x);
1942         }
1943 
1944         x.getSubQuery().accept(this);
1945         return false;
1946     }
1947 
1948     override public bool visit(SQLAlterViewStatement x) {
1949         if (repository !is null
1950                 && x.getParent() is null) {
1951             repository.resolve(x);
1952         }
1953 
1954         x.getSubQuery().accept(this);
1955         return false;
1956     }
1957 
1958     override
1959     public bool visit(SQLAlterTableDropForeignKey x) {
1960         return false;
1961     }
1962 
1963     override
1964     public bool visit(SQLUseStatement x) {
1965         return false;
1966     }
1967 
1968     override
1969     public bool visit(SQLAlterTableDisableConstraint x) {
1970         return false;
1971     }
1972 
1973     override
1974     public bool visit(SQLAlterTableEnableConstraint x) {
1975         return false;
1976     }
1977 
1978     override
1979     public bool visit(SQLAlterTableStatement x) {
1980         if (repository !is null
1981                 && x.getParent() is null) {
1982             repository.resolve(x);
1983         }
1984 
1985         TableStat stat = getTableStat(x.getName());
1986         stat.incrementAlterCount();
1987 
1988 
1989         foreach(SQLAlterTableItem item ; x.getItems()) {
1990             item.setParent(x);
1991             item.accept(this);
1992         }
1993 
1994         return false;
1995     }
1996 
1997     override
1998     public bool visit(SQLAlterTableDropConstraint x) {
1999         return false;
2000     }
2001 
2002     override
2003     public bool visit(SQLDropIndexStatement x) {
2004         setMode(x, TableStat.Mode.DropIndex);
2005         SQLExprTableSource table = x.getTableName();
2006         if (table !is null) {
2007             SQLName name = cast(SQLName) table.getExpr();
2008             TableStat stat = getTableStat(name);
2009             stat.incrementDropIndexCount();
2010         }
2011         return false;
2012     }
2013 
2014     override
2015     public bool visit(SQLCreateIndexStatement x) {
2016         setMode(x, TableStat.Mode.CreateIndex);
2017 
2018         SQLName name = cast(SQLName) (cast(SQLExprTableSource) x.getTable()).getExpr();
2019 
2020         string table = (cast(Object)(name)).toString();
2021 
2022         TableStat stat = getTableStat(name);
2023         stat.incrementCreateIndexCount();
2024 
2025         foreach(SQLSelectOrderByItem item ; x.getItems()) {
2026             SQLExpr expr = item.getExpr();
2027             if (cast(SQLIdentifierExpr)(expr) !is null) {
2028                 SQLIdentifierExpr identExpr = cast(SQLIdentifierExpr) expr;
2029                 string columnName = identExpr.getName();
2030                 addColumn(table, columnName);
2031             }
2032         }
2033 
2034         return false;
2035     }
2036 
2037     override
2038     public bool visit(SQLForeignKeyImpl x) {
2039 
2040         foreach(SQLName column ; x.getReferencingColumns()) {
2041             column.accept(this);
2042         }
2043 
2044         string table = x.getReferencedTableName().getSimpleName();
2045 
2046         TableStat stat = getTableStat(x.getReferencedTableName());
2047         stat.incrementReferencedCount();
2048         foreach(SQLName column ; x.getReferencedColumns()) {
2049             string columnName = column.getSimpleName();
2050             addColumn(table, columnName);
2051         }
2052 
2053         return false;
2054     }
2055 
2056     override
2057     public bool visit(SQLDropSequenceStatement x) {
2058         return false;
2059     }
2060 
2061     override
2062     public bool visit(SQLDropTriggerStatement x) {
2063         return false;
2064     }
2065 
2066     override
2067     public bool visit(SQLDropUserStatement x) {
2068         return false;
2069     }
2070 
2071     override
2072     public bool visit(SQLGrantStatement x) {
2073         if (x.getOn() !is null && (x.getObjectType().name.length == 0 || x.getObjectType() == SQLObjectType.TABLE)) {
2074             x.getOn().accept(this);
2075         }
2076         return false;
2077     }
2078 
2079     override
2080     public bool visit(SQLRevokeStatement x) {
2081         if (x.getOn() !is null) {
2082             x.getOn().accept(this);
2083         }
2084         return false;
2085     }
2086 
2087     override
2088     public bool visit(SQLDropDatabaseStatement x) {
2089         return false;
2090     }
2091 
2092     override
2093     public bool visit(SQLAlterTableAddIndex x) {
2094         foreach(SQLSelectOrderByItem item ; x.getItems()) {
2095             item.accept(this);
2096         }
2097 
2098         SQLName table = (cast(SQLAlterTableStatement) x.getParent()).getName();
2099         TableStat tableStat = this.getTableStat(table);
2100         tableStat.incrementCreateIndexCount();
2101         return false;
2102     }
2103 
2104     override public bool visit(SQLCheck x) {
2105         x.getExpr().accept(this);
2106         return false;
2107     }
2108 
2109     override public bool visit(SQLCreateTriggerStatement x) {
2110         SQLExprTableSource on = x.getOn();
2111         on.accept(this);
2112         return false;
2113     }
2114 
2115     override public bool visit(SQLDropFunctionStatement x) {
2116         return false;
2117     }
2118 
2119     override public bool visit(SQLDropTableSpaceStatement x) {
2120         return false;
2121     }
2122 
2123     override public bool visit(SQLDropProcedureStatement x) {
2124         return false;
2125     }
2126 
2127     override
2128     public bool visit(SQLAlterTableRename x) {
2129         return false;
2130     }
2131 
2132     override
2133     public bool visit(SQLArrayExpr x) {
2134         accept!SQLExpr(x.getValues());
2135 
2136         SQLExpr exp = x.getExpr();
2137         if (cast(SQLIdentifierExpr)(exp) !is null) {
2138             if ((cast(SQLIdentifierExpr) exp).getName() == ("ARRAY")) {
2139                 return false;
2140             }
2141         }
2142         exp.accept(this);
2143         return false;
2144     }
2145     
2146     override
2147     public bool visit(SQLOpenStatement x) {
2148         return false;
2149     }
2150     
2151     override
2152     public bool visit(SQLFetchStatement x) {
2153         return false;
2154     }
2155     
2156     override
2157     public bool visit(SQLCloseStatement x) {
2158         return false;
2159     }
2160 
2161     override
2162     public bool visit(SQLCreateProcedureStatement x) {
2163         if (repository !is null
2164                 && x.getParent() is null) {
2165             repository.resolve(x);
2166         }
2167 
2168         accept(x.getBlock());
2169         return false;
2170     }
2171 
2172     override
2173     public bool visit(SQLCreateFunctionStatement x) {
2174         if (repository !is null
2175                 && x.getParent() is null) {
2176             repository.resolve(x);
2177         }
2178 
2179         accept(x.getBlock());
2180         return false;
2181     }
2182     
2183     override
2184     public bool visit(SQLBlockStatement x) {
2185         if (repository !is null
2186                 && x.getParent() is null) {
2187             repository.resolve(x);
2188         }
2189 
2190         foreach(SQLParameter param ; x.getParameters()) {
2191             param.setParent(x);
2192             param.accept(this);
2193         }
2194 
2195         foreach(SQLStatement stmt ; x.getStatementList()) {
2196             stmt.accept(this);
2197         }
2198 
2199         SQLStatement exception = x.getException();
2200         if (exception !is null) {
2201             exception.accept(this);
2202         }
2203 
2204         return false;
2205     }
2206     
2207     override
2208     public bool visit(SQLShowTablesStatement x) {
2209         return false;
2210     }
2211     
2212     override
2213     public bool visit(SQLDeclareItem x) {
2214         return false;
2215     }
2216     
2217     override
2218     public bool visit(SQLPartitionByHash x) {
2219         return false;
2220     }
2221     
2222     override
2223     public bool visit(SQLPartitionByRange x) {
2224         return false;
2225     }
2226     
2227     override
2228     public bool visit(SQLPartitionByList x) {
2229         return false;
2230     }
2231     
2232     override
2233     public bool visit(SQLPartition x) {
2234         return false;
2235     }
2236     
2237     override
2238     public bool visit(SQLSubPartition x) {
2239         return false;
2240     }
2241     
2242     override
2243     public bool visit(SQLSubPartitionByHash x) {
2244         return false;
2245     }
2246     
2247     override
2248     public bool visit(SQLPartitionValue x) {
2249         return false;
2250     }
2251     
2252     override
2253     public bool visit(SQLAlterDatabaseStatement x) {
2254         return true;
2255     }
2256     
2257     override
2258     public bool visit(SQLAlterTableConvertCharSet x) {
2259         return false;
2260     }
2261     
2262     override
2263     public bool visit(SQLAlterTableDropPartition x) {
2264         return false;
2265     }
2266     
2267     override
2268     public bool visit(SQLAlterTableReOrganizePartition x) {
2269         return false;
2270     }
2271     
2272     override
2273     public bool visit(SQLAlterTableCoalescePartition x) {
2274         return false;
2275     }
2276     
2277     override
2278     public bool visit(SQLAlterTableTruncatePartition x) {
2279         return false;
2280     }
2281     
2282     override
2283     public bool visit(SQLAlterTableDiscardPartition x) {
2284         return false;
2285     }
2286     
2287     override
2288     public bool visit(SQLAlterTableImportPartition x) {
2289         return false;
2290     }
2291     
2292     override
2293     public bool visit(SQLAlterTableAnalyzePartition x) {
2294         return false;
2295     }
2296     
2297     override
2298     public bool visit(SQLAlterTableCheckPartition x) {
2299         return false;
2300     }
2301     
2302     override
2303     public bool visit(SQLAlterTableOptimizePartition x) {
2304         return false;
2305     }
2306     
2307     override
2308     public bool visit(SQLAlterTableRebuildPartition x) {
2309         return false;
2310     }
2311     
2312     override
2313     public bool visit(SQLAlterTableRepairPartition x) {
2314         return false;
2315     }
2316     
2317     override public bool visit(SQLSequenceExpr x) {
2318         return false;
2319     }
2320     
2321     override
2322     public bool visit(SQLMergeStatement x) {
2323         if (repository !is null
2324                 && x.getParent() is null) {
2325             repository.resolve(x);
2326         }
2327 
2328         setMode(x.getUsing(), TableStat.Mode.Select);
2329         x.getUsing().accept(this);
2330 
2331         setMode(x, TableStat.Mode.Merge);
2332 
2333         SQLTableSource into = x.getInto();
2334         if (cast(SQLExprTableSource)(into) !is null) {
2335             string ident = (cast(Object)(cast(SQLExprTableSource) into).getExpr()).toString();
2336             TableStat stat = getTableStat(ident);
2337             stat.incrementMergeCount();
2338         } else {
2339             into.accept(this);
2340         }
2341 
2342         x.getOn().accept(this);
2343 
2344         if (x.getUpdateClause() !is null) {
2345             x.getUpdateClause().accept(this);
2346         }
2347 
2348         if (x.getInsertClause() !is null) {
2349             x.getInsertClause().accept(this);
2350         }
2351 
2352         return false;
2353     }
2354     
2355     override
2356     public bool visit(SQLSetStatement x) {
2357         return false;
2358     }
2359 
2360     public List!(SQLMethodInvokeExpr) getFunctions() {
2361         return this.functions;
2362     }
2363 
2364     override public bool visit(SQLCreateSequenceStatement x) {
2365         return false;
2366     }
2367 
2368     override
2369     public bool visit(SQLAlterTableAddConstraint x) {
2370         SQLConstraint constraint = x.getConstraint();
2371         if (cast(SQLUniqueConstraint)(constraint) !is null) {
2372             SQLAlterTableStatement stmt = cast(SQLAlterTableStatement) x.getParent();
2373             TableStat tableStat = this.getTableStat(stmt.getName());
2374             tableStat.incrementCreateIndexCount();
2375         }
2376         return true;
2377     }
2378 
2379     override
2380     public bool visit(SQLAlterTableDropIndex x) {
2381         SQLAlterTableStatement stmt = cast(SQLAlterTableStatement) x.getParent();
2382         TableStat tableStat = this.getTableStat(stmt.getName());
2383         tableStat.incrementDropIndexCount();
2384         return false;
2385     }
2386 
2387     override
2388     public bool visit(SQLAlterTableDropPrimaryKey x) {
2389         SQLAlterTableStatement stmt = cast(SQLAlterTableStatement) x.getParent();
2390         TableStat tableStat = this.getTableStat(stmt.getName());
2391         tableStat.incrementDropIndexCount();
2392         return false;
2393     }
2394 
2395     override
2396     public bool visit(SQLAlterTableDropKey x) {
2397         SQLAlterTableStatement stmt = cast(SQLAlterTableStatement) x.getParent();
2398         TableStat tableStat = this.getTableStat(stmt.getName());
2399         tableStat.incrementDropIndexCount();
2400         return false;
2401     }
2402 
2403     override
2404     public bool visit(SQLDescribeStatement x) {
2405         string tableName = (cast(Object)x.getObject()).toString();
2406 
2407         TableStat tableStat = this.getTableStat(x.getObject());
2408         tableStat.incrementDropIndexCount();
2409 
2410         SQLName column = x.getColumn();
2411         if (column !is null) {
2412             string columnName = (cast(Object)(column)).toString();
2413             this.addColumn(tableName, columnName);
2414         }
2415         return false;
2416     }
2417 
2418     override public bool visit(SQLExplainStatement x) {
2419         if (repository !is null
2420                 && x.getParent() is null) {
2421             repository.resolve(x);
2422         }
2423 
2424         if (x.getStatement() !is null) {
2425             accept(x.getStatement());
2426         }
2427 
2428         return false;
2429     }
2430 
2431     override public bool visit(SQLCreateMaterializedViewStatement x) {
2432         if (repository !is null
2433                 && x.getParent() is null) {
2434             repository.resolve(x);
2435         }
2436         return true;
2437     }
2438 
2439     override public bool visit(SQLReplaceStatement x) {
2440         if (repository !is null
2441                 && x.getParent() is null) {
2442             repository.resolve(x);
2443         }
2444 
2445         setMode(x, TableStat.Mode.Replace);
2446 
2447         SQLName tableName = x.getTableName();
2448 
2449         TableStat stat = getTableStat(tableName);
2450 
2451         if (stat !is null) {
2452             stat.incrementInsertCount();
2453         }
2454 
2455         accept!SQLExpr(x.getColumns());
2456         accept!(ValuesClause)(x.getValuesList());
2457         accept(x.getQuery());
2458 
2459         return false;
2460     }
2461 
2462     protected  void statExpr(SQLExpr x) {
2463         auto clazz = typeid(x);
2464         if (clazz == typeid(SQLIdentifierExpr)) {
2465             visit(cast(SQLIdentifierExpr) x);
2466         } else if (clazz == typeid(SQLPropertyExpr)) {
2467             visit(cast(SQLPropertyExpr) x);
2468 //        } else if (clazz == typeid(SQLAggregateExpr)) {
2469 //            visit(cast(SQLAggregateExpr) x);
2470         } else if (clazz == typeid(SQLBinaryOpExpr)) {
2471             visit(cast(SQLBinaryOpExpr) x);
2472 //        } else if (clazz == typeid(SQLCharExpr)) {
2473 //            visit(cast(SQLCharExpr) x);
2474 //        } else if (clazz == typeid(SQLNullExpr)) {
2475 //            visit(cast(SQLNullExpr) x);
2476 //        } else if (clazz == typeid(SQLIntegerExpr)) {
2477 //            visit(cast(SQLIntegerExpr) x);
2478 //        } else if (clazz == typeid(SQLNumberExpr)) {
2479 //            visit(cast(SQLNumberExpr) x);
2480 //        } else if (clazz == typeid(SQLMethodInvokeExpr)) {
2481 //            visit(cast(SQLMethodInvokeExpr) x);
2482 //        } else if (clazz == typeid(SQLVariantRefExpr)) {
2483 //            visit(cast(SQLVariantRefExpr) x);
2484 //        } else if (clazz == typeid(SQLBinaryOpExprGroup)) {
2485 //            visit(cast(SQLBinaryOpExprGroup) x);
2486         } else if (cast(SQLLiteralExpr)(x) !is null) {
2487             // skip
2488         } else {
2489             x.accept(this);
2490         }
2491     }
2492 
2493     override public bool visit(SQLAlterFunctionStatement x) {
2494         return false;
2495     }
2496     override public bool visit(SQLDropSynonymStatement x) {
2497         return false;
2498     }
2499 
2500     override public bool visit(SQLAlterTypeStatement x) {
2501         return false;
2502     }
2503     override public bool visit(SQLAlterProcedureStatement x) {
2504         return false;
2505     }
2506 
2507     override public bool visit(SQLExprStatement x) {
2508         SQLExpr expr = x.getExpr();
2509 
2510         if (cast(SQLName)(expr) !is null) {
2511             return false;
2512         }
2513 
2514         return true;
2515     }
2516 
2517     override
2518     public bool visit(SQLDropTypeStatement x) {
2519         return false;
2520     }
2521 
2522     override
2523     public bool visit(SQLExternalRecordFormat x) {
2524         return false;
2525     }
2526 
2527     override public bool visit(SQLCreateDatabaseStatement x) {
2528         return false;
2529     }
2530 
2531     override
2532     public bool visit(SQLAlterTableExchangePartition x) {
2533         SQLExprTableSource table = x.getTable();
2534         if (table !is null) {
2535             table.accept(this);
2536         }
2537         return false;
2538     }
2539 
2540     override public bool visit(SQLDumpStatement x) {
2541         if (repository !is null
2542                 && x.getParent() is null) {
2543             repository.resolve(x);
2544         }
2545 
2546          SQLExprTableSource into = x.getInto();
2547         if (into !is null) {
2548             into.accept(this);
2549         }
2550 
2551          SQLSelect select = x.getSelect();
2552         if (select !is null) {
2553             select.accept(this);
2554         }
2555 
2556         return false;
2557     }
2558 }