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.ast.expr.SQLBinaryOpExpr;
17 
18 
19 import hunt.sql.SQLUtils;
20 import hunt.sql.ast;
21 import hunt.sql.visitor.ParameterizedOutputVisitorUtils;
22 import hunt.sql.visitor.ParameterizedVisitor;
23 import hunt.sql.visitor.SQLASTOutputVisitor;
24 import hunt.sql.visitor.SQLASTVisitor;
25 import hunt.sql.util.Utils;
26 import hunt.sql.ast.expr.SQLBinaryOperator;
27 import hunt.collection;
28 import hunt.sql.ast.expr.SQLExprUtils;
29 import hunt.sql.ast.expr.SQLNullExpr;
30 import hunt.sql.ast.expr.SQLBooleanExpr;
31 import hunt.sql.ast.expr.SQLCharExpr;
32 import hunt.sql.ast.expr.SQLPropertyExpr;
33 import hunt.sql.ast.expr.SQLIdentifierExpr;
34 import hunt.sql.ast.expr.SQLLiteralExpr;
35 import hunt.sql.ast.expr.SQLVariantRefExpr;
36 import hunt.util.StringBuilder;
37 
38 public class SQLBinaryOpExpr : SQLExprImpl , SQLReplaceable//, Serializable 
39 {
40     public SQLExpr           left;
41     public SQLExpr           right;
42     public SQLBinaryOperator operator;
43     protected string            dbType;
44 
45     private bool             bracket  = false;
46 
47     // only for parameterized output
48     protected  List!SQLObject mergedList;
49 
50     public this(){
51 
52     }
53 
54     public this(string dbType){
55         this.dbType = dbType;
56     }
57 
58     public this(SQLExpr left, SQLBinaryOperator operator, SQLExpr right){
59         this(left, operator, right, null);
60     }
61     
62     public this(SQLExpr left, SQLBinaryOperator operator, SQLExpr right, string dbType){
63         if (left !is null) {
64             left.setParent(this);
65         }
66         this.left = left;
67 
68         setRight(right);
69         this.operator = operator;
70 
71         if (dbType is null) {
72             auto obj = cast(SQLBinaryOpExpr) left;
73             if (obj !is null) {
74                 dbType = obj.dbType;
75             }
76         }
77 
78         if (dbType is null) {
79             auto obj = cast(SQLBinaryOpExpr) right;
80             if (obj !is null) {
81                 dbType = obj.dbType;
82             }
83         }
84 
85         this.dbType = dbType;
86     }
87 
88     public this(SQLExpr left, SQLExpr right, SQLBinaryOperator operator){
89 
90         setLeft(left);
91         setRight(right);
92         this.operator = operator;
93     }
94 
95     public string getDbType() {
96         return dbType;
97     }
98 
99     public void setDbType(string dbType) {
100         this.dbType = dbType;
101     }
102 
103     public SQLExpr getLeft() {
104         return this.left;
105     }
106 
107     public void setLeft(SQLExpr left) {
108         if (left !is null) {
109             left.setParent(this);
110         }
111         this.left = left;
112     }
113 
114     public SQLExpr getRight() {
115         return this.right;
116     }
117 
118     public void setRight(SQLExpr right) {
119         if (right !is null) {
120             right.setParent(this);
121         }
122         this.right = right;
123     }
124 
125     public SQLBinaryOperator getOperator() {
126         return this.operator;
127     }
128 
129     public void setOperator(SQLBinaryOperator operator) {
130         this.operator = operator;
131     }
132 
133     public bool isBracket() {
134         return bracket;
135     }
136 
137     public void setBracket(bool bracket) {
138         this.bracket = bracket;
139     }
140 
141     override  protected void accept0(SQLASTVisitor visitor) {
142         if (visitor.visit(this)) {
143             acceptChild(visitor, this.left);
144             acceptChild(visitor, this.right);
145         }
146 
147         visitor.endVisit(this);
148     }
149 
150    override
151     public List!SQLObject getChildren() {
152         //return Arrays.asList(this.left, this.right);
153         List!SQLObject ls = new ArrayList!SQLObject();
154         ls.add(this.left);
155         ls.add(this.right);
156         return ls;
157     }
158 
159    override
160     public size_t toHash() @trusted nothrow {
161          int prime = 31;
162         size_t result = 1;
163         result = prime * result + ((left is null) ? 0 : (cast(Object)left).toHash());
164         result = prime * result + hashOf(operator);
165         result = prime * result + ((right is null) ? 0 : (cast(Object)right).toHash());
166         return result;
167     }
168 
169    override
170     public bool opEquals(Object obj) {
171         if (this is obj) {
172             return true;
173         }
174         if (obj is null) {
175             return false;
176         }
177         // if (!(cast(SQLBinaryOpExpr)(obj) !is null)) {
178         //     return false;
179         // }
180         SQLBinaryOpExpr other = cast(SQLBinaryOpExpr) obj;
181         if(other is null)
182             return false;
183 
184         return operator == other.operator
185                 && SQLExprUtils.opEquals(left, other.left)
186                 &&  SQLExprUtils.opEquals(right, other.right);
187     }
188 
189     public bool opEquals(SQLBinaryOpExpr other) {
190         return operator == other.operator
191                 && SQLExprUtils.opEquals(left, other.left)
192                 &&  SQLExprUtils.opEquals(right, other.right);
193     }
194 
195 
196     public bool equalsIgoreOrder(SQLBinaryOpExpr other) {
197         if (this == other) {
198             return true;
199         }
200         if (other is null) {
201             return false;
202         }
203 
204         if (operator != other.operator) {
205             return false;
206         }
207 
208         return (Utils.equals(cast(Object)(this.left), cast(Object)(other.left))
209                     && Utils.equals(cast(Object)(this.right), cast(Object)(other.right)))
210                 || (Utils.equals(cast(Object)(this.left), cast(Object)(other.right))
211                     && Utils.equals(cast(Object)(this.right), cast(Object)(other.left)));
212     }
213 
214     override public SQLBinaryOpExpr clone() {
215         SQLBinaryOpExpr x = new SQLBinaryOpExpr();
216 
217         if (left !is null) {
218             x.setLeft(left.clone());
219         }
220         if (right !is null) {
221             x.setRight(right.clone());
222         }
223         x.operator = operator;
224         x.dbType = dbType;
225         x.bracket = bracket;
226 
227         return x;
228     }
229 
230     override public string toString() {
231         return SQLUtils.toSQLString(this, getDbType());
232     }
233 
234     override public void output(StringBuilder buf) {
235         SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(buf, dbType);
236         this.accept(visitor);
237     }
238 
239     public static SQLExpr combine(List!SQLExpr items, SQLBinaryOperator op) {
240         if (items is null) {
241             return null;
242         }
243 
244         int size = items.size();
245         if (size == 0) {
246             return null;
247         }
248 
249         if (size == 1) {
250             return items.get(0);
251         }
252 
253         SQLBinaryOpExpr expr = new SQLBinaryOpExpr(items.get(0), op, items.get(1));
254 
255         for (int i = 2; i < size; ++i) {
256             SQLExpr item = items.get(i);
257             expr = new SQLBinaryOpExpr(expr, op, item);
258         }
259 
260         return expr;
261     }
262 
263     public static List!SQLExpr split(SQLBinaryOpExpr x) {
264         return split(x, x.getOperator());
265     }
266 
267     public static List!SQLExpr split(SQLBinaryOpExpr x, SQLBinaryOperator op) {
268         if (x.getOperator() != op) {
269             List!SQLExpr groupList = new ArrayList!SQLExpr(1);
270             groupList.add(x);
271             return groupList;
272         }
273 
274         List!SQLExpr groupList = new ArrayList!SQLExpr();
275         split(groupList, x, op);
276         return groupList;
277     }
278 
279     public static void split(List!SQLExpr outList, SQLExpr expr, SQLBinaryOperator op) {
280         if (expr is null) {
281             return;
282         }
283 
284         // if (!(cast(SQLBinaryOpExpr)(expr) !is null)) {
285         //     outList.add(expr);
286         //     return;
287         // }
288 
289         SQLBinaryOpExpr binaryExpr = cast(SQLBinaryOpExpr) expr;
290         if(binaryExpr is null)
291         {
292             outList.add(expr);
293             return;
294         }
295 
296         if (binaryExpr.getOperator() != op) {
297             outList.add(binaryExpr);
298             return;
299         }
300 
301         List!SQLExpr rightList = new ArrayList!SQLExpr();
302         rightList.add(binaryExpr.getRight());
303         for (SQLExpr left = binaryExpr.getLeft();;) {
304             SQLBinaryOpExpr leftBinary = cast(SQLBinaryOpExpr) left;
305             if (leftBinary !is null) {
306                 if (leftBinary.operator == op) {
307                     left = (cast(SQLBinaryOpExpr) leftBinary).getLeft();
308                     rightList.add(leftBinary.getRight());
309                 } else {
310                     outList.add(leftBinary);
311                     break;
312                 }
313             } else {
314                 outList.add(left);
315                 break;
316             }
317         }
318 
319         for (int i = rightList.size() - 1; i >= 0; --i) {
320             SQLExpr right  = rightList.get(i);
321 
322             SQLBinaryOpExpr binaryRight = cast(SQLBinaryOpExpr) right;
323             if (binaryRight !is null) {
324                 if (binaryRight.operator == op) {
325                     {
326                         SQLExpr rightLeft = binaryRight.getLeft();
327                         SQLBinaryOpExpr rightLeftBinary = cast(SQLBinaryOpExpr) rightLeft;
328                         if (rightLeftBinary !is null) {
329                             if (rightLeftBinary.operator == op) {
330                                 split(outList, rightLeftBinary, op);
331                             } else {
332                                 outList.add(rightLeftBinary);
333                             }
334                         } else {
335                             outList.add(rightLeft);
336                         }
337                     }
338                     {
339                         SQLExpr rightRight = binaryRight.getRight();
340                         SQLBinaryOpExpr rightRightBinary = cast(SQLBinaryOpExpr) rightRight;
341                         if (rightRightBinary !is null) {
342                             if (rightRightBinary.operator == op) {
343                                 split(outList, rightRightBinary, op);
344                             } else {
345                                 outList.add(rightRightBinary);
346                             }
347                         } else {
348                             outList.add(rightRight);
349                         }
350                     }
351                 } else {
352                     outList.add(binaryRight);
353                 }
354             } else {
355                 outList.add(right);
356             }
357         }
358     }
359 
360     public static SQLExpr and(SQLExpr a, SQLExpr b) {
361         if (a is null) {
362             return b;
363         }
364 
365         if (b is null) {
366             return a;
367         }
368         SQLBinaryOpExpr bb = cast(SQLBinaryOpExpr) b;
369         if ( bb !is null) {
370             if (bb.operator == SQLBinaryOperator.BooleanAnd) {
371                 return and(and(a, bb.left), bb.right);
372             }
373         }
374 
375         return new SQLBinaryOpExpr(a, SQLBinaryOperator.BooleanAnd, b);
376     }
377 
378     public static SQLExpr andIfNotExists(SQLExpr a, SQLExpr b) {
379         if (a is null) {
380             return b;
381         }
382 
383         if (b is null) {
384             return a;
385         }
386 
387         List!SQLExpr groupListA = new ArrayList!SQLExpr();
388         List!SQLExpr groupListB = new ArrayList!SQLExpr();
389         split(groupListA, a, SQLBinaryOperator.BooleanAnd);
390         split(groupListB, a, SQLBinaryOperator.BooleanAnd);
391 
392         foreach (SQLExpr itemB ; groupListB) {
393             bool exist = false;
394             foreach (SQLExpr itemA ; groupListA) {
395                 if ((cast(Object)itemA).opEquals(cast(Object)(itemB))) {
396                     exist = true;
397                 } else if ((cast(SQLBinaryOpExpr) itemA !is null)
398                         && (cast(SQLBinaryOpExpr) itemB !is null)) {
399                     if ((cast(SQLBinaryOpExpr) itemA).equalsIgoreOrder(cast(SQLBinaryOpExpr) itemB)) {
400                         exist = true;
401                     }
402                 }
403             }
404             if (!exist) {
405                 groupListA.add(itemB);
406             }
407         }
408         return combine(groupListA, SQLBinaryOperator.BooleanAnd);
409     }
410 
411     public static SQLBinaryOpExpr isNotNull(SQLExpr expr) {
412         return new SQLBinaryOpExpr(expr, SQLBinaryOperator.IsNot, new SQLNullExpr());
413     }
414 
415     public static SQLBinaryOpExpr isNull(SQLExpr expr) {
416         return new SQLBinaryOpExpr(expr, SQLBinaryOperator.Is, new SQLNullExpr());
417     }
418 
419     public bool replace(SQLExpr expr, SQLExpr taget) {
420         SQLObject parent = getParent();
421 
422         if (left == expr) {
423             if (taget is null) {
424                 auto obj = cast(SQLReplaceable) parent;
425                 if (obj !is null) {
426                     return obj.replace(this, right);
427                 } else {
428                     return false;
429                 }
430             }
431             this.setLeft(taget);
432             return true;
433         }
434 
435         if (right == expr) {
436             if (taget is null) {
437                 auto obj = cast(SQLReplaceable) parent;
438                 if (obj !is null) {
439                     return obj.replace(this, left);
440                 } else {
441                     return false;
442                 }
443             }
444             this.setRight(taget);
445             return true;
446         }
447 
448         return false;
449     }
450 
451     public SQLExpr other(SQLExpr x) {
452         if (x == left) {
453             return right;
454         }
455 
456         if (x == right) {
457             return left;
458         }
459 
460         return null;
461     }
462 
463     public bool contains(SQLExpr item) {
464         auto obj = cast(SQLBinaryOpExpr) item;
465         if (obj !is null) {
466             if (this.equalsIgoreOrder(obj)) {
467                 return true;
468             }
469 
470             return (cast(Object)left).opEquals(cast(Object)(item)) || (cast(Object)right).opEquals(cast(Object)(item));
471         }
472 
473         return false;
474     }
475 
476     override public SQLDataType computeDataType() {
477         if (operator.isRelational()) {
478             return SQLBooleanExpr.DEFAULT_DATA_TYPE;
479         }
480 
481         SQLDataType leftDataType = null, rightDataType = null;
482         if (left !is null) {
483             leftDataType = left.computeDataType();
484         }
485         if (right !is null) {
486             rightDataType = right.computeDataType();
487         }
488 
489         if (operator == SQLBinaryOperator.Concat) {
490             if (leftDataType !is null) {
491                 return leftDataType;
492             }
493             if (rightDataType !is null) {
494                 return rightDataType;
495             }
496             return SQLCharExpr.DEFAULT_DATA_TYPE;
497         }
498 
499         return null;
500     }
501 
502     public bool conditionContainsTable(string alias_p) {
503         if (left is null || right is null) {
504             return false;
505         }
506 
507         if (cast(SQLPropertyExpr) left !is null) {
508             if ((cast(SQLPropertyExpr) left).matchOwner(alias_p)) {
509                 return true;
510             }
511         } else if (cast(SQLBinaryOpExpr)left !is null ) {
512             if ((cast(SQLBinaryOpExpr) left).conditionContainsTable(alias_p)) {
513                 return true;
514             }
515         }
516 
517         if (cast(SQLPropertyExpr) right !is null) {
518             if ((cast(SQLPropertyExpr) right).matchOwner(alias_p)) {
519                 return true;
520             }
521         } else if (cast(SQLBinaryOpExpr) right !is null) {
522             return (cast(SQLBinaryOpExpr) right).conditionContainsTable(alias_p);
523         }
524 
525         return false;
526     }
527 
528     public bool conditionContainsColumn(string column) {
529         if (left is null || right is null) {
530             return false;
531         }
532 
533         if (cast(SQLIdentifierExpr) left !is null) {
534             if ((cast(SQLIdentifierExpr) left).nameEquals(column)) {
535                 return true;
536             }
537         } else if (cast(SQLIdentifierExpr) right !is null) {
538             if ((cast(SQLIdentifierExpr) right).nameEquals(column)) {
539                 return true;
540             }
541         }
542 
543         return false;
544     }
545 
546     /**
547      * only for parameterized output
548      * @param v
549      * @param x
550      * @return
551      */
552     public static SQLBinaryOpExpr merge(ParameterizedVisitor v, SQLBinaryOpExpr x) {
553         SQLObject parent = x.parent;
554 
555         for (;;) {
556             SQLBinaryOpExpr rightBinary = cast(SQLBinaryOpExpr) x.right;
557             if (rightBinary !is null) {
558                 SQLBinaryOpExpr leftBinaryExpr = cast(SQLBinaryOpExpr) x.left;
559                 if (leftBinaryExpr !is null) {
560                     if (SQLExprUtils.opEquals(leftBinaryExpr.right, rightBinary)) {
561                         x = leftBinaryExpr;
562                         v.incrementReplaceCunt();
563                         continue;
564                     }
565                 }
566                 SQLExpr mergedRight = merge(v, rightBinary);
567                 if (mergedRight != x.right) {
568                     x = new SQLBinaryOpExpr(x.left, x.operator, mergedRight);
569                     v.incrementReplaceCunt();
570                 }
571 
572                 x.setParent(parent);
573             }
574 
575             break;
576         }
577         auto xobj = cast(SQLBinaryOpExpr) x.left;
578         if (xobj !is null) {
579             SQLExpr mergedLeft = merge(v, xobj);
580             if (mergedLeft != x.left) {
581                 SQLBinaryOpExpr tmp = new SQLBinaryOpExpr(mergedLeft, x.operator, x.right);
582                 tmp.setParent(parent);
583                 x = tmp;
584                 v.incrementReplaceCunt();
585             }
586         }
587 
588         // ID = ? OR ID = ? => ID = ?
589         if (x.operator == SQLBinaryOperator.BooleanOr) {
590             SQLBinaryOpExpr leftBinary = cast(SQLBinaryOpExpr) x.left;
591             SQLBinaryOpExpr rightBinary = cast(SQLBinaryOpExpr) x.right;
592             if ((leftBinary !is null) && (rightBinary !is null)) {
593                 
594                 if (mergeEqual(leftBinary, rightBinary)) {
595                     v.incrementReplaceCunt();
596                     leftBinary.setParent(x.parent);
597                     leftBinary.addMergedItem(rightBinary);
598                     return leftBinary;
599                 }
600 
601                 if (SQLExprUtils.isLiteralExpr(leftBinary.left) //
602                         && leftBinary.operator == SQLBinaryOperator.BooleanOr) {
603                     if (mergeEqual(leftBinary.right, x.right)) {
604                         v.incrementReplaceCunt();
605                         leftBinary.addMergedItem(rightBinary);
606                         return leftBinary;
607                     }
608                 }
609             }
610         }
611 
612         return x;
613     }
614 
615     /**
616      * only for parameterized output
617      * @param item
618      * @return
619      */
620     private void addMergedItem(SQLBinaryOpExpr item) {
621         if (mergedList is null) {
622             mergedList = new ArrayList!SQLObject();
623         }
624         mergedList.add(item);
625     }
626 
627     /**
628      * only for parameterized output
629      * @return
630      */
631     public List!SQLObject getMergedList() {
632         return mergedList;
633     }
634 
635     /**
636      * only for parameterized output
637      * @param a
638      * @param b
639      * @return
640      */
641     private static bool mergeEqual(SQLExpr a, SQLExpr b) {
642 
643 
644         SQLBinaryOpExpr binaryA = cast(SQLBinaryOpExpr) a;
645         SQLBinaryOpExpr binaryB = cast(SQLBinaryOpExpr) b;
646 
647         if (binaryA is null) {
648             return false;
649         }
650         if (binaryB is null) {
651             return false;
652         }
653 
654         if (binaryA.getOperator() != SQLBinaryOperator.Equality) {
655             return false;
656         }
657 
658         if (binaryB.getOperator() != SQLBinaryOperator.Equality) {
659             return false;
660         }
661 
662         if (!(cast(SQLLiteralExpr)(binaryA.getRight()) !is null || cast(SQLVariantRefExpr)(binaryA.getRight()) !is null )) {
663             return false;
664         }
665 
666         if (!(cast(SQLLiteralExpr)(binaryB.getRight()) !is null || cast(SQLVariantRefExpr)(binaryB.getRight()) !is null )) {
667             return false;
668         }
669 
670         return (cast(Object)(binaryA.getLeft())).toString() == (cast(Object)(binaryB.getLeft())).toString();
671     }
672 }