Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » sql » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.jdbc.sql;
   20   
   21   import java.sql.Connection;
   22   import java.sql.PreparedStatement;
   23   import java.sql.ResultSet;
   24   import java.sql.SQLException;
   25   import java.sql.Statement;
   26   import java.sql.Types;
   27   import java.util.AbstractList;
   28   import java.util.ArrayList;
   29   import java.util.BitSet;
   30   import java.util.Collection;
   31   import java.util.Collections;
   32   import java.util.HashMap;
   33   import java.util.HashSet;
   34   import java.util.Iterator;
   35   import java.util.List;
   36   import java.util.Map;
   37   import java.util.Set;
   38   import java.util.SortedMap;
   39   import java.util.Stack;
   40   import java.util.TreeMap;
   41   
   42   import org.apache.commons.collections.iterators.EmptyIterator;
   43   import org.apache.commons.lang.StringUtils;
   44   import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
   45   import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
   46   import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
   47   import org.apache.openjpa.jdbc.kernel.JDBCLockManager;
   48   import org.apache.openjpa.jdbc.kernel.JDBCStore;
   49   import org.apache.openjpa.jdbc.kernel.JDBCStoreManager;
   50   import org.apache.openjpa.jdbc.meta.ClassMapping;
   51   import org.apache.openjpa.jdbc.meta.FieldMapping;
   52   import org.apache.openjpa.jdbc.meta.Joinable;
   53   import org.apache.openjpa.jdbc.meta.ValueMapping;
   54   import org.apache.openjpa.jdbc.schema.Column;
   55   import org.apache.openjpa.jdbc.schema.ForeignKey;
   56   import org.apache.openjpa.jdbc.schema.Table;
   57   import org.apache.openjpa.kernel.StoreContext;
   58   import org.apache.openjpa.lib.log.Log;
   59   import org.apache.openjpa.lib.util.Localizer;
   60   import org.apache.openjpa.util.ApplicationIds;
   61   import org.apache.openjpa.util.Id;
   62   import org.apache.openjpa.util.InternalException;
   63   import serp.util.Numbers;
   64   
   65   /**
   66    * Standard {@link Select} implementation. Usage note: though this class
   67    * implements {@link Joins}, it should not be used for joining directly.
   68    * Instead, use the return value of {@link #newJoins}.
   69    *
   70    * @author Abe White
   71    * @nojavadoc
   72    */
   73   public class SelectImpl
   74       implements Select, PathJoins {
   75   
   76       private static final int NONAUTO_DISTINCT = 2 << 0;
   77       private static final int DISTINCT = 2 << 1;
   78       private static final int NOT_DISTINCT = 2 << 2;
   79       private static final int IMPLICIT_DISTINCT = 2 << 3;
   80       private static final int TO_MANY = 2 << 4;
   81       private static final int AGGREGATE = 2 << 5;
   82       private static final int LOB = 2 << 6;
   83       private static final int OUTER = 2 << 7;
   84       private static final int LRS = 2 << 8;
   85       private static final int EAGER_TO_ONE = 2 << 9;
   86       private static final int EAGER_TO_MANY = 2 << 10;
   87       private static final int RECORD_ORDERED = 2 << 11;
   88       private static final int GROUPING = 2 << 12;
   89       private static final int FORCE_COUNT = 2 << 13;
   90   
   91       private static final String[] TABLE_ALIASES = new String[16];
   92       private static final String[] ORDER_ALIASES = new String[16];
   93       private static final Object[] NULL_IDS = new Object[16];
   94       private static final Object[] PLACEHOLDERS = new Object[50];
   95   
   96       private static final Localizer _loc = Localizer.forPackage(Select.class);
   97   
   98       static {
   99           for (int i = 0; i < TABLE_ALIASES.length; i++)
  100               TABLE_ALIASES[i] = "t" + i;
  101           for (int i = 0; i < ORDER_ALIASES.length; i++)
  102               ORDER_ALIASES[i] = "o" + i;
  103           for (int i = 0; i < NULL_IDS.length; i++)
  104               NULL_IDS[i] = new NullId();
  105           for (int i = 0; i < PLACEHOLDERS.length; i++)
  106               PLACEHOLDERS[i] = new Placeholder();
  107       }
  108   
  109       private final JDBCConfiguration _conf;
  110       private final DBDictionary _dict;
  111   
  112       // map of variable + relation path + table keys to the correct alias index:
  113       // each relation path/table combination should have a unique alias because
  114       // it represents a separate object; for example, if a Person class has a
  115       // 'parent' field representing another Person and also has an 'address'
  116       // field of type Address:
  117       // 'address.street' should map to a different table alias than
  118       // 'parent.address.street' for the purposes of comparisons
  119       private Map _aliases = null;
  120   
  121       // map of indexes to table aliases like 'TABLENAME t0'
  122       private SortedMap _tables = null;
  123   
  124       // combined list of selected ids and map of each id to its alias
  125       protected final Selects _selects = newSelects();
  126       private List _ordered = null;
  127       private List _grouped = null;
  128   
  129       // flags
  130       private int _flags = 0;
  131       private int _joinSyntax = 0;
  132       private long _startIdx = 0;
  133       private long _endIdx = Long.MAX_VALUE;
  134       private int _nullIds = 0;
  135       private int _orders = 0;
  136       private int _placeholders = 0;
  137       private int _expectedResultCount = 0;
  138   
  139       // query clauses
  140       private SQLBuffer _ordering = null;
  141       private SQLBuffer _where = null;
  142       private SQLBuffer _grouping = null;
  143       private SQLBuffer _having = null;
  144   
  145       // joins to add to the end of our where clause, and joins to prepend to
  146       // all selects (see select(classmapping) method)
  147       private SelectJoins _joins = null;
  148       private Stack _preJoins = null;
  149   
  150       // map of joins+keys to eager selects and global set of eager keys; the
  151       // same key can't be used more than once
  152       private Map _eager = null;
  153       private Set _eagerKeys = null;
  154   
  155       // subselect support
  156       private List _subsels = null;
  157       private SelectImpl _parent = null;
  158       private String _subPath = null;
  159   
  160       // from select if this select selects from a tmp table created by another
  161       private SelectImpl _from = null;
  162       protected SelectImpl _outer = null;
  163       
  164       // bitSet indicating if an alias is removed from parent select
  165       // bit 0 : correspond to alias 0
  166       // bit 1 : correspond to alias 1, etc.
  167       // if the bit is set, the corresponding alias has been removed from parent
  168       // and recorded under subselect.
  169       private BitSet _removedAliasFromParent = new BitSet(16);
  170        
  171       /**
  172        * Helper method to return the proper table alias for the given alias index.
  173        */
  174       static String toAlias(int index) {
  175           if (index == -1)
  176               return null;
  177           if (index < TABLE_ALIASES.length)
  178               return TABLE_ALIASES[index];
  179           return "t" + index;
  180       }
  181   
  182       /**
  183        * Helper method to return the proper order alias for the given order
  184        * column index.
  185        */
  186       public static String toOrderAlias(int index) {
  187           if (index == -1)
  188               return null;
  189           if (index < ORDER_ALIASES.length)
  190               return ORDER_ALIASES[index];
  191           return "o" + index;
  192       }
  193   
  194       /**
  195        * Constructor. Supply configuration.
  196        */
  197       public SelectImpl(JDBCConfiguration conf) {
  198           _conf = conf;
  199           _dict = _conf.getDBDictionaryInstance();
  200           _joinSyntax = _dict.joinSyntax;
  201           _selects._dict = _dict;
  202       }
  203   
  204       /////////////////////////////////
  205       // SelectExecutor implementation
  206       /////////////////////////////////
  207   
  208       public JDBCConfiguration getConfiguration() {
  209           return _conf;
  210       }
  211   
  212       public SQLBuffer toSelect(boolean forUpdate, JDBCFetchConfiguration fetch) {
  213           return _dict.toSelect(this, forUpdate, fetch);
  214       }
  215   
  216       public SQLBuffer toSelectCount() {
  217           return _dict.toSelectCount(this);
  218       }
  219   
  220       public boolean getAutoDistinct() {
  221           return (_flags & NONAUTO_DISTINCT) == 0;
  222       }
  223   
  224       public void setAutoDistinct(boolean val) {
  225           if (val)
  226               _flags &= ~NONAUTO_DISTINCT;
  227           else
  228               _flags |= NONAUTO_DISTINCT;
  229       }
  230   
  231       public boolean isDistinct() {
  232           return (_flags & NOT_DISTINCT) == 0 && ((_flags & DISTINCT) != 0
  233               || ((_flags & NONAUTO_DISTINCT) == 0
  234               && (_flags & IMPLICIT_DISTINCT) != 0));
  235       }
  236   
  237       public void setDistinct(boolean distinct) {
  238           // need two flags in case set not_distinct, then a to-many join happens
  239           // and distinct flag gets set automatically
  240           if (distinct) {
  241               _flags |= DISTINCT;
  242               _flags &= ~NOT_DISTINCT;
  243           } else {
  244               _flags |= NOT_DISTINCT;
  245               _flags &= ~DISTINCT;
  246           }
  247       }
  248   
  249       public boolean isLRS() {
  250           return (_flags & LRS) != 0;
  251       }
  252   
  253       public void setLRS(boolean lrs) {
  254           if (lrs)
  255               _flags |= LRS;
  256           else
  257               _flags &= ~LRS;
  258       }
  259   
  260       public int getExpectedResultCount() {
  261           // if the count isn't forced and we have to-many eager joins that could
  262           // throw the count off, don't pay attention to it
  263           if ((_flags & FORCE_COUNT) == 0 && hasEagerJoin(true))
  264               return 0;
  265           return _expectedResultCount;
  266       }
  267   
  268       public void setExpectedResultCount(int expectedResultCount, boolean force) {
  269           _expectedResultCount = expectedResultCount;
  270           if (force)
  271               _flags |= FORCE_COUNT;
  272           else 
  273               _flags &= ~FORCE_COUNT;
  274       }
  275   
  276       public int getJoinSyntax() {
  277           return _joinSyntax;
  278       }
  279   
  280       public void setJoinSyntax(int joinSyntax) {
  281           _joinSyntax = joinSyntax;
  282       }
  283   
  284       public boolean supportsRandomAccess(boolean forUpdate) {
  285           return _dict.supportsRandomAccessResultSet(this, forUpdate);
  286       }
  287   
  288       public boolean supportsLocking() {
  289           return _dict.supportsLocking(this);
  290       }
  291   
  292       public int getCount(JDBCStore store)
  293           throws SQLException {
  294           Connection conn = null;
  295           PreparedStatement stmnt = null;
  296           ResultSet rs = null;
  297           try {
  298               SQLBuffer sql = toSelectCount();
  299               conn = store.getConnection();
  300               stmnt = prepareStatement(conn, sql, null, 
  301                   ResultSet.TYPE_FORWARD_ONLY, 
  302                   ResultSet.CONCUR_READ_ONLY, false);
  303               rs = executeQuery(conn, stmnt, sql, false, store);
  304               return getCount(rs);
  305           } finally {
  306               if (rs != null)
  307                   try { rs.close(); } catch (SQLException se) {}
  308               if (stmnt != null)
  309                   try { stmnt.close(); } catch (SQLException se) {}
  310               if (conn != null)
  311                   try { conn.close(); } catch (SQLException se) {}
  312           }
  313       }
  314   
  315       public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
  316           throws SQLException {
  317           if (fetch == null)
  318               fetch = store.getFetchConfiguration();
  319           return execute(store.getContext(), store, fetch,
  320               fetch.getReadLockLevel());
  321       }
  322   
  323       public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
  324           int lockLevel)
  325           throws SQLException {
  326           if (fetch == null)
  327               fetch = store.getFetchConfiguration();
  328           return execute(store.getContext(), store, fetch, lockLevel);
  329       }
  330   
  331       /**
  332        * Execute this select in the context of the given store manager. The
  333        * context is passed in separately for profiling purposes.
  334        */
  335       protected Result execute(StoreContext ctx, JDBCStore store, 
  336           JDBCFetchConfiguration fetch, int lockLevel)
  337           throws SQLException {
  338           boolean forUpdate = false;
  339           if (!isAggregate() && _grouping == null) {
  340               JDBCLockManager lm = store.getLockManager();
  341               if (lm != null)
  342                   forUpdate = lm.selectForUpdate(this, lockLevel);
  343           }
  344   
  345           SQLBuffer sql = toSelect(forUpdate, fetch);
  346           boolean isLRS = isLRS();
  347           int rsType = (isLRS && supportsRandomAccess(forUpdate))
  348               ? -1 : ResultSet.TYPE_FORWARD_ONLY;
  349           Connection conn = store.getConnection();
  350           PreparedStatement stmnt = null;
  351           ResultSet rs = null;
  352           try {
  353               if (isLRS) 
  354                   stmnt = prepareStatement(conn, sql, fetch, rsType, -1, true); 
  355               else
  356                   stmnt = prepareStatement(conn, sql, null, rsType, -1, false);
  357               
  358               setTimeout(stmnt, forUpdate, fetch);
  359               
  360               rs = executeQuery(conn, stmnt, sql, isLRS, store);
  361           } catch (SQLException se) {
  362               // clean up statement
  363               if (stmnt != null)
  364                   try { stmnt.close(); } catch (SQLException se2) {}
  365               try { conn.close(); } catch (SQLException se2) {}
  366               throw se;
  367           }
  368   
  369           return getEagerResult(conn, stmnt, rs, store, fetch, forUpdate, 
  370               sql.getSQL());
  371       }
  372   
  373       /**
  374        * Execute our eager selects, adding the results under the same keys
  375        * to the given result.
  376        */
  377       private static void addEagerResults(SelectResult res, SelectImpl sel,
  378           JDBCStore store, JDBCFetchConfiguration fetch)
  379           throws SQLException {
  380           if (sel._eager == null)
  381               return;
  382   
  383           // execute eager selects
  384           Map.Entry entry;
  385           Result eres;
  386           Map eager;
  387           for (Iterator itr = sel._eager.entrySet().iterator(); itr.hasNext();) {
  388               entry = (Map.Entry) itr.next();
  389   
  390               // simulated batched selects for inner/outer joins; for separate
  391               // selects, don't pass on lock level, because they're probably
  392               // for relations and therefore should use default level
  393               if (entry.getValue() == sel)
  394                   eres = res;
  395               else
  396                   eres = ((SelectExecutor) entry.getValue()).execute(store,
  397                       fetch);
  398   
  399               eager = res.getEagerMap(false);
  400               if (eager == null) {
  401                   eager = new HashMap();
  402                   res.setEagerMap(eager);
  403               }
  404               eager.put(entry.getKey(), eres);
  405           }
  406       }
  407   
  408   
  409       /**
  410        * This method is to provide override for non-JDBC or JDBC-like 
  411        * implementation of preparing statement.
  412        */
  413       protected PreparedStatement prepareStatement(Connection conn, 
  414           SQLBuffer sql, JDBCFetchConfiguration fetch, int rsType, 
  415           int rsConcur, boolean isLRS) throws SQLException {
  416           if (fetch == null)
  417               return sql.prepareStatement(conn, rsType, rsConcur);
  418           else
  419               return sql.prepareStatement(conn, fetch, rsType, -1);
  420       }
  421       
  422       /**
  423        * This method is to provide override for non-JDBC or JDBC-like 
  424        * implementation of setting query timeout.
  425        */
  426       protected void setTimeout(PreparedStatement stmnt, boolean forUpdate,
  427           JDBCFetchConfiguration fetch) throws SQLException {
  428           // if this is a locking select and the lock timeout is greater than
  429           // the configured query timeout, use the lock timeout
  430           if (forUpdate && _dict.supportsQueryTimeout && fetch != null 
  431               && fetch.getLockTimeout() > stmnt.getQueryTimeout() * 1000) {
  432               int timeout = fetch.getLockTimeout();
  433               if (timeout < 1000) {
  434                   timeout = 1000; 
  435                   Log log = _conf.getLog(JDBCConfiguration.LOG_JDBC);
  436                   if (log.isWarnEnabled())
  437                       log.warn(_loc.get("millis-query-timeout"));
  438               }
  439               stmnt.setQueryTimeout(timeout / 1000);
  440           }
  441       }
  442   
  443       /**
  444        * This method is to provide override for non-JDBC or JDBC-like 
  445        * implementation of executing query.
  446        */
  447       protected ResultSet executeQuery(Connection conn, PreparedStatement stmnt, 
  448           SQLBuffer sql, boolean isLRS, JDBCStore store) throws SQLException {
  449           return stmnt.executeQuery();
  450       }
  451       
  452       /**
  453        * This method is to provide override for non-JDBC or JDBC-like 
  454        * implementation of getting count from the result set.
  455        */
  456       protected int getCount(ResultSet rs) throws SQLException {
  457           rs.next();
  458           return rs.getInt(1);
  459       }
  460       
  461       /**
  462        * This method is to provide override for non-JDBC or JDBC-like 
  463        * implementation of executing eager selects.
  464        */
  465       protected Result getEagerResult(Connection conn, 
  466           PreparedStatement stmnt, ResultSet rs, JDBCStore store, 
  467           JDBCFetchConfiguration fetch, boolean forUpdate, String sqlStr) 
  468           throws SQLException {
  469           SelectResult res = new SelectResult(conn, stmnt, rs, _dict);
  470           res.setSelect(this);
  471           res.setStore(store);
  472           res.setLocking(forUpdate);
  473           try {
  474               addEagerResults(res, this, store, fetch);
  475           } catch (SQLException se) {
  476               res.close();
  477               throw se;
  478           }
  479           return res;
  480       }
  481   
  482       /////////////////////////
  483       // Select implementation
  484       /////////////////////////
  485   
  486       public int indexOf() {
  487           return 0;
  488       }
  489   
  490       public List getSubselects() {
  491           return (_subsels == null) ? Collections.EMPTY_LIST : _subsels;
  492       }
  493   
  494       public Select getParent() {
  495           return _parent;
  496       }
  497   
  498       public String getSubselectPath() {
  499           return _subPath;
  500       }
  501   
  502       public void setParent(Select parent, String path) {
  503           if (path != null)
  504               _subPath = path + ':';
  505           else
  506               _subPath = null;
  507   
  508           if (parent == _parent)
  509               return;
  510           if (_parent != null)
  511               _parent._subsels.remove(this);
  512   
  513           //### right now we can't use sql92 joins with subselects, cause
  514           //### I can't figure out what to do when the subselect has a join
  515           //### with an alias also present in the outer select... you don't want
  516           //### the join to appear in the FROM clause of the subselect cause
  517           //### then it re-aliases both tables in the scope of the subselect
  518           //### and the correlation with the outer select is lost
  519           _parent = (SelectImpl) parent;
  520           if (_parent != null) {
  521               if (_parent._subsels == null)
  522                   _parent._subsels = new ArrayList(2);
  523               _parent._subsels.add(this);
  524               if (_parent._joinSyntax == JoinSyntaxes.SYNTAX_SQL92)
  525                   _joinSyntax = JoinSyntaxes.SYNTAX_TRADITIONAL;
  526               else
  527                   _joinSyntax = _parent._joinSyntax;
  528           }
  529       }
  530   
  531       public Select getFromSelect() {
  532           return _from;
  533       }
  534   
  535       public void setFromSelect(Select sel) {
  536           _from = (SelectImpl) sel;
  537           if (_from != null)
  538               _from._outer = this;
  539       }
  540   
  541       public boolean hasEagerJoin(boolean toMany) {
  542           if (toMany)
  543               return (_flags & EAGER_TO_MANY) != 0;
  544           return (_flags & EAGER_TO_ONE) != 0;
  545       }
  546   
  547       public boolean hasJoin(boolean toMany) {
  548           if (toMany)
  549               return (_flags & TO_MANY) != 0;
  550           return _tables != null && _tables.size() > 1;
  551       }
  552   
  553       public boolean isSelected(Table table) {
  554           PathJoins pj = getJoins(null, false);
  555           if (_from != null)
  556               return _from.getTableIndex(table, pj, false) != -1;
  557           return getTableIndex(table, pj, false) != -1;
  558       }
  559   
  560       public Collection getTableAliases() {
  561           return (_tables == null) ? Collections.EMPTY_SET : _tables.values();
  562       }
  563   
  564       public List getSelects() {
  565           return Collections.unmodifiableList(_selects);
  566       }
  567   
  568       public List getSelectAliases() {
  569           return _selects.getAliases(false, _outer != null);
  570       }
  571   
  572       public List getIdentifierAliases() {
  573           return _selects.getAliases(true, _outer != null);
  574       }
  575   
  576       public SQLBuffer getOrdering() {
  577           return _ordering;
  578       }
  579   
  580       public SQLBuffer getGrouping() {
  581           return _grouping;
  582       }
  583   
  584       public SQLBuffer getWhere() {
  585           return _where;
  586       }
  587   
  588       public SQLBuffer getHaving() {
  589           return _having;
  590       }
  591   
  592       public void addJoinClassConditions() {
  593           if (_joins == null || _joins.joins() == null)
  594               return;
  595   
  596           // join set iterator allows concurrent modification
  597           Join j;
  598           for (Iterator itr = _joins.joins().iterator(); itr.hasNext();) {
  599               j = (Join) itr.next();
  600               if (j.getRelationTarget() != null) {
  601                   j.getRelationTarget().getDiscriminator().addClassConditions
  602                       (this, j.getSubclasses() == SUBS_JOINABLE, 
  603                       j.getRelationJoins());
  604                   j.setRelation(null, 0, null);
  605               }
  606           }
  607       }
  608   
  609       public Joins getJoins() {
  610           return _joins;
  611       }
  612   
  613       public Iterator getJoinIterator() {
  614           if (_joins == null || _joins.isEmpty())
  615               return EmptyIterator.INSTANCE;
  616           return _joins.joins().joinIterator();
  617       }
  618   
  619       public long getStartIndex() {
  620           return _startIdx;
  621       }
  622   
  623       public long getEndIndex() {
  624           return _endIdx;
  625       }
  626   
  627       public void setRange(long start, long end) {
  628           _startIdx = start;
  629           _endIdx = end;
  630       }
  631   
  632       public String getColumnAlias(Column col) {
  633           return getColumnAlias(col, (Joins) null);
  634       }
  635   
  636       public String getColumnAlias(Column col, Joins joins) {
  637           return getColumnAlias(col, getJoins(joins, false));
  638       }
  639   
  640       /**
  641        * Return the alias for the given column.
  642        */
  643       private String getColumnAlias(Column col, PathJoins pj) {
  644           return getColumnAlias(col.getName(), col.getTable(), pj);
  645       }
  646   
  647       public String getColumnAlias(String col, Table table) {
  648           return getColumnAlias(col, table, (Joins) null);
  649       }
  650   
  651       public String getColumnAlias(String col, Table table, Joins joins) {
  652           return getColumnAlias(col, table, getJoins(joins, false));
  653       }
  654   
  655       /**
  656        * Return the alias for the given column.
  657        */
  658       private String getColumnAlias(String col, Table table, PathJoins pj) {
  659           if (_from != null) {
  660               String alias = toAlias(_from.getTableIndex(table, pj, true));
  661               if (_dict.requiresAliasForSubselect)
  662                   return FROM_SELECT_ALIAS + "." + alias + "_" + col;
  663               return alias + "_" + col;
  664           }
  665           return toAlias(getTableIndex(table, pj, true)) + "." + col;
  666       }
  667   
  668       public boolean isAggregate() {
  669           return (_flags & AGGREGATE) != 0;
  670       }
  671   
  672       public void setAggregate(boolean agg) {
  673           if (agg)
  674               _flags |= AGGREGATE;
  675           else
  676               _flags &= ~AGGREGATE;
  677       }
  678   
  679       public boolean isLob() {
  680           return (_flags & LOB) != 0;
  681       }
  682   
  683       public void setLob(boolean lob) {
  684           if (lob)
  685               _flags |= LOB;
  686           else
  687               _flags &= ~LOB;
  688       }
  689   
  690       public void clearSelects() {
  691           _selects.clear();
  692       }
  693   
  694       public boolean select(SQLBuffer sql, Object id) {
  695           return select(sql, id, null);
  696       }
  697   
  698       public boolean select(SQLBuffer sql, Object id, Joins joins) {
  699           if (!isGrouping())
  700               return select((Object) sql, id, joins);
  701           groupBy(sql, joins);
  702           return false;
  703       }
  704   
  705       /**
  706        * Record the select of the given SQL buffer or string.
  707        */
  708       private boolean select(Object sql, Object id, Joins joins) {
  709           getJoins(joins, true);
  710           boolean contains;
  711           if (id == null) {
  712               int idx = _selects.indexOfAlias(sql);
  713               contains = idx != -1;
  714               if (contains)
  715                   id = _selects.get(idx);
  716               else
  717                   id = nullId();
  718           } else
  719               contains = _selects.contains(id);
  720   
  721           if (contains)
  722               return false;
  723           _selects.setAlias(id, sql, false);
  724           return true;
  725       }
  726   
  727       /**
  728        * Returns a unique id for a SQL string whose given id is null.
  729        */
  730       private Object nullId() {
  731           if (_nullIds >= NULL_IDS.length)
  732               return new NullId();
  733           return NULL_IDS[_nullIds++];
  734       }
  735   
  736       public boolean select(String sql, Object id) {
  737           return select(sql, id, null);
  738       }
  739   
  740       public boolean select(String sql, Object id, Joins joins) {
  741           if (!isGrouping())
  742               return select((Object) sql, id, joins);
  743           groupBy(sql, joins);
  744           return true;
  745       }
  746   
  747       public void selectPlaceholder(String sql) {
  748           Object holder = (_placeholders >= PLACEHOLDERS.length)
  749               ? new Placeholder() : PLACEHOLDERS[_placeholders++];
  750           select(sql, holder);
  751       }
  752   
  753       /**
  754        * Insert a placeholder at the given index; use a negative index
  755        * to count from the back of the select list.
  756        */
  757       public void insertPlaceholder(String sql, int pos) {
  758           Object holder = (_placeholders >= PLACEHOLDERS.length)
  759               ? new Placeholder() : PLACEHOLDERS[_placeholders++];
  760           _selects.insertAlias(pos, holder, sql);
  761       }
  762   
  763       /**
  764        * Clear selected placeholders, and return removed select indexes.
  765        */
  766       public void clearPlaceholderSelects() {
  767           _selects.clearPlaceholders();
  768       }
  769   
  770       public boolean select(Column col) {
  771           return select(col, (Joins) null);
  772       }
  773   
  774       public boolean select(Column col, Joins joins) {
  775           if (!isGrouping())
  776               return select(col, getJoins(joins, true), false);
  777           groupBy(col, joins);
  778           return false;
  779       }
  780   
  781       public int select(Column[] cols) {
  782           return select(cols, null);
  783       }
  784   
  785       public int select(Column[] cols, Joins joins) {
  786           if (cols == null || cols.length == 0)
  787               return 0;
  788           if (isGrouping()) {
  789               groupBy(cols, joins);
  790               return 0;
  791           }
  792           PathJoins pj = getJoins(joins, true);
  793           int seld = 0;
  794           for (int i = 0; i < cols.length; i++)
  795               if (select(cols[i], pj, false))
  796                   seld |= 2 << i;
  797           return seld;
  798       }
  799   
  800       /**
  801        * Select the given column after making the given joins.
  802        */
  803       private boolean select(Column col, PathJoins pj, boolean ident) {
  804           // we cache on column object if there are no joins so that when
  805           // looking up columns in the result we don't have to create a string
  806           // buffer for the table + column alias; if there are joins, then
  807           // we key on the alias
  808           String alias = getColumnAlias(col, pj);
  809           Object id;
  810           if (pj == null || pj.path() == null)
  811               id = col;
  812           else
  813               id = alias;
  814           if (_selects.contains(id))
  815               return false;
  816   
  817           if (col.getType() == Types.BLOB || col.getType() == Types.CLOB)
  818               setLob(true);
  819           _selects.setAlias(id, alias, ident);
  820           return true;
  821       }
  822   
  823       public void select(ClassMapping mapping, int subclasses,
  824           JDBCStore store, JDBCFetchConfiguration fetch, int eager) {
  825           select(mapping, subclasses, store, fetch, eager, null);
  826       }
  827   
  828       public void select(ClassMapping mapping, int subclasses,
  829           JDBCStore store, JDBCFetchConfiguration fetch, int eager,
  830           Joins joins) {
  831           select(this, mapping, subclasses, store, fetch, eager, joins, false);
  832       }
  833   
  834       /**
  835        * Select the given mapping.
  836        */
  837       void select(Select wrapper, ClassMapping mapping, int subclasses,
  838           JDBCStore store, JDBCFetchConfiguration fetch, int eager,
  839           Joins joins, boolean ident) {
  840           // note that this is one case where we don't want to use the result
  841           // of getJoins(); just use the given joins, which will either be clean
  842           // or the result of previous pre-joins. this way we don't push extra
  843           // stack stuff when no actual new joins have been made, and we don't
  844           // think the user wants outer joins when actually only the previous
  845           // joins were outer.  we do invoke getJoins(), though, to add these
  846           // joins (if any) to our top-level joins; otherwise it'd be possible
  847           // for the user to immediately do another join and select something,
  848           // and if we're in outer mode all these joins will get switched to outer
  849           // joins.  caching them as their original join type prevents that
  850           getJoins(joins, true);
  851   
  852           PathJoins pj = (PathJoins) joins;
  853           boolean hasJoins = pj != null && pj.isDirty();
  854           if (hasJoins) {
  855               if (_preJoins == null)
  856                   _preJoins = new Stack();
  857               _preJoins.push(pj);
  858           }
  859   
  860           // if they are selecting this mapping with outer joins, then all joins
  861           // from this mapping should also be outer
  862           boolean wasOuter = (_flags & OUTER) != 0;
  863           if (hasJoins && !wasOuter && pj.isOuter())
  864               _flags |= OUTER;
  865   
  866           // delegate to store manager to select in same order it loads result
  867           ((JDBCStoreManager) store).select(wrapper, mapping, subclasses, null,
  868               null, fetch, eager, ident, (_flags & OUTER) != 0);
  869   
  870           // reset
  871           if (hasJoins)
  872               _preJoins.pop();
  873           if (!wasOuter && (_flags & OUTER) != 0)
  874               _flags &= ~OUTER;
  875       }
  876   
  877       public boolean selectIdentifier(Column col) {
  878           return selectIdentifier(col, (Joins) null);
  879       }
  880   
  881       public boolean selectIdentifier(Column col, Joins joins) {
  882           if (!isGrouping())
  883               return select(col, getJoins(joins, true), true);
  884           groupBy(col, joins);
  885           return false;
  886       }
  887   
  888       public int selectIdentifier(Column[] cols) {
  889           return selectIdentifier(cols, null);
  890       }
  891   
  892       public int selectIdentifier(Column[] cols, Joins joins) {
  893           if (cols == null || cols.length == 0)
  894               return 0;
  895           if (isGrouping()) {
  896               groupBy(cols, joins);
  897               return 0;
  898           }
  899           PathJoins pj = getJoins(joins, true);
  900           int seld = 0;
  901           for (int i = 0; i < cols.length; i++)
  902               if (select(cols[i], pj, true))
  903                   seld |= 2 << i;
  904           return seld;
  905       }
  906   
  907       public void selectIdentifier(ClassMapping mapping, int subclasses,
  908           JDBCStore store, JDBCFetchConfiguration fetch, int eager) {
  909           selectIdentifier(mapping, subclasses, store, fetch, eager, null);
  910       }
  911   
  912       public void selectIdentifier(ClassMapping mapping, int subclasses,
  913           JDBCStore store, JDBCFetchConfiguration fetch, int eager,
  914           Joins joins) {
  915           select(this, mapping, subclasses, store, fetch, eager, joins, true);
  916       }
  917   
  918       public int selectPrimaryKey(ClassMapping mapping) {
  919           return selectPrimaryKey(mapping, null);
  920       }
  921   
  922       public int selectPrimaryKey(ClassMapping mapping, Joins joins) {
  923           return primaryKeyOperation(mapping, true, null, joins, false);
  924       }
  925   
  926       /**
  927        * Operate on primary key data. Return a bit mask of selected columns.
  928        */
  929       private int primaryKeyOperation(ClassMapping mapping, boolean sel,
  930           Boolean asc, Joins joins, boolean aliasOrder) {
  931           if (!sel && asc == null)
  932               return 0;
  933   
  934           // if this mapping can't select the full pk values, then join to
  935           // super and recurse
  936           ClassMapping sup;
  937           if (!mapping.isPrimaryKeyObjectId(true)) {
  938               sup = mapping.getJoinablePCSuperclassMapping();
  939               if (joins == null)
  940                   joins = newJoins();
  941               joins = mapping.joinSuperclass(joins, false);
  942               return primaryKeyOperation(sup, sel, asc, joins, aliasOrder);
  943           }
  944   
  945           Column[] cols = mapping.getPrimaryKeyColumns();
  946           if (isGrouping()) {
  947               groupBy(cols, joins);
  948               return 0;
  949           }
  950   
  951           PathJoins pj = getJoins(joins, false);
  952           int seld = 0;
  953           for (int i = 0; i < cols.length; i++)
  954               if (columnOperation(cols[i], sel, asc, pj, aliasOrder))
  955                   seld |= 2 << i;
  956   
  957           // if this mapping has not been used in the select yet (and therefore
  958           // is not joined to anything), but has an other-table superclass that
  959           // has been used, make sure to join to it
  960           boolean joined = false;
  961           for (sup = mapping.getJoinablePCSuperclassMapping(); sup != null;
  962               mapping = sup, sup = mapping.getJoinablePCSuperclassMapping()) {
  963               if (sup.getTable() == mapping.getTable())
  964                   continue;
  965   
  966               if (mapping.getTable() != sup.getTable()
  967                   && getTableIndex(mapping.getTable(), pj, false) == -1
  968                   && getTableIndex(sup.getTable(), pj, false) != -1) {
  969                   if (pj == null)
  970                       pj = (PathJoins) newJoins();
  971                   pj = (PathJoins) mapping.joinSuperclass(pj, false);
  972                   joined = true;
  973               } else
  974                   break;
  975           }
  976           if (joined)
  977               where(pj);
  978   
  979           return seld;
  980       }
  981   
  982       /**
  983        * Perform an operation on a column.
  984        */
  985       private boolean columnOperation(Column col, boolean sel, Boolean asc,
  986           PathJoins pj, boolean aliasOrder) {
  987           String as = null;
  988           if (asc != null && (aliasOrder || (_flags & RECORD_ORDERED) != 0)) {
  989               Object id;
  990               if (pj == null || pj.path() == null)
  991                   id = col;
  992               else
  993                   id = getColumnAlias(col, pj);
  994               if ((_flags & RECORD_ORDERED) != 0) {
  995                   if (_ordered == null)
  996                       _ordered = new ArrayList(5);
  997                   _ordered.add(id);
  998               }
  999               if (aliasOrder) {
 1000                   as = toOrderAlias(_orders++);
 1001                   _selects.setSelectAs(id, as);
 1002               }
 1003           }
 1004   
 1005           boolean seld = sel && select(col, pj, false);
 1006           if (asc != null) {
 1007               String alias = (as != null) ? as : getColumnAlias(col, pj);
 1008               appendOrdering(alias, asc.booleanValue());
 1009           }
 1010           return seld;
 1011       }
 1012   
 1013       /**
 1014        * Append ordering information to our internal buffer.
 1015        */
 1016       private void appendOrdering(Object orderBy, boolean asc) {
 1017           if (_ordering == null)
 1018               _ordering = new SQLBuffer(_dict);
 1019           else
 1020               _ordering.append(", ");
 1021   
 1022           if (orderBy instanceof SQLBuffer)
 1023               _ordering.append((SQLBuffer) orderBy);
 1024           else
 1025               _ordering.append((String) orderBy);
 1026           if (asc)
 1027               _ordering.append(" ASC");
 1028           else
 1029               _ordering.append(" DESC");
 1030       }
 1031   
 1032       public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
 1033           boolean sel) {
 1034           return orderByPrimaryKey(mapping, asc, null, sel);
 1035       }
 1036   
 1037       public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
 1038           Joins joins, boolean sel) {
 1039           return orderByPrimaryKey(mapping, asc, joins, sel, false);
 1040       }
 1041   
 1042       /**
 1043        * Allow unions to set aliases on order columns.
 1044        */
 1045       public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
 1046           Joins joins, boolean sel, boolean aliasOrder) {
 1047           return primaryKeyOperation(mapping, sel,
 1048               (asc) ? Boolean.TRUE : Boolean.FALSE, joins, aliasOrder);
 1049       }
 1050   
 1051       public boolean orderBy(Column col, boolean asc, boolean sel) {
 1052           return orderBy(col, asc, null, sel);
 1053       }
 1054   
 1055       public boolean orderBy(Column col, boolean asc, Joins joins, boolean sel) {
 1056           return orderBy(col, asc, joins, sel, false);
 1057       }
 1058   
 1059       /**
 1060        * Allow unions to set aliases on order columns.
 1061        */
 1062       boolean orderBy(Column col, boolean asc, Joins joins, boolean sel,
 1063           boolean aliasOrder) {
 1064           return columnOperation(col, sel, (asc) ? Boolean.TRUE : Boolean.FALSE,
 1065               getJoins(joins, true), aliasOrder);
 1066       }
 1067   
 1068       public int orderBy(Column[] cols, boolean asc, boolean sel) {
 1069           return orderBy(cols, asc, null, sel);
 1070       }
 1071   
 1072       public int orderBy(Column[] cols, boolean asc, Joins joins, boolean sel) {
 1073           return orderBy(cols, asc, joins, sel, false);
 1074       }
 1075   
 1076       /**
 1077        * Allow unions to set aliases on order columns.
 1078        */
 1079       int orderBy(Column[] cols, boolean asc, Joins joins, boolean sel,
 1080           boolean aliasOrder) {
 1081           PathJoins pj = getJoins(joins, true);
 1082           int seld = 0;
 1083           for (int i = 0; i < cols.length; i++)
 1084               if (columnOperation(cols[i], sel,
 1085                   (asc) ? Boolean.TRUE : Boolean.FALSE, pj, aliasOrder))
 1086                   seld |= 2 << i;
 1087           return seld;
 1088       }
 1089   
 1090       public boolean orderBy(SQLBuffer sql, boolean asc, boolean sel) {
 1091           return orderBy(sql, asc, (Joins) null, sel);
 1092       }
 1093   
 1094       public boolean orderBy(SQLBuffer sql, boolean asc, Joins joins,
 1095           boolean sel) {
 1096           return orderBy(sql, asc, joins, sel, false);
 1097       }
 1098   
 1099       /**
 1100        * Allow unions to set aliases on order columns.
 1101        */
 1102       boolean orderBy(SQLBuffer sql, boolean asc, Joins joins, boolean sel,
 1103           boolean aliasOrder) {
 1104           return orderBy((Object) sql, asc, joins, sel, aliasOrder);
 1105       }
 1106   
 1107       /**
 1108        * Order on a SQL buffer or string.
 1109        */
 1110       private boolean orderBy(Object sql, boolean asc, Joins joins, boolean sel,
 1111           boolean aliasOrder) {
 1112           Object order = sql;
 1113           if (aliasOrder) {
 1114               order = toOrderAlias(_orders++);
 1115               _selects.setSelectAs(sql, (String) order);
 1116           }
 1117           if ((_flags & RECORD_ORDERED) != 0) {
 1118               if (_ordered == null)
 1119                   _ordered = new ArrayList(5);
 1120               _ordered.add(sql);
 1121           }
 1122   
 1123           getJoins(joins, true);
 1124           appendOrdering(order, asc);
 1125           if (sel) {
 1126               int idx = _selects.indexOfAlias(sql);
 1127               if (idx == -1) {
 1128                   _selects.setAlias(nullId(), sql, false);
 1129                   return true;
 1130               }
 1131           }
 1132           return false;
 1133       }
 1134   
 1135       public boolean orderBy(String sql, boolean asc, boolean sel) {
 1136           return orderBy(sql, asc, null, sel);
 1137       }
 1138   
 1139       public boolean orderBy(String sql, boolean asc, Joins joins, boolean sel) {
 1140           return orderBy(sql, asc, joins, sel, false);
 1141       }
 1142   
 1143       /**
 1144        * Allow unions to set aliases on order columns.
 1145        */
 1146       boolean orderBy(String sql, boolean asc, Joins joins, boolean sel,
 1147           boolean aliasOrder) {
 1148           return orderBy((Object) sql, asc, joins, sel, aliasOrder);
 1149       }
 1150   
 1151       public void clearOrdering() {
 1152           _ordering = null;
 1153           _orders = 0;
 1154       }
 1155   
 1156       /**
 1157        * Allow unions to record the select list indexes of items we order by.
 1158        */
 1159       void setRecordOrderedIndexes(boolean record) {
 1160           if (record)
 1161               _flags |= RECORD_ORDERED;
 1162           else {
 1163               _ordered = null;
 1164               _flags &= ~RECORD_ORDERED;
 1165           }
 1166       }
 1167   
 1168       /**
 1169        * Return the indexes in the select list of all items we're ordering
 1170        * by, or null if none. For use with unions.
 1171        */
 1172       List getOrderedIndexes() {
 1173           if (_ordered == null)
 1174               return null;
 1175           List idxs = new ArrayList(_ordered.size());
 1176           for (int i = 0; i < _ordered.size(); i++)
 1177               idxs.add(Numbers.valueOf(_selects.indexOf(_ordered.get(i))));
 1178           return idxs;
 1179       }
 1180   
 1181       public void wherePrimaryKey(Object oid, ClassMapping mapping,
 1182           JDBCStore store) {
 1183           wherePrimaryKey(oid, mapping, null, store);
 1184       }
 1185   
 1186       /**
 1187        * Add where conditions setting the mapping's primary key to the given
 1188        * oid values. If the given mapping does not use oid values for its
 1189        * primary key, we will recursively join to its superclass until we find
 1190        * an ancestor that does.
 1191        */
 1192       private void wherePrimaryKey(Object oid, ClassMapping mapping, Joins joins,
 1193           JDBCStore store) {
 1194           // if this mapping's identifiers include something other than
 1195           // the pk values, join to super and recurse
 1196           if (!mapping.isPrimaryKeyObjectId(false)) {
 1197               ClassMapping sup = mapping.getJoinablePCSuperclassMapping();
 1198               if (joins == null)
 1199                   joins = newJoins();
 1200               joins = mapping.joinSuperclass(joins, false);
 1201               wherePrimaryKey(oid, sup, joins, store);
 1202               return;
 1203           }
 1204   
 1205           Column[] cols = mapping.getPrimaryKeyColumns();
 1206           where(oid, mapping, cols, cols, null, null, getJoins(joins, true),
 1207               store);
 1208       }
 1209   
 1210       public void whereForeignKey(ForeignKey fk, Object oid,
 1211           ClassMapping mapping, JDBCStore store) {
 1212           whereForeignKey(fk, oid, mapping, null, store);
 1213       }
 1214   
 1215       /**
 1216        * Add where conditions setting the given foreign key to the given
 1217        * oid values.
 1218        *
 1219        * @see #wherePrimaryKey
 1220        */
 1221       private void whereForeignKey(ForeignKey fk, Object oid,
 1222           ClassMapping mapping, Joins joins, JDBCStore store) {
 1223           // if this mapping's identifiers include something other than
 1224           // the pk values, or if this foreign key doesn't link to only
 1225           // identifiers, join to table and do a getPrimaryKey
 1226           if (!mapping.isPrimaryKeyObjectId(false) || !containsAll
 1227               (mapping.getPrimaryKeyColumns(), fk.getPrimaryKeyColumns())) {
 1228               if (joins == null)
 1229                   joins = newJoins();
 1230               // traverse to foreign key target mapping
 1231               while (mapping.getTable() != fk.getPrimaryKeyTable()) {
 1232                   if (joins == null)
 1233                       joins = newJoins();
 1234                   joins = mapping.joinSuperclass(joins, false);
 1235                   mapping = mapping.getJoinablePCSuperclassMapping();
 1236                   if (mapping == null)
 1237                       throw new InternalException();
 1238               }
 1239               joins = joins.join(fk, false, false);
 1240               wherePrimaryKey(oid, mapping, joins, store);
 1241               return;
 1242           }
 1243   
 1244           Column[] fromCols = fk.getColumns();
 1245           Column[] toCols = fk.getPrimaryKeyColumns();
 1246           Column[] constCols = fk.getConstantColumns();
 1247           Object[] consts = fk.getConstants();
 1248           where(oid, mapping, toCols, fromCols, consts, constCols,
 1249               getJoins(joins, true), store);
 1250       }
 1251   
 1252       /**
 1253        * Internal method to flush the oid values as where conditions to the
 1254        * given columns.
 1255        */
 1256       private void where(Object oid, ClassMapping mapping, Column[] toCols,
 1257           Column[] fromCols, Object[] vals, Column[] constCols, PathJoins pj,
 1258           JDBCStore store) {
 1259           ValueMapping embed = mapping.getEmbeddingMapping();
 1260           if (embed != null) {
 1261               where(oid, embed.getFieldMapping().getDefiningMapping(),
 1262                   toCols, fromCols, vals, constCols, pj, store);
 1263               return;
 1264           }
 1265   
 1266           // only bother to pack pk values into array if app id
 1267           Object[] pks = null;
 1268           if (mapping.getIdentityType() == ClassMapping.ID_APPLICATION)
 1269               pks = ApplicationIds.toPKValues(oid, mapping);
 1270   
 1271           SQLBuffer buf = new SQLBuffer(_dict);
 1272           Joinable join;
 1273           Object val;
 1274           int count = 0;
 1275           for (int i = 0; i < toCols.length; i++, count++) {
 1276               if (pks == null)
 1277                   val = (oid == null) ? null : Numbers.valueOf(((Id) oid).getId());
 1278               else {
 1279                   // must be app identity; use pk index to get correct pk value
 1280                   join = mapping.assertJoinable(toCols[i]);
 1281                   val = pks[mapping.getField(join.getFieldIndex()).
 1282                       getPrimaryKeyIndex()];
 1283                   val = join.getJoinValue(val, toCols[i], store);
 1284               }
 1285   
 1286               if (count > 0)
 1287                   buf.append(" AND ");
 1288               buf.append(getColumnAlias(fromCols[i], pj));
 1289               if (val == null)
 1290                   buf.append(" IS ");
 1291               else
 1292                   buf.append(" = ");
 1293               buf.appendValue(val, fromCols[i]);
 1294           }
 1295   
 1296           if (constCols != null && constCols.length > 0) {
 1297               for (int i = 0; i < constCols.length; i++, count++) {
 1298                   if (count > 0)
 1299                       buf.append(" AND ");
 1300                   buf.append(getColumnAlias(constCols[i], pj));
 1301   
 1302                   if (vals[i] == null)
 1303                       buf.append(" IS ");
 1304                   else
 1305                       buf.append(" = ");
 1306                   buf.appendValue(vals[i], constCols[i]);
 1307               }
 1308           }
 1309   
 1310           where(buf, pj);
 1311       }
 1312   
 1313       /**
 1314        * Test to see if the given set of columns contains all the
 1315        * columns in the given potential subset.
 1316        */
 1317       private static boolean containsAll(Column[] set, Column[] sub) {
 1318           if (sub.length > set.length)
 1319               return false;
 1320   
 1321           // this is obviously n^2, but the number of columns should be in
 1322           // the 1-2 range, so no biggie
 1323           boolean found = true;
 1324           for (int i = 0; i < sub.length && found; i++) {
 1325               found = false;
 1326               for (int j = 0; j < set.length && !found; j++)
 1327                   found = sub[i] == set[j];
 1328           }
 1329           return found;
 1330       }
 1331   
 1332       public void where(Joins joins) {
 1333           if (joins != null)
 1334               where((String) null, joins);
 1335       }
 1336   
 1337       public void where(SQLBuffer sql) {
 1338           where(sql, (Joins) null);
 1339       }
 1340   
 1341       public void where(SQLBuffer sql, Joins joins) {
 1342           where(sql, getJoins(joins, true));
 1343       }
 1344   
 1345       /**
 1346        * Add the given condition to the WHERE clause.
 1347        */
 1348       private void where(SQLBuffer sql, PathJoins pj) {
 1349           // no need to use joins...
 1350           if (sql == null || sql.isEmpty())
 1351               return;
 1352   
 1353           if (_where == null)
 1354               _where = new SQLBuffer(_dict);
 1355           else if (!_where.isEmpty())
 1356               _where.append(" AND ");
 1357           _where.append(sql);
 1358       }
 1359   
 1360       public void where(String sql) {
 1361           where(sql, (Joins) null);
 1362       }
 1363   
 1364       public void where(String sql, Joins joins) {
 1365           where(sql, getJoins(joins, true));
 1366       }
 1367   
 1368       /**
 1369        * Add the given condition to the WHERE clause.
 1370        */
 1371       private void where(String sql, PathJoins pj) {
 1372           // no need to use joins...
 1373           if (StringUtils.isEmpty(sql))
 1374               return;
 1375   
 1376           if (_where == null)
 1377               _where = new SQLBuffer(_dict);
 1378           else if (!_where.isEmpty())
 1379               _where.append(" AND ");
 1380           _where.append(sql);
 1381       }
 1382   
 1383       public void having(SQLBuffer sql) {
 1384           having(sql, (Joins) null);
 1385       }
 1386   
 1387       public void having(SQLBuffer sql, Joins joins) {
 1388           having(sql, getJoins(joins, true));
 1389       }
 1390   
 1391       /**
 1392        * Add the given condition to the HAVING clause.
 1393        */
 1394       private void having(SQLBuffer sql, PathJoins pj) {
 1395           // no need to use joins...
 1396           if (sql == null || sql.isEmpty())
 1397               return;
 1398   
 1399           if (_having == null)
 1400               _having = new SQLBuffer(_dict);
 1401           else if (!_having.isEmpty())
 1402               _having.append(" AND ");
 1403           _having.append(sql);
 1404       }
 1405   
 1406       public void having(String sql) {
 1407           having(sql, (Joins) null);
 1408       }
 1409   
 1410       public void having(String sql, Joins joins) {
 1411           having(sql, getJoins(joins, true));
 1412       }
 1413   
 1414       /**
 1415        * Add the given condition to the HAVING clause.
 1416        */
 1417       private void having(String sql, PathJoins pj) {
 1418           // no need to use joins...
 1419           if (StringUtils.isEmpty(sql))
 1420               return;
 1421   
 1422           if (_having == null)
 1423               _having = new SQLBuffer(_dict);
 1424           else if (!_having.isEmpty())
 1425               _having.append(" AND ");
 1426           _having.append(sql);
 1427       }
 1428   
 1429       public void groupBy(SQLBuffer sql) {
 1430           groupBy(sql, (Joins) null);
 1431       }
 1432   
 1433       public void groupBy(SQLBuffer sql, Joins joins) {
 1434           getJoins(joins, true);
 1435           groupByAppend(sql.getSQL());
 1436       }
 1437   
 1438       public void groupBy(String sql) {
 1439           groupBy(sql, (Joins) null);
 1440       }
 1441   
 1442       public void groupBy(String sql, Joins joins) {
 1443           getJoins(joins, true);
 1444           groupByAppend(sql);
 1445       }
 1446   
 1447       public void groupBy(Column col) {
 1448           groupBy(col, null);
 1449       }
 1450   
 1451       public void groupBy(Column col, Joins joins) {
 1452           PathJoins pj = getJoins(joins, true);
 1453           groupByAppend(getColumnAlias(col, pj));
 1454       }
 1455   
 1456       public void groupBy(Column[] cols) {
 1457           groupBy(cols, null);
 1458       }
 1459   
 1460       public void groupBy(Column[] cols, Joins joins) {
 1461           PathJoins pj = getJoins(joins, true);
 1462           for (int i = 0; i < cols.length; i++) {
 1463               groupByAppend(getColumnAlias(cols[i], pj));
 1464           }
 1465       }
 1466       
 1467       private void groupByAppend(String sql) {
 1468           if (_grouped == null || !_grouped.contains(sql)) {
 1469               if (_grouping == null) {
 1470                   _grouping = new SQLBuffer(_dict);
 1471                   _grouped = new ArrayList();
 1472               } else
 1473                   _grouping.append(", ");
 1474   
 1475               _grouping.append(sql);
 1476               _grouped.add(sql);
 1477           }
 1478       }
 1479   
 1480       public void groupBy(ClassMapping mapping, int subclasses, JDBCStore store,
 1481           JDBCFetchConfiguration fetch) {
 1482           groupBy(mapping, subclasses, store, fetch, null);
 1483       }
 1484   
 1485       public void groupBy(ClassMapping mapping, int subclasses, JDBCStore store,
 1486           JDBCFetchConfiguration fetch, Joins joins) {
 1487           // we implement this by putting ourselves into grouping mode, where
 1488           // all select invocations are re-routed to group-by invocations instead.
 1489           // this allows us to utilize the same select APIs of the store manager
 1490           // and all the mapping strategies, rather than having to create 
 1491           // equivalent APIs and duplicate logic for grouping
 1492           boolean wasGrouping = isGrouping();
 1493           _flags |= GROUPING;
 1494           try {
 1495               select(mapping, subclasses, store, fetch, 
 1496                   EagerFetchModes.EAGER_NONE, joins);
 1497           } finally {
 1498               if (!wasGrouping)
 1499                   _flags &= ~GROUPING;
 1500           }
 1501       }
 1502   
 1503       /**
 1504        * Whether we're in group mode, where any select is changed to a group-by
 1505        * call.
 1506        */
 1507       private boolean isGrouping() {
 1508           return (_flags & GROUPING) != 0;
 1509       }
 1510   
 1511       /**
 1512        * Return the joins to use for column aliases, etc.
 1513        *
 1514        * @param joins joins given by the user
 1515        * @return the joins to use for aliases, etc
 1516        */
 1517       private PathJoins getJoins(Joins joins, boolean record) {
 1518           PathJoins pj = (PathJoins) joins;
 1519           boolean pre = (pj == null || !pj.isDirty())
 1520               && _preJoins != null && !_preJoins.isEmpty();
 1521           if (pre)
 1522               pj = (PathJoins) _preJoins.peek();
 1523   
 1524           if (pj == null || !pj.isDirty())
 1525               pj = _joins;
 1526           else if (!pre) {
 1527               if ((_flags & OUTER) != 0)
 1528                   pj = (PathJoins) outer(pj);
 1529               if (record) {
 1530                   if (!pj.isEmpty())
 1531                       removeParentJoins(pj);
 1532                   if (!pj.isEmpty()) {
 1533                       removeJoinsFromSubselects(pj);
 1534                       if (_joins == null)
 1535                           _joins = new SelectJoins(this);
 1536                       if (_joins.joins() == null)
 1537                           _joins.setJoins(new JoinSet(pj.joins()));
 1538                       else
 1539                           _joins.joins().addAll(pj.joins());
 1540                   }
 1541               }
 1542           }
 1543           return pj;
 1544       }
 1545   
 1546       /**
 1547        * Remove any joins already in our parent select from the given non-empty
 1548        * join set.
 1549        */
 1550       private void removeParentJoins(PathJoins pj) {
 1551           if (_parent == null)
 1552               return;
 1553           if (_parent._joins != null && !_parent._joins.isEmpty()) {
 1554               boolean removed = false;
 1555               if (!_removedAliasFromParent.isEmpty())
 1556                   removed = _parent._joins.joins().removeAll(pj.joins());
 1557               if (!removed)
 1558                   pj.joins().removeAll(_parent._joins.joins());
 1559           }
 1560           if (!pj.isEmpty())
 1561               _parent.removeParentJoins(pj);
 1562       }
 1563   
 1564       /**
 1565        * Remove the given non-empty joins from the joins of our subselects.
 1566        */
 1567       private void removeJoinsFromSubselects(PathJoins pj) {
 1568           if (_subsels == null)
 1569               return;
 1570           SelectImpl sub;
 1571           for (int i = 0; i < _subsels.size(); i++) {
 1572               sub = (SelectImpl) _subsels.get(i);
 1573               if (sub._joins != null && !sub._joins.isEmpty())
 1574                   sub._joins.joins().removeAll(pj.joins());
 1575           }
 1576       }
 1577   
 1578       public SelectExecutor whereClone(int sels) {
 1579           if (sels < 1)
 1580               sels = 1;
 1581   
 1582           Select[] clones = null;
 1583           SelectImpl sel;
 1584           for (int i = 0; i < sels; i++) {
 1585               sel = (SelectImpl) _conf.getSQLFactoryInstance().newSelect();
 1586               sel._flags = _flags;
 1587               sel._flags &= ~AGGREGATE;
 1588               sel._flags &= ~OUTER;
 1589               sel._flags &= ~LRS;
 1590               sel._flags &= ~EAGER_TO_ONE;
 1591               sel._flags &= ~EAGER_TO_MANY;
 1592               sel._flags &= ~FORCE_COUNT;
 1593               sel._joinSyntax = _joinSyntax;
 1594               if (_aliases != null)
 1595                   sel._aliases = new HashMap(_aliases);
 1596               if (_tables != null)
 1597                   sel._tables = new TreeMap(_tables);
 1598               if (_joins != null)
 1599                   sel._joins = _joins.clone(sel);
 1600               if (_where != null)
 1601                   sel._where = new SQLBuffer(_where);
 1602               if (_from != null) {
 1603                   sel._from = (SelectImpl) _from.whereClone(1);
 1604                   sel._from._outer = sel;
 1605               }
 1606               if (_subsels != null) {
 1607                   sel._subsels = new ArrayList(_subsels.size());
 1608                   SelectImpl sub, selSub;
 1609                   for (int j = 0; j < _subsels.size(); j++) {
 1610                       sub = (SelectImpl) _subsels.get(j);
 1611                       selSub = (SelectImpl) sub.fullClone(1);
 1612                       selSub._parent = sel;
 1613                       selSub._subPath = sub._subPath;
 1614                       sel._subsels.add(selSub);
 1615                       if (sel._where != null)
 1616                           sel._where.replace(sub, selSub);
 1617                   }
 1618               }
 1619   
 1620               if (sels == 1)
 1621                   return sel;
 1622               if (clones == null)
 1623                   clones = new Select[sels];
 1624               clones[i] = sel;
 1625           }
 1626           return _conf.getSQLFactoryInstance().newUnion(clones);
 1627       }
 1628   
 1629       public SelectExecutor fullClone(int sels) {
 1630           if (sels < 1)
 1631               sels = 1;
 1632   
 1633           Select[] clones = null;
 1634           SelectImpl sel;
 1635           for (int i = 0; i < sels; i++) {
 1636               sel = (SelectImpl) whereClone(1);
 1637               sel._flags = _flags;
 1638               sel._expectedResultCount = _expectedResultCount;
 1639               sel._selects.addAll(_selects);
 1640               if (_ordering != null)
 1641                   sel._ordering = new SQLBuffer(_ordering);
 1642               sel._orders = _orders;
 1643               if (_grouping != null)
 1644                   sel._grouping = new SQLBuffer(_grouping);
 1645               if (_having != null)
 1646                   sel._having = new SQLBuffer(_having);
 1647               if (_from != null) {
 1648                   sel._from = (SelectImpl) _from.fullClone(1);
 1649                   sel._from._outer = sel;
 1650               }
 1651   
 1652               if (sels == 1)
 1653                   return sel;
 1654               if (clones == null)
 1655                   clones = new Select[sels];
 1656               clones[i] = sel;
 1657           }
 1658           return _conf.getSQLFactoryInstance().newUnion(clones);
 1659       }
 1660   
 1661       public SelectExecutor eagerClone(FieldMapping key, int eagerType,
 1662           boolean toMany, int sels) {
 1663           if (eagerType == EAGER_OUTER
 1664               && _joinSyntax == JoinSyntaxes.SYNTAX_TRADITIONAL)
 1665               return null;
 1666           if (_eagerKeys != null && _eagerKeys.contains(key))
 1667               return null;
 1668   
 1669           // global set of eager keys
 1670           if (_eagerKeys == null)
 1671               _eagerKeys = new HashSet();
 1672           _eagerKeys.add(key);
 1673   
 1674           SelectExecutor sel;
 1675           if (eagerType != EAGER_PARALLEL) {
 1676               if (toMany)
 1677                   _flags |= EAGER_TO_MANY;
 1678               else
 1679                   _flags |= EAGER_TO_ONE;
 1680               sel = this;
 1681           } else if (sels < 2)
 1682               sel = parallelClone();
 1683           else {
 1684               Select[] clones = new Select[sels];
 1685               for (int i = 0; i < clones.length; i++)
 1686                   clones[i] = parallelClone();
 1687               sel = _conf.getSQLFactoryInstance().newUnion(clones);
 1688           }
 1689   
 1690           if (_eager == null)
 1691               _eager = new HashMap();
 1692           _eager.put(toEagerKey(key, getJoins(null, false)), sel);
 1693           return sel;
 1694       }
 1695   
 1696       /**
 1697        * Return a clone of this select for use in eager parallel selects.
 1698        */
 1699       private SelectImpl parallelClone() {
 1700           SelectImpl sel = (SelectImpl) whereClone(1);
 1701           sel._flags &= ~NONAUTO_DISTINCT;
 1702           sel._eagerKeys = _eagerKeys;
 1703           if (_preJoins != null && !_preJoins.isEmpty()) {
 1704               sel._preJoins = new Stack();
 1705               sel._preJoins.push(((SelectJoins) _preJoins.peek()).
 1706                   clone(sel));
 1707           }
 1708           return sel;
 1709       }
 1710   
 1711       /**
 1712        * Return view of eager selects. May be null.
 1713        */
 1714       public Map getEagerMap() {
 1715           return _eager;
 1716       }
 1717   
 1718       public SelectExecutor getEager(FieldMapping key) {
 1719           if (_eager == null || !_eagerKeys.contains(key))
 1720               return null;
 1721           return (SelectExecutor) _eager.get(toEagerKey(key, getJoins(null,
 1722               false)));
 1723       }
 1724   
 1725       /**
 1726        * Return the eager key to use for the user-given key.
 1727        */
 1728       private static Object toEagerKey(FieldMapping key, PathJoins pj) {
 1729           if (pj == null || pj.path() == null)
 1730               return key;
 1731           return new Key(pj.path().toString(), key);
 1732       }
 1733   
 1734       public Joins newJoins() {
 1735           if (_preJoins != null && !_preJoins.isEmpty()) {
 1736               SelectJoins sj = (SelectJoins) _preJoins.peek();
 1737               return sj.clone(this);
 1738           }
 1739           // return this for efficiency in case no joins end up being made
 1740           return this;
 1741       }
 1742   
 1743       public Joins newOuterJoins() {
 1744           return ((PathJoins) newJoins()).setOuter(true);
 1745       }
 1746   
 1747       public void append(SQLBuffer buf, Joins joins) {
 1748           if (joins == null || joins.isEmpty())
 1749               return;
 1750   
 1751           if (!buf.isEmpty())
 1752               buf.append(" AND ");
 1753           Join join = null;
 1754           for (Iterator itr = ((PathJoins) joins).joins().joinIterator();
 1755               itr.hasNext();) {
 1756               join = (Join) itr.next();
 1757               switch (_joinSyntax) {
 1758                   case JoinSyntaxes.SYNTAX_TRADITIONAL:
 1759                       buf.append(_dict.toTraditionalJoin(join));
 1760                       break;
 1761                   case JoinSyntaxes.SYNTAX_DATABASE:
 1762                       buf.append(_dict.toNativeJoin(join));
 1763                       break;
 1764                   default:
 1765                       throw new InternalException();
 1766               }
 1767   
 1768               if (itr.hasNext())
 1769                   buf.append(" AND ");
 1770           }
 1771       }
 1772   
 1773       public Joins and(Joins joins1, Joins joins2) {
 1774           return and((PathJoins) joins1, (PathJoins) joins2, true);
 1775       }
 1776   
 1777       /**
 1778        * Combine the given joins.
 1779        */
 1780       private SelectJoins and(PathJoins j1, PathJoins j2, boolean nullJoins) {
 1781           if ((j1 == null || j1.isEmpty())
 1782               && (j2 == null || j2.isEmpty()))
 1783               return null;
 1784   
 1785           SelectJoins sj = new SelectJoins(this);
 1786           if (j1 == null || j1.isEmpty()) {
 1787               if (nullJoins)
 1788                   sj.setJoins(j2.joins());
 1789               else
 1790                   sj.setJoins(new JoinSet(j2.joins()));
 1791           } else {
 1792               JoinSet set;
 1793               if (nullJoins)
 1794                   set = j1.joins();
 1795               else
 1796                   set = new JoinSet(j1.joins());
 1797   
 1798               if (j2 != null && !j2.isEmpty())
 1799                   set.addAll(j2.joins());
 1800               sj.setJoins(set);
 1801           }
 1802   
 1803           // null previous joins; all are combined into this one
 1804           if (nullJoins && j1 != null)
 1805               j1.nullJoins();
 1806           if (nullJoins && j2 != null)
 1807               j2.nullJoins();
 1808   
 1809           return sj;
 1810       }
 1811   
 1812       public Joins or(Joins joins1, Joins joins2) {
 1813           PathJoins j1 = (PathJoins) joins1;
 1814           PathJoins j2 = (PathJoins) joins2;
 1815   
 1816           // if no common joins, return null; if one side of the or clause has
 1817           // different joins than the other, then we need to use distinct
 1818           boolean j1Empty = j1 == null || j1.isEmpty();
 1819           boolean j2Empty = j2 == null || j2.isEmpty();
 1820           if (j1Empty || j2Empty) {
 1821               if (j1Empty && !j2Empty) {
 1822                   collectOuterJoins(j2);
 1823                   if (!j2.isEmpty())
 1824                       _flags |= IMPLICIT_DISTINCT;
 1825               } else if (j2Empty && !j1Empty) {
 1826                   collectOuterJoins(j1);
 1827                   if (!j1.isEmpty())
 1828                       _flags |= IMPLICIT_DISTINCT;
 1829               }
 1830               return null;
 1831           }
 1832   
 1833           // if all common joins, move all joins to returned instance
 1834           SelectJoins sj = new SelectJoins(this);
 1835           if (j1.joins().equals(j2.joins())) {
 1836               sj.setJoins(j1.joins());
 1837               j1.nullJoins();
 1838               j2.nullJoins();
 1839           } else {
 1840               JoinSet commonJoins = new JoinSet(j1.joins());
 1841               commonJoins.retainAll(j2.joins());
 1842               if (!commonJoins.isEmpty()) {
 1843                   // put common joins in returned instance; remove them from
 1844                   // each given instance
 1845                   sj.setJoins(commonJoins);
 1846                   j1.joins().removeAll(commonJoins);
 1847                   j2.joins().removeAll(commonJoins);
 1848               }
 1849               collectOuterJoins(j1);
 1850               collectOuterJoins(j2);
 1851   
 1852               // if one side of the or clause has different joins than the other,
 1853               // then we need to use distinct
 1854               if (!j1.isEmpty() || !j2.isEmpty())
 1855                   _flags |= IMPLICIT_DISTINCT;
 1856           }
 1857           return sj;
 1858       }
 1859   
 1860       public Joins outer(Joins joins) {
 1861           if (_joinSyntax == JoinSyntaxes.SYNTAX_TRADITIONAL || joins == null)
 1862               return joins;
 1863   
 1864           // record that this is an outer join set, even if it's empty
 1865           PathJoins pj = ((PathJoins) joins).setOuter(true);
 1866           if (pj.isEmpty())
 1867               return pj;
 1868   
 1869           Join join;
 1870           Join rec;
 1871           boolean hasJoins = _joins != null && _joins.joins() != null;
 1872           for (Iterator itr = pj.joins().iterator(); itr.hasNext();) {
 1873               join = (Join) itr.next();
 1874               if (join.getType() == Join.TYPE_INNER) {
 1875                   if (!hasJoins)
 1876                       join.setType(Join.TYPE_OUTER);
 1877                   else {
 1878                       rec = _joins.joins().getRecordedJoin(join);
 1879                       if (rec == null || rec.getType() == Join.TYPE_OUTER)
 1880                           join.setType(Join.TYPE_OUTER);
 1881                   }
 1882               }
 1883           }
 1884           return joins;
 1885       }
 1886   
 1887       /**
 1888        * Moves the joins from the given instance into our outer joins set.
 1889        */
 1890       private void collectOuterJoins(PathJoins pj) {
 1891           if (_joinSyntax == JoinSyntaxes.SYNTAX_TRADITIONAL || pj == null
 1892               || pj.isEmpty())
 1893               return;
 1894   
 1895           if (_joins == null)
 1896               _joins = new SelectJoins(this);
 1897   
 1898           boolean add = true;
 1899           if (_joins.joins() == null) {
 1900               _joins.setJoins(pj.joins());
 1901               add = false;
 1902           }
 1903   
 1904           Join join;
 1905           for (Iterator itr = pj.joins().iterator(); itr.hasNext();) {
 1906               join = (Join) itr.next();
 1907               if (join.getType() == Join.TYPE_INNER) {
 1908                   if (join.getForeignKey() != null
 1909                       && !_dict.canOuterJoin(_joinSyntax, join.getForeignKey())) {
 1910                       Log log = _conf.getLog(JDBCConfiguration.LOG_JDBC);
 1911                       if (log.isWarnEnabled())
 1912                           log.warn(_loc.get("cant-outer-fk",
 1913                               join.getForeignKey()));
 1914                   } else
 1915                       join.setType(Join.TYPE_OUTER);
 1916               }
 1917               if (add)
 1918                   _joins.joins().add(join);
 1919           }
 1920           pj.nullJoins();
 1921       }
 1922   
 1923       /**
 1924        * Return the alias for the given table under the given joins.
 1925        * NOTE: WE RELY ON THESE INDEXES BEING MONOTONICALLY INCREASING FROM 0
 1926        */
 1927       private int getTableIndex(Table table, PathJoins pj, boolean create) {
 1928           // if we have a from select, then there are no table aliases
 1929           if (_from != null)
 1930               return -1;
 1931   
 1932           Object key = table.getFullName();
 1933           if (pj != null && pj.path() != null)
 1934               key = new Key(pj.path().toString(), key);
 1935   
 1936           // check out existing aliases
 1937           Integer i = findAlias(table, key, false, null);
 1938           if (i != null)
 1939               return i.intValue();
 1940           if (!create)
 1941               return -1;
 1942   
 1943           // not found; create alias
 1944           i = Numbers.valueOf(aliasSize());
 1945           recordTableAlias(table, key, i);
 1946           return i.intValue();
 1947       }
 1948   
 1949       /**
 1950        * Attempt to find the alias for the given key.
 1951        *
 1952        * @param fromParent whether a parent is checking its subselects
 1953        * @param fromSub the subselect checking its parent
 1954        */
 1955       private Integer findAlias(Table table, Object key, boolean fromParent,
 1956           SelectImpl fromSub) {
 1957           Integer alias = null;
 1958           if (_aliases != null) {
 1959               alias = (Integer) ((fromParent) ? _aliases.remove(key)
 1960                   : _aliases.get(key));
 1961               if (alias != null) {
 1962                   if (fromParent)
 1963                       _tables.remove(alias);
 1964                   return alias;
 1965               }
 1966           }
 1967           if (!fromParent && _parent != null) {
 1968               boolean removeAliasFromParent = key.toString().indexOf(":") != -1;
 1969               alias = _parent.findAlias(table, key, removeAliasFromParent, this);
 1970               if (alias != null) {
 1971                   if (removeAliasFromParent) {
 1972                       recordTableAlias(table, key, alias);
 1973                       _removedAliasFromParent.set(alias.intValue());
 1974                   }
 1975                   return alias;
 1976               }
 1977           }
 1978           if (_subsels != null) {
 1979               SelectImpl sub;
 1980               for (int i = 0; i < _subsels.size(); i++) {
 1981                   sub = (SelectImpl) _subsels.get(i);
 1982                   if (sub == fromSub)
 1983                       continue;
 1984                   if (alias != null) {
 1985                       if (sub._aliases != null)
 1986                           sub._aliases.remove(key);
 1987                       if (sub._tables != null)
 1988                           sub._tables.remove(alias);
 1989                   } else {
 1990                       if (key instanceof String) {
 1991                           alias = sub.findAlias(table, key, true, null);
 1992                           if (!fromParent && alias != null)
 1993                               recordTableAlias(table, key, alias);
 1994                       }
 1995                   }
 1996               }
 1997           }
 1998           return alias;
 1999       }
 2000   
 2001       /**
 2002        * Record the mapping of the given key to the given alias.
 2003        */
 2004       private void recordTableAlias(Table table, Object key, Integer alias) {
 2005           if (_aliases == null)
 2006               _aliases = new HashMap();
 2007           _aliases.put(key, alias);
 2008   
 2009           String tableString = _dict.getFullName(table, false) + " "
 2010               + toAlias(alias.intValue());
 2011           if (_tables == null)
 2012               _tables = new TreeMap();
 2013           _tables.put(alias, tableString);
 2014       }
 2015   
 2016       /**
 2017        * Calculate total number of aliases.
 2018        */
 2019       private int aliasSize() {
 2020           return aliasSize(false, null);
 2021       }
 2022   
 2023       /**
 2024        * Calculate total number of aliases.
 2025        *
 2026        * @param fromParent whether a parent is checking its subselects
 2027        * @param fromSub the subselect checking its parent
 2028        */
 2029       private int aliasSize(boolean fromParent, SelectImpl fromSub) {
 2030           int aliases = (fromParent || _parent == null) ? 0
 2031               : _parent.aliasSize(false, this);
 2032           aliases += (_aliases == null) ? 0 : _aliases.size();
 2033           if (_subsels != null) {
 2034               SelectImpl sub;
 2035               for (int i = 0; i < _subsels.size(); i++) {
 2036                   sub = (SelectImpl) _subsels.get(i);
 2037                   if (sub != fromSub)
 2038                       aliases += sub.aliasSize(true, null);
 2039               }
 2040           }
 2041           return aliases;
 2042       }
 2043   
 2044       public String toString() {
 2045           return toSelect(false, null).getSQL();
 2046       }
 2047   
 2048       ////////////////////////////
 2049       // PathJoins implementation
 2050       ////////////////////////////
 2051   
 2052       public boolean isOuter() {
 2053           return false;
 2054       }
 2055   
 2056       public PathJoins setOuter(boolean outer) {
 2057           return new SelectJoins(this).setOuter(true);
 2058       }
 2059   
 2060       public boolean isDirty() {
 2061           return false;
 2062       }
 2063   
 2064       public StringBuffer path() {
 2065           return null;
 2066       }
 2067   
 2068       public JoinSet joins() {
 2069           return null;
 2070       }
 2071   
 2072       public int joinCount() {
 2073           return 0;
 2074       }
 2075   
 2076       public void nullJoins() {
 2077       }
 2078   
 2079       public boolean isEmpty() {
 2080           return true;
 2081       }
 2082   
 2083       public Joins crossJoin(Table localTable, Table foreignTable) {
 2084           return new SelectJoins(this).crossJoin(localTable, foreignTable);
 2085       }
 2086   
 2087       public Joins join(ForeignKey fk, boolean inverse, boolean toMany) {
 2088           return new SelectJoins(this).join(fk, inverse, toMany);
 2089       }
 2090   
 2091       public Joins outerJoin(ForeignKey fk, boolean inverse, boolean toMany) {
 2092           return new SelectJoins(this).outerJoin(fk, inverse, toMany);
 2093       }
 2094   
 2095       public Joins joinRelation(String name, ForeignKey fk, ClassMapping target,
 2096           int subs, boolean inverse, boolean toMany) {
 2097           return new SelectJoins(this).joinRelation(name, fk, target, subs, 
 2098               inverse, toMany);
 2099       }
 2100   
 2101       public Joins outerJoinRelation(String name, ForeignKey fk, 
 2102           ClassMapping target, int subs, boolean inverse, boolean toMany) {
 2103           return new SelectJoins(this).outerJoinRelation(name, fk, target, subs, 
 2104               inverse, toMany);
 2105       }
 2106   
 2107       public Joins setVariable(String var) {
 2108           if (var == null)
 2109               return this;
 2110           return new SelectJoins(this).setVariable(var);
 2111       }
 2112   
 2113       public Joins setSubselect(String alias) {
 2114           if (alias == null)
 2115               return this;
 2116           return new SelectJoins(this).setSubselect(alias);
 2117       }
 2118   
 2119       /**
 2120        * Represents a SQL string selected with null id.
 2121        */
 2122       private static class NullId {
 2123       }
 2124   
 2125       /**
 2126        * Represents a placeholder SQL string.
 2127        */
 2128       private static class Placeholder {
 2129       }
 2130   
 2131       /**
 2132        * Key type used for aliases.
 2133        */
 2134       private static class Key {
 2135   
 2136           private final String _path;
 2137           private final Object _key;
 2138   
 2139           public Key(String path, Object key) {
 2140               _path = path;
 2141               _key = key;
 2142           }
 2143   
 2144           public int hashCode() {
 2145               return _path.hashCode() ^ _key.hashCode();
 2146           }
 2147   
 2148           public boolean equals(Object other) {
 2149               if (other == this)
 2150                   return true;
 2151               if (other.getClass() != getClass())
 2152                   return false;
 2153               Key k = (Key) other;
 2154               return k._path.equals(_path) && k._key.equals(_key);
 2155           }
 2156   
 2157           public String toString() {
 2158               return _path + "|" + _key;
 2159           }
 2160       }
 2161   
 2162       /**
 2163        * A {@link Result} implementation wrapped around this select.
 2164        */
 2165       public static class SelectResult
 2166           extends ResultSetResult
 2167           implements PathJoins {
 2168   
 2169           private SelectImpl _sel = null;
 2170   
 2171           // position in selected columns list where we expect the next load
 2172           private int _pos = 0;
 2173           private Stack _preJoins = null;
 2174   
 2175           /**
 2176            * Constructor.
 2177            */
 2178           public SelectResult(Connection conn, Statement stmnt, ResultSet rs,
 2179               DBDictionary dict) {
 2180               super(conn, stmnt, rs, dict);
 2181           }
 2182   
 2183           /**
 2184            * Select for this result.
 2185            */
 2186           public SelectImpl getSelect() {
 2187               return _sel;
 2188           }
 2189   
 2190           /**
 2191            * Select for this result.
 2192            */
 2193           public void setSelect(SelectImpl sel) {
 2194               _sel = sel;
 2195           }
 2196   
 2197           public Object getEager(FieldMapping key) {
 2198               // don't bother creating key if we know we don't have any
 2199               // eager results
 2200               if (_sel._eager == null || !_sel._eagerKeys.contains(key))
 2201                   return null;
 2202               Map map = SelectResult.this.getEagerMap(true);
 2203               if (map == null)
 2204                   return null;
 2205               return map.get(_sel.toEagerKey(key, getJoins(null)));
 2206           }
 2207   
 2208           public void putEager(FieldMapping key, Object res) {
 2209               Map map = SelectResult.this.getEagerMap(true);
 2210               if (map == null) {
 2211                   map = new HashMap();
 2212                   setEagerMap(map);
 2213               }
 2214               map.put(_sel.toEagerKey(key, getJoins(null)), res);
 2215           }
 2216   
 2217           public Object load(ClassMapping mapping, JDBCStore store,
 2218               JDBCFetchConfiguration fetch, Joins joins)
 2219               throws SQLException {
 2220               boolean hasJoins = joins != null
 2221                   && ((PathJoins) joins).path() != null;
 2222               if (hasJoins) {
 2223                   if (_preJoins == null)
 2224                       _preJoins = new Stack();
 2225                   _preJoins.push(joins);
 2226               }
 2227   
 2228               Object obj = super.load(mapping, store, fetch, joins);
 2229   
 2230               // reset
 2231               if (hasJoins)
 2232                   _preJoins.pop();
 2233               return obj;
 2234           }
 2235   
 2236           public Joins newJoins() {
 2237               PathJoins pre = getPreJoins();
 2238               if (pre == null || pre.path() == null)
 2239                   return this;
 2240   
 2241               PathJoinsImpl pj = new PathJoinsImpl();
 2242               pj.path = new StringBuffer(pre.path().toString());
 2243               return pj;
 2244           }
 2245   
 2246           protected boolean containsInternal(Object obj, Joins joins) {
 2247               // we key directly on objs and join-less cols, or on the alias
 2248               // for cols with joins
 2249               PathJoins pj = getJoins(joins);
 2250               if (pj != null && pj.path() != null)
 2251                   obj = getColumnAlias((Column) obj, pj);
 2252               return obj != null && _sel._selects.contains(obj);
 2253           }
 2254   
 2255           protected boolean containsAllInternal(Object[] objs, Joins joins)
 2256               throws SQLException {
 2257               PathJoins pj = getJoins(joins);
 2258               Object obj;
 2259               for (int i = 0; i < objs.length; i++) {
 2260                   if (pj != null && pj.path() != null)
 2261                       obj = getColumnAlias((Column) objs[i], pj);
 2262                   else
 2263                       obj = objs[i];
 2264                   if (obj == null || !_sel._selects.contains(obj))
 2265                       return false;
 2266               }
 2267               return true;
 2268           }
 2269   
 2270           public void pushBack()
 2271               throws SQLException {
 2272               _pos = 0;
 2273               super.pushBack();
 2274           }
 2275   
 2276           protected boolean absoluteInternal(int row)
 2277               throws SQLException {
 2278               _pos = 0;
 2279               return super.absoluteInternal(row);
 2280           }
 2281   
 2282           protected boolean nextInternal()
 2283               throws SQLException {
 2284               _pos = 0;
 2285               return super.nextInternal();
 2286           }
 2287   
 2288           protected int findObject(Object obj, Joins joins)
 2289               throws SQLException {
 2290               if (_pos == _sel._selects.size())
 2291                   _pos = 0;
 2292   
 2293               // we key directly on objs and join-less cols, or on the alias
 2294               // for cols with joins
 2295               PathJoins pj = getJoins(joins);
 2296               Boolean pk = null;
 2297               if (pj != null && pj.path() != null) {
 2298                   Column col = (Column) obj;
 2299                   pk = (col.isPrimaryKey()) ? Boolean.TRUE : Boolean.FALSE;
 2300                   obj = getColumnAlias(col, pj);
 2301                   if (obj == null)
 2302                       throw new SQLException(col.getTable() + ": "
 2303                           + pj.path() + " (" + _sel._aliases + ")");
 2304               }
 2305   
 2306               // we load in the same order we select, more or less...
 2307               if (_sel._selects.get(_pos).equals(obj))
 2308                   return ++_pos;
 2309   
 2310               // if we're looking for a primary key, try back a couple places,
 2311               // since pks might be selected in a slightly different order than
 2312               // they are loaded back; don't change the marker position
 2313               if (pk == null)
 2314                   pk = (obj instanceof Column && ((Column) obj).isPrimaryKey())
 2315                       ? Boolean.TRUE : Boolean.FALSE;
 2316               if (pk.booleanValue()) {
 2317                   for (int i = _pos - 1; i >= 0 && i >= _pos - 3; i--)
 2318                       if (_sel._selects.get(i).equals(obj))
 2319                           return i + 1;
 2320               }
 2321   
 2322               // search forward on the assumption that we might be skipping
 2323               // selects for sibling classes; advance the position if we find
 2324               // something forward
 2325               for (int i = _pos + 1; i < _sel._selects.size(); i++) {
 2326                   if (_sel._selects.get(i).equals(obj)) {
 2327                       _pos = i;
 2328                       return ++_pos;
 2329                   }
 2330               }
 2331   
 2332               // maybe the column was selected by 2 different mappings, so it's
 2333               // somewhere prior to the current position; in this case leave the
 2334               // position marker at its current place cause subsequent loads will
 2335               // still probably start from there
 2336               for (int i = 0; i < _pos; i++)
 2337                   if (_sel._selects.get(i).equals(obj))
 2338                       return i + 1;
 2339   
 2340               // somethings's wrong...
 2341               throw new SQLException(obj.toString());
 2342           }
 2343   
 2344           /**
 2345            * Return the joins to use to find column data.
 2346            */
 2347           private PathJoins getJoins(Joins joins) {
 2348               PathJoins pj = (PathJoins) joins;
 2349               if (pj != null && pj.path() != null)
 2350                   return pj;
 2351               return getPreJoins();
 2352           }
 2353   
 2354           /**
 2355            * Return the pre joins for the result, or null if none. Note that
 2356            * we have to take the Select's pre joins into account too, since
 2357            * batched selects can have additional pre joins on the stack even
 2358            * on execution.
 2359            */
 2360           private PathJoins getPreJoins() {
 2361               if (_preJoins != null && !_preJoins.isEmpty())
 2362                   return (PathJoins) _preJoins.peek();
 2363               if (_sel._preJoins != null && !_sel._preJoins.isEmpty())
 2364                   return (PathJoins) _sel._preJoins.peek();
 2365               return null;
 2366           }
 2367   
 2368           /**
 2369            * Return the alias used to key on the column data, considering the
 2370            * given joins.
 2371            */
 2372           private String getColumnAlias(Column col, PathJoins pj) {
 2373               String alias;
 2374               if (_sel._from != null) {
 2375                   alias = _sel.toAlias(_sel._from.getTableIndex
 2376                       (col.getTable(), pj, false));
 2377                   if (alias == null)
 2378                       return null;
 2379                   if (_sel._dict.requiresAliasForSubselect)
 2380                       return FROM_SELECT_ALIAS + "." + alias + "_" + col;
 2381                   return alias + "_" + col;
 2382               }
 2383               alias = _sel.toAlias(_sel.getTableIndex(col.getTable(), pj, false));
 2384               return (alias == null) ? null : alias + "." + col;
 2385           }
 2386   
 2387           ////////////////////////////
 2388           // PathJoins implementation
 2389           ////////////////////////////
 2390   
 2391           public boolean isOuter() {
 2392               return false;
 2393           }
 2394   
 2395           public PathJoins setOuter(boolean outer) {
 2396               return this;
 2397           }
 2398   
 2399           public boolean isDirty() {
 2400               return false;
 2401           }
 2402   
 2403           public StringBuffer path() {
 2404               return null;
 2405           }
 2406   
 2407           public JoinSet joins() {
 2408               return null;
 2409           }
 2410   
 2411           public int joinCount() {
 2412               return 0;
 2413           }
 2414   
 2415           public void nullJoins() {
 2416           }
 2417   
 2418           public boolean isEmpty() {
 2419               return true;
 2420           }
 2421   
 2422           public Joins crossJoin(Table localTable, Table foreignTable) {
 2423               return this;
 2424           }
 2425   
 2426           public Joins join(ForeignKey fk, boolean inverse, boolean toMany) {
 2427               return this;
 2428           }
 2429   
 2430           public Joins outerJoin(ForeignKey fk, boolean inverse, boolean toMany) {
 2431               return this;
 2432           }
 2433   
 2434           public Joins joinRelation(String name, ForeignKey fk, 
 2435               ClassMapping target, int subs, boolean inverse, boolean toMany) {
 2436               return new PathJoinsImpl().joinRelation(name, fk, target, subs, 
 2437                   inverse, toMany);
 2438           }
 2439   
 2440           public Joins outerJoinRelation(String name, ForeignKey fk,
 2441               ClassMapping target, int subs, boolean inverse, boolean toMany) {
 2442               return new PathJoinsImpl().outerJoinRelation(name, fk, target, subs,
 2443                   inverse, toMany);
 2444           }
 2445   
 2446           public Joins setVariable(String var) {
 2447               if (var == null)
 2448                   return this;
 2449               return new PathJoinsImpl().setVariable(var);
 2450           }
 2451   
 2452           public Joins setSubselect(String alias) {
 2453               if (alias == null)
 2454                   return this;
 2455               return new PathJoinsImpl().setSubselect(alias);
 2456           }
 2457       }
 2458   
 2459       /**
 2460        * Base joins implementation.
 2461        */
 2462       private static class PathJoinsImpl
 2463           implements PathJoins {
 2464   
 2465           protected StringBuffer path = null;
 2466           protected String var = null;
 2467   
 2468           public boolean isOuter() {
 2469               return false;
 2470           }
 2471   
 2472           public PathJoins setOuter(boolean outer) {
 2473               return this;
 2474           }
 2475   
 2476           public boolean isDirty() {
 2477               return var != null || path != null;
 2478           }
 2479   
 2480           public StringBuffer path() {
 2481               return path;
 2482           }
 2483   
 2484           public JoinSet joins() {
 2485               return null;
 2486           }
 2487   
 2488           public int joinCount() {
 2489               return 0;
 2490           }
 2491   
 2492           public void nullJoins() {
 2493           }
 2494   
 2495           public Joins setVariable(String var) {
 2496               this.var = var;
 2497               return this;
 2498           }
 2499   
 2500           public Joins setSubselect(String alias) {
 2501               if (!alias.endsWith(":"))
 2502                   alias += ':';
 2503               append(alias);
 2504               return this;
 2505           }
 2506   
 2507           public boolean isEmpty() {
 2508               return true;
 2509           }
 2510   
 2511           public Joins crossJoin(Table localTable, Table foreignTable) {
 2512               append(var);
 2513               var = null;
 2514               return this;
 2515           }
 2516   
 2517           public Joins join(ForeignKey fk, boolean inverse, boolean toMany) {
 2518               append(var);
 2519               var = null;
 2520               return this;
 2521           }
 2522   
 2523           public Joins outerJoin(ForeignKey fk, boolean inverse, boolean toMany) {
 2524               append(var);
 2525               var = null;
 2526               return this;
 2527           }
 2528   
 2529           public Joins joinRelation(String name, ForeignKey fk, 
 2530               ClassMapping target, int subs, boolean inverse, boolean toMany) {
 2531               append(name);
 2532               append(var);
 2533               var = null;
 2534               return this;
 2535           }
 2536   
 2537           public Joins outerJoinRelation(String name, ForeignKey fk,
 2538               ClassMapping target, int subs, boolean inverse, boolean toMany) {
 2539               append(name);
 2540               append(var);
 2541               var = null;
 2542               return this;
 2543           }
 2544   
 2545           protected void append(String str) {
 2546               if (str != null) {
 2547                   if (path == null)
 2548                       path = new StringBuffer(str);
 2549                   else
 2550                       path.append('.').append(str);
 2551               }
 2552           }
 2553   
 2554           public String toString() {
 2555               return "PathJoinsImpl<" + hashCode() + ">: "
 2556                   + String.valueOf(path);
 2557           }
 2558       }
 2559   
 2560       /**
 2561        * Joins implementation.
 2562        */
 2563       private static class SelectJoins
 2564           extends PathJoinsImpl 
 2565           implements Cloneable {
 2566   
 2567           private final SelectImpl _sel;
 2568           private JoinSet _joins = null;
 2569           private boolean _outer = false;
 2570           private int _count = 0;
 2571   
 2572           public SelectJoins(SelectImpl sel) {
 2573               _sel = sel;
 2574           }
 2575   
 2576           public boolean isOuter() {
 2577               return _outer;
 2578           }
 2579   
 2580           public PathJoins setOuter(boolean outer) {
 2581               _outer = outer;
 2582               return this;
 2583           }
 2584   
 2585           public boolean isDirty() {
 2586               return super.isDirty() || !isEmpty();
 2587           }
 2588   
 2589           public JoinSet joins() {
 2590               return _joins;
 2591           }
 2592   
 2593           public void setJoins(JoinSet joins) {
 2594               _joins = joins;
 2595               _outer = joins != null && joins.last() != null
 2596                   && joins.last().getType() == Join.TYPE_OUTER;
 2597           }
 2598   
 2599           public int joinCount() {
 2600               if (_joins == null)
 2601                   return _count;
 2602               return Math.max(_count, _joins.size());
 2603           }
 2604   
 2605           public void nullJoins() {
 2606               if (_joins != null)
 2607                   _count = Math.max(_count, _joins.size());
 2608               _joins = null;
 2609           }
 2610   
 2611           public boolean isEmpty() {
 2612               return _joins == null || _joins.isEmpty();
 2613           }
 2614   
 2615           public Joins crossJoin(Table localTable, Table foreignTable) {
 2616               // cross joins are for unbound variables; unfortunately we have
 2617               // to always go DISTINCT for unbound vars because there are certain
 2618               // cases that require it, and we can't differentiate them from the
 2619               // cases that don't
 2620               _sel._flags |= IMPLICIT_DISTINCT;
 2621   
 2622               if (_sel.getJoinSyntax() != JoinSyntaxes.SYNTAX_SQL92
 2623                   || _sel._from != null) {
 2624                   // don't make any joins, but update the path if a variable
 2625                   // has been set
 2626                   this.append(this.var);
 2627                   this.var = null;
 2628                   _outer = false;
 2629                   return this;
 2630               }
 2631   
 2632               // don't let the get alias methods see that a var has been set
 2633               // until we get past the local table
 2634               String var = this.var;
 2635               this.var = null;
 2636   
 2637               int alias1 = _sel.getTableIndex(localTable, this, true);
 2638               this.append(var);
 2639               int alias2 = _sel.getTableIndex(foreignTable, this, true);
 2640               Join j = new Join(localTable, alias1, foreignTable, alias2,
 2641                   null, false);
 2642               j.setType(Join.TYPE_CROSS);
 2643   
 2644               if (_joins == null)
 2645                   _joins = new JoinSet();
 2646               _joins.add(j);
 2647               _outer = false;
 2648               return this;
 2649           }
 2650   
 2651           public Joins join(ForeignKey fk, boolean inverse, boolean toMany) {
 2652               return join(null, fk, null, -1, inverse, toMany, false);
 2653           }
 2654   
 2655           public Joins outerJoin(ForeignKey fk, boolean inverse, boolean toMany) {
 2656               return join(null, fk, null, -1, inverse, toMany, true);
 2657           }
 2658   
 2659           public Joins joinRelation(String name, ForeignKey fk, 
 2660               ClassMapping target, int subs, boolean inverse, boolean toMany) {
 2661               return join(name, fk, target, subs, inverse, toMany, false);
 2662           }
 2663   
 2664           public Joins outerJoinRelation(String name, ForeignKey fk,
 2665               ClassMapping target, int subs, boolean inverse, boolean toMany) {
 2666               return join(name, fk, target, subs, inverse, toMany, true);
 2667           }
 2668   
 2669           private Joins join(String name, ForeignKey fk, ClassMapping target,
 2670               int subs, boolean inverse, boolean toMany, boolean outer) {
 2671               // don't let the get alias methods see that a var has been set
 2672               // until we get past the local table
 2673               String var = this.var;
 2674               this.var = null;
 2675   
 2676               // get first table alias before updating path; if there is a from
 2677               // select then we shouldn't actually create a join object, since
 2678               // the joins will all be done in the from select
 2679               boolean createJoin = _sel._from == null;
 2680               Table table1 = null;
 2681               int alias1 = -1;
 2682               if (createJoin) {
 2683                   table1 = (inverse) ? fk.getPrimaryKeyTable() : fk.getTable();
 2684                   alias1 = _sel.getTableIndex(table1, this, true);
 2685               }
 2686   
 2687               // update the path with the relation name before getting pk alias
 2688               this.append(name);
 2689               this.append(var);
 2690               if (toMany) {
 2691                   _sel._flags |= IMPLICIT_DISTINCT;
 2692                   _sel._flags |= TO_MANY;
 2693               }
 2694               _outer = outer;
 2695   
 2696               if (createJoin) {
 2697                   Table table2 = (inverse) ? fk.getTable() 
 2698                       : fk.getPrimaryKeyTable();
 2699                   int alias2 = _sel.getTableIndex(table2, this, true);
 2700                   Join j = new Join(table1, alias1, table2, alias2, fk, inverse);
 2701                   j.setType((outer) ? Join.TYPE_OUTER : Join.TYPE_INNER);
 2702   
 2703                   if (_joins == null)
 2704                       _joins = new JoinSet();
 2705                   if (_joins.add(j) && (subs == Select.SUBS_JOINABLE 
 2706                       || subs == Select.SUBS_NONE))
 2707                       j.setRelation(target, subs, clone(_sel));
 2708               }
 2709               return this;
 2710           }
 2711   
 2712           public SelectJoins clone(SelectImpl sel) {
 2713               SelectJoins sj = new SelectJoins(sel);
 2714               sj.var = var;
 2715               if (path != null)
 2716                   sj.path = new StringBuffer(path.toString());
 2717               if (_joins != null && !_joins.isEmpty())
 2718                   sj._joins = new JoinSet(_joins);
 2719               sj._outer = _outer;
 2720               return sj;
 2721           }
 2722   
 2723           public String toString() {
 2724               return super.toString() + " (" + _outer + "): " + _joins;
 2725           }
 2726       }
 2727       
 2728       protected Selects newSelects() {
 2729           return new Selects();
 2730       }
 2731   
 2732       /**
 2733        * Helper class to track selected columns, with fast contains method.
 2734        * Acts as a list of select ids, with additional methods to manipulate
 2735        * the alias of each selected id.
 2736        */
 2737       protected static class Selects
 2738           extends AbstractList {
 2739   
 2740           protected List _ids = null;
 2741           protected List _idents = null;
 2742           protected Map _aliases = null;
 2743           protected Map _selectAs = null;
 2744           protected DBDictionary _dict = null;
 2745   
 2746           /**
 2747            * Add all aliases from another instance.
 2748            */
 2749           public void addAll(Selects sels) {
 2750               if (_ids == null && sels._ids != null)
 2751                   _ids = new ArrayList(sels._ids);
 2752               else if (sels._ids != null)
 2753                   _ids.addAll(sels._ids);
 2754   
 2755               if (_idents == null && sels._idents != null)
 2756                   _idents = new ArrayList(sels._idents);
 2757               else if (sels._idents != null)
 2758                   _idents.addAll(sels._idents);
 2759   
 2760               if (_aliases == null && sels._aliases != null)
 2761                   _aliases = new HashMap(sels._aliases);
 2762               else if (sels._aliases != null)
 2763                   _aliases.putAll(sels._aliases);
 2764   
 2765               if (_selectAs == null && sels._selectAs != null)
 2766                   _selectAs = new HashMap(sels._selectAs);
 2767               else if (sels._selectAs != null)
 2768                   _selectAs.putAll(sels._selectAs);
 2769           }
 2770   
 2771           /**
 2772            * Returns the alias of a given id.
 2773            */
 2774           public Object getAlias(Object id) {
 2775               return (_aliases == null) ? null : _aliases.get(id);
 2776           }
 2777   
 2778           /**
 2779            * Set an alias for a given id.
 2780            */
 2781           public int setAlias(Object id, Object alias, boolean ident) {
 2782               if (_ids == null) {
 2783                   _ids = new ArrayList();
 2784                   _aliases = new HashMap();
 2785               }
 2786   
 2787               int idx;
 2788               if (_aliases.put(id, alias) != null)
 2789                   idx = _ids.indexOf(id);
 2790               else {
 2791                   _ids.add(id);
 2792                   idx = _ids.size() - 1;
 2793   
 2794                   if (ident) {
 2795                       if (_idents == null)
 2796                           _idents = new ArrayList(3);
 2797                       _idents.add(id);
 2798                   }
 2799               }
 2800               return idx;
 2801           }
 2802   
 2803           /**
 2804            * Set an alias for a given index.
 2805            */
 2806           public void setAlias(int idx, Object alias) {
 2807               Object id = _ids.get(idx);
 2808               _aliases.put(id, alias);
 2809           }
 2810   
 2811           /**
 2812            * Insert an alias before the given index, using negative indexes
 2813            * to count backwards.
 2814            */
 2815           public void insertAlias(int idx, Object id, Object alias) {
 2816               _aliases.put(id, alias);
 2817               if (idx >= 0)
 2818                   _ids.add(idx, id);
 2819               else
 2820                   _ids.add(_ids.size() + idx, id);
 2821           }
 2822   
 2823           /**
 2824            * Return the index of the given alias.
 2825            */
 2826           public int indexOfAlias(Object alias) {
 2827               if (_aliases == null)
 2828                   return -1;
 2829               for (int i = 0; i < _ids.size(); i++)
 2830                   if (alias.equals(_aliases.get(_ids.get(i))))
 2831                       return i;
 2832               return -1;
 2833           }
 2834   
 2835           /**
 2836            * A list representation of the aliases, in select order, with
 2837            * AS aliases present.
 2838            */
 2839           public List getAliases(final boolean ident, final boolean inner) {
 2840               if (_ids == null)
 2841                   return Collections.EMPTY_LIST;
 2842   
 2843               return new AbstractList() {
 2844                   public int size() {
 2845                       return (ident && _idents != null) ? _idents.size()
 2846                           : _ids.size();
 2847                   }
 2848   
 2849                   public Object get(int i) {
 2850                       Object id = (ident && _idents != null) ? _idents.get(i)
 2851                           : _ids.get(i);
 2852                       Object alias = _aliases.get(id);
 2853                       if (id instanceof Column && ((Column) id).isXML())
 2854                           alias = alias + _dict.getStringVal;
 2855                           
 2856                       String as = null;
 2857                       if (inner)
 2858                           as = ((String) alias).replace('.', '_');
 2859                       else if (_selectAs != null)
 2860                           as = (String) _selectAs.get(id);
 2861   
 2862                       if (as != null) {
 2863                           if (ident && _idents != null)
 2864                               return as;
 2865                           if (alias instanceof SQLBuffer)
 2866                               alias = new SQLBuffer((SQLBuffer) alias).
 2867                                   append(" AS ").append(as);
 2868                           else
 2869                               alias = alias + " AS " + as;
 2870                       }
 2871                       return alias;
 2872                   }
 2873               };
 2874           }
 2875   
 2876           /**
 2877            * Set that a given id's alias has an AS value.
 2878            */
 2879           public void setSelectAs(Object id, String as) {
 2880               if (_selectAs == null)
 2881                   _selectAs = new HashMap((int) (5 * 1.33 + 1));
 2882               _selectAs.put(id, as);
 2883           }
 2884   
 2885           /**
 2886            * Clear all placeholders and select AS clauses.
 2887            */
 2888           public void clearPlaceholders() {
 2889               if (_ids == null)
 2890                   return;
 2891   
 2892               Object id;
 2893               for (Iterator itr = _ids.iterator(); itr.hasNext();) {
 2894                   id = itr.next();
 2895                   if (id instanceof Placeholder) {
 2896                       itr.remove();
 2897                       _aliases.remove(id);
 2898                   }
 2899               }
 2900           }
 2901   
 2902           public boolean contains(Object id) {
 2903               return _aliases != null && _aliases.containsKey(id);
 2904           }
 2905   
 2906           public Object get(int i) {
 2907               if (_ids == null)
 2908                   throw new ArrayIndexOutOfBoundsException();
 2909               return _ids.get(i);
 2910           }
 2911   
 2912           public int size() {
 2913               return (_ids == null) ? 0 : _ids.size();
 2914           }
 2915   
 2916           public void clear() {
 2917               _ids = null;
 2918               _aliases = null;
 2919               _selectAs = null;
 2920               _idents = null;
 2921           }
 2922       }
 2923   }
 2924   
 2925   /**
 2926    * Common joins interface used internally. Cannot be made an inner class
 2927    * because the outer class (Select) has to implement it.
 2928    */
 2929   interface PathJoins
 2930       extends Joins {
 2931   
 2932       /**
 2933        * Mark this as an outer joins set.
 2934        */
 2935       public PathJoins setOuter(boolean outer);
 2936   
 2937       /**
 2938        * Return true if this instance has a path, any joins, or a variable.
 2939        */
 2940       public boolean isDirty();
 2941   
 2942       /**
 2943        * Return the relation path traversed by these joins, or null if none.
 2944        */
 2945       public StringBuffer path();
 2946   
 2947       /**
 2948        * Return the set of {@link Join} elements, or null if none.
 2949        */
 2950       public JoinSet joins();
 2951   
 2952       /**
 2953        * Return the maximum number of joins contained in this instance at any
 2954        * time.
 2955        */
 2956       public int joinCount();
 2957   
 2958       /**
 2959        * Null the set of {@link Join} elements.
 2960        */
 2961       public void nullJoins();
 2962   }
 2963   

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » sql » [javadoc | source]