I applied my changes to 1.70 and am sending you the entire java file. I created a patch file with eclipse, but it did not seem to want to do an intelligent diff (just -entire old file contents and +entire new file contents). There was no improvement if I turned off ignore whitespace in the compare preferences. Maybe there's some eclipse trick I need to know here.
Anyway, hopefully now you have all my work related to this change.
Looking forward to your feedback,
Phil
package org.apache.ojb.broker.accesslayer.sql;
/* Copyright 2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.ojb.broker.PersistenceBrokerSQLException; import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes; import org.apache.ojb.broker.metadata.ClassDescriptor; import org.apache.ojb.broker.metadata.CollectionDescriptor; import org.apache.ojb.broker.metadata.DescriptorRepository; import org.apache.ojb.broker.metadata.FieldDescriptor; import org.apache.ojb.broker.metadata.FieldHelper; import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor; import org.apache.ojb.broker.metadata.fieldaccess.AnonymousPersistentFieldForInheritance; import org.apache.ojb.broker.platforms.Platform; import org.apache.ojb.broker.query.*; import org.apache.ojb.broker.util.SqlHelper; import org.apache.ojb.broker.util.SqlHelper.PathInfo; import org.apache.ojb.broker.util.logging.Logger; import org.apache.ojb.broker.util.logging.LoggerFactory; /** * Model a Statement based on Query. * * @author <a href="mailto:[EMAIL PROTECTED]">Jakob Braeuchi</a> * @version $Id: SqlQueryStatement.java,v 1.68 2004/03/11 18:16:08 brianm Exp $ */ public abstract class SqlQueryStatement implements SqlStatement, JoinSyntaxTypes { private SqlQueryStatement m_parentStatement; /** the logger */ private Logger m_logger; /** the target table of the query */ private TableAlias m_root; /** the search table of the query */ private TableAlias m_search; /** the query */ private QueryByCriteria m_query; /** the mapping of paths to TableAliases */ private HashMap m_pathToAlias = new HashMap(); /** maps trees of joins to criteria */ private HashMap m_joinTreeToCriteria = new HashMap(); private Platform m_platform; private ClassDescriptor m_baseCld; private ClassDescriptor m_searchCld; private int m_aliasCount = 0; /** * Constructor for SqlCriteriaStatement. * * @param pf the Platform * @param cld the ClassDescriptor * @param query the Query * @param logger the Logger */ public SqlQueryStatement(Platform pf, ClassDescriptor cld, Query query, Logger logger) { this(null, pf, cld, query, logger); } /** * Constructor for SqlCriteriaStatement. * * @param parent the Parent Query * @param pf the Platform * @param cld the ClassDescriptor * @param query the Query * @param logger the Logger */ public SqlQueryStatement(SqlQueryStatement parent, Platform pf, ClassDescriptor cld, Query query, Logger logger) { m_logger = logger != null ? logger : LoggerFactory.getLogger(SqlQueryStatement.class); m_parentStatement = parent; m_query = (QueryByCriteria) query; m_platform = pf; m_searchCld = cld; if ((m_query == null) || (m_query.getBaseClass() == m_query.getSearchClass())) { m_baseCld = m_searchCld; } else { m_baseCld = cld.getRepository().getDescriptorFor(query.getBaseClass()); } m_root = createTableAlias(m_baseCld, null, ""); if (m_searchCld == m_baseCld) { m_search = m_root; } else { // PAW //m_search = getTableAlias(m_query.getObjectProjectionAttribute(), false, null, null); m_search = getTableAlias(m_query.getObjectProjectionAttribute(), false, null, null, m_query.getPathClasses()); } // Walk the super reference-descriptor buildSuperJoinTree(m_root, m_baseCld, ""); // In some cases it is necessary to split the query criteria // and then to generate UNION of several SELECTs // We build the joinTreeToCriteria mapping, if (query != null) { splitCriteria(); } } protected ClassDescriptor getBaseClassDescriptor() { return m_baseCld; } protected ClassDescriptor getSearchClassDescriptor() { return m_searchCld; } /** * Return the TableAlias and the PathInfo for an Attribute name<br> * field names in functions (ie: sum(name) ) are tried to resolve ie: name * from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br> * also resolve pathExpression adress.city or owner.konti.saldo * @param attr * @param useOuterJoins * @param aUserAlias * @return ColumnInfo */ // PAW //protected AttributeInfo getAttributeInfo(String attr, boolean useOuterJoins, String aUserAlias) protected AttributeInfo getAttributeInfo(String attr, boolean useOuterJoins, String aUserAlias, Map pathClasses) //protected AttributeInfo getAttributeInfo(SelectionCriteria crit, boolean useOuterJoins) { AttributeInfo result = new AttributeInfo(); TableAlias tableAlias = null; SqlHelper.PathInfo pathInfo = SqlHelper.splitPath(attr); String colName = pathInfo.column; int sp; // BRJ: // check if we refer to an attribute in the parent query // this prefix is temporary ! if (colName.startsWith(Criteria.PARENT_QUERY_PREFIX) && m_parentStatement != null) { String[] fieldNameRef = {colName.substring(Criteria.PARENT_QUERY_PREFIX.length())}; // PAW: is pathClasses the correct argument to pass to parent? //return m_parentStatement.getAttributeInfo(fieldNameRef[0], useOuterJoins, aUserAlias); return m_parentStatement.getAttributeInfo(fieldNameRef[0], useOuterJoins, aUserAlias, pathClasses); } sp = colName.lastIndexOf("."); if (sp == -1) { tableAlias = getRoot(); } else { String pathName = colName.substring(0, sp); String[] fieldNameRef = {colName.substring(sp + 1)}; // PAW //tableAlias = getTableAlias(pathName, useOuterJoins, aUserAlias, fieldNameRef); tableAlias = getTableAlias(pathName, useOuterJoins, aUserAlias, fieldNameRef, pathClasses); /** * if we have not found an alias by the pathName or * aliasName (if given), try again because pathName * may be an aliasname. it can be only and only if it is not * a path, which means there may be no path separators (,) * in the pathName. */ if ((tableAlias == null) && (colName.lastIndexOf(".") == -1)) { /** * pathName might be an alias, so check this first */ // PAW //tableAlias = getTableAlias(null, useOuterJoins, pathName, null); tableAlias = getTableAlias(null, useOuterJoins, pathName, null, pathClasses); } if (tableAlias != null) { // correct column name to match the alias // productGroup.groupName -> groupName pathInfo.column = fieldNameRef[0]; } } result.tableAlias = tableAlias; result.pathInfo = pathInfo; return result; } /** * Answer the column name for alias and path info<br> * if translate try to convert attribute name into column name otherwise use attribute name<br> * if a FieldDescriptor is found for the attribute name the column name is taken from * there prefixed with the alias (firstname -> A0.F_NAME). * * @param aTableAlias * @param aPathInfo * @param translate * @return */ protected String getColName(TableAlias aTableAlias, PathInfo aPathInfo, boolean translate) { FieldDescriptor fld = null; String result = null; if (translate) { // translate attribute name into column name fld = getFieldDescriptor(aTableAlias, aPathInfo); if (fld != null) { // added to suport the super reference descriptor if (!fld.getClassDescriptor().getFullTableName().equals(aTableAlias.table) && aTableAlias.hasJoins()) { Iterator itr = aTableAlias.joins.iterator(); while (itr.hasNext()) { Join join = (Join) itr.next(); if (join.right.table.equals(fld.getClassDescriptor().getFullTableName())) { result = join.right.alias + "." + fld.getColumnName(); break; } } if (result == null) { result = aPathInfo.column; } } else { result = aTableAlias.alias + "." + fld.getColumnName(); } } else if ("*".equals(aPathInfo.column)) { result = aPathInfo.column; } else { // throw new IllegalArgumentException("No Field found for : " + aPathInfo.column); result = aPathInfo.column; } } else { // use attribute name result = aPathInfo.column; } return result; } /** * Add the Column to the StringBuffer <br> * * @param aTableAlias * @param aPathInfo * @param translate, flag to indicate translation of pathInfo * @param buf * @return true if appended */ protected boolean appendColName(TableAlias aTableAlias, PathInfo aPathInfo, boolean translate, StringBuffer buf) { String prefix = aPathInfo.prefix; String suffix = aPathInfo.suffix; String colName = getColName(aTableAlias, aPathInfo, translate); if (prefix != null) // rebuild function contains ( { buf.append(prefix); } buf.append(colName); if (suffix != null) // rebuild function { buf.append(suffix); } return true; } /** * Get the FieldDescriptor for the PathInfo * * @param aTableAlias * @param aPathInfo * @return FieldDescriptor */ protected FieldDescriptor getFieldDescriptor(TableAlias aTableAlias, PathInfo aPathInfo) { FieldDescriptor fld = null; String colName = aPathInfo.column; if (aTableAlias != null) { fld = aTableAlias.cld.getFieldDescriptorByName(colName); if (fld == null) { ObjectReferenceDescriptor ord = aTableAlias.cld.getObjectReferenceDescriptorByName(colName); if (ord != null) { fld = getFldFromReference(aTableAlias, ord); } else { fld = getFldFromJoin(aTableAlias, colName); } } } return fld; } /** * get FieldDescriptor from joined superclass * @param aTableAlias * @param aColName * @return */ private FieldDescriptor getFldFromJoin(TableAlias aTableAlias, String aColName) { FieldDescriptor fld = null; // Search Join Structure for attribute if (aTableAlias.joins != null) { Iterator itr = aTableAlias.joins.iterator(); while (itr.hasNext()) { Join join = (Join) itr.next(); ClassDescriptor cld = join.right.cld; if (cld != null) { fld = cld.getFieldDescriptorByName(aColName); if (fld != null) { break; } } } } return fld; } /** * Get FieldDescriptor from Reference * @param aTableAlias * @param anOrd * @return */ private FieldDescriptor getFldFromReference(TableAlias aTableAlias, ObjectReferenceDescriptor anOrd) { FieldDescriptor fld = null; if (aTableAlias == getRoot()) { // no path expression FieldDescriptor[] fk = anOrd.getForeignKeyFieldDescriptors(aTableAlias.cld); if (fk.length > 0) { fld = fk[0]; } } else { // attribute with path expression /** * MBAIRD * potentially people are referring to objects, not to the object's primary key, * and then we need to take the primary key attribute of the referenced object * to help them out. */ ClassDescriptor cld = aTableAlias.cld.getRepository().getDescriptorFor(anOrd.getItemClass()); if (cld != null) { fld = aTableAlias.cld.getFieldDescriptorByName(cld.getPkFields()[0].getPersistentField().getName()); } } return fld; } /** * Append the appropriate ColumnName to the buffer<br> * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from * there otherwise its taken from Criteria. <br> * field names in functions (ie: sum(name) ) are tried to resolve * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br> * also resolve pathExpression adress.city or owner.konti.saldo * * @param attr * @param useOuterJoins * @param aUserAlias * @param buf * @return */ protected boolean appendColName(String attr, boolean useOuterJoins, String aUserAlias, StringBuffer buf) { // // PAW: is this correct for pathClasses ? //AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias); AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias, getQuery().getPathClasses()); TableAlias tableAlias = attrInfo.tableAlias; return appendColName(tableAlias, attrInfo.pathInfo, true, buf); } /** * Append the appropriate ColumnName to the buffer<br> * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from * there otherwise its taken from Criteria. <br> * field names in functions (ie: sum(name) ) are tried to resolve * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br> * also resolve pathExpression adress.city or owner.konti.saldo * * @param attr * @param attrAlias column alias * @param useOuterJoins * @param aUserAlias * @param buf * @return */ protected boolean appendColName(String attr, String attrAlias, boolean useOuterJoins, String aUserAlias, StringBuffer buf) { // PAW //AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias); AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias, getQuery().getPathClasses()); TableAlias tableAlias = attrInfo.tableAlias; PathInfo pi = attrInfo.pathInfo; if (pi.suffix != null) { pi.suffix = pi.suffix + " as " + attrAlias; } else { pi.suffix = " as " + attrAlias; } return appendColName(tableAlias, pi, true, buf); } /** * appends a WHERE-clause to the Statement * @param where * @param crit * @param stmt */ protected void appendWhereClause(StringBuffer where, Criteria crit, StringBuffer stmt) { if (where.length() == 0) { where = null; } if (where != null || (crit != null && !crit.isEmpty())) { stmt.append(" WHERE "); appendClause(where, crit, stmt); } } /** * appends a HAVING-clause to the Statement * @param having * @param crit * @param stmt */ protected void appendHavingClause(StringBuffer having, Criteria crit, StringBuffer stmt) { if (having.length() == 0) { having = null; } if (having != null || crit != null) { stmt.append(" HAVING "); appendClause(having, crit, stmt); } } /** * appends a WHERE/HAVING-clause to the Statement * @param clause * @param crit * @param stmt */ protected void appendClause(StringBuffer clause, Criteria crit, StringBuffer stmt) { /** * MBAIRD * when generating the "WHERE/HAVING" clause we need to append the criteria for multi-mapped * tables. We only need to do this for the root classdescriptor and not for joined tables * because we assume you cannot make a relation of the wrong type upon insertion. Of course, * you COULD mess the data up manually and this would cause a problem. */ if (clause != null) { stmt.append(clause.toString()); } if (crit != null) { if (clause == null) { stmt.append(asSQLStatement(crit)); } else { stmt.append(" AND ("); stmt.append(asSQLStatement(crit)); stmt.append(")"); } } } /** * create SQL-String based on Criteria * @param crit * @return */ private String asSQLStatement(Criteria crit) { Enumeration e = crit.getElements(); StringBuffer statement = new StringBuffer(); while (e.hasMoreElements()) { Object o = e.nextElement(); if (o instanceof Criteria) { String addAtStart; String addAtEnd; Criteria pc = (Criteria) o; // need to add parenthesises? if (pc.isEmbraced()) { addAtStart = " ("; addAtEnd = ")"; } else { addAtStart = ""; addAtEnd = ""; } switch (pc.getType()) { case (Criteria.OR) : { if (statement.length() > 0) { statement.append(" OR "); } statement.append(addAtStart); statement.append(asSQLStatement(pc)); statement.append(addAtEnd); break; } case (Criteria.AND) : { if (statement.length() > 0) { statement.insert(0, "( "); statement.append(") AND "); } statement.append(addAtStart); statement.append(asSQLStatement(pc)); statement.append(addAtEnd); break; } } } else { SelectionCriteria c = (SelectionCriteria) o; if (statement.length() > 0) { statement.insert(0, "("); statement.append(") AND "); } appendSQLClause(c, statement); } } // while // BRJ : negative Criteria surrounded by NOT (...) if (crit.isNegative()) { statement.insert(0, " NOT ("); statement.append(")"); } return (statement.length() == 0 ? null : statement.toString()); } /** * Answer the SQL-Clause for a BetweenCriteria * * @param alias * @param pathInfo * @param c BetweenCriteria * @param buf */ private void appendBetweenCriteria(TableAlias alias, PathInfo pathInfo, BetweenCriteria c, StringBuffer buf) { appendColName(alias, pathInfo, c.isTranslateAttribute(), buf); buf.append(c.getClause()); appendParameter(c.getValue(), buf); buf.append(" AND "); appendParameter(c.getValue2(), buf); } /** * Answer the SQL-Clause for an ExistsCriteria * @param c ExistsCriteria */ private void appendExistsCriteria(ExistsCriteria c, StringBuffer buf) { Query subQuery = (Query) c.getValue(); buf.append(c.getClause()); appendSubQuery(subQuery, buf); } /** * Answer the SQL-Clause for a FieldCriteria<br> * The value of the FieldCriteria will be translated * * @param alias * @param pathInfo * @param c ColumnCriteria * @param buf */ private void appendFieldCriteria(TableAlias alias, PathInfo pathInfo, FieldCriteria c, StringBuffer buf) { appendColName(alias, pathInfo, c.isTranslateAttribute(), buf); buf.append(c.getClause()); if (c.isTranslateField()) { appendColName((String) c.getValue(), false, c.getAlias(), buf); } else { buf.append(c.getValue()); } } /** * Answer the SQL-Clause for an InCriteria * * @param alias * @param pathInfo * @param c InCriteria * @param buf */ private void appendInCriteria(TableAlias alias, PathInfo pathInfo, InCriteria c, StringBuffer buf) { appendColName(alias, pathInfo, c.isTranslateAttribute(), buf); buf.append(c.getClause()); if (c.getValue() instanceof Collection) { Object[] values = ((Collection) c.getValue()).toArray(); int size = ((Collection) c.getValue()).size(); buf.append("("); for (int i = 0; i < size - 1; i++) { appendParameter(values[i], buf); buf.append(","); } appendParameter(values[size - 1], buf); buf.append(")"); } else { appendParameter(c.getValue(), buf); } } /** * Answer the SQL-Clause for a NullCriteria * * @param alias * @param pathInfo * @param c NullCriteria * @param buf */ private void appendNullCriteria(TableAlias alias, PathInfo pathInfo, NullCriteria c, StringBuffer buf) { appendColName(alias, pathInfo, c.isTranslateAttribute(), buf); buf.append(c.getClause()); } /** * Answer the SQL-Clause for a SqlCriteria * */ private void appendSQLCriteria(SqlCriteria c, StringBuffer buf) { buf.append(c.getClause()); } /** * Answer the SQL-Clause for a SelectionCriteria * * @param c * @param buf */ private void appendSelectionCriteria(TableAlias alias, PathInfo pathInfo, SelectionCriteria c, StringBuffer buf) { appendColName(alias, pathInfo, c.isTranslateAttribute(), buf); buf.append(c.getClause()); appendParameter(c.getValue(), buf); } /** * Answer the SQL-Clause for a LikeCriteria * * @param c * @param buf */ private void appendLikeCriteria(TableAlias alias, PathInfo pathInfo, LikeCriteria c, StringBuffer buf) { appendColName(alias, pathInfo, c.isTranslateAttribute(), buf); buf.append(c.getClause()); appendParameter(c.getValue(), buf); buf.append(m_platform.getEscapeClause(c)); } /** * Answer the SQL-Clause for a SelectionCriteria * * @param alias * @param pathInfo * @param c SelectionCriteria * @param buf */ protected void appendCriteria(TableAlias alias, PathInfo pathInfo, SelectionCriteria c, StringBuffer buf) { if (c instanceof FieldCriteria) { appendFieldCriteria(alias, pathInfo, (FieldCriteria) c, buf); } else if (c instanceof NullCriteria) { appendNullCriteria(alias, pathInfo, (NullCriteria) c, buf); } else if (c instanceof BetweenCriteria) { appendBetweenCriteria(alias, pathInfo, (BetweenCriteria) c, buf); } else if (c instanceof InCriteria) { appendInCriteria(alias, pathInfo, (InCriteria) c, buf); } else if (c instanceof SqlCriteria) { appendSQLCriteria((SqlCriteria) c, buf); } else if (c instanceof ExistsCriteria) { appendExistsCriteria((ExistsCriteria) c, buf); } else if (c instanceof LikeCriteria) { appendLikeCriteria(alias, pathInfo, (LikeCriteria) c, buf); } else { appendSelectionCriteria(alias, pathInfo, c, buf); } } /** * Answer the SQL-Clause for a SelectionCriteria * If the Criteria references a class with extents an OR-Clause is * added for each extent * @param c SelectionCriteria */ protected void appendSQLClause(SelectionCriteria c, StringBuffer buf) { // BRJ : handle SqlCriteria if (c instanceof SqlCriteria) { buf.append(c.getAttribute()); return; } // BRJ : criteria attribute is a query if (c.getAttribute() instanceof Query) { Query q = (Query) c.getAttribute(); buf.append("("); buf.append(getSubQuerySQL(q)); buf.append(")"); buf.append(c.getClause()); appendParameter(c.getValue(), buf); return; } // PAW // AttributeInfo attrInfo = getAttributeInfo((String) c.getAttribute(), false, c.getAlias()); AttributeInfo attrInfo = getAttributeInfo((String) c.getAttribute(), false, c.getAlias(), c.getPathClasses()); TableAlias alias = attrInfo.tableAlias; if (alias != null) { boolean hasExtents = alias.hasExtents(); if (hasExtents) { // BRJ : surround with braces if alias has extents buf.append("("); appendCriteria(alias, attrInfo.pathInfo, c, buf); c.setNumberOfExtentsToBind(alias.extents.size()); Iterator iter = alias.iterateExtents(); while (iter.hasNext()) { TableAlias tableAlias = (TableAlias) iter.next(); buf.append(" OR "); appendCriteria(tableAlias, attrInfo.pathInfo, c, buf); } buf.append(")"); } else { // no extents appendCriteria(alias, attrInfo.pathInfo, c, buf); } } else { // alias null appendCriteria(alias, attrInfo.pathInfo, c, buf); } } /** * Append the Parameter * Add the place holder ? or the SubQuery * @param value the value of the criteria */ private void appendParameter(Object value, StringBuffer buf) { if (value instanceof Query) { appendSubQuery((Query) value, buf); } else { buf.append("?"); } } /** * Append a SubQuery the SQL-Clause * @param subQuery the subQuery value of SelectionCriteria */ private void appendSubQuery(Query subQuery, StringBuffer buf) { buf.append(" ("); buf.append(getSubQuerySQL(subQuery)); buf.append(") "); } /** * Convert subQuery to SQL * @param subQuery the subQuery value of SelectionCriteria */ private String getSubQuerySQL(Query subQuery) { ClassDescriptor cld = getRoot().cld.getRepository().getDescriptorFor(subQuery.getSearchClass()); String sql; if (subQuery instanceof QueryBySQL) { sql = ((QueryBySQL) subQuery).getSql(); } else { sql = new SqlSelectStatement(this, m_platform, cld, subQuery, m_logger).getStatement(); } return sql; } /** * Get TableAlias by the path from the target table of the query. * @param aPath the path from the target table of the query to this TableAlias. * @param useOuterJoins use outer join to join this table with the previous * table in the path. * @param aUserAlias if specified, overrides alias in crit * @param fieldRef String[1] contains the field name. * In the case of related table's primary key the "related.pk" attribute * must not add new join, but use the value of foreign key */ // PAW: copied entire getTableAlias(String, boolean, String, String[]) private TableAlias getTableAlias(String aPath, boolean useOuterJoins, String aUserAlias, String[] fieldRef, Map pathClasses) { TableAlias curr, prev, indirect; String attr, attrPath = null; ObjectReferenceDescriptor ord; CollectionDescriptor cod; ClassDescriptor cld = null; Object[] prevKeys = null; Object[] keys = null; ArrayList descriptors; boolean outer = useOuterJoins; int pathLength; curr = getTableAliasForPath(aPath, aUserAlias); if (curr != null) { return curr; } // PAW //descriptors = getRoot().cld.getAttributeDescriptorsForPath(aPath, getQuery().getPathClasses()); descriptors = getRoot().cld.getAttributeDescriptorsForPath(aPath, pathClasses); prev = getRoot(); if (descriptors == null || descriptors.size() == 0) { if (prev.hasJoins()) { for (Iterator itr = prev.iterateJoins(); itr.hasNext();) { prev = ((Join) itr.next()).left; // PAW //descriptors = prev.cld.getAttributeDescriptorsForPath(aPath, getQuery().getPathClasses()); descriptors = prev.cld.getAttributeDescriptorsForPath(aPath, pathClasses); if (descriptors.size() > 0) { break; } } } } pathLength = descriptors.size(); for (int i = 0; i < pathLength; i++) { if (!(descriptors.get(i) instanceof ObjectReferenceDescriptor)) { // only use Collection- and ObjectReferenceDescriptor continue; } ord = (ObjectReferenceDescriptor) descriptors.get(i); attr = ord.getAttributeName(); if (attrPath == null) { attrPath = attr; } else { attrPath = attrPath + "." + attr; } // look for outer join hint outer = outer || getQuery().isPathOuterJoin(attr); // look for 1:n or m:n if (ord instanceof CollectionDescriptor) { cod = (CollectionDescriptor) ord; // PAW //cld = getItemClassDescriptor(cod, attrPath); cld = getItemClassDescriptor(cod, attrPath, pathClasses); if (!cod.isMtoNRelation()) { prevKeys = prev.cld.getPkFields(); keys = cod.getForeignKeyFieldDescriptors(cld); } else { String mnAttrPath = attrPath + "*"; String mnPath = aPath + "*"; String mnUserAlias = (aUserAlias == null ? null : aUserAlias + "*"); indirect = getTableAliasForPath(mnAttrPath, mnUserAlias, mnPath); if (indirect == null) { indirect = createTableAlias(cod.getIndirectionTable(), mnAttrPath, mnUserAlias, mnPath); // we need two Joins for m:n // 1.) prev class to indirectionTable prevKeys = prev.cld.getPkFields(); keys = cod.getFksToThisClass(); addJoin(prev, prevKeys, indirect, keys, outer, attr + "*"); } // 2.) indirectionTable to the current Class prev = indirect; prevKeys = cod.getFksToItemClass(); keys = cld.getPkFields(); } } else { // must be n:1 or 1:1 // PAW //cld = getItemClassDescriptor(ord, attrPath); cld = getItemClassDescriptor(ord, attrPath, pathClasses); prevKeys = ord.getForeignKeyFieldDescriptors(prev.cld); keys = cld.getPkFields(); // [olegnitz] // a special case: the last element of the path is // reference and the field is one of PK fields => // use the correspondent foreign key field, don't add the join if ((fieldRef != null) && (i == (pathLength - 1))) { FieldDescriptor[] pk = cld.getPkFields(); for (int j = 0; j < pk.length; j++) { if (pk[j].getAttributeName().equals(fieldRef[0])) { fieldRef[0] = ((FieldDescriptor) prevKeys[j]).getAttributeName(); return prev; } } } } curr = getTableAliasForPath(attrPath, aUserAlias, aPath); if (curr == null) { // PAW // List hintClasses = (List) getQuery().getPathClasses().get(aPath); List hintClasses = (List) pathClasses.get(aPath); curr = createTableAlias(cld, attrPath, aUserAlias, aPath, hintClasses); outer = outer || (curr.cld == prev.cld) || curr.hasExtents() || useOuterJoins; addJoin(prev, prevKeys, curr, keys, outer, attr); buildSuperJoinTree(curr, cld, aPath); } prev = curr; } return curr; } /** * add a join between two aliases * * TODO BRJ : This needs refactoring, it looks kind of weird * * no extents * A1 -> A2 * * extents on the right * A1 -> A2 * A1 -> A2E0 * * extents on the left : copy alias on right, extents point to copies * A1 -> A2 * A1E0 -> A2C0 * * extents on the left and right * A1 -> A2 * A1 -> A2E0 * A1E0 -> A2C0 * A1E0 -> A2E0C0 * * @param left * @param leftKeys * @param right * @param rightKeys * @param outer * @param name */ private void addJoin(TableAlias left, Object[] leftKeys, TableAlias right, Object[] rightKeys, boolean outer, String name) { TableAlias extAlias, rightCopy; left.addJoin(new Join(left, leftKeys, right, rightKeys, outer, name)); // build join between left and extents of right if (right.hasExtents()) { for (int i = 0; i < right.extents.size(); i++) { extAlias = (TableAlias) right.extents.get(i); FieldDescriptor[] extKeys = getExtentFieldDescriptors(extAlias, (FieldDescriptor[]) rightKeys); left.addJoin(new Join(left, leftKeys, extAlias, extKeys, true, name)); } } // we need to copy the alias on the right for each extent on the left if (left.hasExtents()) { for (int i = 0; i < left.extents.size(); i++) { extAlias = (TableAlias) left.extents.get(i); FieldDescriptor[] extKeys = getExtentFieldDescriptors(extAlias, (FieldDescriptor[]) leftKeys); rightCopy = right.copy("C" + i); // copies are treated like normal extents right.extents.add(rightCopy); right.extents.addAll(rightCopy.extents); addJoin(extAlias, extKeys, rightCopy, rightKeys, true, name); } } } /** * Get the FieldDescriptors of the extent based on the FieldDescriptors of the parent * @param extAlias * @param fds * @return */ private FieldDescriptor[] getExtentFieldDescriptors(TableAlias extAlias, FieldDescriptor[] fds) { FieldDescriptor[] result = new FieldDescriptor[fds.length]; for (int i = 0; i < fds.length; i++) { result[i] = extAlias.cld.getFieldDescriptorByName(fds[i].getAttributeName()); } return result; } private char getAliasChar() { char result = 'A'; if (m_parentStatement != null) { result = (char) (m_parentStatement.getAliasChar() + 1); } return result; } /** * Create a TableAlias for path or userAlias * @param aCld * @param aPath * @param aUserAlias * @param anOriginalPath * @param hints a List os Class objects to be used as hints for path expressions * @return TableAlias * */ private TableAlias createTableAlias(ClassDescriptor aCld, String aPath, String aUserAlias, String anOriginalPath, List hints) { // PAW //if (aUserAlias == null || !anOriginalPath.equals(aPath)) if (aUserAlias == null) { return createTableAlias(aCld, hints, aPath); } else { // PAW //return createTableAlias(aCld, hints, aUserAlias); return createTableAlias(aCld, hints, aUserAlias+aPath); } } /** * Create new TableAlias for path * @param cld the class descriptor for the TableAlias * @param path the path from the target table of the query to this TableAlias. * @@param hints a List of Class objects to be used on path expressions */ private TableAlias createTableAlias(ClassDescriptor cld, List hints, String path) { TableAlias alias; boolean lookForExtents = false; if (!cld.getExtentClasses().isEmpty() && path.length() > 0) { lookForExtents = true; } String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++; // m_pathToAlias.size(); alias = new TableAlias(cld, aliasName, lookForExtents, hints); m_pathToAlias.put(path, alias); return alias; } /** * Create a TableAlias for path or userAlias * @param aTable * @param aPath * @param aUserAlias * @param anOriginalPath * @return TableAlias */ private TableAlias createTableAlias(String aTable, String aPath, String aUserAlias, String anOriginalPath) { // PAW //if (aUserAlias == null || !anOriginalPath.equals(aPath)) if (aUserAlias == null) { return createTableAlias(aTable, aPath); } else { // PAW //return createTableAlias(aTable, aUserAlias); return createTableAlias(aTable, aUserAlias+aPath); } } /** * Create new TableAlias for path * @param table the table name * @param path the path from the target table of the query to this TableAlias. */ private TableAlias createTableAlias(String table, String path) { TableAlias alias; if (table == null) { getLogger().warn("Creating TableAlias without table for path: " + path); } String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++; // + m_pathToAlias.size(); alias = new TableAlias(table, aliasName); m_pathToAlias.put(path, alias); return alias; } /** * Answer the TableAlias for aPath * @param aPath * @return TableAlias, null if none */ private TableAlias getTableAliasForPath(String aPath) { return (TableAlias) m_pathToAlias.get(aPath); } /** * Answer the TableAlias for aPath or aUserAlias * @param aPath * @param aUserAlias * @return TableAlias, null if none */ private TableAlias getTableAliasForPath(String aPath, String aUserAlias) { if (aUserAlias == null) { return getTableAliasForPath(aPath); } else { // PAW // return getTableAliasForPath(aUserAlias); return getTableAliasForPath(aUserAlias+aPath); } } /** * Answer the TableAlias for aPath or aUserAlias * @param aPath * @param aUserAlias * @param anOriginalPath * @return TableAlias, null if none */ private TableAlias getTableAliasForPath(String aPath, String aUserAlias, String anOriginalPath) { // PAW //if (aUserAlias == null || !anOriginalPath.equals(aPath)) if (aUserAlias == null) { return getTableAliasForPath(aPath); } else { // PAW //return getTableAliasForPath(aUserAlias); return getTableAliasForPath(aUserAlias+aPath); } } /** * TODO: add super ClassDescriptor * answer the ClassDescriptor for itemClass for an ObjectReferenceDescriptor * check optional hint; */ // PAW // private ClassDescriptor getItemClassDescriptor(ObjectReferenceDescriptor ord, String attr) private ClassDescriptor getItemClassDescriptor(ObjectReferenceDescriptor ord, String attr, Map pathClasses) { // PAW //List itemClasses = null; // Query q = getQuery(); // // if (q instanceof QueryByCriteria) // { // itemClasses = ((QueryByCriteria) q).getClassesForPath(attr); // } List itemClasses = (List)pathClasses.get(attr); if (itemClasses == null) { itemClasses = new ArrayList(); itemClasses.add(ord.getItemClass()); } List classDescriptors = new ArrayList(itemClasses.size()); DescriptorRepository repo = ord.getClassDescriptor().getRepository(); for (Iterator iter = itemClasses.iterator(); iter.hasNext();) { Class clazz = (Class) iter.next(); classDescriptors.add(repo.getDescriptorFor(clazz)); } return (ClassDescriptor) classDescriptors.get(0); } /** * Appends to the statement the ORDER BY clause for the Query */ protected void appendOrderByClause(List orderByFields, int[] orderByColumnNumbers, StringBuffer buf) { FieldHelper cf; if (orderByColumnNumbers == null) { return; } buf.append(" ORDER BY "); for (int i = 0; i < orderByColumnNumbers.length; i++) { cf = (FieldHelper) orderByFields.get(i); if (i > 0) { buf.append(","); } buf.append(orderByColumnNumbers[i]); if (!cf.isAscending) { buf.append(" DESC"); } } } /** * Appends to the statement the GROUP BY clause for the Query */ protected void appendGroupByClause(List groupByFields, StringBuffer buf) { FieldHelper cf; if (groupByFields == null || groupByFields.size() == 0) { return; } buf.append(" GROUP BY "); for (int i = 0; i < groupByFields.size(); i++) { cf = (FieldHelper) groupByFields.get(i); if (i > 0) { buf.append(","); } appendColName(cf.name, false, null, buf); } } /** * Appends to the statement table and all tables joined to it. * @param alias the table alias * @param where append conditions for WHERE clause here */ protected void appendTableWithJoins(TableAlias alias, StringBuffer where, StringBuffer buf) { int stmtFromPos = 0; byte joinSyntax = getJoinSyntaxType(); if (joinSyntax == SQL92_JOIN_SYNTAX) { stmtFromPos = buf.length(); // store position of join (by: Terry Dexter) } if (!(joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX && alias != getRoot())) { buf.append(alias.getTableAndAlias()); if (getQuery() instanceof MtoNQuery) { buf.append(","); buf.append(((MtoNQuery) getQuery()).getIndirectionTable()); } } if (!alias.hasJoins()) { return; } for (Iterator it = alias.iterateJoins(); it.hasNext();) { Join join = (Join) it.next(); if (joinSyntax == SQL92_JOIN_SYNTAX) { appendJoinSQL92(join, where, buf); if (it.hasNext()) { buf.insert(stmtFromPos, "("); buf.append(")"); } } else if (joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX) { appendJoinSQL92NoParen(join, where, buf); } else { appendJoin(where, buf, join); } } } /** * Append Join for non SQL92 Syntax */ private void appendJoin(StringBuffer where, StringBuffer buf, Join join) { buf.append(","); appendTableWithJoins(join.right, where, buf); if (where.length() > 0) { where.append(" AND "); } join.appendJoinEqualities(where); } /** * Append Join for SQL92 Syntax */ private void appendJoinSQL92(Join join, StringBuffer where, StringBuffer buf) { if (join.isOuter) { buf.append(" LEFT OUTER JOIN "); } else { buf.append(" INNER JOIN "); } if (join.right.hasJoins()) { buf.append("("); appendTableWithJoins(join.right, where, buf); buf.append(")"); } else { appendTableWithJoins(join.right, where, buf); } buf.append(" ON "); join.appendJoinEqualities(buf); } /** * Append Join for SQL92 Syntax without parentheses */ private void appendJoinSQL92NoParen(Join join, StringBuffer where, StringBuffer buf) { if (join.isOuter) { buf.append(" LEFT OUTER JOIN "); } else { buf.append(" INNER JOIN "); } buf.append(join.right.getTableAndAlias()); buf.append(" ON "); join.appendJoinEqualities(buf); appendTableWithJoins(join.right, where, buf); } /** * Appends to the statement columns if they are not found among the existingColumns. * Columns added here use a column-alias "ojb_col_x", x being the number of existing columns * @param columns the list of columns represented by Criteria.Field to ensure * @param existingColumns the list of column names (String) that are already appended * @return the array of column numbers (base 1) */ protected int[] ensureColumns(List columns, List existingColumns, StringBuffer buf) { FieldHelper cf; int[] columnNumbers = new int[columns.size()]; for (int i = 0; i < columnNumbers.length; i++) { cf = (FieldHelper) columns.get(i); columnNumbers[i] = existingColumns.indexOf(cf.name); if (columnNumbers[i] == -1) { columnNumbers[i] = existingColumns.size(); existingColumns.add(cf.name); buf.append(","); appendColName(cf.name, "ojb_col_" + columnNumbers[i], false, null, buf); } columnNumbers[i]++; // columns numbers have base 1 } return columnNumbers; } /** * Build the tree of joins for the given criteria */ private void buildJoinTree(Criteria crit) { Enumeration e = crit.getElements(); while (e.hasMoreElements()) { Object o = e.nextElement(); if (o instanceof Criteria) { buildJoinTree((Criteria) o); } else { SelectionCriteria c = (SelectionCriteria) o; // BRJ skip SqlCriteria if (c instanceof SqlCriteria) { continue; } // BRJ: Outer join for OR boolean useOuterJoin = (crit.getType() == Criteria.OR); // BRJ: do not build join tree for subQuery attribute if (c.getAttribute() != null && c.getAttribute() instanceof String) { // PAW //buildJoinTreeForColumn((String) c.getAttribute(), useOuterJoin, c.getAlias()); buildJoinTreeForColumn((String) c.getAttribute(), useOuterJoin, c.getAlias(), c.getPathClasses()); } if (c instanceof FieldCriteria) { FieldCriteria cc = (FieldCriteria) c; // PAW //buildJoinTreeForColumn((String) cc.getValue(), useOuterJoin, c.getAlias()); buildJoinTreeForColumn((String) cc.getValue(), useOuterJoin, c.getAlias(), c.getPathClasses()); } } } } /** * build the Join-Information for name * functions and the last segment are removed * ie: avg(accounts.amount) -> accounts */ // PAW //private void buildJoinTreeForColumn(String aColName, boolean useOuterJoin, String aUserAlias) private void buildJoinTreeForColumn(String aColName, boolean useOuterJoin, String aUserAlias, Map pathClasses) { String pathName = SqlHelper.cleanPath(aColName); int sepPos = pathName.lastIndexOf("."); if (sepPos >= 0) { // PAW //getTableAlias(pathName.substring(0, sepPos), useOuterJoin, aUserAlias, new String[]{pathName // .substring(sepPos + 1)}); getTableAlias(pathName.substring(0, sepPos), useOuterJoin, aUserAlias, new String[]{pathName.substring(sepPos + 1)}, pathClasses); } } /** * build the Join-Information if a super reference exists * * @param left * @param cld * @param name */ protected void buildSuperJoinTree(TableAlias left, ClassDescriptor cld, String name) { Iterator objRefs = cld.getObjectReferenceDescriptors().iterator(); while (objRefs.hasNext()) { ObjectReferenceDescriptor objRef = (ObjectReferenceDescriptor) objRefs.next(); FieldDescriptor[] leftFields = objRef.getForeignKeyFieldDescriptors(cld); ClassDescriptor refCld = cld.getRepository().getDescriptorFor(objRef.getItemClassName()); if (objRef.getPersistentField() instanceof AnonymousPersistentFieldForInheritance) { TableAlias base_alias = getTableAliasForPath(name, null); String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++; TableAlias right = new TableAlias(refCld, aliasName, false, null); Join join1to1 = new Join(left, leftFields, right, refCld.getPkFields(), false, "superClass"); base_alias.addJoin(join1to1); buildSuperJoinTree(right, refCld, name); } } } /** * First reduce the Criteria to the normal disjunctive form, then * calculate the necessary tree of joined tables for each item, then group * items with the same tree of joined tables. */ protected void splitCriteria() { Criteria whereCrit = getQuery().getCriteria(); Criteria havingCrit = getQuery().getHavingCriteria(); if (whereCrit == null || whereCrit.isEmpty()) { getJoinTreeToCriteria().put(getRoot(), null); } else { // TODO: parameters list shold be modified when the form is reduced to DNF. getJoinTreeToCriteria().put(getRoot(), whereCrit); buildJoinTree(whereCrit); } if (havingCrit == null || havingCrit.isEmpty()) { } else { buildJoinTree(havingCrit); } } /** * Gets the query. * @return Returns a Query */ protected QueryByCriteria getQuery() { return m_query; } /** * Gets the root. * @return Returns a TableAlias */ protected TableAlias getRoot() { return m_root; } /** * Sets the root. * @param root The root to set */ protected void setRoot(TableAlias root) { this.m_root = root; } /** * Gets the search table of this query. * @return Returns a TableAlias */ protected TableAlias getSearchTable() { return m_search; } /** * Gets the joinTreeToCriteria. * @return Returns a HashMap */ protected HashMap getJoinTreeToCriteria() { return m_joinTreeToCriteria; } /** * Returns the joinSyntaxType. * @return byte */ protected byte getJoinSyntaxType() { return m_platform.getJoinSyntaxType(); } /** * Returns the logger. * @return Logger */ protected Logger getLogger() { return m_logger; } //----------------------------------------------------------------- // ------------------- Inner classes ------------------------------ //----------------------------------------------------------------- /** * This class is a helper to return TableAlias and PathInfo */ static final class AttributeInfo { TableAlias tableAlias; PathInfo pathInfo; } /** * This class represents one table (possibly with alias) in the SQL query */ static final class TableAlias { Logger logger = LoggerFactory.getLogger(TableAlias.class); ClassDescriptor cld; // Is null for indirection table of M:N relation String table; final String alias; List extents = new ArrayList(); List hints = new ArrayList(); List joins; TableAlias(String aTable, String anAlias) { this.cld = null; this.table = aTable; this.alias = anAlias; } TableAlias(ClassDescriptor aCld, String anAlias) { this(aCld, anAlias, false, null); } TableAlias(ClassDescriptor aCld, String anAlias, boolean lookForExtents, List hints) { this.cld = aCld; this.table = aCld.getFullTableName(); this.alias = anAlias; boolean useHintsOnExtents = false; //LEANDRO: use hints if (hints != null && hints.size() > 0) { useHintsOnExtents = true; } logger.debug("TableAlias(): using hints ? " + useHintsOnExtents); // BRJ : build alias for extents, only one per Table if (lookForExtents) { ClassDescriptor[] extCLDs = (ClassDescriptor[]) aCld.getRepository().getAllConcreteSubclassDescriptors( aCld).toArray(new ClassDescriptor[0]); ClassDescriptor extCd; Class extClass; String extTable; Map extMap = new HashMap(); // only one Alias per Table int firtsNonAbstractExtentIndex = 0; for (int i = 0; i < extCLDs.length; i++) { extCd = extCLDs[i]; extClass = extCd.getClassOfObject(); if (useHintsOnExtents && (!hints.contains(extClass))) { //LEANDRO: don't include this class logger.debug("Skipping class [" + extClass + "] from extents List"); firtsNonAbstractExtentIndex++; continue; } extTable = extCd.getFullTableName(); // BRJ : Use the first non abstract extent // if the main cld is abstract //logger.debug("cld abstract["+aCld.isAbstract()+"] i["+i+"] index ["+firtsNonAbstractExtentIndex+"]"); if (aCld.isAbstract() && i == firtsNonAbstractExtentIndex) { this.cld = extCd; this.table = extTable; } else { // Add a new extent entry only if the table of the extent // does not match the table of the 'base' class. if (extMap.get(extTable) == null && !extTable.equals(table)) { extMap.put(extTable, new TableAlias(extCd, anAlias + "E" + i, false, hints)); } } } extents.addAll(extMap.values()); } if (cld == null) { throw new PersistenceBrokerSQLException("Table is NULL for alias: " + alias); } } ClassDescriptor getClassDescriptor() { return cld; } String getTableAndAlias() { return table + " " + alias; } boolean hasExtents() { return (!extents.isEmpty()); } Iterator iterateExtents() { return extents.iterator(); } /** * Copy the Alias and all it's extents adding a Postfix * Joins are not copied * @param aPostfix * @return */ TableAlias copy(String aPostfix) { TableAlias result, temp; Iterator iter = iterateExtents(); if (cld == null) { result = new TableAlias(table, alias + aPostfix); } else { result = new TableAlias(cld, alias + aPostfix); } while (iter.hasNext()) { temp = (TableAlias) iter.next(); result.extents.add(temp.copy(aPostfix)); } return result; } void addJoin(Join join) { if (joins == null) { joins = new ArrayList(); } joins.add(join); } Iterator iterateJoins() { return joins.iterator(); } boolean hasJoins() { return (joins != null); } public String toString() { StringBuffer sb = new StringBuffer(1024); boolean first = true; sb.append(getTableAndAlias()); if (joins != null) { sb.append(" ["); for (Iterator it = joins.iterator(); it.hasNext();) { Join join = (Join) it.next(); if (first) { first = false; } else { sb.append(", "); } sb.append("-("); sb.append(join.name); sb.append(")->"); sb.append(join.right); } sb.append("]"); } return sb.toString(); } public boolean equals(Object obj) { TableAlias t = (TableAlias) obj; return table.equals(t.table); // BRJ: check table only } public int hashCode() { return table.hashCode(); } } /** * This class represents join between two TableAliases */ final class Join { final TableAlias left; final String[] leftKeys; final TableAlias right; final String[] rightKeys; final boolean isOuter; /** This is the name of the field corresponding to this join */ final String name; /** * leftKeys and rightKeys should be either FieldDescriptor[] or String[] */ Join(TableAlias left, Object[] leftKeys, TableAlias right, Object[] rightKeys, boolean isOuter, String name) { this.left = left; this.leftKeys = getColumns(leftKeys); this.right = right; this.rightKeys = getColumns(rightKeys); this.isOuter = isOuter; this.name = name; } private String[] getColumns(Object[] keys) { String[] columns = new String[keys.length]; if (keys instanceof FieldDescriptor[]) { FieldDescriptor[] kd = (FieldDescriptor[]) keys; for (int i = 0; i < columns.length; i++) { columns[i] = kd[i].getColumnName(); } } else { for (int i = 0; i < columns.length; i++) { columns[i] = keys[i].toString(); } } return columns; } void appendJoinEqualities(StringBuffer buf) { byte joinSyntax = getJoinSyntaxType(); for (int i = 0; i < leftKeys.length; i++) { if (i > 0) { buf.append(" AND "); } buf.append(left.alias); buf.append("."); buf.append(leftKeys[i]); if (isOuter && joinSyntax == SYBASE_JOIN_SYNTAX) { buf.append("*="); } else { buf.append("="); } buf.append(right.alias); buf.append("."); buf.append(rightKeys[i]); if (isOuter && joinSyntax == ORACLE_JOIN_SYNTAX) { buf.append("(+)"); } } } public boolean equals(Object obj) { Join j = (Join) obj; return name.equals(j.name) && (isOuter == j.isOuter) && right.equals(j.right); } public int hashCode() { return name.hashCode(); } public String toString() { return left.alias + " -> " + right.alias; } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
