0001: /*
0002: * Copyright (c) 1998 - 2005 Versant Corporation
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * Versant Corporation - initial API and implementation
0010: */
0011: package com.versant.core.jdbc.sql.exp;
0012:
0013: import com.versant.core.common.Debug;
0014: import com.versant.core.metadata.ClassMetaData;
0015: import com.versant.core.metadata.MDStatics;
0016: import com.versant.core.jdo.query.*;
0017: import com.versant.core.jdbc.metadata.*;
0018: import com.versant.core.jdbc.sql.SqlDriver;
0019: import com.versant.core.jdbc.query.JDOQLNodeToSqlExp;
0020: import com.versant.core.jdbc.fetch.FetchSpec;
0021: import com.versant.core.util.CharBuf;
0022:
0023: import java.util.Map;
0024:
0025: import com.versant.core.common.BindingSupportImpl;
0026:
0027: /**
0028: * A 'select ... from ... where ... order by ...' expression.
0029: */
0030: public class SelectExp extends LeafExp {
0031: public static final ColumnStruct[] EMPTY_COLUMS_STRUCT = new ColumnStruct[0];
0032:
0033: /**
0034: * Linked list of expressions to select (null if none).
0035: */
0036: public SqlExp selectList;
0037: /**
0038: * Is this a 'select distinct'?
0039: */
0040: public boolean distinct;
0041: /**
0042: * Is this a 'select for update'?
0043: */
0044: public boolean forUpdate;
0045: /**
0046: * The table to select from.
0047: */
0048: public JdbcTable table;
0049: /**
0050: * The field that was navigated to get to this table (null if none).
0051: */
0052: public JdbcField jdbcField;
0053: /**
0054: * The variable this expression belongs to (null if none).
0055: */
0056: public VarNode var;
0057: /**
0058: * The alias assigned to our table (null if none).
0059: */
0060: public String alias;
0061: /**
0062: * The 'join' expression for a sub select (null if none). This will end
0063: * up in the where clause but is kept separately so nested sub selects can
0064: * be converted into joins easily.
0065: */
0066: public SqlExp subSelectJoinExp;
0067: /**
0068: * The where clause expression (null if none).
0069: */
0070: public SqlExp whereExp;
0071: /**
0072: * Linked list of expressions to order by (null if none).
0073: */
0074: public SqlExp orderByList;
0075: public SqlExp resultList;
0076: public SqlExp groupByList;
0077: public HavingExp havingExp;
0078: /**
0079: * Is table involved in an outer join as the outer table?
0080: */
0081: public boolean outer;
0082: /**
0083: * Linked list of joins (null if none). Note that the selectExp for
0084: * each entry in the list may have its own joinList and so on
0085: * recusively (i.e. this is actually a tree).
0086: */
0087: public Join joinList;
0088:
0089: /**
0090: * Index of the first column in the select list as of the last call to
0091: * appendSQL.
0092: */
0093: public int selectListStartIndex;
0094: /**
0095: * Index of the last charcter of the first column in the select list + 1
0096: * as of the last call to appendSQL.
0097: */
0098: public int selectListFirstColEndIndex;
0099: /**
0100: * Index of the last column in the select list + 1 as of the last call
0101: * to appendSQL.
0102: */
0103: public int selectListEndIndex;
0104: /**
0105: * Index of the start of the "order by ..." clause as of the last call to
0106: * appendSQL.
0107: */
0108: public int orderByStartIndex;
0109: /**
0110: * Index of the end of the "order by ..." clause + 1 as of the last call to
0111: * appendSQL.
0112: */
0113: public int orderByEndIndex;
0114:
0115: public int selectListCountBeforeAggregate;
0116:
0117: public FetchSpec fetchSpec;
0118:
0119: private static final char ALIAS_PREPEND_CONSTANT = 'x';
0120:
0121: public SelectExp() {
0122: }
0123:
0124: public SqlExp createInstance() {
0125: return new SelectExp();
0126: }
0127:
0128: public SqlExp getClone(SqlExp clone, Map cloneMap) {
0129: SelectExp cst = (SelectExp) clone;
0130: super .getClone(cst, cloneMap);
0131:
0132: if (selectList != null)
0133: cst.selectList = createClone(selectList, cloneMap);
0134: cst.distinct = distinct;
0135: cst.forUpdate = forUpdate;
0136: cst.table = table;
0137: cst.jdbcField = jdbcField;
0138: cst.var = var;
0139: cst.alias = alias;
0140: if (subSelectJoinExp != null)
0141: cst.subSelectJoinExp = createClone(subSelectJoinExp,
0142: cloneMap);
0143: if (whereExp != null)
0144: cst.whereExp = createClone(whereExp, cloneMap);
0145: if (orderByList != null)
0146: cst.orderByList = createClone(orderByList, cloneMap);
0147: cst.outer = outer;
0148: if (joinList != null)
0149: cst.joinList = joinList.getClone(cloneMap);
0150: cst.selectListStartIndex = selectListStartIndex;
0151: cst.selectListEndIndex = selectListEndIndex;
0152: cst.orderByStartIndex = orderByStartIndex;
0153: cst.orderByEndIndex = orderByEndIndex;
0154:
0155: return cst;
0156: }
0157:
0158: public String toString() {
0159: return super .toString() + (outer ? " OUTER " : " ")
0160: + table.name
0161: + (alias == null ? "" : "aliased to " + alias)
0162: + (jdbcField != null ? " " + jdbcField : "")
0163: + (var != null ? " " + var : "");
0164: }
0165:
0166: /**
0167: * Count the number of columns in the select list and all joined to lists.
0168: */
0169: public int getSelectListColumnCount() {
0170: if (selectList == null)
0171: return 0;
0172: int count = 0;
0173: for (SqlExp se = selectList; se != null; se = se.next) {
0174: count++;
0175: }
0176: count += countSelect(joinList);
0177: return count;
0178: }
0179:
0180: /**
0181: * Dump debugging info to System.out.
0182: */
0183: public void dump(String indent) {
0184: Debug.OUT.println(indent + this );
0185: String is = indent + " ";
0186: if (selectList != null) {
0187: Debug.OUT.println(indent + "selectList");
0188: selectList.dumpList(is);
0189: }
0190: if (joinList != null) {
0191: Debug.OUT.println(indent + "joinList");
0192: joinList.dumpList(is);
0193: }
0194: if (subSelectJoinExp != null) {
0195: Debug.OUT.println(indent + "subSelectJoinExp");
0196: subSelectJoinExp.dump(is);
0197: }
0198: if (whereExp != null) {
0199: Debug.OUT.println(indent + "whereExp");
0200: whereExp.dump(is);
0201: }
0202: if (orderByList != null) {
0203: Debug.OUT.println(indent + "orderByList");
0204: orderByList.dumpList(is);
0205: }
0206: }
0207:
0208: /**
0209: * Add a Join to end of the joinList.
0210: */
0211: public void addJoin(Join join) {
0212: if (joinList == null) {
0213: joinList = join;
0214: } else {
0215: for (Join j = joinList;;) {
0216: Join next = j.next;
0217: if (next == null) {
0218: j.next = join;
0219: return;
0220: }
0221: j = next;
0222: }
0223: }
0224: }
0225:
0226: /**
0227: * Add a Join to end of the joinList. This is an attempt to not add uncess.
0228: * joins.
0229: */
0230: public void addJoinMerge(Join aJoin) {
0231: Join origJoin = aJoin;
0232: if (joinList == null) {
0233: joinList = aJoin;
0234: } else {
0235: getTailJoin(joinList).next = aJoin;
0236: addJoinImp(joinList, aJoin, origJoin);
0237: }
0238: }
0239:
0240: private static Join getPrevJoin(Join joinList, Join aJoin) {
0241: if (joinList.next == null) {
0242: throw BindingSupportImpl.getInstance().internal("");
0243: }
0244:
0245: for (Join j = joinList;;) {
0246: if (j.next == aJoin) {
0247: return j;
0248: }
0249: j = j.next;
0250: if (j == null)
0251: return null;
0252: }
0253: }
0254:
0255: private static Join getTailJoin(Join joinList) {
0256: for (Join j = joinList;;) {
0257: if (j.next == null)
0258: return j;
0259: j = j.next;
0260: }
0261: }
0262:
0263: private static void addJoinImp(Join currentJoin, Join aJoinToAdd,
0264: Join origJoin) {
0265: Join j = currentJoin;
0266: Join restartJoin = null;
0267: Join stopJoin = getTailJoin(currentJoin);
0268: for (; j != null;) {
0269: if (aJoinToAdd == null) {
0270: break;
0271: }
0272: if (Join.isCurrentEqaul(j, aJoinToAdd)) {
0273: /**
0274: * Update the whereExp of the origJoin with the new selectExp
0275: */
0276: if (origJoin.selectExp.whereExp != null) {
0277: origJoin.selectExp.whereExp.replaceSelectExpRef(
0278: origJoin.selectExp, j.selectExp);
0279: }
0280:
0281: Join subCurrentJoin = j.selectExp.joinList;
0282: Join subJoinToAdd = aJoinToAdd.selectExp.joinList;
0283:
0284: if (subCurrentJoin == null && subJoinToAdd == null) {
0285: } else if (subJoinToAdd == null) {
0286: } else if (subCurrentJoin == null) {
0287: //just set it
0288: j.selectExp.joinList = aJoinToAdd.selectExp.joinList;
0289: ((JoinExp) aJoinToAdd.selectExp.joinList.exp)
0290: .setLeftTable(j.selectExp);
0291: } else {
0292: addJoinImp(subJoinToAdd, subCurrentJoin, origJoin);
0293: }
0294: Join tmpJoin = aJoinToAdd.next;
0295: /**
0296: * Fix up the lList
0297: */
0298: getPrevJoin(currentJoin, aJoinToAdd).next = aJoinToAdd.next;
0299: aJoinToAdd.next = null;
0300:
0301: aJoinToAdd = tmpJoin;
0302: j = j.next;
0303: } else {
0304: if (j.next == stopJoin) {
0305: //no match found so move to next aJoinToAdd and to currentJoin
0306: aJoinToAdd = aJoinToAdd.next;
0307: j = restartJoin;
0308: } else {
0309: j = j.next;
0310: }
0311: }
0312:
0313: }
0314: }
0315:
0316: /**
0317: * Add a join to the end of the join list.
0318: */
0319: public Join addJoin(JdbcColumn[] leftCols, JdbcColumn[] rightCols,
0320: SelectExp right) {
0321: if (Debug.DEBUG) {
0322: System.out.println("SelectExp.addJoin from " + table.name
0323: + " to " + right.table.name);
0324: String join = "";
0325: if (right.outer) {
0326: join = "OUTER";
0327: } else {
0328: join = "INNER";
0329: }
0330: System.out.println("--- SelectExp.addJoin from "
0331: + leftCols[0].table.name + " -- " + join + " --> "
0332: + right.table.name);
0333: }
0334: Join j = new Join();
0335: j.exp = createJoinExp(leftCols, rightCols, right);
0336: j.selectExp = right;
0337: addJoin(j);
0338: return j;
0339: }
0340:
0341: /**
0342: * Search our join list for a join for the field. This is not recursive
0343: * i.e. it only finds joins in our join list. Returns null if not found.
0344: * @see #findJoinRec(com.versant.core.jdbc.metadata.JdbcField)
0345: */
0346: public Join findJoin(JdbcField jdbcField) {
0347: for (Join j = joinList; j != null; j = j.next) {
0348: if (j.selectExp.jdbcField == jdbcField)
0349: return j;
0350: }
0351: return null;
0352: }
0353:
0354: /**
0355: * Find a join by table and refField.
0356: */
0357: public Join findJoin(JdbcTable table, JdbcField jdbcField) {
0358: for (Join j = joinList; j != null; j = j.next) {
0359: if (j.selectExp.jdbcField == jdbcField
0360: && j.selectExp.table == table)
0361: return j;
0362: }
0363: return null;
0364: }
0365:
0366: public SelectExp findTableRecursive(JdbcTable t, JdbcField field) {
0367: if (t == table && jdbcField == field)
0368: return this ;
0369: for (Join j = joinList; j != null; j = j.next) {
0370: if (j.selectExp.table == t
0371: && j.selectExp.jdbcField == field)
0372: return j.selectExp;
0373: }
0374:
0375: for (Join j = joinList; j != null; j = j.next) {
0376: SelectExp se = j.selectExp.findTableRecursive(t, field);
0377: if (se != null)
0378: return se;
0379: }
0380: return null;
0381: }
0382:
0383: /**
0384: * Recursively search our join list for a join for the field. Returns null
0385: * if not found.
0386: * @see #findJoin(com.versant.core.jdbc.metadata.JdbcField)
0387: */
0388: public Join findJoinRec(JdbcField jdbcField) {
0389: for (Join j = joinList; j != null; j = j.next) {
0390: if (j.selectExp.jdbcField == jdbcField)
0391: return j;
0392: Join ans = j.selectExp.findJoinRec(jdbcField);
0393: if (ans != null)
0394: return ans;
0395: }
0396: return null;
0397: }
0398:
0399: /**
0400: * Search our join list for a join to the expression. This is not recursive
0401: * i.e. it only finds joins in our join list. Returns null if not found.
0402: */
0403: public Join findJoin(SelectExp se) {
0404: for (Join j = joinList; j != null; j = j.next) {
0405: if (j.selectExp == se)
0406: return j;
0407: }
0408: return null;
0409: }
0410:
0411: /**
0412: * If our table is t then return this. Otherwise search our join list
0413: * for a join for the table. This is not recursive i.e. it only finds
0414: * tables in our join list. Returns null if not found.
0415: */
0416: public SelectExp findTable(JdbcTable t) {
0417: if (t == table) {
0418: return this ;
0419: }
0420: for (Join j = joinList; j != null; j = j.next) {
0421: if (j.selectExp.table == t) {
0422: return j.selectExp;
0423: }
0424: }
0425: return null;
0426: }
0427:
0428: public SelectExp findTableRecursive(JdbcTable t) {
0429: if (t == table)
0430: return this ;
0431: for (Join j = joinList; j != null; j = j.next) {
0432: if (j.selectExp.table == t)
0433: return j.selectExp;
0434: }
0435:
0436: for (Join j = joinList; j != null; j = j.next) {
0437: SelectExp se = j.selectExp.findTable(t);
0438: if (se != null)
0439: return se;
0440: }
0441: return null;
0442: }
0443:
0444: /**
0445: * Create an expression to join us to right.
0446: */
0447: public SqlExp createJoinExp(JdbcColumn[] leftCols,
0448: JdbcColumn[] rightCols, SelectExp right) {
0449: if (leftCols.length == 1) {
0450: return new JoinExp(leftCols[0], this , rightCols[0], right);
0451: } else {
0452: JoinExp first = new JoinExp(leftCols[0], this ,
0453: rightCols[0], right);
0454: int nc = leftCols.length;
0455: SqlExp j = first;
0456: for (int i = 1; i < nc; i++) {
0457: j = j.next = new JoinExp(leftCols[i], this ,
0458: rightCols[i], right);
0459: }
0460: return new AndJoinExp(first);
0461: }
0462: }
0463:
0464: public Join getLastJoin() {
0465: for (Join j = joinList; j != null; j = j.next) {
0466: if (j.next == null) {
0467: return j;
0468: }
0469: }
0470: return null;
0471: }
0472:
0473: /**
0474: * Create an aliases for any tables we may have if we do not already have
0475: * an alias.
0476: */
0477: public int createAlias(int index) {
0478: if (alias == null) {
0479: if (index < 26) {
0480: alias = new String(new char[] { (char) (index + 'a') });
0481: } else {
0482: alias = "t" + index;
0483: }
0484: ++index;
0485: for (Join j = joinList; j != null; j = j.next) {
0486: index = j.createAlias(index);
0487: }
0488: for (SqlExp e = whereExp; e != null; e = e.next) {
0489: index = e.createAlias(index);
0490: }
0491: }
0492: return index;
0493: }
0494:
0495: /**
0496: * Replace any references to old with nw. This is used when redundant
0497: * joins are removed.
0498: */
0499: public void replaceSelectExpRef(SelectExp old, SelectExp nw) {
0500: if (whereExp != null)
0501: whereExp.replaceSelectExpRef(old, nw);
0502: if (subSelectJoinExp != null)
0503: subSelectJoinExp.replaceSelectExpRef(old, nw);
0504: }
0505:
0506: /**
0507: * Normalize this node i.e. transform it into its simplist possible form.
0508: * This will turn sub selects into joins and so on.
0509: */
0510: public SqlExp normalize(SqlDriver driver, SelectExp sel,
0511: boolean convertExists) {
0512: for (Join j = joinList; j != null; j = j.next) {
0513: SelectExp se = j.selectExp;
0514: se.normalize(driver, sel, convertExists);
0515: if (convertExists && se.whereExp != null) {
0516: j.findDeepestJoin().appendJoinExp(se.whereExp);
0517: se.whereExp = null;
0518: }
0519: }
0520:
0521: if (whereExp == null)
0522: return null;
0523: SqlExp r = whereExp.normalize(driver, this , convertExists);
0524: if (r != null)
0525: whereExp = r;
0526:
0527: int cj = whereExp.getConvertToJoin();
0528: if (cj == SqlExp.YES
0529: || (convertExists && cj >= SqlExp.YES_DISTINCT)) {
0530: // convert to join
0531: boolean not = convertExists
0532: && cj == SqlExp.YES_DISTINCT_NOT;
0533: SelectExp sub;
0534: if (not) {
0535: sub = (SelectExp) (whereExp.childList.childList);
0536: } else {
0537: sub = (SelectExp) (whereExp.childList);
0538: }
0539: if (sub != null) {
0540: Join j = new Join();
0541: j.selectExp = sub;
0542: j.exp = sub.subSelectJoinExp;
0543: sub.subSelectJoinExp = null;
0544: if (sub.whereExp != null) { // add to join clause
0545: Join jj = j;
0546: for (;; jj = jj.selectExp.joinList) {
0547: if (jj.selectExp.joinList == null)
0548: break;
0549: }
0550: jj.appendJoinExp(sub.whereExp);
0551: sub.whereExp = null;
0552: }
0553: addJoin(j);
0554: }
0555: if (not) {
0556: sub.outer = true;
0557: whereExp = sub.getOuterJoinNotMatchedExp();
0558: } else {
0559: whereExp = null;
0560: }
0561: if (cj >= SqlExp.YES_DISTINCT)
0562: distinct = true;
0563: return null;
0564: }
0565: return null;
0566: }
0567:
0568: /**
0569: * Append SQL for this node to s.
0570: *
0571: * @param driver The driver being used
0572: * @param s Append the SQL here
0573: * @param leftSibling
0574: */
0575: public void appendSQLImp(SqlDriver driver, CharBuf s,
0576: SqlExp leftSibling) {
0577: // put in the select list (ours and all of our join tables)
0578: boolean putOrderColsInSelect = driver.isPutOrderColsInSelect();
0579: boolean selectListAppendedWithOrderByExp = false;
0580:
0581: //Create a ColumnStruct[] for all the fields in the 'order by' exp.
0582: ColumnStruct[] orderByStruct = createColumnStruct(orderByList);
0583:
0584: int start = s.size();
0585: if (distinct)
0586: s.append("SELECT DISTINCT ");
0587: else
0588: s.append("SELECT ");
0589: selectListStartIndex = s.size();
0590: int colCount = appendFirstSelect(selectList, driver, s);
0591: colCount += appendSelect(joinList, driver, s);
0592: if (Debug.DEBUG) {
0593: if (colCount != getSelectListColumnCount()) {
0594: throw BindingSupportImpl.getInstance().internal("");
0595: }
0596: }
0597:
0598: if (orderByList != null) {
0599: if (putOrderColsInSelect
0600: || (groupByList != null && driver
0601: .putOrderColsInGroupBy())) {
0602: selectListAppendedWithOrderByExp = appendSelectForOrderBy(
0603: orderByList, driver, s, orderByStruct);
0604: }
0605: }
0606:
0607: finishSelectList(s, start);
0608: selectListEndIndex = s.size();
0609:
0610: // put in the from list
0611: s.append(" FROM ");
0612: driver.appendSqlFrom(table, alias, s);
0613: appendFrom(joinList, driver, s);
0614:
0615: // put all the expressions for the where clause into a list
0616: SqlExp list = new SqlExp();
0617: SqlExp pos = list;
0618: if (subSelectJoinExp != null)
0619: pos = pos.next = subSelectJoinExp;
0620: if (!driver.isAnsiJoinSyntax()) {
0621: findWhereExp(findWhereJoinExp(pos));
0622: } else {
0623: findWhereExp(pos);
0624: }
0625:
0626: // build a single expression from this list (joins exps with and)
0627: SqlExp exp = merge(list.next);
0628: if (exp != null) {
0629: s.append(" WHERE ");
0630: exp.appendSQL(driver, s, null);
0631: }
0632:
0633: exp = groupByList;
0634: int gIndex = 1;
0635: if (exp != null) {
0636: boolean this Done = false;
0637: s.append(" GROUP BY ");
0638: for (;;) {
0639: if (gIndex > 1) {
0640: s.append(',');
0641: s.append(' ');
0642: }
0643: if (exp instanceof GroupByThisExp) {
0644: if (!this Done) {
0645: this Done = true;
0646: appendFirstSelect(selectList, driver, s,
0647: selectListCountBeforeAggregate);
0648: appendSelect(joinList, driver, s);
0649: }
0650: } else {
0651: // The group by list can contain ColumnExp and OrderExp
0652: // expressions. The OrderExp's just wrap a ColumnExp and
0653: // add a descending indicator.
0654: ColumnExp ce;
0655: if (exp instanceof ColumnExp) {
0656: ce = (ColumnExp) exp;
0657: } else {
0658: ce = (ColumnExp) exp.childList;
0659: }
0660:
0661: if (ce.isAliasedColumn()) {
0662: exp.appendSQL(driver, s, null);
0663: } else if (driver.useColumnIndexForGroupBy()) {
0664: int i = findIndexInSelectList(selectList,
0665: ce.selectExp, ce.col.name);
0666: if (i >= 0) {
0667: s.append(i);
0668: } else {
0669: exp.appendSQL(driver, s, null);
0670: }
0671: } else {
0672: exp.appendSQL(driver, s, null);
0673: }
0674: }
0675: gIndex++;
0676: if ((exp = exp.next) == null)
0677: break;
0678: }
0679: }
0680:
0681: //update the groupBy expression with fields that was added to the selectlist
0682: if (selectListAppendedWithOrderByExp && groupByList != null) {
0683: appendGroupBy(s, orderByStruct, gIndex == 1, driver);
0684: }
0685:
0686: exp = havingExp;
0687: if (exp != null) {
0688: havingExp.appendSQL(driver, s, null);
0689: }
0690:
0691: // put in the order by list
0692: exp = orderByList;
0693: if (exp != null) {
0694: orderByStartIndex = s.size();
0695: s.append(" ORDER BY ");
0696: if (driver.isUseIndexesForOrderCols()) {
0697: int index = 1;
0698: boolean first = true;
0699: for (;;) {
0700: if (first) {
0701: first = false;
0702: } else {
0703: s.append(',');
0704: s.append(' ');
0705: }
0706: // The order by list can contain ColumnExp and OrderExp
0707: // expressions. The OrderExp's just wrap a ColumnExp and
0708: // add a descending indicator.
0709: ColumnExp ce;
0710: if (exp instanceof ColumnExp) {
0711: ce = (ColumnExp) exp;
0712: } else {
0713: ce = (ColumnExp) exp.childList;
0714: }
0715:
0716: if (ce.isAliasedColumn()) {
0717: exp.appendSQL(driver, s, null);
0718: } else {
0719: ColumnStruct colStruct = orderByStruct[index - 1];
0720: int i = colStruct.indexInSelectList;
0721: if (i > 0) {
0722: s.append(i);
0723: if (ce != exp) { // exp is OrderExp as it is not ColumnExp
0724: OrderExp oe = (OrderExp) exp;
0725: if (oe.isDesc())
0726: s.append(" DESC");
0727: }
0728: } else {
0729: //if appended to selectList
0730: if (colStruct.columnNme != null) {
0731: if (driver.useColAliasForAddedCols()) {
0732: s.append(colStruct.alias);
0733: } else {
0734: s.append(colStruct.columnNme);
0735: }
0736:
0737: if (ce != exp) { // exp is OrderExp as it is not ColumnExp
0738: OrderExp oe = (OrderExp) exp;
0739: if (oe.isDesc())
0740: s.append(" DESC");
0741: }
0742: } else {
0743: exp.appendSQL(driver, s, null);
0744: }
0745: }
0746: }
0747: index++;
0748: if ((exp = exp.next) == null)
0749: break;
0750: }
0751: } else {
0752: //use the column name
0753: int index = 1;
0754: boolean first = true;
0755: for (; exp != null; exp = exp.next) {
0756: if (first) {
0757: first = false;
0758: } else {
0759: s.append(',');
0760: s.append(' ');
0761: }
0762: ColumnExp ce;
0763: if (exp instanceof ColumnExp) {
0764: ce = (ColumnExp) exp;
0765: } else {
0766: ce = (ColumnExp) exp.childList;
0767: }
0768: if (ce.isAliasedColumn()) {
0769: exp.appendSQL(driver, s, null);
0770: } else {
0771: ColumnStruct colStruct = orderByStruct[index - 1];
0772: int i = colStruct.indexInSelectList;
0773: if (i > 0) {
0774: exp.appendSQL(driver, s, null);
0775: } else {
0776: if (selectListAppendedWithOrderByExp) {
0777: if (driver.useColAliasForAddedCols()) {
0778: s.append(colStruct.alias);
0779: } else {
0780: s.append(colStruct.columnNme);
0781: }
0782: if (ce != exp) { // exp is OrderExp as it is not ColumnExp
0783: OrderExp oe = (OrderExp) exp;
0784: if (oe.isDesc())
0785: s.append(" DESC");
0786: }
0787: } else {
0788: exp.appendSQL(driver, s, null);
0789: }
0790: }
0791: }
0792: index++;
0793: }
0794: }
0795: orderByEndIndex = s.size();
0796: } else {
0797: orderByStartIndex = orderByEndIndex = 0;
0798: }
0799:
0800: if (forUpdate
0801: && (!distinct || driver
0802: .isSelectForUpdateWithDistinctOk())) {
0803: char[] a = driver.getSelectForUpdate();
0804: if (a != null) {
0805: s.append(a);
0806: if (driver.isSelectForUpdateAppendTable()) {
0807: if (alias == null) {
0808: s.append(table.name);
0809: } else {
0810: s.append(alias);
0811: }
0812: }
0813: }
0814: }
0815: }
0816:
0817: /**
0818: * Find the index of a matching entry (i.e. same column from same table)
0819: * in the select list (first is 1) for ce or -1 if none found.
0820: */
0821: private int findIndexInSelectList(SqlExp toSearch, SelectExp se,
0822: String name) {
0823: int[] indexArray = new int[2];
0824: findIndexInSelectListImp(toSearch, se, name, indexArray);
0825:
0826: if (indexArray[1] == 0) {
0827: //must check more
0828: findIndexInJoin(joinList, se, name, indexArray);
0829: }
0830:
0831: if (indexArray[1] == 1) {
0832: return indexArray[0] + 1;
0833: } else {
0834: return -1;
0835: }
0836: }
0837:
0838: private void findIndexInJoin(Join j, SelectExp exp, String name,
0839: int[] index) {
0840: for (; j != null; j = j.next) {
0841: SelectExp se = j.selectExp;
0842: findIndexInSelectListImp(se.selectList, exp, name, index);
0843:
0844: if (index[1] == 0) {
0845: findIndexInJoin(se.joinList, exp, name, index);
0846: }
0847: break;
0848: }
0849: }
0850:
0851: private void findIndexInSelectListImp(SqlExp se, SelectExp exp,
0852: String name, int[] index) {
0853: for (SqlExp e = se; e != null; e = e.next, index[0]++) {
0854: if (e instanceof ColumnExp) {
0855: ColumnExp ce = (ColumnExp) e;
0856: if (ce.selectExp == exp && ce.col.name.equals(name)) {
0857: index[1] = 1;
0858: return;
0859: }
0860: }
0861: }
0862: }
0863:
0864: private static boolean isDesc(SqlExp e) {
0865: return e instanceof OrderExp && ((OrderExp) e).isDesc();
0866: }
0867:
0868: /**
0869: * Check that the select list is valid.
0870: */
0871: protected void finishSelectList(CharBuf s, int start) {
0872: int sz = s.size();
0873: int n = sz - start;
0874: if (n <= 7) {
0875: if (start == 0) {
0876: throw BindingSupportImpl.getInstance().internal(
0877: "no columns in select list");
0878: }
0879: s.append('1');
0880: } else {
0881: s.setSize(sz - 2); // remove the extra comma and space
0882: }
0883: }
0884:
0885: /**
0886: * Recursively append the select lists for all the joins to s using a
0887: * depth first traversal. NOP if j is null.
0888: */
0889: private int appendSelect(Join j, SqlDriver driver, CharBuf s) {
0890: int count = 0;
0891: for (; j != null; j = j.next) {
0892: SelectExp se = j.selectExp;
0893: count += appendSelect(se.selectList, driver, s);
0894: count += appendSelect(se.joinList, driver, s);
0895: }
0896: return count;
0897: }
0898:
0899: /**
0900: * Recursively count the colums of all joined to selectExp's selectList.
0901: */
0902: private int countSelect(Join j) {
0903: int count = 0;
0904: for (; j != null; j = j.next) {
0905: SelectExp se = j.selectExp;
0906: count += countSelect(se.selectList);
0907: count += countSelect(se.joinList);
0908: }
0909: return count;
0910: }
0911:
0912: /**
0913: * Append all the expressions in the list starting at e. NOP if e is null.
0914: * Follow each expression with ', '. Record the last index of
0915: */
0916: private int appendFirstSelect(SqlExp e, SqlDriver driver, CharBuf s) {
0917: if (e == null)
0918: return 0;
0919: int count = 1;
0920: e.appendSQL(driver, s, null);
0921: selectListFirstColEndIndex = s.size();
0922: s.append(',');
0923: s.append(' ');
0924: for (e = e.next; e != null; e = e.next) {
0925: e.appendSQL(driver, s, null);
0926: s.append(',');
0927: s.append(' ');
0928: count++;
0929: }
0930: return count;
0931: }
0932:
0933: private void appendFirstSelect(SqlExp e, SqlDriver driver,
0934: CharBuf s, int columnCount) {
0935: if (e == null)
0936: return;
0937: e.appendSQL(driver, s, null);
0938: selectListFirstColEndIndex = s.size();
0939: int count = 1;
0940: for (e = e.next; e != null; e = e.next) {
0941: if (count++ >= columnCount)
0942: break;
0943: s.append(',');
0944: s.append(' ');
0945: e.appendSQL(driver, s, null);
0946: }
0947: }
0948:
0949: /**
0950: * Append all the expressions in the list starting at e. NOP if e is null.
0951: * Follow each expression with ', '.
0952: */
0953: private int appendSelect(SqlExp e, SqlDriver driver, CharBuf s) {
0954: int count = 0;
0955: for (; e != null; e = e.next) {
0956: count++;
0957: e.appendSQL(driver, s, null);
0958: s.append(',');
0959: s.append(' ');
0960: }
0961: return count;
0962: }
0963:
0964: private int countSelect(SqlExp e) {
0965: int count = 0;
0966: for (; e != null; e = e.next) {
0967: count++;
0968: }
0969: return count;
0970: }
0971:
0972: /**
0973: * Append all the expressions in the list starting at e. NOP if e is null.
0974: * Follow each expression with an alias and ', '. This is used to add
0975: * columns used in the order by to the select list for databases that
0976: * require this e.g. informix.
0977: */
0978: private boolean appendSelectForOrderBy(SqlExp e, SqlDriver driver,
0979: CharBuf s, ColumnStruct[] columnStructs) {
0980: boolean appended = false;
0981: int c = 1;
0982: for (; e != null; e = e.next) {
0983: ColumnStruct colStruct = columnStructs[c - 1];
0984: if (colStruct.indexInSelectList == -1) {
0985: int offset = s.size();
0986: if (e instanceof OrderExp) {
0987: e.childList.appendSQL(driver, s, null);
0988: } else {
0989: e.appendSQL(driver, s, null);
0990: }
0991: colStruct.columnNme = s.toString(offset, s.size()
0992: - offset);
0993: colStruct.alias = ALIAS_PREPEND_CONSTANT + "j" + c;
0994:
0995: //not in select list
0996: s.append(driver.getAliasPrepend());
0997: s.append(colStruct.alias);
0998: s.append(',');
0999: s.append(' ');
1000: appended = true;
1001: }
1002: c++;
1003: }
1004: return appended;
1005: }
1006:
1007: private ColumnStruct[] createColumnStruct(SqlExp e) {
1008: SqlExp startExp = e;
1009: int count = 0;
1010: for (; e != null; e = e.next) {
1011: count++;
1012: }
1013: if (count == 0)
1014: return EMPTY_COLUMS_STRUCT;
1015: ColumnStruct[] colStructs = new ColumnStruct[count];
1016: count = 0;
1017: e = startExp;
1018: for (; e != null; e = e.next) {
1019: ColumnExp ce;
1020: if (e instanceof ColumnExp) {
1021: ce = (ColumnExp) e;
1022: } else {
1023: ce = (ColumnExp) e.childList;
1024: }
1025: if (ce.isAliasedColumn()) {
1026: colStructs[count++] = new ColumnStruct(0);
1027: } else {
1028: colStructs[count++] = new ColumnStruct(
1029: findIndexInSelectList(selectList, ce.selectExp,
1030: ce.col.name));
1031: }
1032:
1033: }
1034: return colStructs;
1035: }
1036:
1037: /**
1038: * This is a struct to hold information about the order by columns that were added.
1039: */
1040: private class ColumnStruct {
1041: /**
1042: * The alias of the appended column.
1043: */
1044: String alias;
1045: /**
1046: * The columnName of the appended column. This field is only filled in
1047: * if this column was appended to the original select exp.
1048: */
1049: String columnNme;
1050: /**
1051: * The index of this column in the select list.
1052: */
1053: int indexInSelectList;
1054:
1055: public ColumnStruct(int indexInSelectList) {
1056: this .indexInSelectList = indexInSelectList;
1057: }
1058: }
1059:
1060: /**
1061: * Some drivers will automatically add fields that are in the orderBy
1062: * to the select exp. This method must check to see if these fields
1063: * are in the groupby and if not it must be added.
1064: */
1065: private void appendGroupBy(CharBuf s, ColumnStruct[] columnStructs,
1066: boolean first, SqlDriver driver) {
1067: for (int i = 0; i < columnStructs.length; i++) {
1068: ColumnStruct columnStruct = columnStructs[i];
1069: if (columnStruct.columnNme != null) {
1070: if (first) {
1071: first = false;
1072: } else {
1073: s.append(',');
1074: }
1075: if (driver.useColAliasForAddedCols()) {
1076: s.append(columnStruct.alias);
1077: } else {
1078: s.append(columnStruct.columnNme);
1079: }
1080: }
1081: }
1082: }
1083:
1084: /**
1085: * Recursively append the from list entries for all the joins to s using a
1086: * depth first traversal. NOP if j is null.
1087: */
1088: private void appendFrom(Join j, SqlDriver driver, CharBuf s) {
1089: for (; j != null; j = j.next) {
1090: SelectExp se = j.selectExp;
1091: if (j.isMerged())
1092: continue;
1093: driver.appendSqlFromJoin(se.table, se.alias, j.exp,
1094: se.outer, s);
1095: appendFrom(se.joinList, driver, s);
1096: }
1097: }
1098:
1099: /**
1100: * Recursively add all where expressions we can find to list and
1101: * return the new head of list.
1102: */
1103: private SqlExp findWhereExp(SqlExp list) {
1104: SqlExp e = whereExp;
1105: if (e != null)
1106: list = list.next = e;
1107: for (Join j = joinList; j != null; j = j.next) {
1108: list = j.selectExp.findWhereExp(list);
1109: }
1110: return list;
1111: }
1112:
1113: /**
1114: * Recursively add all join expressions we can find to list and
1115: * return the new head of list.
1116: */
1117: private SqlExp findWhereJoinExp(SqlExp list) {
1118: for (Join j = joinList; j != null; j = j.next) {
1119: SqlExp e = j.exp;
1120: if (e != null)
1121: list = list.next = e;
1122: list = j.selectExp.findWhereJoinExp(list);
1123: }
1124: return list;
1125: }
1126:
1127: /**
1128: * Combine a list of expressions into a single expression by anding
1129: * all the expressions together. This flattens nested 'and' expressions.
1130: */
1131: private SqlExp merge(SqlExp first) {
1132: if (first == null)
1133: return null;
1134: if (first.next == null)
1135: return first;
1136: // flatten enclosed 'and' expressions by adding them to the list
1137: // and processing their children
1138: for (SqlExp pos = first; pos != null;) {
1139: SqlExp e = pos.next;
1140: if (e instanceof AndExp) {
1141: pos = pos.next = e.childList;
1142: SqlExp f = pos;
1143: for (; f.next != null; f = f.next)
1144: ;
1145: f.next = e.next;
1146: } else {
1147: pos = e;
1148: }
1149: }
1150: return new AndExp(first);
1151: }
1152:
1153: /**
1154: * Set our outer flag and follow all our joins and make then outer as
1155: * well.
1156: */
1157: public void setOuterRec() {
1158: outer = true;
1159: for (Join j = joinList; j != null; j = j.next) {
1160: j.selectExp.setOuterRec();
1161: }
1162: }
1163:
1164: /**
1165: * Get an expression that is true if this join does not produce a fully
1166: * populated row (i.e. one or more of the outer most pk columns are null
1167: * indicating that an outer join was not matched).
1168: */
1169: public SqlExp getOuterJoinNotMatchedExp() {
1170: if (joinList == null) {
1171: return new IsNullExp(table.pk[0].toSqlExp(this ));
1172: } else if (joinList.next == null) {
1173: return joinList.selectExp.getOuterJoinNotMatchedExp();
1174: } else {
1175: // join expressions with OrExp
1176: SqlExp root = joinList.selectExp
1177: .getOuterJoinNotMatchedExp();
1178: SqlExp p = root;
1179: for (Join j = joinList.next;;) {
1180: p = p.next = j.selectExp.getOuterJoinNotMatchedExp();
1181: if ((j = j.next) == null)
1182: break;
1183: }
1184: return new OrExp(root);
1185: }
1186: }
1187:
1188: /**
1189: * Get an expression that is true if this join produces a fully
1190: * populated row (i.e. all of the outer most pk columns are not null
1191: * indicating that all outer joins were matched).
1192: */
1193: public SqlExp getOuterJoinMatchedExp() {
1194: if (joinList == null) {
1195: return new IsNotNullExp(table.pk[0].toSqlExp(this ));
1196: } else if (joinList.next == null) {
1197: return joinList.selectExp.getOuterJoinMatchedExp();
1198: } else {
1199: // join expressions with AndExp
1200: SqlExp root = joinList.selectExp.getOuterJoinMatchedExp();
1201: SqlExp p = root;
1202: for (Join j = joinList.next;;) {
1203: p = p.next = j.selectExp.getOuterJoinMatchedExp();
1204: if ((j = j.next) == null)
1205: break;
1206: }
1207: return new AndExp(root);
1208: }
1209: }
1210:
1211: /**
1212: * Add an order-by expression this to this select.
1213: */
1214: public SqlExp addOrderBy(OrderNode[] orders, boolean append) {
1215: return addOrderBy(orders, append, JDOQLNodeToSqlExp.INSTANCE);
1216: }
1217:
1218: /**
1219: * Add an order-by expression this to this select.
1220: */
1221: public SqlExp addOrderBy(OrderNode[] orders, boolean append,
1222: JDOQLNodeToSqlExp visitor) {
1223: // create the order by list including joins to pickup columns as needed
1224: int len = orders.length;
1225: SqlExp oePos = null;
1226: if (append) {
1227: oePos = findTailOrderByExp();
1228: }
1229: for (int i = 0; i < len; i++) {
1230: OrderNode on = orders[i];
1231: SelectExp se = this ;
1232: Node prevNode = null;
1233: for (Node n = on.childList; n != null; n = n.childList) {
1234: if (n instanceof FieldNode) {
1235: SelectExp oSe = se;
1236: if (prevNode != null
1237: && prevNode instanceof FieldNavNode) {
1238: JdbcField refField = (JdbcField) ((FieldNavNode) prevNode).fmd.storeField;
1239: JdbcField orderByField = (JdbcField) ((FieldNode) n).fmd.storeField;
1240:
1241: Join join = se.findJoin(orderByField.mainTable,
1242: refField);
1243: if (join == null) {
1244: oSe = new SelectExp();
1245: oSe.table = orderByField.mainTable;
1246: oSe.outer = true;
1247: oSe.jdbcField = refField;
1248:
1249: if (refField instanceof JdbcPolyRefField) {
1250: se
1251: .addJoin(
1252: ((JdbcPolyRefField) refField).refCols,
1253: oSe.table.pk, oSe);
1254: } else {
1255: se.addJoin(refField.mainTableCols,
1256: oSe.table.pk, oSe);
1257: }
1258:
1259: } else {
1260: oSe = join.selectExp;
1261: }
1262:
1263: } else if (prevNode == null) {
1264: //look to join to superTable if in different table
1265: JdbcField orderByField = (JdbcField) ((FieldNode) n).fmd.storeField;
1266: if (orderByField.mainTable != se.table) {
1267: oSe = se.findTable(orderByField.mainTable);
1268: if (oSe == null) {
1269: oSe = new SelectExp();
1270: oSe.table = orderByField.mainTable;
1271: oSe.outer = false;
1272: se.addJoin(se.table.pk, oSe.table.pk,
1273: oSe);
1274: }
1275: }
1276: }
1277:
1278: oePos = addOrderExp(handleOrderByFieldNode(
1279: (FieldNode) n, oSe, visitor),
1280: (on.order == OrderNode.ORDER_DESCENDING),
1281: oePos);
1282: } else if (n instanceof AsValueNode) {
1283: oePos = addOrderExp(new ColumnExp(
1284: ((AsValueNode) n).value),
1285: (on.order == OrderNode.ORDER_DESCENDING),
1286: oePos);
1287: } else if (n instanceof ReservedFieldNode) {
1288: ColumnExp[] cExps = createColumnExpForOrdering(
1289: ((ReservedFieldNode) n).getTarget(), se);
1290: for (int j = 0; j < cExps.length; j++) {
1291: oePos = addOrderExp(
1292: cExps[j],
1293: (on.order == OrderNode.ORDER_DESCENDING),
1294: oePos);
1295: }
1296: } else if (n instanceof FieldNavNode) {
1297: se = handleOrderByFieldNavNode((FieldNavNode) n,
1298: se, prevNode);
1299: } else {
1300: throw BindingSupportImpl.getInstance().internal(
1301: "Invalid node in orders: " + n);
1302: }
1303: prevNode = n;
1304: }
1305: }
1306: return oePos;
1307: }
1308:
1309: /**
1310: * Insert the columns at the start of the orderByList as ascending.
1311: */
1312: public void prependOrderByForColumns(JdbcColumn[] columns) {
1313: SqlExp current = orderByList;
1314:
1315: SqlExp oePos = null;
1316: ColumnExp[] columnExps = new ColumnExp[columns.length];
1317: for (int i = 0; i < columnExps.length; i++) {
1318: columnExps[i] = new ColumnExp(columns[i], this , null);
1319: oePos = addOrderExp(columnExps[i], false, oePos);
1320: }
1321:
1322: if (current != null) {
1323: oePos.next = current;
1324: }
1325: }
1326:
1327: /**
1328: * Append the columns at the end of the orderByList as ascending.
1329: */
1330: public void appendOrderByForColumns(JdbcColumn[] columns) {
1331: SqlExp oePos = findTailOrderByExp();
1332: for (int i = 0; i < columns.length; i++) {
1333: if (containsOrderExp(columns[i])) {
1334: continue;
1335: }
1336: oePos = addOrderExp(new ColumnExp(columns[i], this , null),
1337: false, oePos);
1338: }
1339: }
1340:
1341: /**
1342: * Append the column at the end of the orderByList as ascending.
1343: */
1344: public void appendOrderByForColumns(JdbcColumn column) {
1345: SqlExp oePos = findTailOrderByExp();
1346: if (!containsOrderExp(column)) {
1347: oePos = addOrderExp(new ColumnExp(column, this , null),
1348: false, oePos);
1349: }
1350: }
1351:
1352: /**
1353: * Append the orderExp at the end of the current orderExp.
1354: */
1355: public void appendOrderByExp(SqlExp orderExp) {
1356: SqlExp oePos = findTailOrderByExp();
1357: if (oePos == null) {
1358: orderByList = orderExp;
1359: } else {
1360: if (oePos != orderExp) {
1361: oePos.next = orderExp;
1362: }
1363: }
1364: }
1365:
1366: /**
1367: * Append the columns to the orderByList
1368: */
1369: public void appendOrderByForColumns(JdbcColumn[] columns,
1370: SelectExp se) {
1371: SqlExp oePos = findTailOrderByExp();
1372: for (int i = 0; i < columns.length; i++) {
1373: if (containsOrderExp(columns[i])/* || se.containsOrderExp(columns[i])*/) {
1374: continue;
1375: }
1376: oePos = addOrderExp(new ColumnExp(columns[i], se, null),
1377: false, oePos);
1378: }
1379: }
1380:
1381: public boolean containsOrderExp(JdbcColumn jdbcColumn) {
1382: for (SqlExp e = orderByList; e != null; e = e.next) {
1383: if (((ColumnExp) ((OrderExp) e).childList).col == jdbcColumn)
1384: return true;
1385: }
1386: return false;
1387: }
1388:
1389: /**
1390: * return the tail of the orderExp
1391: */
1392: private SqlExp findTailOrderByExp() {
1393: for (SqlExp e = orderByList; e != null; e = e.next) {
1394: if (e.next == null)
1395: return e;
1396: }
1397: return null;
1398: }
1399:
1400: public ColumnExp[] createColumnExpForOrdering(ClassMetaData target,
1401: SelectExp root) {
1402: JdbcColumn[] pk = ((JdbcClass) target.storeClass).table.pk;
1403: ColumnExp[] columnExps = new ColumnExp[pk.length];
1404: for (int i = 0; i < pk.length; i++) {
1405: columnExps[i] = new ColumnExp(pk[i], root, pk[i].refField);
1406: }
1407: return columnExps;
1408: }
1409:
1410: private SqlExp addOrderExp(SqlExp list, boolean desc, SqlExp oePos) {
1411: // add a new OrderExp to the order by list
1412: OrderExp oe = new OrderExp(list, desc);
1413: if (oePos == null) {
1414: orderByList = oe;
1415: } else {
1416: oePos.next = oe;
1417: }
1418: oePos = oe;
1419: return oePos;
1420: }
1421:
1422: /**
1423: * The idea here is to only add a join if the previous node is a FieldNavNode.
1424: */
1425: private SelectExp handleOrderByFieldNavNode(FieldNavNode nav,
1426: SelectExp se, Node prevNode) {
1427: if (prevNode != null && prevNode instanceof FieldNavNode) {
1428: //check if join is needed
1429: // see if there is a join to the table for the class we are navigating to
1430: JdbcField f = (JdbcField) ((FieldNavNode) prevNode).fmd.storeField;
1431: Join j = se.findJoin(f);
1432: if (j != null)
1433: return j.selectExp;
1434:
1435: // no join so add one
1436: SelectExp next = new SelectExp();
1437: next.jdbcField = f;
1438: ClassMetaData targetClass = ((FieldNavNode) prevNode).targetClass;
1439: next.table = ((JdbcClass) targetClass.storeClass).table;
1440: if (f instanceof JdbcPolyRefField) {
1441: se.addJoin(((JdbcPolyRefField) f).refCols,
1442: next.table.pk, next);
1443: } else {
1444: se.addJoin(f.mainTableCols, next.table.pk, next);
1445: }
1446: next.outer = f.fmd.nullValue != MDStatics.NULL_VALUE_EXCEPTION;
1447: return next;
1448: } else {
1449: return se;
1450: }
1451: }
1452:
1453: private SqlExp handleOrderByFieldNode(FieldNode fn, SelectExp se,
1454: JDOQLNodeToSqlExp visitor) {
1455: if (!(fn.fmd.storeField instanceof JdbcSimpleField)) {
1456: throw BindingSupportImpl.getInstance().internal(
1457: "Only simple fields may be used in an "
1458: + "ordering statement: " + fn.fmd);
1459: }
1460: SqlExp list = visitor.toSqlExp(fn, se, null, 0, null);
1461: if (list.childList != null) {
1462: throw BindingSupportImpl.getInstance().internal(
1463: "Only single column fields may be used in an "
1464: + "ordering statement: " + fn.fmd);
1465: }
1466: return list;
1467: }
1468:
1469: /**
1470: * Does this select contain a join to a many table that may produce
1471: * multiple rows? This is used to decide on setting the distinct flag
1472: * if this select is converterd into an outer join.
1473: */
1474: public boolean isJoinToManyTable() {
1475: return jdbcField instanceof JdbcCollectionField;
1476: }
1477:
1478: /**
1479: * Append e to the where clause of this select. This will create a new
1480: * AndExp if needed.
1481: */
1482: public void appendToWhereExp(SqlExp e) {
1483: whereExp = SqlExp.appendWithAnd(whereExp, e);
1484: }
1485:
1486: /**
1487: * Append e to the subSelectJoinExp clause of this select. This will
1488: * create a new AndExp if needed.
1489: */
1490: public void appendToSubSelectJoinExp(SqlExp e) {
1491: subSelectJoinExp = SqlExp.appendWithAnd(subSelectJoinExp, e);
1492: }
1493:
1494: /**
1495: * Used to obtain a selectexp for a field. This will add a join to the supertable
1496: * if not already added.
1497: */
1498: public static SelectExp createJoinToSuperTable(SelectExp root,
1499: JdbcField jdbcField) {
1500: return createJoinToSuperTable(
1501: root,
1502: ((JdbcClass) jdbcField.fmd.classMetaData.storeClass).table);
1503: }
1504:
1505: /**
1506: * Used to obtain a selectexp for a field. This will add a join to the supertable
1507: * if not already added.
1508: */
1509: public static SelectExp createJoinToSuperTable(SelectExp root,
1510: SelectExp joinFromExp, JdbcColumn[] lJoinColumns,
1511: JdbcField jdbcField) {
1512: if (Debug.DEBUG) {
1513: JdbcRefField.isSubTableOf(root.table,
1514: jdbcField.fmd.classMetaData);
1515: }
1516: if (root.table != ((JdbcClass) jdbcField.fmd.classMetaData.storeClass).table) {
1517: Join join = joinFromExp.findJoin(jdbcField);
1518: if (join == null) {
1519: SelectExp se = new SelectExp();
1520: se.outer = root.outer;
1521: se.table = ((JdbcClass) jdbcField.fmd.classMetaData.storeClass).table;
1522: joinFromExp.addJoin(lJoinColumns, se.table.pk, se);
1523: return se;
1524: } else {
1525: return join.selectExp;
1526: }
1527: }
1528: return root;
1529: }
1530:
1531: /**
1532: * Used to obtain a selectexp for a field. This will add a join to the supertable
1533: * if not already added.
1534: */
1535: public static SelectExp createJoinToSuperTable(SelectExp root,
1536: JdbcTable table) {
1537: // If the field is not in the table then join to the sub class table
1538: SelectExp se = (SelectExp) root.findTable(table);
1539: if (se == null) {
1540: se = new SelectExp();
1541: se.outer = root.outer;
1542: se.table = table;
1543: root.addJoin(root.table.pk, se.table.pk, se);
1544: }
1545: return se;
1546: }
1547:
1548: public static void dumpJoinList(SelectExp joinFromExp, String val) {
1549: System.out.println(val + "dumping joinlist");
1550: Join j = joinFromExp.joinList;
1551: if (j == null) {
1552: System.out.println("-- no joins ");
1553: return;
1554: }
1555: for (;;) {
1556: System.out.println("j = " + j);
1557: j = j.next;
1558: if (j == null)
1559: break;
1560: }
1561: }
1562:
1563: public static void dumpJoinListRec(SelectExp joinFromExp,
1564: String indent) {
1565: Join j = joinFromExp.joinList;
1566: if (j == null) {
1567: // System.out.println(indent + "-- no joins --");
1568: return;
1569: }
1570: for (;;) {
1571: System.out.println(indent + j);
1572: dumpJoinListRec(j.selectExp, indent + " ");
1573: j = j.next;
1574: if (j == null)
1575: break;
1576: }
1577: }
1578:
1579: public static void mergeJoinList(Join j) {
1580: if (j == null)
1581: return;
1582: if (j.next == null)
1583: return;
1584:
1585: for (Join nextJoin = j.next; nextJoin != null; nextJoin = nextJoin.next) {
1586: if (nextJoin.isMerged()) {
1587: continue;
1588: }
1589: if (Join.isCurrentEqaul(j, nextJoin)) {
1590: nextJoin.setMergedWith(j);
1591: continue;
1592: }
1593: }
1594: mergeJoinList(j.selectExp.joinList);
1595: mergeJoinList(j.next);
1596: }
1597:
1598: }
|