1 56 package org.objectstyle.cayenne.access.trans; 57 58 import java.sql.Types ; 59 import java.util.ArrayList ; 60 import java.util.Collection ; 61 import java.util.Collections ; 62 import java.util.HashMap ; 63 import java.util.HashSet ; 64 import java.util.Iterator ; 65 import java.util.List ; 66 import java.util.Map ; 67 import java.util.Set ; 68 69 import org.objectstyle.cayenne.CayenneRuntimeException; 70 import org.objectstyle.cayenne.access.jdbc.ColumnDescriptor; 71 import org.objectstyle.cayenne.exp.Expression; 72 import org.objectstyle.cayenne.map.DbAttribute; 73 import org.objectstyle.cayenne.map.DbEntity; 74 import org.objectstyle.cayenne.map.DbJoin; 75 import org.objectstyle.cayenne.map.DbRelationship; 76 import org.objectstyle.cayenne.map.DerivedDbEntity; 77 import org.objectstyle.cayenne.map.EntityInheritanceTree; 78 import org.objectstyle.cayenne.map.ObjAttribute; 79 import org.objectstyle.cayenne.map.ObjEntity; 80 import org.objectstyle.cayenne.map.ObjRelationship; 81 import org.objectstyle.cayenne.query.PrefetchSelectQuery; 82 import org.objectstyle.cayenne.query.SelectQuery; 83 84 91 public class SelectTranslator extends QueryAssembler { 92 93 protected static final int[] UNSUPPORTED_DISTINCT_TYPES = new int[] { 94 Types.BLOB, Types.CLOB, Types.LONGVARBINARY, Types.LONGVARCHAR 95 }; 96 97 protected static boolean isUnsupportedForDistinct(int type) { 98 for (int i = 0; i < UNSUPPORTED_DISTINCT_TYPES.length; i++) { 99 if (UNSUPPORTED_DISTINCT_TYPES[i] == type) { 100 return true; 101 } 102 } 103 104 return false; 105 } 106 107 final Map aliasLookup = new HashMap (); 108 109 final List tableList = new ArrayList (); 110 final List aliasList = new ArrayList (); 111 final List dbRelList = new ArrayList (); 112 113 List resultColumns; 114 List groupByList; 115 116 int aliasCounter; 117 118 boolean suppressingDistinct; 119 120 125 boolean forcingDistinct; 126 127 134 protected List getColumns() { 135 return resultColumns; 136 } 137 138 142 public String createSqlString() throws Exception { 143 forcingDistinct = false; 144 145 this.resultColumns = buildResultColumns(); 147 148 QualifierTranslator tr = adapter.getQualifierTranslator(this); 149 150 String parentQualifierStr = null; 155 if (getSelectQuery().isQualifiedOnParent()) { 156 tr.setTranslateParentQual(true); 157 parentQualifierStr = tr.doTranslation(); 158 } 159 160 tr.setTranslateParentQual(false); 162 String qualifierStr = tr.doTranslation(); 163 164 this.groupByList = buildGroupByList(); 166 167 OrderingTranslator orderingTranslator = new OrderingTranslator(this); 169 String orderByStr = orderingTranslator.doTranslation(); 170 171 StringBuffer queryBuf = new StringBuffer (); 173 queryBuf.append("SELECT "); 174 175 if (forcingDistinct || getSelectQuery().isDistinct()) { 178 179 suppressingDistinct = false; 180 Iterator it = resultColumns.iterator(); 181 while (it.hasNext()) { 182 ColumnDescriptor column = (ColumnDescriptor) it.next(); 183 if (isUnsupportedForDistinct(column.getJdbcType())) { 184 suppressingDistinct = true; 185 break; 186 } 187 } 188 189 if (!suppressingDistinct) { 190 queryBuf.append("DISTINCT "); 191 } 192 } 193 194 List selectColumnExpList = new ArrayList (); 196 197 Iterator it = resultColumns.iterator(); 198 while (it.hasNext()) { 199 ColumnDescriptor column = (ColumnDescriptor) it.next(); 200 selectColumnExpList.add(column.getQualifiedColumnName()); 201 } 202 203 if (forcingDistinct || getSelectQuery().isDistinct()) { 206 List orderByColumnList = orderingTranslator.getOrderByColumnList(); 207 for (int i = 0; i < orderByColumnList.size(); i++) { 208 Object orderByColumnExp = orderByColumnList.get(i); 210 if (!selectColumnExpList.contains(orderByColumnExp)) { 211 selectColumnExpList.add(orderByColumnExp); 212 } 213 } 214 } 215 216 int columnCount = selectColumnExpList.size(); 218 queryBuf.append((String ) selectColumnExpList.get(0)); 219 for (int i = 1; i < columnCount; i++) { 221 queryBuf.append(", "); 222 queryBuf.append((String ) selectColumnExpList.get(i)); 223 } 224 225 queryBuf.append(" FROM "); 227 228 int tableCount = tableList.size(); 230 appendTable(queryBuf, 0); for (int i = 1; i < tableCount; i++) { 232 queryBuf.append(", "); 233 appendTable(queryBuf, i); 234 } 235 236 boolean hasWhere = false; 238 int dbRelCount = dbRelList.size(); 239 if (dbRelCount > 0) { 240 hasWhere = true; 241 queryBuf.append(" WHERE "); 242 243 appendJoins(queryBuf, 0); 244 for (int i = 1; i < dbRelCount; i++) { 245 queryBuf.append(" AND "); 246 appendJoins(queryBuf, i); 247 } 248 } 249 250 if (parentQualifierStr != null) { 252 if (hasWhere) { 253 queryBuf.append(" AND ("); 254 queryBuf.append(parentQualifierStr); 255 queryBuf.append(")"); 256 } 257 else { 258 hasWhere = true; 259 queryBuf.append(" WHERE "); 260 queryBuf.append(parentQualifierStr); 261 } 262 } 263 264 boolean hasGroupBy = false; 266 if (groupByList != null) { 267 int groupByCount = groupByList.size(); 268 if (groupByCount > 0) { 269 hasGroupBy = true; 270 queryBuf.append(" GROUP BY "); 271 appendGroupBy(queryBuf, 0); 272 for (int i = 1; i < groupByCount; i++) { 273 queryBuf.append(", "); 274 appendGroupBy(queryBuf, i); 275 } 276 } 277 } 278 279 if (qualifierStr != null) { 281 if (hasGroupBy) { 282 queryBuf.append(" HAVING "); 283 queryBuf.append(qualifierStr); 284 } 285 else { 286 if (hasWhere) { 287 queryBuf.append(" AND ("); 288 queryBuf.append(qualifierStr); 289 queryBuf.append(")"); 290 } 291 else { 292 hasWhere = true; 293 queryBuf.append(" WHERE "); 294 queryBuf.append(qualifierStr); 295 } 296 } 297 } 298 299 if (orderByStr != null) { 301 queryBuf.append(" ORDER BY ").append(orderByStr); 302 } 303 304 return queryBuf.toString(); 305 } 306 307 312 public ColumnDescriptor[] getResultColumns() { 313 if (resultColumns == null || resultColumns.isEmpty()) { 314 return new ColumnDescriptor[0]; 315 } 316 317 return (ColumnDescriptor[]) resultColumns 318 .toArray(new ColumnDescriptor[resultColumns.size()]); 319 } 320 321 328 public boolean isSuppressingDistinct() { 329 return suppressingDistinct; 330 } 331 332 private SelectQuery getSelectQuery() { 333 return (SelectQuery) getQuery(); 334 } 335 336 339 private List buildGroupByList() { 340 DbEntity dbEntity = getRootDbEntity(); 341 return (dbEntity instanceof DerivedDbEntity) ? ((DerivedDbEntity) dbEntity) 342 .getGroupByAttributes() : Collections.EMPTY_LIST; 343 } 344 345 List buildResultColumns() { 346 347 newAliasForTable(getRootDbEntity()); 349 350 List columns = new ArrayList (); 351 SelectQuery query = getSelectQuery(); 352 353 if (query.isFetchingCustomAttributes()) { 355 appendCustomColumns(columns, query); 356 } 357 else { 358 appendQueryColumns(columns, query); 359 } 360 361 return columns; 362 } 363 364 367 List appendQueryColumns(List columns, SelectQuery query) { 369 370 Set attributes = new HashSet (); 371 372 380 ObjEntity oe = getRootEntity(); 381 382 EntityInheritanceTree tree = null; 384 385 if (query.isResolvingInherited()) { 386 tree = getRootInheritanceTree(); 387 } 388 389 Iterator attrs = (tree != null) ? tree.allAttributes().iterator() : oe 391 .getAttributes() 392 .iterator(); 393 while (attrs.hasNext()) { 394 ObjAttribute oa = (ObjAttribute) attrs.next(); 395 Iterator dbPathIterator = oa.getDbPathIterator(); 396 while (dbPathIterator.hasNext()) { 397 Object pathPart = dbPathIterator.next(); 398 if (pathPart instanceof DbRelationship) { 399 DbRelationship rel = (DbRelationship) pathPart; 400 dbRelationshipAdded(rel); 401 } 402 else if (pathPart instanceof DbAttribute) { 403 DbAttribute dbAttr = (DbAttribute) pathPart; 404 if (dbAttr == null) { 405 throw new CayenneRuntimeException( 406 "ObjAttribute has no DbAttribute: " + oa.getName()); 407 } 408 409 appendColumn(columns, oa, dbAttr, attributes, null); 410 } 411 } 412 } 413 414 Iterator rels = (tree != null) ? tree.allRelationships().iterator() : oe 416 .getRelationships() 417 .iterator(); 418 while (rels.hasNext()) { 419 ObjRelationship rel = (ObjRelationship) rels.next(); 420 DbRelationship dbRel = (DbRelationship) rel.getDbRelationships().get(0); 421 422 List joins = dbRel.getJoins(); 423 int len = joins.size(); 424 for (int i = 0; i < len; i++) { 425 DbJoin join = (DbJoin) joins.get(i); 426 DbAttribute src = join.getSource(); 427 appendColumn(columns, null, src, attributes, null); 428 } 429 } 430 431 433 DbEntity table = getRootDbEntity(); 434 Iterator pk = table.getPrimaryKey().iterator(); 435 while (pk.hasNext()) { 436 DbAttribute dba = (DbAttribute) pk.next(); 437 appendColumn(columns, null, dba, attributes, null); 438 } 439 440 if (query instanceof PrefetchSelectQuery) { 442 PrefetchSelectQuery pq = (PrefetchSelectQuery) query; 443 ObjRelationship r = pq.getLastPrefetchHint(); 444 if ((r != null) && (r.getReverseRelationship() == null)) { 445 446 450 DbRelationship dbRel = (DbRelationship) r.getDbRelationships().get(0); 454 455 List joins = dbRel.getJoins(); 456 for (int j = 0; j < joins.size(); j++) { 457 DbJoin join = (DbJoin) joins.get(j); 458 DbAttribute target = join.getTarget(); 459 appendColumn(columns, null, target, attributes, null); 460 } 461 } 462 } 463 464 if (!query.getJointPrefetches().isEmpty()) { 466 Iterator jointPrefetches = query.getJointPrefetches().iterator(); 467 while (jointPrefetches.hasNext()) { 468 String prefetch = (String ) jointPrefetches.next(); 469 470 Expression prefetchExp = Expression.fromString(prefetch); 472 Expression dbPrefetch = oe.translateToDbPath(prefetchExp); 473 474 Iterator it = table.resolvePathComponents(dbPrefetch); 476 477 DbRelationship r = null; 478 while (it.hasNext()) { 479 r = (DbRelationship) it.next(); 480 dbRelationshipAdded(r); 481 } 482 483 if (r == null) { 484 throw new CayenneRuntimeException("Invalid joint prefetch '" 485 + prefetch 486 + "' for entity: " 487 + oe.getName()); 488 } 489 490 493 Collection skipColumns = Collections.EMPTY_LIST; 494 if (r.getSourceEntity() == table) { 495 skipColumns = new ArrayList (2); 496 Iterator joins = r.getJoins().iterator(); 497 while (joins.hasNext()) { 498 DbJoin join = (DbJoin) joins.next(); 499 if (attributes.contains(join.getSource())) { 500 skipColumns.add(join.getTarget()); 501 } 502 } 503 } 504 505 ObjRelationship targetRel = (ObjRelationship) prefetchExp.evaluate(oe); 507 Iterator targetObjAttrs = targetRel 508 .getTargetEntity() 509 .getAttributes() 510 .iterator(); 511 while (targetObjAttrs.hasNext()) { 512 ObjAttribute oa = (ObjAttribute) targetObjAttrs.next(); 513 Iterator dbPathIterator = oa.getDbPathIterator(); 514 while (dbPathIterator.hasNext()) { 515 Object pathPart = dbPathIterator.next(); 516 if (pathPart instanceof DbRelationship) { 517 DbRelationship rel = (DbRelationship) pathPart; 518 dbRelationshipAdded(rel); 519 } 520 else if (pathPart instanceof DbAttribute) { 521 DbAttribute attribute = (DbAttribute) pathPart; 522 if (attribute == null) { 523 throw new CayenneRuntimeException( 524 "ObjAttribute has no DbAttribute: " 525 + oa.getName()); 526 } 527 528 if (!skipColumns.contains(attribute)) { 529 appendColumn(columns, 530 oa, 531 attribute, 532 attributes, 533 dbPrefetch); 534 } 535 } 536 } 537 } 538 539 Iterator targetAttributes = r 541 .getTargetEntity() 542 .getAttributes() 543 .iterator(); 544 while (targetAttributes.hasNext()) { 545 DbAttribute attribute = (DbAttribute) targetAttributes.next(); 546 if (!skipColumns.contains(attribute)) { 547 appendColumn(columns, null, attribute, attributes, dbPrefetch); 548 } 549 } 550 } 551 } 552 553 return columns; 554 } 555 556 559 List appendCustomColumns(List columns, SelectQuery query) { 560 561 List customAttributes = query.getCustomDbAttributes(); 562 DbEntity table = getRootDbEntity(); 563 int len = customAttributes.size(); 564 565 for (int i = 0; i < len; i++) { 566 DbAttribute attribute = (DbAttribute) table 567 .getAttribute((String ) customAttributes.get(i)); 568 if (attribute == null) { 569 throw new CayenneRuntimeException("Attribute does not exist: " 570 + customAttributes.get(i)); 571 } 572 573 String alias = aliasForTable((DbEntity) attribute.getEntity()); 574 columns.add(new ColumnDescriptor(attribute, alias)); 575 } 576 577 return columns; 578 } 579 580 private void appendColumn( 581 List columns, 582 ObjAttribute objAttribute, 583 DbAttribute attribute, 584 Set skipSet, 585 Expression dbPath) { 586 587 if (skipSet.add(attribute)) { 588 String alias = aliasForTable((DbEntity) attribute.getEntity()); 589 ColumnDescriptor column = (objAttribute != null) ? new ColumnDescriptor( 590 objAttribute, 591 attribute, 592 alias) : new ColumnDescriptor(attribute, alias); 593 594 if (dbPath != null) { 596 String path = dbPath.toString().substring("db:".length()); 597 column.setLabel(path + '.' + attribute.getName()); 598 } 599 600 columns.add(column); 601 } 602 } 603 604 private void appendGroupBy(StringBuffer queryBuf, int index) { 605 DbAttribute column = (DbAttribute) groupByList.get(index); 606 String alias = aliasForTable((DbEntity) column.getEntity()); 607 queryBuf.append(column.getAliasedName(alias)); 608 } 609 610 private void appendTable(StringBuffer queryBuf, int index) { 611 DbEntity ent = (DbEntity) tableList.get(index); 612 queryBuf.append(ent.getFullyQualifiedName()); 613 queryBuf.append(' ').append((String ) aliasList.get(index)); 616 } 617 618 private void appendJoins(StringBuffer queryBuf, int index) { 619 DbRelationship rel = (DbRelationship) dbRelList.get(index); 620 String srcAlias = aliasForTable((DbEntity) rel.getSourceEntity()); 621 String targetAlias = (String ) aliasLookup.get(rel); 622 623 boolean andFlag = false; 624 625 List joins = rel.getJoins(); 626 int len = joins.size(); 627 for (int i = 0; i < len; i++) { 628 DbJoin join = (DbJoin) joins.get(i); 629 630 if (andFlag) { 631 queryBuf.append(" AND "); 632 } 633 else { 634 andFlag = true; 635 } 636 637 queryBuf 638 .append(srcAlias) 639 .append('.') 640 .append(join.getSourceName()) 641 .append(" = ") 642 .append(targetAlias) 643 .append('.') 644 .append(join.getTargetName()); 645 } 646 } 647 648 652 public void dbRelationshipAdded(DbRelationship rel) { 653 if (rel.isToMany()) { 654 forcingDistinct = true; 655 } 656 657 String existAlias = (String ) aliasLookup.get(rel); 658 659 if (existAlias == null) { 660 dbRelList.add(rel); 661 662 String newAlias = newAliasForTable((DbEntity) rel.getTargetEntity()); 664 aliasLookup.put(rel, newAlias); 665 } 666 } 667 668 671 protected String newAliasForTable(DbEntity ent) { 672 if (ent instanceof DerivedDbEntity) { 673 ent = ((DerivedDbEntity) ent).getParentEntity(); 674 } 675 676 String newAlias = "t" + aliasCounter++; 677 tableList.add(ent); 678 aliasList.add(newAlias); 679 return newAlias; 680 } 681 682 public String aliasForTable(DbEntity ent, DbRelationship rel) { 683 return (String ) aliasLookup.get(rel); 684 } 685 686 691 public String aliasForTable(DbEntity ent) { 692 if (ent instanceof DerivedDbEntity) { 693 ent = ((DerivedDbEntity) ent).getParentEntity(); 694 } 695 696 int entIndex = tableList.indexOf(ent); 697 if (entIndex >= 0) { 698 return (String ) aliasList.get(entIndex); 699 } 700 else { 701 StringBuffer msg = new StringBuffer (); 702 msg.append("Alias not found, DbEntity: '").append(ent != null 703 ? ent.getName() 704 : "<null entity>").append("'\nExisting aliases:"); 705 706 int len = aliasList.size(); 707 for (int i = 0; i < len; i++) { 708 String dbeName = (tableList.get(i) != null) ? ((DbEntity) tableList 709 .get(i)).getName() : "<null entity>"; 710 msg.append("\n").append(aliasList.get(i)).append(" => ").append(dbeName); 711 } 712 713 throw new CayenneRuntimeException(msg.toString()); 714 } 715 } 716 717 720 public boolean supportsTableAliases() { 721 return true; 722 } 723 } | Popular Tags |