| 1 22 23 package org.xquark.extractor.mysql; 24 25 import java.util.*; 26 27 import org.xquark.extractor.algebra.*; 28 import org.xquark.extractor.algebra.Constants; 29 import org.xquark.extractor.common.*; 30 import org.xquark.extractor.runtime.IDProvider; 31 32 48 public class MysqlRemoveSubQueries extends DefaultCompleteVisitor { 49 50 private static final String RCSRevision = "$Revision: 1.26 $"; 51 private static final String RCSName = "$Name: $"; 52 53 protected Expression _rewrittenQuery; 54 protected Mapper _topMapper; protected FindQuerySignatureVisitor _keyNodesFinder = new FindQuerySignatureVisitor(); 56 protected SplitPredicateVisitor _splitVisitor = null; protected RemoveAggregateFunctionVisitor _removeAggrVisitor = null; protected RenameAttributeExpressionVisitor _renameVisitor = null; 59 protected ForeignAttributeGrabber _foreignAttributeGrabber = null; 60 protected AttributeGrabber _attributeGrabber = null; 61 protected RestrictionVisitor _restrictionVisitor = new RestrictionVisitor(); 62 protected AddKeys _addKeysVisitor = new AddKeys(); 63 protected Stack _subQueryStack = new Stack(); 64 protected IDProvider _attIDProvider = null; 65 protected IDProvider _relIDProvider = null; 66 67 public MysqlRemoveSubQueries(IDProvider attIDProvider, IDProvider relIDProvider) { 68 _attIDProvider = attIDProvider; 69 _relIDProvider = relIDProvider; 70 } 71 72 public void reinit(Expression query) { 73 _rewrittenQuery = query; 74 _topMapper = query.getMapper(); 75 } 76 77 public Expression getRewrittenQuery() { 78 return _rewrittenQuery; 79 } 80 81 public void setRewrittenQuery(SubQueryNode query) { 82 switch (query._type) { 83 case SubQueryNode.TYPE_MAIN : 84 ((Expression) query._query).setMapper(_topMapper); 85 if (query._referringNode == null) 86 _rewrittenQuery = (Expression) query._query; 87 else 88 _rewrittenQuery = query._referringNode; 89 break; 90 case SubQueryNode.TYPE_FROM : 91 if (query._renamedQuery == null) 94 ((UnaryOperator) query._referringNode).setOperand((Expression) query._query); 95 break; 96 default : 97 } 98 } 100 101 public void visit(UnOpProject arg) { 102 browseProjection(arg, SubQueryNode.CONTEXT_PROJECT); 104 } 106 107 public void visit(UnOpAggregate arg) { 108 browseProjection(arg, SubQueryNode.CONTEXT_AGGR); 110 } 112 113 private void browseProjection(UnOpProject arg, byte context) { 114 SubQueryNode query = null; 115 116 SubQueryNode parentQuery = null; 117 if (_subQueryStack.isEmpty()) 118 query = new SubQueryNode(arg); 119 else { 120 parentQuery = (SubQueryNode) _subQueryStack.peek(); 121 122 if (parentQuery.getType() == SubQueryNode.TYPE_PREDICATE_EXIST) 123 query = (SubQueryNode) _subQueryStack.peek(); 124 else 125 query = new SubQueryNode(arg, context); 126 } 127 128 if (query != null) 129 query.setBrowsingStep(SubQueryNode.STEP_OPERAND); 130 131 arg.getOperand().accept(this); 132 133 if (query != null) 134 query.setBrowsingStep(SubQueryNode.STEP_ITEMS); 135 136 List list = arg.getParameterList(); 137 if (null != list) { 138 for (int i = 0; i < list.size(); i++) { 139 query.setCurrent((Expression) list.get(i)); 140 query.getCurrent().accept(this); 141 } 142 } 143 144 if (query != parentQuery) 145 setRewrittenQuery(query.rewrite()); 146 } 147 148 public void visit(UnOpRestrict arg) { 149 SubQueryNode query = (SubQueryNode) _subQueryStack.peek(); 151 152 query.setBrowsingStep(SubQueryNode.STEP_PREDICATES); 153 List list = arg.getPredicateList(); 154 if (null != list) { 155 for (int i = 0; i < list.size(); i++) { 156 query.setCurrent((Expression) list.get(i)); 157 query.getCurrent().accept(this); 158 } 159 } 160 161 query.setBrowsingStep(SubQueryNode.STEP_OPERAND); 162 arg.getOperand().accept(this); 163 164 } 166 167 public void visit(Join arg) { 168 SubQueryNode query = (SubQueryNode) _subQueryStack.peek(); 170 171 query.setBrowsingStep(SubQueryNode.STEP_PREDICATES); 172 List list = arg.getPredicateList(); 173 if (null != list) { 174 for (int i = 0; i < list.size(); i++) { 175 query.setCurrent((Expression) list.get(i)); 176 query.getCurrent().accept(this); 177 } 178 } 179 180 query.setBrowsingStep(SubQueryNode.STEP_OPERAND); 181 list = arg.getOperandList(); 182 if (null != list) { 183 for (int i = 0; i < list.size(); i++) { 184 query.setCurrent((Expression) list.get(i)); 185 query.getCurrent().accept(this); 186 } 187 } 188 189 } 191 192 public void visit(BinOpCompareAny arg) { 193 if (!(arg.getRightOperand() instanceof LitList)) { 194 SubQueryNode query = new SubQueryNode((Relation) arg.getRightOperand(), SubQueryNode.CONTEXT_ANY); 195 super.visit((BinaryAtomicOp) arg); 196 query.rewrite(); 197 } 198 } 200 201 public void visit(UnOpExists arg) { 202 SubQueryNode query = new SubQueryNode((Relation) arg.getOperand(), SubQueryNode.CONTEXT_EXIST); 203 super.visit((UnaryAtomicOp) arg); 204 query.rewrite(); 205 } 206 207 221 236 237 protected SubQueryNode transformSubquery(SubQueryNode query) { 238 return query; 240 } 241 252 protected RenameRelation createOuterJoin(Expression mainRelation, List newRelationPredicates, InnerRelationInfo subQuery, boolean distinct, boolean mainIsAggregate) { 253 254 256 257 Expression retRelation = null; 258 if (subQuery._keys == null) { retRelation = subQuery._relation; 260 if (newRelationPredicates == null) 261 newRelationPredicates = subQuery._predicates; 262 else 263 newRelationPredicates.addAll(subQuery._predicates); 264 } else if (subQuery._predicates.isEmpty()) { if (mainRelation instanceof Join) { Join join = (Join) mainRelation; 267 if (subQuery._relation instanceof Join) { 268 Join subJoin = (Join) subQuery._relation; 269 join.addOperandList(subJoin.getOperandList()); 270 join.addPredicateList(subJoin.getPredicateList()); 271 } else { 272 join.addOperand(subQuery._relation); 273 } 274 retRelation = mainRelation; 275 } else { 276 if (subQuery._relation instanceof Join) { 277 Join join = (Join) subQuery._relation; 278 join.addOperand(mainRelation); 279 retRelation = join; 280 } else { 281 List operands = new ArrayList(2); 282 operands.add(mainRelation); 283 operands.add(subQuery._relation); 284 retRelation = new Join(operands, Collections.EMPTY_LIST); 285 } 286 } 287 } else { 288 if (subQuery._relation instanceof Join) { 289 boolean canUseAJoin = false; 293 if (subQuery._havingClauses != null) { 294 Expression predicate = (Expression) subQuery._havingClauses.get(0); 295 if (predicate instanceof BinOpCompare) { 296 BinOpCompare comp = (BinOpCompare) predicate; 297 if (comp.getRightOperand() instanceof LitInteger) { 298 canUseAJoin = ((LitInteger) comp.getRightOperand()).getValue().intValue() == 0 && comp.getOperator() == Constants.GT_COMPOP && comp.getLeftOperand() instanceof FunAggregate && ((FunAggregate) comp.getLeftOperand()).getOperator() == FunAggregate.COUNT; 299 } else if (comp.getLeftOperand() instanceof LitInteger) { 300 canUseAJoin = ((LitInteger) comp.getLeftOperand()).getValue().intValue() == 0 && comp.getOperator() == Constants.LT_COMPOP && comp.getRightOperand() instanceof FunAggregate && ((FunAggregate) comp.getRightOperand()).getOperator() == FunAggregate.COUNT; 301 } 302 } 303 } 304 if (canUseAJoin) { 305 Join join = (Join) subQuery._relation; 306 if (mainRelation instanceof Join) { 307 join.addOperandList(((Join) mainRelation).getOperandList()); 308 join.addPredicateList(((Join) mainRelation).getPredicateList()); } else 310 join.addOperand(mainRelation); 311 join.addPredicateList(subQuery._predicates); 312 retRelation = join; 313 } else 314 throw new SqlWrapperException(MessageLibrary.getMessage("T_N_SUP_QUERY", "MySQL: joins in inner query")); 315 320 } else retRelation = new BinOpOuterJoin(mainRelation, subQuery._relation, Constants.LEFT_OUTER_JOIN, subQuery._predicates); 325 } 326 327 331 332 if (newRelationPredicates != null) 333 retRelation = new UnOpRestrict(retRelation, newRelationPredicates); 334 335 336 if (subQuery._keys != null) 337 retRelation = new UnOpGroup(retRelation, subQuery._keys); 338 339 340 if (subQuery._havingClauses != null && !subQuery._havingClauses.isEmpty()) 341 retRelation = new UnOpRestrict(retRelation, subQuery._havingClauses, true); 342 343 344 if (mainIsAggregate) { 345 List proj2Items = null; 349 350 UnOpProject proj1 = null; 351 if (subQuery.hasAggregate()) 352 proj1 = new UnOpAggregate(retRelation, subQuery._items, distinct, _attIDProvider); 353 else 354 proj1 = new UnOpProject(retRelation, subQuery._items, distinct, _attIDProvider); 355 356 RenameRelation rel1 = new RenameRelation(proj1, _relIDProvider); 357 358 proj2Items = processAggregateItems(proj1, rel1); 359 360 361 retRelation = new UnOpAggregate(rel1, proj2Items, false, _attIDProvider); 363 } else { 364 365 retRelation = new UnOpAggregate(retRelation, subQuery._items, distinct, _attIDProvider); 366 } 367 subQuery._alias.setOperand(retRelation); 368 369 return subQuery._alias; 371 } 372 373 private List processAggregateItems(UnOpProject proj1, RenameRelation rel1) { 374 List aggrItemList = proj1.getItemList(); 375 376 if (_removeAggrVisitor == null) 378 _removeAggrVisitor = new RemoveAggregateFunctionVisitor(); 379 else 380 _removeAggrVisitor.reinit(); 381 382 List aggrItems = new ArrayList(); 383 ListIterator lit = aggrItemList.listIterator(); 384 Expression item = null; 385 while (lit.hasNext()) { 386 item = (Expression) lit.next(); 387 _removeAggrVisitor.newItem(); 388 item.accept(_removeAggrVisitor); 389 if (_removeAggrVisitor.aggregateFound()) { 390 lit.remove(); 391 aggrItems.add(item); 392 } 393 } 394 395 List proj2Items = null; 397 try { 398 proj2Items = AlgebraTools.clone(aggrItemList); 399 } catch (CloneNotSupportedException e) { 400 throw new InternalErrorException(MessageLibrary.getMessage("IE_ERR", "Could not clone item list")); 401 } 402 403 405 Iterator it = _removeAggrVisitor.getCollectedItems().iterator(); 406 FunAggregate funAggr = null; 407 AttributeExpression att = null; 408 409 while (it.hasNext()) { 410 item = (Expression) it.next(); 411 funAggr = (FunAggregate) item.getFather(); item = proj1.addItem(item); att = new AttributeExpression(rel1, item.getName()); 414 att.setUnderlyingExpr(item); 415 funAggr.setOperand(att); } 417 418 proj2Items.addAll(aggrItems); 419 420 return proj2Items; 421 } 422 423 429 protected List pickOutPredicates(Expression tree, boolean blockOnProject) { 430 List retVal = new ArrayList(); 432 433 434 List joinPredicateList = new ArrayList(); 435 _restrictionVisitor.reinit(true, blockOnProject); 436 tree.accept(_restrictionVisitor); 437 List restrictionList = _restrictionVisitor.getRestrictions(); 438 439 440 UnOpRestrict restrict = null; 441 Join join = null; 442 Set vti = null; 443 444 Expression expr; 445 for (int i = 0; i < restrictionList.size(); i++) { 446 expr = (Expression) restrictionList.get(i); 447 448 if (expr instanceof UnOpRestrict) { 449 restrict = (UnOpRestrict) expr; 450 retVal.addAll(restrict.getPredicateList()); 451 452 Expression father = restrict.getFather(); 453 if (father != null) 454 father.replaceChild(restrict, restrict.getOperand()); 455 } else if (expr instanceof Join) { 456 join = (Join) expr; 457 if (join.getPredicateList() != null) { 458 retVal.addAll(join.getPredicateList()); 459 join.getPredicateList().clear(); 460 } 461 } else 462 Debug.nyi("pickOutPredicates(Expression tree)"); 463 } 464 465 return retVal; 467 } 468 469 479 480 protected class SubQueryNode { 481 482 public static final byte CONTEXT_PROJECT = 0; 483 public static final byte CONTEXT_AGGR = 1; 484 public static final byte CONTEXT_ANY = 2; 485 public static final byte CONTEXT_EXIST = 3; 486 487 488 public static final byte TYPE_MAIN = 0; 489 public static final byte TYPE_SELECT_AGGR = 1; 490 public static final byte TYPE_PREDICATE_AGGR = 2; 491 public static final byte TYPE_PREDICATE_EXIST = 3; 492 public static final byte TYPE_PREDICATE_ANY = 4; 493 public static final byte TYPE_FROM = 5; 495 496 public static final byte STEP_NONE = 0; 497 public static final byte STEP_ITEMS = 1; 498 public static final byte STEP_PREDICATES = 2; 499 public static final byte STEP_OPERAND = 3; 500 public static final byte STEP_DONE = 4; 501 502 public byte _type = TYPE_MAIN; 503 504 public byte _browsingStep = STEP_NONE; 505 506 507 private Expression _current; 508 509 512 private Expression _anchor = null; 513 514 517 private UnaryOperator _renamedQuery = null; 518 519 522 private Expression _referringNode = null; 523 524 527 private Relation _query = null; 528 529 532 private List _subQueryList = new ArrayList(); 533 534 public SubQueryNode(Expression mainQuery) { 535 _type = TYPE_MAIN; 536 _query = (UnaryAlgebra) mainQuery; 537 _referringNode = mainQuery.getFather(); 538 539 _subQueryStack.push(this); 541 } 542 543 544 public SubQueryNode(Relation root, byte context) { 545 SubQueryNode parentQuery = (SubQueryNode) _subQueryStack.peek(); 546 547 _anchor = parentQuery.getCurrent(); 548 Expression subQuery = (Expression) root; 549 _referringNode = subQuery.getFather(); 550 551 552 switch (context) { 553 case CONTEXT_PROJECT : 554 555 _type = TYPE_FROM; _query = root; 559 if (_referringNode instanceof RenameRelation) { 560 _renamedQuery = (UnaryOperator) subQuery.getFather(); 561 _referringNode = _renamedQuery.getFather(); 562 } 563 564 break; 565 566 case CONTEXT_AGGR : 567 568 switch (parentQuery.getBrowsingStep()) { 569 case STEP_ITEMS : 570 _type = TYPE_SELECT_AGGR; 571 _renamedQuery = (UnaryOperator) _referringNode; 572 _referringNode = _renamedQuery.getFather(); 573 break; 575 case STEP_PREDICATES : 576 _type = TYPE_PREDICATE_AGGR; break; 578 default : 579 _type = TYPE_FROM; } 581 _query = root; 583 584 break; 585 586 case CONTEXT_ANY : 587 588 _type = TYPE_PREDICATE_ANY; 589 if (root instanceof RenameRelation) { 591 _renamedQuery = (UnaryOperator) subQuery; 592 _query = (UnaryAlgebra) ((RenameRelation) root).getOperand(); 593 } else 594 _query = root; 595 596 break; 597 case CONTEXT_EXIST : 598 599 _type = TYPE_PREDICATE_EXIST; 600 if (root instanceof RenameRelation) { 602 _renamedQuery = (UnaryOperator) subQuery; 603 _query = (UnaryAlgebra) ((RenameRelation) root).getOperand(); 604 } else 605 _query = root; 606 break; 607 608 default : 609 break; 610 } 611 612 if (_referringNode == null) { 614 switch (_type) { 615 case TYPE_PREDICATE_ANY : 616 case TYPE_PREDICATE_EXIST : 617 case TYPE_FROM : 618 _referringNode = new RenameRelation((Expression) _query, _relIDProvider); 619 break; 620 621 default : 622 Debug.nyi("Aggregates should have surrounding RenameItem."); 623 } 624 } 625 626 canonize(); 628 629 if (_type != TYPE_FROM) 631 parentQuery.addSubQuery(this); 632 633 _subQueryStack.push(this); 635 } 636 637 640 private void canonize() { 641 642 646 switch (_type) { 647 case TYPE_PREDICATE_ANY : 648 653 UnOpProject subQueryProjection = (UnOpProject) _query; 655 BinOpCompareAny original = (BinOpCompareAny) _referringNode; 656 Expression scalarExpr1 = original.getLeftOperand(); 658 Expression attExpr2 = (Expression) subQueryProjection.getItemList().get(0); 659 if (attExpr2 instanceof RenameItem) 660 attExpr2 = ((RenameItem)attExpr2).getOperand(); 661 BinOpCompare newPredicate = new BinOpCompare(original.getOperator(), scalarExpr1, attExpr2); 662 663 664 Set refAtts = new HashSet(); 665 if (scalarExpr1 instanceof AttributeExpression) 666 refAtts.add(scalarExpr1); 667 if (_attributeGrabber == null) 668 _attributeGrabber = new AttributeGrabber(); 669 else 670 _attributeGrabber.reinit(); 671 attExpr2.accept(_attributeGrabber); 672 refAtts.add(_attributeGrabber.getAttributeExpression()); 673 newPredicate.setReferredAttributes(refAtts); 674 675 676 _keyNodesFinder.reinit(); 677 subQueryProjection.accept(_keyNodesFinder); 678 PredicateHolder restrict = _keyNodesFinder.getRestriction(); 679 if (restrict == null) { Expression relation = _keyNodesFinder.getRelation(); 681 relation.getFather().replaceChild(relation, new UnOpRestrict(relation, newPredicate)); 682 } else { 683 restrict.addPredicate(newPredicate); 684 } 685 686 687 _type = TYPE_PREDICATE_EXIST; 688 _query = (Relation) subQueryProjection.getOperand(); 689 _referringNode = new UnOpExists((Expression) _query); break; 691 692 case TYPE_PREDICATE_EXIST : 693 694 if (_anchor instanceof UnOpNot) { 696 UnOpExists exists = (UnOpExists) _referringNode; 697 exists.setNot(exists.getNot() ? false : true); 700 } 701 702 703 if (_query instanceof UnOpProject) { 704 UnOpProject project = (UnOpProject) _query; 705 _query = (Relation) project.getOperand(); 706 if (_referringNode != null) 707 _referringNode.replaceChild(project, (Expression) _query); 708 else if (_renamedQuery != null) 709 _renamedQuery.setOperand((Expression) _query); 710 } 711 break; 712 713 default : 714 } 715 } 716 717 public SubQueryNode rewrite() { 718 719 if (_subQueryList.isEmpty()) { 722 _keyNodesFinder.reinit(); 723 ((Expression) _query).accept(_keyNodesFinder); 724 725 if (_keyNodesFinder.getRestriction() instanceof UnOpRestrict) { 726 UnOpRestrict restrict = (UnOpRestrict) _keyNodesFinder.getRestriction(); 727 if (_keyNodesFinder.getRelation() instanceof DummyTable) { 728 729 UnOpProject innerProject = null; 730 try { 731 innerProject = (UnOpProject) _keyNodesFinder.getProjection().clone(); 732 } catch (CloneNotSupportedException ex) { 733 throw new SqlWrapperException(ex.getMessage(), ex); 734 } 735 736 737 RenameRelation rename = new RenameRelation(innerProject, _relIDProvider); 738 739 740 DummyTable dummy = (DummyTable) _keyNodesFinder.getRelation(); 741 dummy.getFather().replaceChild(dummy, rename); 742 innerProject.setOperand(dummy); 743 } 744 } 745 } else { ListIterator it = _subQueryList.listIterator(); 747 SubQueryNode subQuery = null, lastSubQuery = null; 748 InnerRelationInfo relation = null; 749 List subRelations = new ArrayList(); 750 List aggrPairs = null; 751 List aggrItems = null; 753 754 while (it.hasNext()) { 756 subQuery = (SubQueryNode) it.next(); 757 758 switch (subQuery.getType()) { 759 case TYPE_SELECT_AGGR : 760 ((UnOpProject) subQuery._anchor.getFather()).removeItem(subQuery._anchor); 761 if (aggrItems == null) 762 aggrItems = new ArrayList(); 763 aggrItems.add(subQuery._anchor); 764 break; 765 766 case TYPE_PREDICATE_AGGR : 767 if (lastSubQuery != null && subQuery._anchor == lastSubQuery._anchor) { 768 if (aggrPairs == null) 769 aggrPairs = new ArrayList(); 770 it.remove(); 771 it.previous(); 772 it.remove(); 773 aggrPairs.add(new AggregatePair(lastSubQuery, subQuery)); 774 } 775 776 ((PredicateHolder) subQuery._anchor.getFather()).removePredicate(subQuery._anchor); 777 break; 778 779 case TYPE_PREDICATE_ANY : case TYPE_PREDICATE_EXIST : 781 if (subQuery._anchor instanceof BinOpBoolean && ((BinOpBoolean) subQuery._anchor).getOperator() == Constants.OR) 782 throw new SqlWrapperException(MessageLibrary.getMessage("T_N_SUP_QUERY", "MySQL: multiple existential predicates in OR predicate")); 783 784 785 &n
|