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.statement.SQLSelectQueryBlock; 17 18 import hunt.sql.ast.statement.SQLSelectGroupByClause; 19 20 import hunt.collection; 21 22 import hunt.sql.SQLUtils; 23 import hunt.sql.ast; 24 import hunt.sql.ast.expr; 25 import hunt.sql.visitor.SQLASTVisitor; 26 import hunt.sql.util.FnvHash; 27 import hunt.sql.ast.statement.SQLSelectItem; 28 import hunt.sql.ast.statement.SQLSelectOrderByItem; 29 import hunt.sql.ast.statement.SQLExprTableSource; 30 import hunt.sql.ast.statement.SQLSelect; 31 import hunt.sql.ast.statement.SQLSelectQuery; 32 import hunt.sql.ast.statement.SQLTableSource; 33 import hunt.sql.ast.statement.SQLColumnDefinition; 34 import hunt.sql.ast.statement.SQLSubqueryTableSource; 35 36 public class SQLSelectQueryBlock : SQLObjectImpl , SQLSelectQuery, SQLReplaceable { 37 private bool bracket = false; 38 protected int distionOption; 39 protected List!SQLSelectItem selectList; 40 41 protected SQLTableSource from; 42 protected SQLExprTableSource into; 43 protected SQLExpr where; 44 45 // for oracle & oceanbase 46 protected SQLExpr startWith; 47 protected SQLExpr connectBy; 48 protected bool prior = false; 49 protected bool noCycle = false; 50 protected SQLOrderBy orderBySiblings; 51 52 protected SQLSelectGroupByClause groupBy; 53 protected List!SQLWindow windows; 54 protected SQLOrderBy orderBy; 55 protected bool parenthesized = false; 56 protected bool forUpdate = false; 57 protected bool noWait = false; 58 protected SQLExpr waitTime; 59 protected SQLLimit _limit; 60 61 // for oracle 62 protected List!SQLExpr forUpdateOf; 63 protected List!SQLExpr distributeBy; 64 protected List!SQLSelectOrderByItem sortBy; 65 66 protected string cachedSelectList; // optimized for SelectListCache 67 protected long cachedSelectListHash; // optimized for SelectListCache 68 69 protected List!SQLCommentHint hints; 70 public string dbType; 71 72 public this(){ 73 selectList = new ArrayList!SQLSelectItem(); 74 } 75 76 public SQLExprTableSource getInto() { 77 return into; 78 } 79 80 public void setInto(SQLExpr into) { 81 this.setInto(new SQLExprTableSource(into)); 82 } 83 84 public void setInto(SQLExprTableSource into) { 85 if (into !is null) { 86 into.setParent(this); 87 } 88 this.into = into; 89 } 90 91 public SQLSelectGroupByClause getGroupBy() { 92 return this.groupBy; 93 } 94 95 public void setGroupBy(SQLSelectGroupByClause groupBy) { 96 if (groupBy !is null) { 97 groupBy.setParent(this); 98 } 99 this.groupBy = groupBy; 100 } 101 102 public SQLExpr getWhere() { 103 return this.where; 104 } 105 106 public void setWhere(SQLExpr where) { 107 if (where !is null) { 108 where.setParent(this); 109 } 110 this.where = where; 111 } 112 113 public void addWhere(SQLExpr condition) { 114 if (condition is null) { 115 return; 116 } 117 118 if (where is null) { 119 where = condition; 120 } else { 121 where = SQLBinaryOpExpr.and(where, condition); 122 } 123 } 124 125 public SQLOrderBy getOrderBy() { 126 return orderBy; 127 } 128 129 public void setOrderBy(SQLOrderBy orderBy) { 130 if (orderBy !is null) { 131 orderBy.setParent(this); 132 } 133 134 this.orderBy = orderBy; 135 } 136 137 public SQLOrderBy getOrderBySiblings() { 138 return orderBySiblings; 139 } 140 141 public void setOrderBySiblings(SQLOrderBy orderBySiblings) { 142 if (orderBySiblings !is null) { 143 orderBySiblings.setParent(this); 144 } 145 this.orderBySiblings = orderBySiblings; 146 } 147 148 public int getDistionOption() { 149 return this.distionOption; 150 } 151 152 public void setDistionOption(int distionOption) { 153 this.distionOption = distionOption; 154 } 155 156 public List!SQLSelectItem getSelectList() { 157 return this.selectList; 158 } 159 160 public void addSelectItem(SQLSelectItem item) { 161 this.selectList.add(item); 162 item.setParent(this); 163 } 164 165 public void addSelectItem(SQLExpr expr) { 166 this.addSelectItem(new SQLSelectItem(expr)); 167 } 168 169 public void addSelectItem(SQLExpr expr, string alias_p) { 170 this.addSelectItem(new SQLSelectItem(expr, alias_p)); 171 } 172 173 public SQLTableSource getFrom() { 174 return this.from; 175 } 176 177 public void setFrom(SQLTableSource from) { 178 if (from !is null) { 179 from.setParent(this); 180 } 181 this.from = from; 182 } 183 184 public void setFrom(SQLSelectQueryBlock queryBlock, string alias_p) { 185 if (queryBlock is null) { 186 this.from = null; 187 return; 188 } 189 190 this.setFrom(new SQLSelect(queryBlock), alias_p); 191 } 192 193 public void setFrom(SQLSelect select, string alias_p) { 194 if (select is null) { 195 this.from = null; 196 return; 197 } 198 199 SQLSubqueryTableSource from = new SQLSubqueryTableSource(select); 200 from.setAlias(alias_p); 201 this.setFrom(from); 202 } 203 204 public void setFrom(string tableName, string alias_p) { 205 SQLExprTableSource from; 206 if (tableName is null || tableName.length == 0) { 207 from = null; 208 } else { 209 from = new SQLExprTableSource(new SQLIdentifierExpr(tableName), alias_p); 210 } 211 this.setFrom(from); 212 } 213 214 public bool isParenthesized() { 215 return parenthesized; 216 } 217 218 public void setParenthesized(bool parenthesized) { 219 this.parenthesized = parenthesized; 220 } 221 222 public bool isForUpdate() { 223 return forUpdate; 224 } 225 226 public void setForUpdate(bool forUpdate) { 227 this.forUpdate = forUpdate; 228 } 229 230 public bool isNoWait() { 231 return noWait; 232 } 233 234 public void setNoWait(bool noWait) { 235 this.noWait = noWait; 236 } 237 238 public SQLExpr getWaitTime() { 239 return waitTime; 240 } 241 242 public void setWaitTime(SQLExpr waitTime) { 243 if (waitTime !is null) { 244 waitTime.setParent(this); 245 } 246 this.waitTime = waitTime; 247 } 248 249 public SQLLimit getLimit() { 250 return _limit; 251 } 252 253 public void setLimit(SQLLimit _limit) { 254 if (_limit !is null) { 255 _limit.setParent(this); 256 } 257 this._limit = _limit; 258 } 259 260 public SQLExpr getFirst() { 261 if (_limit is null) { 262 return null; 263 } 264 265 return _limit.getRowCount(); 266 } 267 268 public void setFirst(SQLExpr first) { 269 if (_limit is null) { 270 _limit = new SQLLimit(); 271 } 272 this._limit.setRowCount(first); 273 } 274 275 public SQLExpr getOffset() { 276 if (_limit is null) { 277 return null; 278 } 279 280 return _limit.getOffset(); 281 } 282 283 public void setOffset(SQLExpr offset) { 284 if (_limit is null) { 285 _limit = new SQLLimit(); 286 } 287 this._limit.setOffset(offset); 288 } 289 290 public bool isPrior() { 291 return prior; 292 } 293 294 public void setPrior(bool prior) { 295 this.prior = prior; 296 } 297 298 public SQLExpr getStartWith() { 299 return this.startWith; 300 } 301 302 public void setStartWith(SQLExpr startWith) { 303 if (startWith !is null) { 304 startWith.setParent(this); 305 } 306 this.startWith = startWith; 307 } 308 309 public SQLExpr getConnectBy() { 310 return this.connectBy; 311 } 312 313 public void setConnectBy(SQLExpr connectBy) { 314 if (connectBy !is null) { 315 connectBy.setParent(this); 316 } 317 this.connectBy = connectBy; 318 } 319 320 public bool isNoCycle() { 321 return this.noCycle; 322 } 323 324 public void setNoCycle(bool noCycle) { 325 this.noCycle = noCycle; 326 } 327 328 public List!SQLExpr getDistributeBy() { 329 return distributeBy; 330 } 331 332 public List!SQLSelectOrderByItem getSortBy() { 333 return sortBy; 334 } 335 336 public void addSortBy(SQLSelectOrderByItem item) { 337 if (sortBy is null) { 338 sortBy = new ArrayList!SQLSelectOrderByItem(); 339 } 340 if (item !is null) { 341 item.setParent(this); 342 } 343 this.sortBy.add(item); 344 } 345 346 347 override protected void accept0(SQLASTVisitor visitor) { 348 if (visitor.visit(this)) { 349 acceptChild!SQLSelectItem(visitor, this.selectList); 350 acceptChild(visitor, this.from); 351 acceptChild(visitor, this.into); 352 acceptChild(visitor, this.where); 353 acceptChild(visitor, this.startWith); 354 acceptChild(visitor, this.connectBy); 355 acceptChild(visitor, this.groupBy); 356 acceptChild(visitor, this.orderBy); 357 acceptChild!SQLExpr(visitor, this.distributeBy); 358 acceptChild!SQLSelectOrderByItem(visitor, this.sortBy); 359 acceptChild(visitor, this.waitTime); 360 acceptChild(visitor, this._limit); 361 } 362 visitor.endVisit(this); 363 } 364 365 override 366 public size_t toHash() @trusted nothrow { 367 int prime = 31; 368 size_t result = 1; 369 result = prime * result + hashOf(parenthesized); 370 result = prime * result + distionOption; 371 result = prime * result + ((from is null) ? 0 : (cast(Object)from).toHash()); 372 result = prime * result + ((groupBy is null) ? 0 : (cast(Object)groupBy).toHash()); 373 result = prime * result + ((into is null) ? 0 : (cast(Object)into).toHash()); 374 result = prime * result + ((selectList is null) ? 0 : (cast(Object)selectList).toHash()); 375 result = prime * result + ((where is null) ? 0 : (cast(Object)where).toHash()); 376 return result; 377 } 378 379 override 380 public bool opEquals(Object obj) { 381 if (this == obj) return true; 382 if (obj is null) return false; 383 if (typeid(this) != typeid(obj)) return false; 384 SQLSelectQueryBlock other = cast(SQLSelectQueryBlock) obj; 385 if (parenthesized ^ other.parenthesized) return false; 386 if (distionOption != other.distionOption) return false; 387 if (from is null) { 388 if (other.from !is null) return false; 389 } else if (!(cast(Object)(from)).opEquals(cast(Object)(other.from))) return false; 390 if (groupBy is null) { 391 if (other.groupBy !is null) return false; 392 } else if (!(cast(Object)(groupBy)).opEquals(cast(Object)(other.groupBy))) return false; 393 if (into is null) { 394 if (other.into !is null) return false; 395 } else if (!(cast(Object)(into)).opEquals(cast(Object)(other.into))) return false; 396 if (selectList is null) { 397 if (other.selectList !is null) return false; 398 } else if (!(cast(Object)(selectList)).opEquals(cast(Object)(other.selectList))) return false; 399 if (where is null) { 400 if (other.where !is null) return false; 401 } else if (!(cast(Object)(where)).opEquals(cast(Object)(other.where))) return false; 402 return true; 403 } 404 405 override public SQLSelectQueryBlock clone() { 406 SQLSelectQueryBlock x = new SQLSelectQueryBlock(); 407 cloneTo(x); 408 return x; 409 } 410 411 public List!SQLExpr getForUpdateOf() { 412 if (forUpdateOf is null) { 413 forUpdateOf = new ArrayList!SQLExpr(1); 414 } 415 return forUpdateOf; 416 } 417 418 public int getForUpdateOfSize() { 419 if (forUpdateOf is null) { 420 return 0; 421 } 422 423 return forUpdateOf.size(); 424 } 425 426 public void cloneSelectListTo(SQLSelectQueryBlock x) { 427 x.distionOption = distionOption; 428 foreach (SQLSelectItem item ; this.selectList) { 429 SQLSelectItem item2 = item.clone(); 430 item2.setParent(x); 431 x.selectList.add(item2); 432 } 433 } 434 435 public void cloneTo(SQLSelectQueryBlock x) { 436 437 x.distionOption = distionOption; 438 439 foreach (SQLSelectItem item ; this.selectList) { 440 x.addSelectItem(item.clone()); 441 } 442 443 if (from !is null) { 444 x.setFrom(from.clone()); 445 } 446 447 if (into !is null) { 448 x.setInto(into.clone()); 449 } 450 451 if (where !is null) { 452 x.setWhere(where.clone()); 453 } 454 455 if (startWith !is null) { 456 x.setStartWith(startWith.clone()); 457 } 458 459 if (connectBy !is null) { 460 x.setConnectBy(connectBy.clone()); 461 } 462 463 x.prior = prior; 464 x.noCycle = noCycle; 465 466 if (orderBySiblings !is null) { 467 x.setOrderBySiblings(orderBySiblings.clone()); 468 } 469 470 if (groupBy !is null) { 471 x.setGroupBy(groupBy.clone()); 472 } 473 474 if (orderBy !is null) { 475 x.setOrderBy(orderBy.clone()); 476 } 477 478 x.parenthesized = parenthesized; 479 x.forUpdate = forUpdate; 480 x.noWait = noWait; 481 if (waitTime !is null) { 482 x.setWaitTime(waitTime.clone()); 483 } 484 485 if (_limit !is null) { 486 x.setLimit(_limit.clone()); 487 } 488 } 489 490 override 491 public bool isBracket() { 492 return bracket; 493 } 494 495 override 496 public void setBracket(bool bracket) { 497 this.bracket = bracket; 498 } 499 500 public SQLTableSource findTableSource(string alias_p) { 501 if (from is null) { 502 return null; 503 } 504 return from.findTableSource(alias_p); 505 } 506 507 public SQLTableSource findTableSourceWithColumn(string column) { 508 if (from is null) { 509 return null; 510 } 511 return from.findTableSourceWithColumn(column); 512 } 513 514 public SQLTableSource findTableSourceWithColumn(long columnHash) { 515 if (from is null) { 516 return null; 517 } 518 return from.findTableSourceWithColumn(columnHash); 519 } 520 521 override 522 public bool replace(SQLExpr expr, SQLExpr target) { 523 if (where == expr) { 524 setWhere(target); 525 return true; 526 } 527 return false; 528 } 529 530 public SQLSelectItem findSelectItem(string ident) { 531 if (ident is null) { 532 return null; 533 } 534 535 long hash = FnvHash.hashCode64(ident); 536 return findSelectItem(hash); 537 } 538 539 public SQLSelectItem findSelectItem(long identHash) { 540 foreach (SQLSelectItem item ; this.selectList) { 541 if (item.match(identHash)) { 542 return item; 543 } 544 } 545 546 return null; 547 } 548 549 public bool selectItemHasAllColumn() { 550 return selectItemHasAllColumn(true); 551 } 552 553 public bool selectItemHasAllColumn(bool recursive) { 554 foreach (SQLSelectItem item ; this.selectList) { 555 SQLExpr expr = item.getExpr(); 556 557 bool allColumn = (cast(SQLAllColumnExpr)expr !is null) 558 || ( cast(SQLPropertyExpr)expr !is null && (cast(SQLPropertyExpr) expr).getName() == ("*")); 559 560 if (allColumn) { 561 if (recursive && cast(SQLSubqueryTableSource)from !is null) { 562 SQLSelect subSelect = (cast(SQLSubqueryTableSource) from).select; 563 SQLSelectQueryBlock queryBlock = subSelect.getQueryBlock(); 564 if (queryBlock !is null) { 565 return queryBlock.selectItemHasAllColumn(); 566 } 567 } 568 return true; 569 } 570 } 571 572 return false; 573 } 574 575 public SQLSelectItem findAllColumnSelectItem() { 576 SQLSelectItem allColumnItem = null; 577 foreach (SQLSelectItem item ; this.selectList) { 578 SQLExpr expr = item.getExpr(); 579 580 bool allColumn = (cast(SQLAllColumnExpr)expr !is null) 581 || ( cast(SQLPropertyExpr)expr !is null && (cast(SQLPropertyExpr) expr).getName() == ("*")); 582 583 if (allColumnItem !is null) { 584 return null; // duplicateAllColumn 585 } 586 allColumnItem = item; 587 } 588 589 return allColumnItem; 590 } 591 592 public SQLColumnDefinition findColumn(string columnName) { 593 if (from is null) { 594 return null; 595 } 596 597 long hash = FnvHash.hashCode64(columnName); 598 return from.findColumn(hash); 599 } 600 601 public void addCondition(string conditionSql) { 602 if (conditionSql is null || conditionSql.length == 0) { 603 return; 604 } 605 606 SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, dbType); 607 addCondition(condition); 608 } 609 610 public void addCondition(SQLExpr expr) { 611 if (expr is null) { 612 return; 613 } 614 615 this.setWhere(SQLBinaryOpExpr.and(where, expr)); 616 } 617 618 public bool removeCondition(string conditionSql) { 619 if (conditionSql is null || conditionSql.length == 0) { 620 return false; 621 } 622 623 SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, dbType); 624 625 return removeCondition(condition); 626 } 627 628 public bool removeCondition(SQLExpr condition) { 629 if (condition is null) { 630 return false; 631 } 632 633 if ( cast(SQLBinaryOpExprGroup)where !is null) { 634 SQLBinaryOpExprGroup group = cast(SQLBinaryOpExprGroup) where; 635 636 int removedCount = 0; 637 List!SQLExpr items = group.getItems(); 638 for (int i = items.size() - 1; i >= 0; i--) { 639 if ((cast(Object)(items.get(i))).opEquals(cast(Object)(condition))) { 640 items.removeAt(i); 641 removedCount++; 642 } 643 } 644 if (items.size() == 0) { 645 where = null; 646 } 647 648 return removedCount > 0; 649 } 650 651 if ( cast(SQLBinaryOpExpr)where !is null) { 652 SQLBinaryOpExpr binaryOpWhere = cast(SQLBinaryOpExpr) where; 653 SQLBinaryOperator operator = binaryOpWhere.getOperator(); 654 if (operator == SQLBinaryOperator.BooleanAnd || operator == SQLBinaryOperator.BooleanOr) { 655 List!SQLExpr items = SQLBinaryOpExpr.split(binaryOpWhere); 656 657 int removedCount = 0; 658 for (int i = items.size() - 1; i >= 0; i--) { 659 SQLExpr item = items.get(i); 660 if ((cast(Object)(item)).opEquals(cast(Object)(condition))) { 661 if (SQLUtils.replaceInParent(item, null)) { 662 removedCount++; 663 } 664 } 665 } 666 667 return removedCount > 0; 668 } 669 } 670 671 if ((cast(Object)(condition)).opEquals(cast(Object)(where))) { 672 where = null; 673 return true; 674 } 675 676 return false; 677 } 678 679 public void limit(int rowCount, int offset) { 680 SQLLimit _limit = new SQLLimit(); 681 _limit.setRowCount(new SQLIntegerExpr(rowCount)); 682 if (offset > 0) { 683 _limit.setOffset(new SQLIntegerExpr(offset)); 684 } 685 686 setLimit(_limit); 687 } 688 689 public string getCachedSelectList() { 690 return cachedSelectList; 691 } 692 693 public void setCachedSelectList(string cachedSelectList, long cachedSelectListHash) { 694 this.cachedSelectList = cachedSelectList; 695 this.cachedSelectListHash = cachedSelectListHash; 696 } 697 698 public long getCachedSelectListHash() { 699 return cachedSelectListHash; 700 } 701 702 public List!SQLCommentHint getHintsDirect() { 703 return hints; 704 } 705 706 public List!SQLCommentHint getHints() { 707 if (hints is null) { 708 hints = new ArrayList!SQLCommentHint(2); 709 } 710 return hints; 711 } 712 713 public void setHints(List!SQLCommentHint hints) { 714 this.hints = hints; 715 } 716 717 public int getHintsSize() { 718 if (hints is null) { 719 return 0; 720 } 721 722 return hints.size(); 723 } 724 725 public string getDbType() { 726 return dbType; 727 } 728 729 public void setDbType(string dbType) { 730 this.dbType = dbType; 731 } 732 733 public List!SQLWindow getWindows() { 734 return windows; 735 } 736 737 public void addWindow(SQLWindow x) { 738 if (x !is null) { 739 x.setParent(this); 740 } 741 if (windows is null) { 742 windows = new ArrayList!SQLWindow(4); 743 } 744 this.windows.add(x); 745 } 746 }