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 }