thanks for the file.
generating a patch in eclipse is quite easy (in most cases). seems you reformatted the code in some way, so the diff get's out of sync.
i'll have a look at your changes when i return from my vacation, that's 19. april.
jakob
Phil Warrick wrote:
Hi Jakob,
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]
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
