0001: package org.apache.ojb.broker.accesslayer.sql;
0002:
0003: /* Copyright 2002-2005 The Apache Software Foundation
0004: *
0005: * Licensed under the Apache License, Version 2.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: import java.util.ArrayList;
0019: import java.util.Collection;
0020: import java.util.Enumeration;
0021: import java.util.HashMap;
0022: import java.util.Iterator;
0023: import java.util.List;
0024: import java.util.Map;
0025:
0026: import org.apache.ojb.broker.PersistenceBrokerSQLException;
0027: import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes;
0028: import org.apache.ojb.broker.metadata.ClassDescriptor;
0029: import org.apache.ojb.broker.metadata.CollectionDescriptor;
0030: import org.apache.ojb.broker.metadata.DescriptorRepository;
0031: import org.apache.ojb.broker.metadata.FieldDescriptor;
0032: import org.apache.ojb.broker.metadata.FieldHelper;
0033: import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
0034: import org.apache.ojb.broker.metadata.SuperReferenceDescriptor;
0035: import org.apache.ojb.broker.platforms.Platform;
0036: import org.apache.ojb.broker.query.BetweenCriteria;
0037: import org.apache.ojb.broker.query.Criteria;
0038: import org.apache.ojb.broker.query.ExistsCriteria;
0039: import org.apache.ojb.broker.query.FieldCriteria;
0040: import org.apache.ojb.broker.query.InCriteria;
0041: import org.apache.ojb.broker.query.LikeCriteria;
0042: import org.apache.ojb.broker.query.MtoNQuery;
0043: import org.apache.ojb.broker.query.NullCriteria;
0044: import org.apache.ojb.broker.query.Query;
0045: import org.apache.ojb.broker.query.QueryByCriteria;
0046: import org.apache.ojb.broker.query.QueryBySQL;
0047: import org.apache.ojb.broker.query.SelectionCriteria;
0048: import org.apache.ojb.broker.query.SqlCriteria;
0049: import org.apache.ojb.broker.query.UserAlias;
0050: import org.apache.ojb.broker.util.SqlHelper;
0051: import org.apache.ojb.broker.util.SqlHelper.PathInfo;
0052: import org.apache.ojb.broker.util.logging.Logger;
0053: import org.apache.ojb.broker.util.logging.LoggerFactory;
0054:
0055: /**
0056: * Model a Statement based on Query.
0057: *
0058: * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
0059: * @version $Id: SqlQueryStatement.java,v 1.75.2.23 2005/12/22 18:25:51 brj Exp $
0060: */
0061: public abstract class SqlQueryStatement implements SqlStatement,
0062: JoinSyntaxTypes {
0063: private static final String ALIAS_SEPARATOR = ".";
0064: private static final String M_N_ALIAS = "M_N";
0065: private String sql;
0066:
0067: private SqlQueryStatement m_parentStatement;
0068: /** the logger */
0069: private Logger m_logger;
0070: /** the target table of the query */
0071: private TableAlias m_root;
0072: /** the search table of the query */
0073: private TableAlias m_search;
0074: /** the query */
0075: private QueryByCriteria m_query;
0076: /** the mapping of paths to TableAliases. the key is built using the path and the path class hints. */
0077: private HashMap m_pathToAlias = new HashMap();
0078: /** the mapping of ClassDescriptor to TableAliases */
0079: private HashMap m_cldToAlias = new HashMap();
0080: /** maps trees of joins to criteria */
0081: private HashMap m_joinTreeToCriteria = new HashMap();
0082:
0083: private Platform m_platform;
0084: private ClassDescriptor m_baseCld;
0085: private ClassDescriptor m_searchCld;
0086:
0087: private int m_aliasCount = 0;
0088: protected HashMap m_attrToFld = new HashMap(); //attribute -> FieldDescriptor
0089:
0090: /**
0091: * Constructor for SqlCriteriaStatement.
0092: *
0093: * @param pf the Platform
0094: * @param cld the ClassDescriptor
0095: * @param query the Query
0096: * @param logger the Logger
0097: */
0098: public SqlQueryStatement(Platform pf, ClassDescriptor cld,
0099: Query query, Logger logger) {
0100: this (null, pf, cld, query, logger);
0101: }
0102:
0103: /**
0104: * Constructor for SqlCriteriaStatement.
0105: *
0106: * @param parent the Parent Query
0107: * @param pf the Platform
0108: * @param cld the ClassDescriptor
0109: * @param query the Query
0110: * @param logger the Logger
0111: */
0112: public SqlQueryStatement(SqlQueryStatement parent, Platform pf,
0113: ClassDescriptor cld, Query query, Logger logger) {
0114: m_logger = logger != null ? logger : LoggerFactory
0115: .getLogger(SqlQueryStatement.class);
0116: m_parentStatement = parent;
0117: m_query = (QueryByCriteria) query;
0118: m_platform = pf;
0119: m_searchCld = cld;
0120:
0121: if ((m_query == null)
0122: || (m_query.getBaseClass() == m_query.getSearchClass())) {
0123: m_baseCld = m_searchCld;
0124: } else {
0125: m_baseCld = cld.getRepository().getDescriptorFor(
0126: query.getBaseClass());
0127: }
0128:
0129: m_root = createTableAlias(m_baseCld, null, "");
0130:
0131: // BRJ: create a special alias for the indirection table
0132: if (m_query instanceof MtoNQuery) {
0133: MtoNQuery mnQuery = (MtoNQuery) m_query;
0134: TableAlias mnAlias = new TableAlias(mnQuery
0135: .getIndirectionTable(), M_N_ALIAS);
0136: setTableAliasForPath(mnQuery.getIndirectionTable(), null,
0137: mnAlias);
0138: }
0139:
0140: if (m_searchCld == m_baseCld) {
0141: m_search = m_root;
0142: } else {
0143: m_search = getTableAlias(m_query
0144: .getObjectProjectionAttribute(), false, null, null,
0145: m_query.getPathClasses());
0146: }
0147:
0148: // Walk the super reference-descriptor
0149: buildSuperJoinTree(m_root, m_baseCld, "", false);
0150:
0151: buildMultiJoinTree(m_root, m_baseCld, "", true);
0152:
0153: // In some cases it is necessary to split the query criteria
0154: // and then to generate UNION of several SELECTs
0155: // We build the joinTreeToCriteria mapping,
0156: if (query != null) {
0157: splitCriteria();
0158: }
0159: }
0160:
0161: protected ClassDescriptor getBaseClassDescriptor() {
0162: return m_baseCld;
0163: }
0164:
0165: protected ClassDescriptor getSearchClassDescriptor() {
0166: return m_searchCld;
0167: }
0168:
0169: /**
0170: * Return the TableAlias and the PathInfo for an Attribute name<br>
0171: * field names in functions (ie: sum(name) ) are tried to resolve ie: name
0172: * from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
0173: * also resolve pathExpression adress.city or owner.konti.saldo
0174: * @param attr
0175: * @param useOuterJoins
0176: * @param aUserAlias
0177: * @param pathClasses
0178: * @return ColumnInfo
0179: */
0180: protected AttributeInfo getAttributeInfo(String attr,
0181: boolean useOuterJoins, UserAlias aUserAlias, Map pathClasses) {
0182: AttributeInfo result = new AttributeInfo();
0183: TableAlias tableAlias;
0184: SqlHelper.PathInfo pathInfo = SqlHelper.splitPath(attr);
0185: String colName = pathInfo.column;
0186: int sp;
0187:
0188: // BRJ:
0189: // check if we refer to an attribute in the parent query
0190: // this prefix is temporary !
0191: if (colName.startsWith(Criteria.PARENT_QUERY_PREFIX)
0192: && m_parentStatement != null) {
0193: String[] fieldNameRef = { colName
0194: .substring(Criteria.PARENT_QUERY_PREFIX.length()) };
0195: return m_parentStatement.getAttributeInfo(fieldNameRef[0],
0196: useOuterJoins, aUserAlias, pathClasses);
0197: }
0198:
0199: sp = colName.lastIndexOf(".");
0200: if (sp == -1) {
0201: tableAlias = getRoot();
0202: } else {
0203: String pathName = colName.substring(0, sp);
0204: String[] fieldNameRef = { colName.substring(sp + 1) };
0205:
0206: tableAlias = getTableAlias(pathName, useOuterJoins,
0207: aUserAlias, fieldNameRef, pathClasses);
0208: /**
0209: * if we have not found an alias by the pathName or
0210: * aliasName (if given), try again because pathName
0211: * may be an aliasname. it can be only and only if it is not
0212: * a path, which means there may be no path separators (,)
0213: * in the pathName.
0214: */
0215: if ((tableAlias == null)
0216: && (colName.lastIndexOf(".") == -1)) {
0217: /**
0218: * pathName might be an alias, so check this first
0219: */
0220: tableAlias = getTableAlias(pathName, useOuterJoins,
0221: new UserAlias(pathName, pathName, pathName),
0222: null, pathClasses);
0223: }
0224:
0225: if (tableAlias != null) {
0226: // correct column name to match the alias
0227: // productGroup.groupName -> groupName
0228: pathInfo.column = fieldNameRef[0];
0229: }
0230: }
0231:
0232: result.tableAlias = tableAlias;
0233: result.pathInfo = pathInfo;
0234: return result;
0235: }
0236:
0237: /**
0238: * Answer the column name for alias and path info<br>
0239: * if translate try to convert attribute name into column name otherwise use attribute name<br>
0240: * if a FieldDescriptor is found for the attribute name the column name is taken from
0241: * there prefixed with the alias (firstname -> A0.F_NAME).
0242: */
0243: protected String getColName(TableAlias aTableAlias,
0244: PathInfo aPathInfo, boolean translate) {
0245: String result = null;
0246:
0247: // no translation required, use attribute name
0248: if (!translate) {
0249: return aPathInfo.column;
0250: }
0251:
0252: // BRJ: special alias for the indirection table has no ClassDescriptor
0253: if (aTableAlias.cld == null
0254: && M_N_ALIAS.equals(aTableAlias.alias)) {
0255: return getIndirectionTableColName(aTableAlias,
0256: aPathInfo.path);
0257: }
0258:
0259: // translate attribute name into column name
0260: FieldDescriptor fld = getFieldDescriptor(aTableAlias, aPathInfo);
0261:
0262: if (fld != null) {
0263: m_attrToFld.put(aPathInfo.path, fld);
0264:
0265: // added to suport the super reference descriptor
0266: if (!fld.getClassDescriptor().getFullTableName().equals(
0267: aTableAlias.table)
0268: && aTableAlias.hasJoins()) {
0269: Iterator itr = aTableAlias.joins.iterator();
0270: while (itr.hasNext()) {
0271: Join join = (Join) itr.next();
0272: if (join.right.table.equals(fld
0273: .getClassDescriptor().getFullTableName())) {
0274: result = join.right.alias + "."
0275: + fld.getColumnName();
0276: break;
0277: }
0278: }
0279:
0280: if (result == null) {
0281: result = aPathInfo.column;
0282: }
0283: } else {
0284: result = aTableAlias.alias + "." + fld.getColumnName();
0285: }
0286: } else if ("*".equals(aPathInfo.column)) {
0287: result = aPathInfo.column;
0288: } else {
0289: // throw new IllegalArgumentException("No Field found for : " + aPathInfo.column);
0290: result = aPathInfo.column;
0291: }
0292:
0293: return result;
0294: }
0295:
0296: /**
0297: * Add the Column to the StringBuffer <br>
0298: *
0299: * @param aTableAlias
0300: * @param aPathInfo
0301: * @param translate flag to indicate translation of pathInfo
0302: * @param buf
0303: * @return true if appended
0304: */
0305: protected boolean appendColName(TableAlias aTableAlias,
0306: PathInfo aPathInfo, boolean translate, StringBuffer buf) {
0307: String prefix = aPathInfo.prefix;
0308: String suffix = aPathInfo.suffix;
0309: String colName = getColName(aTableAlias, aPathInfo, translate);
0310:
0311: if (prefix != null) // rebuild function contains (
0312: {
0313: buf.append(prefix);
0314: }
0315:
0316: buf.append(colName);
0317:
0318: if (suffix != null) // rebuild function
0319: {
0320: buf.append(suffix);
0321: }
0322:
0323: return true;
0324: }
0325:
0326: /**
0327: * Get the FieldDescriptor for the PathInfo
0328: *
0329: * @param aTableAlias
0330: * @param aPathInfo
0331: * @return FieldDescriptor
0332: */
0333: protected FieldDescriptor getFieldDescriptor(
0334: TableAlias aTableAlias, PathInfo aPathInfo) {
0335: FieldDescriptor fld = null;
0336: String colName = aPathInfo.column;
0337:
0338: if (aTableAlias != null) {
0339: fld = aTableAlias.cld.getFieldDescriptorByName(colName);
0340: if (fld == null) {
0341: ObjectReferenceDescriptor ord = aTableAlias.cld
0342: .getObjectReferenceDescriptorByName(colName);
0343: if (ord != null) {
0344: fld = getFldFromReference(aTableAlias, ord);
0345: } else {
0346: fld = getFldFromJoin(aTableAlias, colName);
0347: }
0348: }
0349: }
0350:
0351: return fld;
0352: }
0353:
0354: /**
0355: * Get FieldDescriptor from joined superclass.
0356: */
0357: private FieldDescriptor getFldFromJoin(TableAlias aTableAlias,
0358: String aColName) {
0359: FieldDescriptor fld = null;
0360:
0361: // Search Join Structure for attribute
0362: if (aTableAlias.joins != null) {
0363: Iterator itr = aTableAlias.joins.iterator();
0364: while (itr.hasNext()) {
0365: Join join = (Join) itr.next();
0366: ClassDescriptor cld = join.right.cld;
0367:
0368: if (cld != null) {
0369: fld = cld.getFieldDescriptorByName(aColName);
0370: if (fld != null) {
0371: break;
0372: }
0373:
0374: }
0375: }
0376: }
0377: return fld;
0378: }
0379:
0380: /**
0381: * Get FieldDescriptor from Reference
0382: */
0383: private FieldDescriptor getFldFromReference(TableAlias aTableAlias,
0384: ObjectReferenceDescriptor anOrd) {
0385: FieldDescriptor fld = null;
0386:
0387: if (aTableAlias == getRoot()) {
0388: // no path expression
0389: FieldDescriptor[] fk = anOrd
0390: .getForeignKeyFieldDescriptors(aTableAlias.cld);
0391: if (fk.length > 0) {
0392: fld = fk[0];
0393: }
0394: } else {
0395: // attribute with path expression
0396: /**
0397: * MBAIRD
0398: * potentially people are referring to objects, not to the object's primary key,
0399: * and then we need to take the primary key attribute of the referenced object
0400: * to help them out.
0401: */
0402: ClassDescriptor cld = aTableAlias.cld.getRepository()
0403: .getDescriptorFor(anOrd.getItemClass());
0404: if (cld != null) {
0405: fld = aTableAlias.cld.getFieldDescriptorByName(cld
0406: .getPkFields()[0].getPersistentField()
0407: .getName());
0408: }
0409: }
0410:
0411: return fld;
0412: }
0413:
0414: /**
0415: * Append the appropriate ColumnName to the buffer<br>
0416: * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from
0417: * there otherwise its taken from Criteria. <br>
0418: * field names in functions (ie: sum(name) ) are tried to resolve
0419: * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
0420: * also resolve pathExpression adress.city or owner.konti.saldo
0421: */
0422: protected boolean appendColName(String attr, boolean useOuterJoins,
0423: UserAlias aUserAlias, StringBuffer buf) {
0424: AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins,
0425: aUserAlias, getQuery().getPathClasses());
0426: TableAlias tableAlias = attrInfo.tableAlias;
0427:
0428: return appendColName(tableAlias, attrInfo.pathInfo,
0429: (tableAlias != null), buf);
0430: }
0431:
0432: /**
0433: * Append the appropriate ColumnName to the buffer<br>
0434: * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from
0435: * there otherwise its taken from Criteria. <br>
0436: * field names in functions (ie: sum(name) ) are tried to resolve
0437: * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
0438: * also resolve pathExpression adress.city or owner.konti.saldo
0439: */
0440: protected boolean appendColName(String attr, String attrAlias,
0441: boolean useOuterJoins, UserAlias aUserAlias,
0442: StringBuffer buf) {
0443: AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins,
0444: aUserAlias, getQuery().getPathClasses());
0445: TableAlias tableAlias = attrInfo.tableAlias;
0446: PathInfo pi = attrInfo.pathInfo;
0447:
0448: if (pi.suffix != null) {
0449: pi.suffix = pi.suffix + " as " + attrAlias;
0450: } else {
0451: pi.suffix = " as " + attrAlias;
0452: }
0453:
0454: return appendColName(tableAlias, pi, true, buf);
0455: }
0456:
0457: /**
0458: * Builds the Join for columns if they are not found among the existingColumns.
0459: * @param columns the list of columns represented by Criteria.Field to ensure
0460: * @param existingColumns the list of column names (String) that are already appended
0461: */
0462: protected void ensureColumns(List columns, List existingColumns) {
0463: if (columns == null || columns.isEmpty()) {
0464: return;
0465: }
0466:
0467: Iterator iter = columns.iterator();
0468:
0469: while (iter.hasNext()) {
0470: FieldHelper cf = (FieldHelper) iter.next();
0471: if (!existingColumns.contains(cf.name)) {
0472: getAttributeInfo(cf.name, false, null, getQuery()
0473: .getPathClasses());
0474: }
0475: }
0476: }
0477:
0478: /**
0479: * Builds the Join for columns if they are not found among the existingColumns.
0480: * These <b>columns are added to the statement</b> using a column-alias "ojb_col_x",
0481: * x being the number of existing columns
0482: * @param columns the list of columns represented by Criteria.Field to ensure
0483: * @param existingColumns the list of column names (String) that are already appended
0484: * @param buf the statement
0485: * @return List of existingColumns including ojb_col_x
0486: */
0487: protected List ensureColumns(List columns, List existingColumns,
0488: StringBuffer buf) {
0489: if (columns == null || columns.isEmpty()) {
0490: return existingColumns;
0491: }
0492:
0493: Iterator iter = columns.iterator();
0494: int ojb_col = existingColumns.size() + 1;
0495:
0496: while (iter.hasNext()) {
0497: FieldHelper cf = (FieldHelper) iter.next();
0498: if (!existingColumns.contains(cf.name)) {
0499: existingColumns.add(cf.name);
0500:
0501: buf.append(",");
0502: appendColName(cf.name, "ojb_col_" + ojb_col, false,
0503: null, buf);
0504: ojb_col++;
0505: }
0506: }
0507:
0508: return existingColumns;
0509: }
0510:
0511: /**
0512: * appends a WHERE-clause to the Statement
0513: * @param where
0514: * @param crit
0515: * @param stmt
0516: */
0517: protected void appendWhereClause(StringBuffer where, Criteria crit,
0518: StringBuffer stmt) {
0519: if (where.length() == 0) {
0520: where = null;
0521: }
0522:
0523: if (where != null || (crit != null && !crit.isEmpty())) {
0524: stmt.append(" WHERE ");
0525: appendClause(where, crit, stmt);
0526: }
0527: }
0528:
0529: /**
0530: * appends a HAVING-clause to the Statement
0531: * @param having
0532: * @param crit
0533: * @param stmt
0534: */
0535: protected void appendHavingClause(StringBuffer having,
0536: Criteria crit, StringBuffer stmt) {
0537: if (having.length() == 0) {
0538: having = null;
0539: }
0540:
0541: if (having != null || crit != null) {
0542: stmt.append(" HAVING ");
0543: appendClause(having, crit, stmt);
0544: }
0545: }
0546:
0547: /**
0548: * appends a WHERE/HAVING-clause to the Statement
0549: * @param clause
0550: * @param crit
0551: * @param stmt
0552: */
0553: protected void appendClause(StringBuffer clause, Criteria crit,
0554: StringBuffer stmt) {
0555: /**
0556: * MBAIRD
0557: * when generating the "WHERE/HAVING" clause we need to append the criteria for multi-mapped
0558: * tables. We only need to do this for the root classdescriptor and not for joined tables
0559: * because we assume you cannot make a relation of the wrong type upon insertion. Of course,
0560: * you COULD mess the data up manually and this would cause a problem.
0561: */
0562:
0563: if (clause != null) {
0564: stmt.append(clause.toString());
0565: }
0566: if (crit != null) {
0567: if (clause == null) {
0568: stmt.append(asSQLStatement(crit));
0569: } else {
0570: stmt.append(" AND (");
0571: stmt.append(asSQLStatement(crit));
0572: stmt.append(")");
0573: }
0574:
0575: }
0576: }
0577:
0578: /**
0579: * Create SQL-String based on Criteria
0580: */
0581: private String asSQLStatement(Criteria crit) {
0582: Enumeration e = crit.getElements();
0583: StringBuffer statement = new StringBuffer();
0584:
0585: while (e.hasMoreElements()) {
0586: Object o = e.nextElement();
0587: if (o instanceof Criteria) {
0588: Criteria pc = (Criteria) o;
0589:
0590: if (pc.isEmpty()) {
0591: continue; //skip empty criteria
0592: }
0593:
0594: String addAtStart = "";
0595: String addAtEnd = "";
0596:
0597: // need to add parenthesises?
0598: if (pc.isEmbraced()) {
0599: addAtStart = " (";
0600: addAtEnd = ")";
0601: }
0602:
0603: switch (pc.getType()) {
0604: case (Criteria.OR): {
0605: if (statement.length() > 0) {
0606: statement.append(" OR ");
0607: }
0608: statement.append(addAtStart);
0609: statement.append(asSQLStatement(pc));
0610: statement.append(addAtEnd);
0611: break;
0612: }
0613: case (Criteria.AND): {
0614: if (statement.length() > 0) {
0615: statement.insert(0, "( ");
0616: statement.append(") AND ");
0617: }
0618: statement.append(addAtStart);
0619: statement.append(asSQLStatement(pc));
0620: statement.append(addAtEnd);
0621: break;
0622: }
0623: }
0624: } else {
0625: SelectionCriteria c = (SelectionCriteria) o;
0626: if (statement.length() > 0) {
0627: statement.insert(0, "(");
0628: statement.append(") AND ");
0629: }
0630: appendSQLClause(c, statement);
0631: }
0632: } // while
0633:
0634: // BRJ : negative Criteria surrounded by NOT (...)
0635: if (crit.isNegative()) {
0636: statement.insert(0, " NOT (");
0637: statement.append(")");
0638: }
0639:
0640: return (statement.length() == 0 ? null : statement.toString());
0641: }
0642:
0643: /**
0644: * Answer the SQL-Clause for a BetweenCriteria
0645: *
0646: * @param alias
0647: * @param pathInfo
0648: * @param c BetweenCriteria
0649: * @param buf
0650: */
0651: private void appendBetweenCriteria(TableAlias alias,
0652: PathInfo pathInfo, BetweenCriteria c, StringBuffer buf) {
0653: appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
0654: buf.append(c.getClause());
0655: appendParameter(c.getValue(), buf);
0656: buf.append(" AND ");
0657: appendParameter(c.getValue2(), buf);
0658: }
0659:
0660: /**
0661: * Answer the SQL-Clause for an ExistsCriteria
0662: * @param c ExistsCriteria
0663: */
0664: private void appendExistsCriteria(ExistsCriteria c, StringBuffer buf) {
0665: Query subQuery = (Query) c.getValue();
0666:
0667: buf.append(c.getClause());
0668: appendSubQuery(subQuery, buf);
0669: }
0670:
0671: /**
0672: * Answer the SQL-Clause for a FieldCriteria<br>
0673: * The value of the FieldCriteria will be translated
0674: *
0675: * @param alias
0676: * @param pathInfo
0677: * @param c ColumnCriteria
0678: * @param buf
0679: */
0680: private void appendFieldCriteria(TableAlias alias,
0681: PathInfo pathInfo, FieldCriteria c, StringBuffer buf) {
0682: appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
0683: buf.append(c.getClause());
0684:
0685: if (c.isTranslateField()) {
0686: appendColName((String) c.getValue(), false, c
0687: .getUserAlias(), buf);
0688: } else {
0689: buf.append(c.getValue());
0690: }
0691: }
0692:
0693: /**
0694: * Get the column name from the indirection table.
0695: * @param mnAlias
0696: * @param path
0697: */
0698: private String getIndirectionTableColName(TableAlias mnAlias,
0699: String path) {
0700: int dotIdx = path.lastIndexOf(".");
0701: String column = path.substring(dotIdx);
0702: return mnAlias.alias + column;
0703: }
0704:
0705: /**
0706: * Answer the SQL-Clause for an InCriteria
0707: *
0708: * @param alias
0709: * @param pathInfo
0710: * @param c InCriteria
0711: * @param buf
0712: */
0713: private void appendInCriteria(TableAlias alias, PathInfo pathInfo,
0714: InCriteria c, StringBuffer buf) {
0715: appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
0716: buf.append(c.getClause());
0717:
0718: if (c.getValue() instanceof Collection) {
0719: Object[] values = ((Collection) c.getValue()).toArray();
0720: int size = ((Collection) c.getValue()).size();
0721:
0722: buf.append("(");
0723: if (size > 0) {
0724: for (int i = 0; i < size - 1; i++) {
0725: appendParameter(values[i], buf);
0726: buf.append(",");
0727: }
0728: appendParameter(values[size - 1], buf);
0729: }
0730: buf.append(")");
0731: } else {
0732: appendParameter(c.getValue(), buf);
0733: }
0734: }
0735:
0736: /**
0737: * Answer the SQL-Clause for a NullCriteria
0738: *
0739: * @param alias
0740: * @param pathInfo
0741: * @param c NullCriteria
0742: * @param buf
0743: */
0744: private void appendNullCriteria(TableAlias alias,
0745: PathInfo pathInfo, NullCriteria c, StringBuffer buf) {
0746: appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
0747: buf.append(c.getClause());
0748: }
0749:
0750: /**
0751: * Answer the SQL-Clause for a SqlCriteria
0752: *
0753: */
0754: private void appendSQLCriteria(SqlCriteria c, StringBuffer buf) {
0755: buf.append(c.getClause());
0756: }
0757:
0758: /**
0759: * Answer the SQL-Clause for a SelectionCriteria
0760: *
0761: * @param c
0762: * @param buf
0763: */
0764: private void appendSelectionCriteria(TableAlias alias,
0765: PathInfo pathInfo, SelectionCriteria c, StringBuffer buf) {
0766: appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
0767: buf.append(c.getClause());
0768: appendParameter(c.getValue(), buf);
0769: }
0770:
0771: /**
0772: * Answer the SQL-Clause for a LikeCriteria
0773: *
0774: * @param c
0775: * @param buf
0776: */
0777: private void appendLikeCriteria(TableAlias alias,
0778: PathInfo pathInfo, LikeCriteria c, StringBuffer buf) {
0779: appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
0780: buf.append(c.getClause());
0781: appendParameter(c.getValue(), buf);
0782:
0783: buf.append(m_platform.getEscapeClause(c));
0784: }
0785:
0786: /**
0787: * Answer the SQL-Clause for a SelectionCriteria
0788: *
0789: * @param alias
0790: * @param pathInfo
0791: * @param c SelectionCriteria
0792: * @param buf
0793: */
0794: protected void appendCriteria(TableAlias alias, PathInfo pathInfo,
0795: SelectionCriteria c, StringBuffer buf) {
0796: if (c instanceof FieldCriteria) {
0797: appendFieldCriteria(alias, pathInfo, (FieldCriteria) c, buf);
0798: } else if (c instanceof NullCriteria) {
0799: appendNullCriteria(alias, pathInfo, (NullCriteria) c, buf);
0800: } else if (c instanceof BetweenCriteria) {
0801: appendBetweenCriteria(alias, pathInfo, (BetweenCriteria) c,
0802: buf);
0803: } else if (c instanceof InCriteria) {
0804: appendInCriteria(alias, pathInfo, (InCriteria) c, buf);
0805: } else if (c instanceof SqlCriteria) {
0806: appendSQLCriteria((SqlCriteria) c, buf);
0807: } else if (c instanceof ExistsCriteria) {
0808: appendExistsCriteria((ExistsCriteria) c, buf);
0809: } else if (c instanceof LikeCriteria) {
0810: appendLikeCriteria(alias, pathInfo, (LikeCriteria) c, buf);
0811: } else {
0812: appendSelectionCriteria(alias, pathInfo, c, buf);
0813: }
0814: }
0815:
0816: /**
0817: * Answer the SQL-Clause for a SelectionCriteria
0818: * If the Criteria references a class with extents an OR-Clause is
0819: * added for each extent
0820: * @param c SelectionCriteria
0821: */
0822: protected void appendSQLClause(SelectionCriteria c, StringBuffer buf) {
0823: // BRJ : handle SqlCriteria
0824: if (c instanceof SqlCriteria) {
0825: buf.append(c.getAttribute());
0826: return;
0827: }
0828:
0829: // BRJ : criteria attribute is a query
0830: if (c.getAttribute() instanceof Query) {
0831: Query q = (Query) c.getAttribute();
0832: buf.append("(");
0833: buf.append(getSubQuerySQL(q));
0834: buf.append(")");
0835: buf.append(c.getClause());
0836: appendParameter(c.getValue(), buf);
0837: return;
0838: }
0839:
0840: AttributeInfo attrInfo = getAttributeInfo((String) c
0841: .getAttribute(), false, c.getUserAlias(), c
0842: .getPathClasses());
0843: TableAlias alias = attrInfo.tableAlias;
0844:
0845: if (alias != null) {
0846: boolean hasExtents = alias.hasExtents();
0847:
0848: if (hasExtents) {
0849: // BRJ : surround with braces if alias has extents
0850: buf.append("(");
0851: appendCriteria(alias, attrInfo.pathInfo, c, buf);
0852:
0853: c.setNumberOfExtentsToBind(alias.extents.size());
0854: Iterator iter = alias.iterateExtents();
0855: while (iter.hasNext()) {
0856: TableAlias tableAlias = (TableAlias) iter.next();
0857: buf.append(" OR ");
0858: appendCriteria(tableAlias, attrInfo.pathInfo, c,
0859: buf);
0860: }
0861: buf.append(")");
0862: } else {
0863: // no extents
0864: appendCriteria(alias, attrInfo.pathInfo, c, buf);
0865: }
0866: } else {
0867: // alias null
0868: appendCriteria(alias, attrInfo.pathInfo, c, buf);
0869: }
0870:
0871: }
0872:
0873: /**
0874: * Append the Parameter
0875: * Add the place holder ? or the SubQuery
0876: * @param value the value of the criteria
0877: */
0878: private void appendParameter(Object value, StringBuffer buf) {
0879: if (value instanceof Query) {
0880: appendSubQuery((Query) value, buf);
0881: } else {
0882: buf.append("?");
0883: }
0884: }
0885:
0886: /**
0887: * Append a SubQuery the SQL-Clause
0888: * @param subQuery the subQuery value of SelectionCriteria
0889: */
0890: private void appendSubQuery(Query subQuery, StringBuffer buf) {
0891: buf.append(" (");
0892: buf.append(getSubQuerySQL(subQuery));
0893: buf.append(") ");
0894: }
0895:
0896: /**
0897: * Convert subQuery to SQL
0898: * @param subQuery the subQuery value of SelectionCriteria
0899: */
0900: private String getSubQuerySQL(Query subQuery) {
0901: ClassDescriptor cld = getRoot().cld.getRepository()
0902: .getDescriptorFor(subQuery.getSearchClass());
0903: String sql;
0904:
0905: if (subQuery instanceof QueryBySQL) {
0906: sql = ((QueryBySQL) subQuery).getSql();
0907: } else {
0908: sql = new SqlSelectStatement(this , m_platform, cld,
0909: subQuery, m_logger).getStatement();
0910: }
0911:
0912: return sql;
0913: }
0914:
0915: /**
0916: * Get TableAlias by the path from the target table of the query.
0917: * @param aPath the path from the target table of the query to this TableAlias.
0918: * @param useOuterJoins use outer join to join this table with the previous
0919: * table in the path.
0920: * @param aUserAlias if specified, overrides alias in crit
0921: * @param fieldRef String[1] contains the field name.
0922: * In the case of related table's primary key the "related.pk" attribute
0923: * must not add new join, but use the value of foreign key
0924: * @param pathClasses the hints
0925: */
0926: private TableAlias getTableAlias(String aPath,
0927: boolean useOuterJoins, UserAlias aUserAlias,
0928: String[] fieldRef, Map pathClasses) {
0929: TableAlias curr, prev, indirect;
0930: String attr, attrPath = null;
0931: ObjectReferenceDescriptor ord;
0932: CollectionDescriptor cod;
0933: ClassDescriptor cld;
0934: Object[] prevKeys;
0935: Object[] keys;
0936: ArrayList descriptors;
0937: boolean outer = useOuterJoins;
0938: int pathLength;
0939: List hintClasses = null;
0940: String pathAlias = aUserAlias == null ? null : aUserAlias
0941: .getAlias(aPath);
0942:
0943: if (pathClasses != null) {
0944: hintClasses = (List) pathClasses.get(aPath);
0945: }
0946:
0947: curr = getTableAliasForPath(aPath, pathAlias, hintClasses);
0948: if (curr != null) {
0949: return curr;
0950: }
0951:
0952: descriptors = getRoot().cld.getAttributeDescriptorsForPath(
0953: aPath, pathClasses);
0954: prev = getRoot();
0955:
0956: if (descriptors == null || descriptors.size() == 0) {
0957: if (prev.hasJoins()) {
0958: for (Iterator itr = prev.iterateJoins(); itr.hasNext();) {
0959: prev = ((Join) itr.next()).left;
0960: descriptors = prev.cld
0961: .getAttributeDescriptorsForPath(aPath,
0962: pathClasses);
0963: if (descriptors.size() > 0) {
0964: break;
0965: }
0966: }
0967: }
0968: }
0969:
0970: pathLength = descriptors.size();
0971: for (int i = 0; i < pathLength; i++) {
0972: if (!(descriptors.get(i) instanceof ObjectReferenceDescriptor)) {
0973: // only use Collection- and ObjectReferenceDescriptor
0974: continue;
0975: }
0976:
0977: ord = (ObjectReferenceDescriptor) descriptors.get(i);
0978: attr = ord.getAttributeName();
0979: if (attrPath == null) {
0980: attrPath = attr;
0981: } else {
0982: attrPath = attrPath + "." + attr;
0983: }
0984:
0985: // use clas hints for path
0986: if (pathClasses != null) {
0987: hintClasses = (List) pathClasses.get(attrPath);
0988: }
0989:
0990: // look for outer join hint
0991: outer = outer || getQuery().isPathOuterJoin(attrPath);
0992:
0993: // look for 1:n or m:n
0994: if (ord instanceof CollectionDescriptor) {
0995: cod = (CollectionDescriptor) ord;
0996: cld = getItemClassDescriptor(cod, hintClasses);
0997:
0998: if (!cod.isMtoNRelation()) {
0999: prevKeys = prev.cld.getPkFields();
1000: keys = cod.getForeignKeyFieldDescriptors(cld);
1001: } else {
1002: String mnAttrPath = attrPath + "*";
1003: String mnUserAlias = (aUserAlias == null ? null
1004: : aUserAlias + "*");
1005: indirect = getTableAliasForPath(mnAttrPath,
1006: mnUserAlias, null);
1007: if (indirect == null) {
1008: indirect = createTableAlias(cod
1009: .getIndirectionTable(), mnAttrPath,
1010: mnUserAlias);
1011:
1012: // we need two Joins for m:n
1013: // 1.) prev class to indirectionTable
1014: prevKeys = prev.cld.getPkFields();
1015: keys = cod.getFksToThisClass();
1016: addJoin(prev, prevKeys, indirect, keys, outer,
1017: attr + "*");
1018: }
1019: // 2.) indirectionTable to the current Class
1020: prev = indirect;
1021: prevKeys = cod.getFksToItemClass();
1022: keys = cld.getPkFields();
1023: }
1024: } else {
1025: // must be n:1 or 1:1
1026: cld = getItemClassDescriptor(ord, hintClasses);
1027:
1028: // BRJ : if ord is taken from 'super' we have to change prev accordingly
1029: if (!prev.cld.equals(ord.getClassDescriptor())) {
1030: TableAlias ordAlias = getTableAliasForClassDescriptor(ord
1031: .getClassDescriptor());
1032: Join join = prev.getJoin(ordAlias);
1033: if (join != null) {
1034: join.isOuter = join.isOuter || outer;
1035: }
1036: prev = ordAlias;
1037: }
1038:
1039: prevKeys = ord.getForeignKeyFieldDescriptors(prev.cld);
1040: keys = cld.getPkFields();
1041:
1042: // [olegnitz]
1043: // a special case: the last element of the path is
1044: // reference and the field is one of PK fields =>
1045: // use the correspondent foreign key field, don't add the join
1046: if ((fieldRef != null) && (i == (pathLength - 1))) {
1047: FieldDescriptor[] pk = cld.getPkFields();
1048:
1049: for (int j = 0; j < pk.length; j++) {
1050: if (pk[j].getAttributeName()
1051: .equals(fieldRef[0])) {
1052: fieldRef[0] = ((FieldDescriptor) prevKeys[j])
1053: .getAttributeName();
1054: return prev;
1055: }
1056: }
1057: }
1058: }
1059:
1060: pathAlias = aUserAlias == null ? null : aUserAlias
1061: .getAlias(attrPath);
1062: curr = getTableAliasForPath(attrPath, pathAlias,
1063: hintClasses);
1064:
1065: if (curr == null) {
1066: curr = createTableAlias(cld, attrPath, pathAlias,
1067: hintClasses);
1068:
1069: outer = outer || (curr.cld == prev.cld)
1070: || curr.hasExtents() || useOuterJoins;
1071: addJoin(prev, prevKeys, curr, keys, outer, attr);
1072:
1073: buildSuperJoinTree(curr, cld, aPath, outer);
1074: }
1075:
1076: prev = curr;
1077: }
1078:
1079: m_logger.debug("Result of getTableAlias(): " + curr);
1080: return curr;
1081: }
1082:
1083: /**
1084: * add a join between two aliases
1085: *
1086: * TODO BRJ : This needs refactoring, it looks kind of weird
1087: *
1088: * no extents
1089: * A1 -> A2
1090: *
1091: * extents on the right
1092: * A1 -> A2
1093: * A1 -> A2E0
1094: *
1095: * extents on the left : copy alias on right, extents point to copies
1096: * A1 -> A2
1097: * A1E0 -> A2C0
1098: *
1099: * extents on the left and right
1100: * A1 -> A2
1101: * A1 -> A2E0
1102: * A1E0 -> A2C0
1103: * A1E0 -> A2E0C0
1104: *
1105: * @param left
1106: * @param leftKeys
1107: * @param right
1108: * @param rightKeys
1109: * @param outer
1110: * @param name
1111: */
1112: private void addJoin(TableAlias left, Object[] leftKeys,
1113: TableAlias right, Object[] rightKeys, boolean outer,
1114: String name) {
1115: TableAlias extAlias, rightCopy;
1116:
1117: left.addJoin(new Join(left, leftKeys, right, rightKeys, outer,
1118: name));
1119:
1120: // build join between left and extents of right
1121: if (right.hasExtents()) {
1122: for (int i = 0; i < right.extents.size(); i++) {
1123: extAlias = (TableAlias) right.extents.get(i);
1124: FieldDescriptor[] extKeys = getExtentFieldDescriptors(
1125: extAlias, (FieldDescriptor[]) rightKeys);
1126:
1127: left.addJoin(new Join(left, leftKeys, extAlias,
1128: extKeys, true, name));
1129: }
1130: }
1131:
1132: // we need to copy the alias on the right for each extent on the left
1133: if (left.hasExtents()) {
1134: for (int i = 0; i < left.extents.size(); i++) {
1135: extAlias = (TableAlias) left.extents.get(i);
1136: FieldDescriptor[] extKeys = getExtentFieldDescriptors(
1137: extAlias, (FieldDescriptor[]) leftKeys);
1138: rightCopy = right.copy("C" + i);
1139:
1140: // copies are treated like normal extents
1141: right.extents.add(rightCopy);
1142: right.extents.addAll(rightCopy.extents);
1143:
1144: addJoin(extAlias, extKeys, rightCopy, rightKeys, true,
1145: name);
1146: }
1147: }
1148: }
1149:
1150: /**
1151: * Get the FieldDescriptors of the extent based on the FieldDescriptors of the parent.
1152: */
1153: private FieldDescriptor[] getExtentFieldDescriptors(
1154: TableAlias extAlias, FieldDescriptor[] fds) {
1155: FieldDescriptor[] result = new FieldDescriptor[fds.length];
1156:
1157: for (int i = 0; i < fds.length; i++) {
1158: result[i] = extAlias.cld.getFieldDescriptorByName(fds[i]
1159: .getAttributeName());
1160: }
1161:
1162: return result;
1163: }
1164:
1165: private char getAliasChar() {
1166: char result = 'A';
1167:
1168: if (m_parentStatement != null) {
1169: result = (char) (m_parentStatement.getAliasChar() + 1);
1170: }
1171:
1172: return result;
1173: }
1174:
1175: /**
1176: * Create a TableAlias for path or userAlias
1177: * @param aCld
1178: * @param aPath
1179: * @param aUserAlias
1180: * @param hints a List os Class objects to be used as hints for path expressions
1181: * @return TableAlias
1182: *
1183: */
1184: private TableAlias createTableAlias(ClassDescriptor aCld,
1185: String aPath, String aUserAlias, List hints) {
1186: if (aUserAlias == null) {
1187: return createTableAlias(aCld, hints, aPath);
1188: } else {
1189: return createTableAlias(aCld, hints, aUserAlias
1190: + ALIAS_SEPARATOR + aPath);
1191: }
1192: }
1193:
1194: /**
1195: * Create new TableAlias for path
1196: * @param cld the class descriptor for the TableAlias
1197: * @param path the path from the target table of the query to this TableAlias.
1198: * @param hints a List of Class objects to be used on path expressions
1199: */
1200: private TableAlias createTableAlias(ClassDescriptor cld,
1201: List hints, String path) {
1202: TableAlias alias;
1203: boolean lookForExtents = false;
1204:
1205: if (!cld.getExtentClasses().isEmpty() && path.length() > 0) {
1206: lookForExtents = true;
1207: }
1208:
1209: String aliasName = String.valueOf(getAliasChar())
1210: + m_aliasCount++; // m_pathToAlias.size();
1211: alias = new TableAlias(cld, aliasName, lookForExtents, hints);
1212:
1213: setTableAliasForPath(path, hints, alias);
1214: return alias;
1215: }
1216:
1217: /**
1218: * Create a TableAlias for path or userAlias
1219: * @param aTable
1220: * @param aPath
1221: * @param aUserAlias
1222: * @return TableAlias
1223: */
1224: private TableAlias createTableAlias(String aTable, String aPath,
1225: String aUserAlias) {
1226: if (aUserAlias == null) {
1227: return createTableAlias(aTable, aPath);
1228: } else {
1229: return createTableAlias(aTable, aUserAlias
1230: + ALIAS_SEPARATOR + aPath);
1231: }
1232: }
1233:
1234: /**
1235: * Create new TableAlias for path
1236: * @param table the table name
1237: * @param path the path from the target table of the query to this TableAlias.
1238: */
1239: private TableAlias createTableAlias(String table, String path) {
1240: TableAlias alias;
1241:
1242: if (table == null) {
1243: getLogger().warn(
1244: "Creating TableAlias without table for path: "
1245: + path);
1246: }
1247:
1248: String aliasName = String.valueOf(getAliasChar())
1249: + m_aliasCount++; // + m_pathToAlias.size();
1250: alias = new TableAlias(table, aliasName);
1251: setTableAliasForPath(path, null, alias);
1252: m_logger.debug("createTableAlias2: path: " + path
1253: + " tableAlias: " + alias);
1254:
1255: return alias;
1256: }
1257:
1258: /**
1259: * Answer the TableAlias for aPath
1260: * @param aPath
1261: * @param hintClasses
1262: * @return TableAlias, null if none
1263: */
1264: private TableAlias getTableAliasForPath(String aPath,
1265: List hintClasses) {
1266: return (TableAlias) m_pathToAlias.get(buildAliasKey(aPath,
1267: hintClasses));
1268: }
1269:
1270: /**
1271: * Set the TableAlias for aPath
1272: * @param aPath
1273: * @param hintClasses
1274: * @param TableAlias
1275: */
1276: private void setTableAliasForPath(String aPath, List hintClasses,
1277: TableAlias anAlias) {
1278: m_pathToAlias.put(buildAliasKey(aPath, hintClasses), anAlias);
1279: }
1280:
1281: /**
1282: * Build the key for the TableAlias based on the path and the hints
1283: * @param aPath
1284: * @param hintClasses
1285: * @return the key for the TableAlias
1286: */
1287: private String buildAliasKey(String aPath, List hintClasses) {
1288: if (hintClasses == null || hintClasses.isEmpty()) {
1289: return aPath;
1290: }
1291:
1292: StringBuffer buf = new StringBuffer(aPath);
1293: for (Iterator iter = hintClasses.iterator(); iter.hasNext();) {
1294: Class hint = (Class) iter.next();
1295: buf.append(" ");
1296: buf.append(hint.getName());
1297: }
1298: return buf.toString();
1299: }
1300:
1301: /**
1302: * Answer the TableAlias for ClassDescriptor.
1303: */
1304: protected TableAlias getTableAliasForClassDescriptor(
1305: ClassDescriptor aCld) {
1306: return (TableAlias) m_cldToAlias.get(aCld);
1307: }
1308:
1309: /**
1310: * Set the TableAlias for ClassDescriptor
1311: */
1312: private void setTableAliasForClassDescriptor(ClassDescriptor aCld,
1313: TableAlias anAlias) {
1314: if (m_cldToAlias.get(aCld) == null) {
1315: m_cldToAlias.put(aCld, anAlias);
1316: }
1317: }
1318:
1319: /**
1320: * Answer the TableAlias for aPath or aUserAlias
1321: * @param aPath
1322: * @param aUserAlias
1323: * @param hintClasses
1324: * @return TableAlias, null if none
1325: */
1326: private TableAlias getTableAliasForPath(String aPath,
1327: String aUserAlias, List hintClasses) {
1328: if (aUserAlias == null) {
1329: return getTableAliasForPath(aPath, hintClasses);
1330: } else {
1331: return getTableAliasForPath(aUserAlias + ALIAS_SEPARATOR
1332: + aPath, hintClasses);
1333: }
1334: }
1335:
1336: /**
1337: * Answer the ClassDescriptor for itemClass for an ObjectReferenceDescriptor
1338: * check optional hint. The returned Class is to highest superclass contained in the hint list.
1339: * TODO: add super ClassDescriptor
1340: */
1341: private ClassDescriptor getItemClassDescriptor(
1342: ObjectReferenceDescriptor ord, List hintClasses) {
1343: DescriptorRepository repo = ord.getClassDescriptor()
1344: .getRepository();
1345:
1346: if (hintClasses == null || hintClasses.isEmpty()) {
1347: return repo.getDescriptorFor(ord.getItemClass());
1348: }
1349:
1350: Class resultClass = (Class) hintClasses.get(0);
1351:
1352: for (Iterator iter = hintClasses.iterator(); iter.hasNext();) {
1353: Class clazz = (Class) iter.next();
1354: Class super Clazz = clazz.getSuperclass();
1355:
1356: if (super Clazz != null
1357: && resultClass.equals(super Clazz.getSuperclass())) {
1358: continue; // skip if we already have a super superclass
1359: }
1360:
1361: if (hintClasses.contains(super Clazz)) {
1362: resultClass = super Clazz; // use superclass if it's in the hints
1363: }
1364: }
1365:
1366: return repo.getDescriptorFor(resultClass);
1367: }
1368:
1369: /**
1370: * Appends the ORDER BY clause for the Query.
1371: * <br>
1372: * If the orderByField is found in the list of selected fields it's index is added.
1373: * Otherwise it's name is added.
1374: * @param orderByFields
1375: * @param selectedFields the names of the fields in the SELECT clause
1376: * @param buf
1377: */
1378: protected void appendOrderByClause(List orderByFields,
1379: List selectedFields, StringBuffer buf) {
1380:
1381: if (orderByFields == null || orderByFields.size() == 0) {
1382: return;
1383: }
1384:
1385: buf.append(" ORDER BY ");
1386: for (int i = 0; i < orderByFields.size(); i++) {
1387: FieldHelper cf = (FieldHelper) orderByFields.get(i);
1388: int colNumber = selectedFields.indexOf(cf.name);
1389:
1390: if (i > 0) {
1391: buf.append(",");
1392: }
1393:
1394: if (colNumber >= 0) {
1395: buf.append(colNumber + 1);
1396: } else {
1397: appendColName(cf.name, false, null, buf);
1398: }
1399:
1400: if (!cf.isAscending) {
1401: buf.append(" DESC");
1402: }
1403: }
1404: }
1405:
1406: /**
1407: * Appends the GROUP BY clause for the Query
1408: * @param groupByFields
1409: * @param buf
1410: */
1411: protected void appendGroupByClause(List groupByFields,
1412: StringBuffer buf) {
1413: if (groupByFields == null || groupByFields.size() == 0) {
1414: return;
1415: }
1416:
1417: buf.append(" GROUP BY ");
1418: for (int i = 0; i < groupByFields.size(); i++) {
1419: FieldHelper cf = (FieldHelper) groupByFields.get(i);
1420:
1421: if (i > 0) {
1422: buf.append(",");
1423: }
1424:
1425: appendColName(cf.name, false, null, buf);
1426: }
1427: }
1428:
1429: /**
1430: * Appends to the statement table and all tables joined to it.
1431: * @param alias the table alias
1432: * @param where append conditions for WHERE clause here
1433: */
1434: protected void appendTableWithJoins(TableAlias alias,
1435: StringBuffer where, StringBuffer buf) {
1436: int stmtFromPos = 0;
1437: byte joinSyntax = getJoinSyntaxType();
1438:
1439: if (joinSyntax == SQL92_JOIN_SYNTAX) {
1440: stmtFromPos = buf.length(); // store position of join (by: Terry Dexter)
1441: }
1442:
1443: if (alias == getRoot()) {
1444: // BRJ: also add indirection table to FROM-clause for MtoNQuery
1445: if (getQuery() instanceof MtoNQuery) {
1446: MtoNQuery mnQuery = (MtoNQuery) m_query;
1447: buf.append(getTableAliasForPath(
1448: mnQuery.getIndirectionTable(), null)
1449: .getTableAndAlias());
1450: buf.append(", ");
1451: }
1452: buf.append(alias.getTableAndAlias());
1453: } else if (joinSyntax != SQL92_NOPAREN_JOIN_SYNTAX) {
1454: buf.append(alias.getTableAndAlias());
1455: }
1456:
1457: if (!alias.hasJoins()) {
1458: return;
1459: }
1460:
1461: for (Iterator it = alias.iterateJoins(); it.hasNext();) {
1462: Join join = (Join) it.next();
1463:
1464: if (joinSyntax == SQL92_JOIN_SYNTAX) {
1465: appendJoinSQL92(join, where, buf);
1466: if (it.hasNext()) {
1467: buf.insert(stmtFromPos, "(");
1468: buf.append(")");
1469: }
1470: } else if (joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX) {
1471: appendJoinSQL92NoParen(join, where, buf);
1472: } else {
1473: appendJoin(where, buf, join);
1474: }
1475:
1476: }
1477: }
1478:
1479: /**
1480: * Append Join for non SQL92 Syntax
1481: */
1482: private void appendJoin(StringBuffer where, StringBuffer buf,
1483: Join join) {
1484: buf.append(",");
1485: appendTableWithJoins(join.right, where, buf);
1486: if (where.length() > 0) {
1487: where.append(" AND ");
1488: }
1489: join.appendJoinEqualities(where);
1490: }
1491:
1492: /**
1493: * Append Join for SQL92 Syntax
1494: */
1495: private void appendJoinSQL92(Join join, StringBuffer where,
1496: StringBuffer buf) {
1497: if (join.isOuter) {
1498: buf.append(" LEFT OUTER JOIN ");
1499: } else {
1500: buf.append(" INNER JOIN ");
1501: }
1502: if (join.right.hasJoins()) {
1503: buf.append("(");
1504: appendTableWithJoins(join.right, where, buf);
1505: buf.append(")");
1506: } else {
1507: appendTableWithJoins(join.right, where, buf);
1508: }
1509: buf.append(" ON ");
1510: join.appendJoinEqualities(buf);
1511: }
1512:
1513: /**
1514: * Append Join for SQL92 Syntax without parentheses
1515: */
1516: private void appendJoinSQL92NoParen(Join join, StringBuffer where,
1517: StringBuffer buf) {
1518: if (join.isOuter) {
1519: buf.append(" LEFT OUTER JOIN ");
1520: } else {
1521: buf.append(" INNER JOIN ");
1522: }
1523:
1524: buf.append(join.right.getTableAndAlias());
1525: buf.append(" ON ");
1526: join.appendJoinEqualities(buf);
1527:
1528: appendTableWithJoins(join.right, where, buf);
1529: }
1530:
1531: /**
1532: * Build the tree of joins for the given criteria
1533: */
1534: private void buildJoinTree(Criteria crit) {
1535: Enumeration e = crit.getElements();
1536:
1537: while (e.hasMoreElements()) {
1538: Object o = e.nextElement();
1539: if (o instanceof Criteria) {
1540: buildJoinTree((Criteria) o);
1541: } else {
1542: SelectionCriteria c = (SelectionCriteria) o;
1543:
1544: // BRJ skip SqlCriteria
1545: if (c instanceof SqlCriteria) {
1546: continue;
1547: }
1548:
1549: // BRJ: Outer join for OR
1550: boolean useOuterJoin = (crit.getType() == Criteria.OR);
1551:
1552: // BRJ: do not build join tree for subQuery attribute
1553: if (c.getAttribute() != null
1554: && c.getAttribute() instanceof String) {
1555: //buildJoinTreeForColumn((String) c.getAttribute(), useOuterJoin, c.getAlias(), c.getPathClasses());
1556: buildJoinTreeForColumn((String) c.getAttribute(),
1557: useOuterJoin, c.getUserAlias(), c
1558: .getPathClasses());
1559: }
1560: if (c instanceof FieldCriteria) {
1561: FieldCriteria cc = (FieldCriteria) c;
1562: buildJoinTreeForColumn((String) cc.getValue(),
1563: useOuterJoin, c.getUserAlias(), c
1564: .getPathClasses());
1565: }
1566: }
1567: }
1568: }
1569:
1570: /**
1571: * build the Join-Information for name
1572: * functions and the last segment are removed
1573: * ie: avg(accounts.amount) -> accounts
1574: */
1575: private void buildJoinTreeForColumn(String aColName,
1576: boolean useOuterJoin, UserAlias aUserAlias, Map pathClasses) {
1577: String pathName = SqlHelper.cleanPath(aColName);
1578: int sepPos = pathName.lastIndexOf(".");
1579:
1580: if (sepPos >= 0) {
1581: getTableAlias(pathName.substring(0, sepPos), useOuterJoin,
1582: aUserAlias, new String[] { pathName
1583: .substring(sepPos + 1) }, pathClasses);
1584: }
1585: }
1586:
1587: /**
1588: * build the Join-Information if a super reference exists
1589: *
1590: * @param left
1591: * @param cld
1592: * @param name
1593: */
1594: protected void buildSuperJoinTree(TableAlias left,
1595: ClassDescriptor cld, String name, boolean useOuterJoin) {
1596: ClassDescriptor super Cld = cld.getSuperClassDescriptor();
1597: if (super Cld != null) {
1598: SuperReferenceDescriptor super Ref = cld.getSuperReference();
1599: FieldDescriptor[] leftFields = super Ref
1600: .getForeignKeyFieldDescriptors(cld);
1601: TableAlias base_alias = getTableAliasForPath(name, null,
1602: null);
1603: String aliasName = String.valueOf(getAliasChar())
1604: + m_aliasCount++;
1605: TableAlias right = new TableAlias(super Cld, aliasName,
1606: useOuterJoin, null);
1607:
1608: Join join1to1 = new Join(left, leftFields, right, super Cld
1609: .getPkFields(), useOuterJoin, "superClass");
1610: base_alias.addJoin(join1to1);
1611:
1612: buildSuperJoinTree(right, super Cld, name, useOuterJoin);
1613: }
1614: }
1615:
1616: /**
1617: * build the Join-Information for Subclasses having a super reference to this class
1618: *
1619: * @param left
1620: * @param cld
1621: * @param name
1622: */
1623: private void buildMultiJoinTree(TableAlias left,
1624: ClassDescriptor cld, String name, boolean useOuterJoin) {
1625: DescriptorRepository repository = cld.getRepository();
1626: Class[] multiJoinedClasses = repository
1627: .getSubClassesMultipleJoinedTables(cld, false);
1628:
1629: for (int i = 0; i < multiJoinedClasses.length; i++) {
1630: ClassDescriptor subCld = repository
1631: .getDescriptorFor(multiJoinedClasses[i]);
1632: SuperReferenceDescriptor srd = subCld.getSuperReference();
1633: if (srd != null) {
1634: FieldDescriptor[] leftFields = subCld.getPkFields();
1635: FieldDescriptor[] rightFields = srd
1636: .getForeignKeyFieldDescriptors(subCld);
1637: TableAlias base_alias = getTableAliasForPath(name,
1638: null, null);
1639:
1640: String aliasName = String.valueOf(getAliasChar())
1641: + m_aliasCount++;
1642: TableAlias right = new TableAlias(subCld, aliasName,
1643: false, null);
1644:
1645: Join join1to1 = new Join(left, leftFields, right,
1646: rightFields, useOuterJoin, "subClass");
1647: base_alias.addJoin(join1to1);
1648:
1649: buildMultiJoinTree(right, subCld, name, useOuterJoin);
1650: }
1651: }
1652: }
1653:
1654: /**
1655: * First reduce the Criteria to the normal disjunctive form, then
1656: * calculate the necessary tree of joined tables for each item, then group
1657: * items with the same tree of joined tables.
1658: */
1659: protected void splitCriteria() {
1660: Criteria whereCrit = getQuery().getCriteria();
1661: Criteria havingCrit = getQuery().getHavingCriteria();
1662:
1663: if (whereCrit == null || whereCrit.isEmpty()) {
1664: getJoinTreeToCriteria().put(getRoot(), null);
1665: } else {
1666: // TODO: parameters list shold be modified when the form is reduced to DNF.
1667: getJoinTreeToCriteria().put(getRoot(), whereCrit);
1668: buildJoinTree(whereCrit);
1669: }
1670:
1671: if (havingCrit != null && !havingCrit.isEmpty()) {
1672: buildJoinTree(havingCrit);
1673: }
1674:
1675: }
1676:
1677: /**
1678: * Gets the query.
1679: * @return Returns a Query
1680: */
1681: protected QueryByCriteria getQuery() {
1682: return m_query;
1683: }
1684:
1685: /**
1686: * Gets the root.
1687: * @return Returns a TableAlias
1688: */
1689: protected TableAlias getRoot() {
1690: return m_root;
1691: }
1692:
1693: /**
1694: * Sets the root.
1695: * @param root The root to set
1696: */
1697: protected void setRoot(TableAlias root) {
1698: this .m_root = root;
1699: }
1700:
1701: /**
1702: * Gets the search table of this query.
1703: * @return Returns a TableAlias
1704: */
1705: protected TableAlias getSearchTable() {
1706: return m_search;
1707: }
1708:
1709: /**
1710: * Gets the joinTreeToCriteria.
1711: * @return Returns a HashMap
1712: */
1713: protected HashMap getJoinTreeToCriteria() {
1714: return m_joinTreeToCriteria;
1715: }
1716:
1717: /**
1718: * Returns the joinSyntaxType.
1719: * @return byte
1720: */
1721: protected byte getJoinSyntaxType() {
1722: return m_platform.getJoinSyntaxType();
1723: }
1724:
1725: /**
1726: * Returns the logger.
1727: * @return Logger
1728: */
1729: protected Logger getLogger() {
1730: return m_logger;
1731: }
1732:
1733: public String getStatement() {
1734: if (sql == null) {
1735: sql = buildStatement();
1736: }
1737: return sql;
1738: }
1739:
1740: /**
1741: * Build the SQL String.
1742: * @return SQL String
1743: */
1744: protected abstract String buildStatement();
1745:
1746: //-----------------------------------------------------------------
1747: // ------------------- Inner classes ------------------------------
1748: //-----------------------------------------------------------------
1749:
1750: /**
1751: * This class is a helper to return TableAlias and PathInfo
1752: */
1753: static final class AttributeInfo {
1754: TableAlias tableAlias;
1755: PathInfo pathInfo;
1756: }
1757:
1758: /**
1759: * This class represents one table (possibly with alias) in the SQL query
1760: */
1761: final class TableAlias {
1762: Logger logger = LoggerFactory.getLogger(TableAlias.class);
1763: ClassDescriptor cld; // Is null for indirection table of M:N relation
1764: String table;
1765: final String alias;
1766: List extents = new ArrayList();
1767: List hints = new ArrayList();
1768: List joins;
1769:
1770: TableAlias(String aTable, String anAlias) {
1771: this .cld = null;
1772: this .table = aTable;
1773: this .alias = anAlias;
1774: }
1775:
1776: TableAlias(ClassDescriptor aCld, String anAlias) {
1777: this (aCld, anAlias, false, null);
1778: }
1779:
1780: TableAlias(ClassDescriptor aCld, String anAlias,
1781: boolean lookForExtents, List hints) {
1782: this .cld = aCld;
1783: this .table = aCld.getFullTableName();
1784: this .alias = anAlias;
1785: boolean useHintsOnExtents = false;
1786:
1787: // BRJ: store alias map of in enclosing class
1788: setTableAliasForClassDescriptor(aCld, this );
1789:
1790: //LEANDRO: use hints
1791: if (hints != null && hints.size() > 0) {
1792: useHintsOnExtents = true;
1793: }
1794:
1795: logger.debug("TableAlias(): using hints ? "
1796: + useHintsOnExtents);
1797:
1798: // BRJ : build alias for extents, only one per Table
1799: if (lookForExtents) {
1800: ClassDescriptor[] extCLDs = (ClassDescriptor[]) aCld
1801: .getRepository()
1802: .getAllConcreteSubclassDescriptors(aCld)
1803: .toArray(new ClassDescriptor[0]);
1804:
1805: ClassDescriptor extCd;
1806: Class extClass;
1807: String extTable;
1808: Map extMap = new HashMap(); // only one Alias per Table
1809: int firstNonAbstractExtentIndex = 0;
1810:
1811: for (int i = 0; i < extCLDs.length; i++) {
1812: extCd = extCLDs[i];
1813: extClass = extCd.getClassOfObject();
1814: if (useHintsOnExtents
1815: && (!hints.contains(extClass))) {
1816: //LEANDRO: don't include this class
1817: logger.debug("Skipping class [" + extClass
1818: + "] from extents List");
1819: firstNonAbstractExtentIndex++;
1820: continue;
1821: }
1822: extTable = extCd.getFullTableName();
1823:
1824: // BRJ : Use the first non abstract extent
1825: // if the main cld is abstract
1826: //logger.debug("cld abstract["+aCld.isAbstract()+"] i["+i+"] index ["+firtsNonAbstractExtentIndex+"]");
1827: if (aCld.isAbstract()
1828: && i == firstNonAbstractExtentIndex) {
1829: this .cld = extCd;
1830: this .table = extTable;
1831: } else {
1832: // Add a new extent entry only if the table of the extent
1833: // does not match the table of the 'base' class.
1834: if (extMap.get(extTable) == null
1835: && !extTable.equals(table)) {
1836: extMap.put(extTable, new TableAlias(extCd,
1837: anAlias + "E" + i, false, hints));
1838: }
1839: }
1840: }
1841: extents.addAll(extMap.values());
1842: }
1843:
1844: if (cld == null) {
1845: throw new PersistenceBrokerSQLException(
1846: "Table is NULL for alias: " + alias);
1847: }
1848: }
1849:
1850: ClassDescriptor getClassDescriptor() {
1851: return cld;
1852: }
1853:
1854: String getTableAndAlias() {
1855: return table + " " + alias;
1856: }
1857:
1858: boolean hasExtents() {
1859: return (!extents.isEmpty());
1860: }
1861:
1862: Iterator iterateExtents() {
1863: return extents.iterator();
1864: }
1865:
1866: /**
1867: * Copy the Alias and all it's extents adding a Postfix
1868: * Joins are not copied.
1869: */
1870: TableAlias copy(String aPostfix) {
1871: TableAlias result, temp;
1872: Iterator iter = iterateExtents();
1873:
1874: if (cld == null) {
1875: result = new TableAlias(table, alias + aPostfix);
1876: } else {
1877: result = new TableAlias(cld, alias + aPostfix);
1878: }
1879:
1880: while (iter.hasNext()) {
1881: temp = (TableAlias) iter.next();
1882: result.extents.add(temp.copy(aPostfix));
1883: }
1884:
1885: return result;
1886: }
1887:
1888: void addJoin(Join join) {
1889: if (joins == null) {
1890: joins = new ArrayList();
1891: }
1892: joins.add(join);
1893: }
1894:
1895: Iterator iterateJoins() {
1896: return joins.iterator();
1897: }
1898:
1899: boolean hasJoins() {
1900: return (joins != null);
1901: }
1902:
1903: /**
1904: * Get the Join ponting to anAlias.
1905: */
1906: Join getJoin(TableAlias anAlias) {
1907: Join result = null;
1908:
1909: if (joins != null) {
1910: Iterator iter = joins.iterator();
1911: while (iter.hasNext()) {
1912: Join join = (Join) iter.next();
1913: if (join.right.equals(anAlias)) {
1914: result = join;
1915: break;
1916: }
1917: }
1918: }
1919: return result;
1920: }
1921:
1922: public String toString() {
1923: StringBuffer sb = new StringBuffer(1024);
1924: boolean first = true;
1925:
1926: sb.append(getTableAndAlias());
1927: if (joins != null) {
1928: sb.append(" [");
1929: for (Iterator it = joins.iterator(); it.hasNext();) {
1930: Join join = (Join) it.next();
1931:
1932: if (first) {
1933: first = false;
1934: } else {
1935: sb.append(", ");
1936: }
1937: sb.append("-(");
1938: sb.append(join.name);
1939: sb.append(")->");
1940: sb.append(join.right);
1941: }
1942: sb.append("]");
1943: }
1944: return sb.toString();
1945: }
1946:
1947: public boolean equals(Object obj) {
1948: TableAlias t = (TableAlias) obj;
1949:
1950: return table.equals(t.table); // BRJ: check table only
1951: }
1952:
1953: public int hashCode() {
1954: return table.hashCode();
1955: }
1956:
1957: }
1958:
1959: /**
1960: * This class represents join between two TableAliases
1961: */
1962: final class Join {
1963: final TableAlias left;
1964: final String[] leftKeys;
1965: final TableAlias right;
1966: final String[] rightKeys;
1967: boolean isOuter;
1968: /** This is the name of the field corresponding to this join */
1969: final String name;
1970:
1971: /**
1972: * leftKeys and rightKeys should be either FieldDescriptor[] or String[]
1973: */
1974: Join(TableAlias left, Object[] leftKeys, TableAlias right,
1975: Object[] rightKeys, boolean isOuter, String name) {
1976: this .left = left;
1977: this .leftKeys = getColumns(leftKeys);
1978: this .right = right;
1979: this .rightKeys = getColumns(rightKeys);
1980: this .isOuter = isOuter;
1981: this .name = name;
1982: }
1983:
1984: private String[] getColumns(Object[] keys) {
1985: String[] columns = new String[keys.length];
1986:
1987: if (keys instanceof FieldDescriptor[]) {
1988: FieldDescriptor[] kd = (FieldDescriptor[]) keys;
1989: for (int i = 0; i < columns.length; i++) {
1990: columns[i] = kd[i].getColumnName();
1991: }
1992: } else {
1993: for (int i = 0; i < columns.length; i++) {
1994: columns[i] = keys[i].toString();
1995: }
1996: }
1997: return columns;
1998: }
1999:
2000: void appendJoinEqualities(StringBuffer buf) {
2001: byte joinSyntax = getJoinSyntaxType();
2002:
2003: for (int i = 0; i < leftKeys.length; i++) {
2004: if (i > 0) {
2005: buf.append(" AND ");
2006: }
2007: buf.append(left.alias);
2008: buf.append(".");
2009: buf.append(leftKeys[i]);
2010:
2011: if (isOuter && joinSyntax == SYBASE_JOIN_SYNTAX) {
2012: buf.append("*=");
2013: } else {
2014: buf.append("=");
2015: }
2016:
2017: buf.append(right.alias);
2018: buf.append(".");
2019: buf.append(rightKeys[i]);
2020:
2021: if (isOuter && joinSyntax == ORACLE_JOIN_SYNTAX) {
2022: buf.append("(+)");
2023: }
2024: }
2025: }
2026:
2027: public boolean equals(Object obj) {
2028: Join j = (Join) obj;
2029: return name.equals(j.name) && (isOuter == j.isOuter)
2030: && right.equals(j.right);
2031: }
2032:
2033: public int hashCode() {
2034: return name.hashCode();
2035: }
2036:
2037: public String toString() {
2038: return left.alias + " -> " + right.alias;
2039: }
2040: }
2041: }
|