You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/07/19 23:35:07 UTC

svn commit: r423615 [32/44] - in /incubator/openjpa/trunk: ./ openjpa-jdbc-5/ openjpa-jdbc-5/src/ openjpa-jdbc-5/src/main/ openjpa-jdbc-5/src/main/java/ openjpa-jdbc-5/src/main/java/org/ openjpa-jdbc-5/src/main/java/org/apache/ openjpa-jdbc-5/src/main/...

Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSet.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSet.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSet.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSet.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.sql;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.lang.ObjectUtils;
+
+/**
+ * Set type that recognizes that inner joins should take precedence
+ * over otherwise equal outer joins.
+ *
+ * @author Abe White
+ */
+class JoinSet {
+
+    // the joins are stored as an adjacency list graph.  it's a reasonably
+    // efficient representation with O(1) lookup, add, remove operations for
+    // typical sets of joins, and it means we'd have to create a graph anyway
+    // when joinIterator() is called
+    private final List _graph = new ArrayList();
+    private int _size = 0;
+    private List _sorted = null;
+
+    public JoinSet() {
+    }
+
+    public JoinSet(JoinSet copy) {
+        for (int i = 0; i < copy._graph.size(); i++) {
+            if (copy._graph.get(i) == null)
+                _graph.add(null);
+            else
+                _graph.add(((Node) copy._graph.get(i)).clone());
+        }
+        _size = copy._size;
+        _sorted = copy._sorted;
+    }
+
+    /**
+     * Return the recorded join matching the given join's aliases.
+     */
+    public Join getRecordedJoin(Join join) {
+        if (join == null)
+            return null;
+        Node node = getNode(join, join.getIndex1());
+        return (node == null) ? null : node.join;
+    }
+
+    /**
+     * Return the node for the specified join and index.
+     */
+    private Node getNode(Join join, int idx) {
+        if (_graph.size() <= idx)
+            return null;
+        Node node = (Node) _graph.get(idx);
+        for (; node != null; node = node.next)
+            if (node.join.equals(join))
+                return node;
+        return null;
+    }
+
+    /**
+     * Return the logical last join.
+     */
+    public Join last() {
+        if (_size == 0)
+            return null;
+        Node node = (Node) _graph.get(_graph.size() - 1);
+        while (node.next != null)
+            node = node.next;
+        return node.join;
+    }
+
+    /**
+     * Iterator over joins that prepares them for SQL translation.
+     */
+    public Iterator joinIterator() {
+        if (_size < 2)
+            return iterator();
+        if (_sorted != null)
+            return _sorted.iterator();
+
+        List sorted = new ArrayList(_size);
+        LinkedList queue = new LinkedList();
+        BitSet seen = new BitSet(_graph.size() * _graph.size()
+            + _graph.size());
+
+        // traverse graph
+        Node n;
+        int idx, sidx;
+        for (int i = 0; i < _graph.size(); i++) {
+            // seed queue with next set of disconnected joins
+            for (n = (Node) _graph.get(i); n != null; n = n.next) {
+                sidx = getSeenIndex(n.join);
+                if (!seen.get(sidx)) {
+                    seen.set(sidx);
+                    queue.add(n);
+                }
+            }
+            if (queue.isEmpty())
+                continue;
+
+            // traverse from those joins to reachables
+            while (!queue.isEmpty()) {
+                n = (Node) queue.removeFirst();
+
+                // don't repeat a join to a table we've already joined, but
+                // do traverse through it in the graph (the first indexes of
+                // the seeen bitset are reserved for joined-to tables)
+                idx = (n.forward) ? n.join.getIndex2() : n.join.getIndex1();
+                if (!seen.get(idx)) {
+                    sorted.add((n.forward) ? n.join : n.join.reverse());
+                    seen.set(idx);
+                }
+
+                for (n = (Node) _graph.get(idx); n != null; n = n.next) {
+                    sidx = getSeenIndex(n.join);
+                    if (!seen.get(sidx)) {
+                        seen.set(sidx);
+                        queue.add(n);
+                    }
+                }
+            }
+        }
+        _sorted = sorted;
+        return _sorted.iterator();
+    }
+
+    /**
+     * We create a unique index for recording seen nodes by
+     * treating the joined indexes as a base (max-index) number.
+     */
+    private int getSeenIndex(Join join) {
+        // we reserve indexes 0..._graph.size() for joined tables
+        return join.getIndex1() * _graph.size() + join.getIndex2()
+            + _graph.size();
+    }
+
+    public boolean add(Join join) {
+        if (join.getType() == Join.TYPE_OUTER) {
+            // outer shouldn't override any other join type
+            if (!contains(join)) {
+                addNode(join);
+                return true;
+            }
+            return false;
+        }
+
+        // replace any existing join with this one
+        Node node = getNode(join, join.getIndex1());
+        if (node != null) {
+            node.join = join;
+            getNode(join, join.getIndex2()).join = join;
+            _sorted = null;
+        } else
+            addNode(join);
+        return true;
+    }
+
+    public boolean addAll(JoinSet js) {
+        if (js.isEmpty())
+            return false;
+
+        boolean added = false;
+        for (Iterator itr = js.iterator(); itr.hasNext();)
+            added = add((Join) itr.next()) || added;
+        return added;
+    }
+
+    /**
+     * Add the give join to our graph.
+     */
+    private void addNode(Join join) {
+        _sorted = null;
+
+        int size = Math.max(join.getIndex1(), join.getIndex2()) + 1;
+        while (_graph.size() < size)
+            _graph.add(null);
+
+        Node node = (Node) _graph.get(join.getIndex1());
+        if (node == null)
+            _graph.set(join.getIndex1(), new Node(join, true));
+        else {
+            while (node.next != null)
+                node = node.next;
+            node.next = new Node(join, true);
+        }
+
+        node = (Node) _graph.get(join.getIndex2());
+        if (node == null)
+            _graph.set(join.getIndex2(), new Node(join, false));
+        else {
+            while (node.next != null)
+                node = node.next;
+            node.next = new Node(join, false);
+        }
+        _size++;
+    }
+
+    public Iterator iterator() {
+        return new Iterator() {
+            private Node _next = null;
+            private int _idx = -1;
+
+            public boolean hasNext() {
+                if (_next != null)
+                    return true;
+
+                while (++_idx < _graph.size()) {
+                    _next = (Node) _graph.get(_idx);
+                    while (_next != null && !_next.forward)
+                        _next = _next.next;
+                    if (_next != null)
+                        return true;
+                }
+                return false;
+            }
+
+            public Object next() {
+                if (!hasNext())
+                    throw new NoSuchElementException();
+                Join j = _next.join;
+                do {
+                    _next = _next.next;
+                } while (_next != null && !_next.forward);
+                return j;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public boolean remove(Join join) {
+        if (join == null || _graph.size() <= join.getIndex1())
+            return false;
+        if (remove(join, join.getIndex1())) {
+            _size--;
+            return remove(join, join.getIndex2());
+        }
+        return false;
+    }
+
+    /**
+     * Each join is recorded one at its first index and once at its second;
+     * remove the join at one of its indexes.
+     */
+    private boolean remove(Join join, int idx) {
+        Node node = (Node) _graph.get(idx);
+        for (Node prev = null; node != null; prev = node, node = node.next) {
+            if (!node.join.equals(join))
+                continue;
+
+            if (prev != null)
+                prev.next = node.next;
+            else {
+                _graph.set(idx, node.next);
+                // trim to size
+                while (!_graph.isEmpty() && _graph.get(idx) == null
+                    && idx == _graph.size() - 1)
+                    _graph.remove(idx--);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public boolean removeAll(JoinSet js) {
+        boolean remd = false;
+        for (Iterator itr = js.iterator(); itr.hasNext();)
+            remd = remove((Join) itr.next()) || remd;
+        return remd;
+    }
+
+    public boolean retainAll(JoinSet js) {
+        boolean remd = false;
+        Join join;
+        for (Iterator itr = iterator(); itr.hasNext();) {
+            join = (Join) itr.next();
+            if (!js.contains(join))
+                remd = remove(join);
+        }
+        return remd;
+    }
+
+    public void clear() {
+        _graph.clear();
+        _sorted = null;
+        _size = 0;
+    }
+
+    public boolean contains(Join join) {
+        return getRecordedJoin(join) != null;
+    }
+
+    public boolean containsAll(JoinSet js) {
+        if (js._size > _size || js._graph.size() > _graph.size())
+            return false;
+        for (Iterator itr = js.iterator(); itr.hasNext();)
+            if (!contains((Join) itr.next()))
+                return false;
+        return true;
+    }
+
+    public boolean isEmpty() {
+        return _size == 0;
+    }
+
+    public int size() {
+        return _size;
+    }
+
+    public boolean equals(Object other) {
+        if (other == this)
+            return true;
+        if (!(other instanceof JoinSet))
+            return false;
+        return _graph.equals(((JoinSet) other)._graph);
+    }
+
+    public int hashCode() {
+        return _graph.hashCode();
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("[");
+        for (Iterator itr = iterator(); itr.hasNext();) {
+            buf.append("<").append(itr.next()).append(">");
+            if (itr.hasNext())
+                buf.append(", ");
+        }
+        return buf.append("]").toString();
+    }
+
+    /**
+     * A graph node.
+     */
+    private static class Node
+        implements Cloneable {
+
+        public Join join;
+        public Node next;
+        public boolean forward;
+
+        public Node(Join join, boolean forward) {
+            this.join = join;
+            this.forward = forward;
+        }
+
+        public int hashCode() {
+            int rs = 17;
+            rs = 37 * rs + join.hashCode();
+            if (next != null)
+                rs = 37 * rs + next.hashCode();
+            return rs;
+        }
+
+        public boolean equals(Object other) {
+            if (!(other instanceof Node))
+                return false;
+            Node node = (Node) other;
+            return ObjectUtils.equals(join, node.join)
+                && ObjectUtils.equals(next, node.next);
+        }
+
+        public Object clone() {
+            try {
+                Node node = (Node) super.clone();
+                if (node.next != null)
+                    node.next = (Node) node.next.clone();
+                return node;
+            } catch (CloneNotSupportedException cnse) {
+                // can't happen
+                return null;
+            }
+        }
+
+        public String toString() {
+            return join + "(" + ((forward) ? "forward" : "backward") + ")"
+                + "; next: " + next;
+        }
+    }
+}	

Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSet.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSyntaxes.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSyntaxes.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSyntaxes.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSyntaxes.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.sql;
+
+/**
+ * Constants for ways of describing joins in SQL.
+ *
+ * @author Abe White
+ */
+public interface JoinSyntaxes {
+
+    /**
+     * ANSI SQL 92 join syntax; outer joins are supported.
+     */
+    public static final int SYNTAX_SQL92 = 0;
+
+    /**
+     * Traditional join syntax; outer joins are not supported.
+     */
+    public static final int SYNTAX_TRADITIONAL = 1;
+
+    /**
+     * Native database join syntax; outer joins are supported.
+     */
+    public static final int SYNTAX_DATABASE = 2;
+}

Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/JoinSyntaxes.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Joins.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Joins.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Joins.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Joins.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.sql;
+
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Table;
+
+/**
+ * Tracks joins made when traversing relations in a select.
+ *
+ * @author Abe White
+ */
+public interface Joins {
+
+    /**
+     * Whether we have any joins.
+     */
+    public boolean isEmpty();
+
+    /**
+     * Perform a cross join on the given tables.
+     */
+    public Joins crossJoin(Table localTable, Table foreignTable);
+
+    /**
+     * Join the columns of the given foreign key.
+     */
+    public Joins join(ForeignKey fk, boolean inverse, boolean toMany);
+
+    /**
+     * Join the columns of the given foreign key.
+     */
+    public Joins outerJoin(ForeignKey fk, boolean inverse, boolean toMany);
+
+    /**
+     * Join the columns of the given foreign key, which represents a relation
+     * via the given field name.
+     */
+    public Joins joinRelation(String name, ForeignKey fk, boolean inverse,
+        boolean toMany);
+
+    /**
+     * Join the columns of the given foreign key, which represents a relation
+     * via the given field name.
+     */
+    public Joins outerJoinRelation(String name, ForeignKey fk, boolean inverse,
+        boolean toMany);
+
+    /**
+     * Set the variable name being traversed into with the next join.
+     */
+    public Joins setVariable(String var);
+
+    /**
+     * Set the subquery alias.
+     */
+    public Joins setSubselect(String alias);
+}

Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Joins.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,900 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.sql;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchState;
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.UnsupportedException;
+import org.apache.openjpa.util.UserException;
+
+/**
+ * A logical union made up of multiple distinct selects whose results are
+ * combined in memory.
+ *
+ * @author Abe White
+ */
+public class LogicalUnion
+    implements Union {
+
+    private static final Localizer _loc = Localizer.forPackage
+        (LogicalUnion.class);
+
+    protected final UnionSelect[] sels;
+    protected final DBDictionary dict;
+    protected final ClassMapping[] mappings;
+    protected final BitSet desc = new BitSet();
+    private boolean _distinct = true;
+    private boolean _single = false;
+
+    /**
+     * Constructor.
+     *
+     * @param conf system configuration
+     * @param sels the number of SQL selects to union together
+     */
+    public LogicalUnion(JDBCConfiguration conf, int sels) {
+        this(conf, sels, null);
+    }
+
+    /**
+     * Constructor used to seed the internal selects.
+     */
+    public LogicalUnion(JDBCConfiguration conf, Select[] seeds) {
+        this(conf, seeds.length, seeds);
+    }
+
+    /**
+     * Delegate constructor.
+     */
+    protected LogicalUnion(JDBCConfiguration conf, int sels, Select[] seeds) {
+        if (sels == 0)
+            throw new InternalException("sels == 0");
+
+        dict = conf.getDBDictionaryInstance();
+        mappings = new ClassMapping[sels];
+        this.sels = new UnionSelect[sels];
+
+        SelectImpl seed;
+        for (int i = 0; i < sels; i++) {
+            seed = (seeds == null) ? new SelectImpl(conf)
+                : (SelectImpl) seeds[i];
+            this.sels[i] = newUnionSelect(seed, i);
+        }
+    }
+
+    /**
+     * Create a new union select with the given delegate and union position.
+     */
+    protected UnionSelect newUnionSelect(SelectImpl seed, int pos) {
+        return new UnionSelect(seed, pos);
+    }
+
+    public Select[] getSelects() {
+        return sels;
+    }
+
+    public boolean isSingleResult() {
+        return _single;
+    }
+
+    public void setSingleResult(boolean single) {
+        _single = single;
+    }
+
+    public boolean isUnion() {
+        return false;
+    }
+
+    public void abortUnion() {
+    }
+
+    public String getOrdering() {
+        return null;
+    }
+
+    public JDBCConfiguration getConfiguration() {
+        return sels[0].getConfiguration();
+    }
+
+    public SQLBuffer toSelect(boolean forUpdate, JDBCFetchConfiguration fetch) {
+        return dict.toSelect(sels[0], forUpdate, fetch);
+    }
+
+    public SQLBuffer toSelectCount() {
+        return dict.toSelectCount(sels[0]);
+    }
+
+    public boolean getAutoDistinct() {
+        return sels[0].getAutoDistinct();
+    }
+
+    public void setAutoDistinct(boolean distinct) {
+        for (int i = 0; i < sels.length; i++)
+            sels[i].setAutoDistinct(distinct);
+    }
+
+    public boolean isDistinct() {
+        return _distinct;
+    }
+
+    public void setDistinct(boolean distinct) {
+        _distinct = distinct;
+    }
+
+    public boolean isLRS() {
+        return sels[0].isLRS();
+    }
+
+    public void setLRS(boolean lrs) {
+        for (int i = 0; i < sels.length; i++)
+            sels[i].setLRS(lrs);
+    }
+
+    public int getJoinSyntax() {
+        return sels[0].getJoinSyntax();
+    }
+
+    public void setJoinSyntax(int syntax) {
+        for (int i = 0; i < sels.length; i++)
+            sels[i].setJoinSyntax(syntax);
+    }
+
+    public boolean supportsRandomAccess(boolean forUpdate) {
+        if (sels.length == 1)
+            return sels[0].supportsRandomAccess(forUpdate);
+        return false;
+    }
+
+    public boolean supportsLocking() {
+        if (sels.length == 1)
+            return sels[0].supportsLocking();
+        for (int i = 0; i < sels.length; i++)
+            if (!sels[i].supportsLocking())
+                return false;
+        return true;
+    }
+
+    public int getCount(JDBCStore store)
+        throws SQLException {
+        int count = 0;
+        for (int i = 0; i < sels.length; i++)
+            count += sels[i].getCount(store);
+        return count;
+    }
+
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
+        throws SQLException {
+        if (fetch == null)
+            fetch = store.getFetchConfiguration();
+        return execute(store, fetch, fetch.getReadLockLevel());
+    }
+
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
+        int lockLevel)
+        throws SQLException {
+        if (fetch == null)
+            fetch = store.getFetchConfiguration();
+
+        if (sels.length == 1) {
+            Result res = sels[0].execute(store, fetch, lockLevel);
+            ((AbstractResult) res).setBaseMapping(mappings[0]);
+            return res;
+        }
+
+        if (_single) {
+            AbstractResult res;
+            for (int i = 0; i < sels.length; i++) {
+                res = (AbstractResult) sels[i].execute(store, fetch,
+                    lockLevel);
+                res.setBaseMapping(mappings[i]);
+                res.setIndexOf(i);
+
+                // if we get to the last select, just return its result
+                if (i == sels.length - 1)
+                    return res;
+
+                // return the first result that has a row
+                try {
+                    if (!res.next())
+                        res.close();
+                    else {
+                        res.pushBack();
+                        return res;
+                    }
+                }
+                catch (SQLException se) {
+                    res.close();
+                    throw se;
+                }
+            }
+        }
+
+        // create a single result from each select in our fake union, merging
+        // them as needed
+        AbstractResult[] res = new AbstractResult[sels.length];
+        List[] orderIdxs = null;
+        try {
+            List l;
+            for (int i = 0; i < res.length; i++) {
+                res[i] = (AbstractResult) sels[i].execute(store, fetch,
+                    lockLevel);
+                res[i].setBaseMapping(mappings[i]);
+                res[i].setIndexOf(i);
+
+                l = sels[i].getSelectedOrderIndexes();
+                if (l != null) {
+                    if (orderIdxs == null)
+                        orderIdxs = new List[sels.length];
+                    orderIdxs[i] = l;
+                }
+            }
+        } catch (SQLException se) {
+            for (int i = 0; res[i] != null; i++)
+                res[i].close();
+            throw se;
+        }
+
+        // if multiple selects have ordering, use a comparator to collate
+        ResultComparator comp = null;
+        if (orderIdxs != null)
+            comp = new ResultComparator(orderIdxs, desc, dict);
+        return new MergedResult(res, comp);
+    }
+
+    public void select(Union.Selector selector) {
+        for (int i = 0; i < sels.length; i++)
+            selector.select(sels[i], i);
+    }
+
+    public String toString() {
+        return toSelect(false, null).getSQL();
+    }
+
+    /**
+     * A callback used to create the selects in a SQL union.
+     */
+    public static interface Selector {
+
+        /**
+         * Populate the <code>i</code>th select in the union.
+         */
+        public void select(Select sel, int i);
+    }
+
+    /**
+     * A select that is part of a logical union.
+     */
+    protected class UnionSelect
+        implements Select {
+
+        protected final SelectImpl sel;
+        protected final int pos;
+        protected int orders = 0;
+        protected List orderIdxs = null;
+
+        public UnionSelect(SelectImpl sel, int pos) {
+            this.sel = sel;
+            this.pos = pos;
+            sel.setRecordOrderedIndexes(true);
+        }
+
+        /**
+         * Delegate select.
+         */
+        public SelectImpl getDelegate() {
+            return sel;
+        }
+
+        /**
+         * Return the indexes of the data in the select clause this query is
+         * ordered by.
+         */
+        public List getSelectedOrderIndexes() {
+            if (orderIdxs == null)
+                orderIdxs = sel.getOrderedIndexes();
+            return orderIdxs;
+        }
+
+        public JDBCConfiguration getConfiguration() {
+            return sel.getConfiguration();
+        }
+
+        public int indexOf() {
+            return pos;
+        }
+
+        public SQLBuffer toSelect(boolean forUpdate,
+            JDBCFetchConfiguration fetch) {
+            return sel.toSelect(forUpdate, fetch);
+        }
+
+        public SQLBuffer toSelectCount() {
+            return sel.toSelectCount();
+        }
+
+        public boolean getAutoDistinct() {
+            return sel.getAutoDistinct();
+        }
+
+        public void setAutoDistinct(boolean distinct) {
+            sel.setAutoDistinct(distinct);
+        }
+
+        public boolean isDistinct() {
+            return sel.isDistinct();
+        }
+
+        public void setDistinct(boolean distinct) {
+            sel.setDistinct(distinct);
+        }
+
+        public boolean isLRS() {
+            return sel.isLRS();
+        }
+
+        public void setLRS(boolean lrs) {
+            sel.setLRS(lrs);
+        }
+
+        public int getJoinSyntax() {
+            return sel.getJoinSyntax();
+        }
+
+        public void setJoinSyntax(int joinSyntax) {
+            sel.setJoinSyntax(joinSyntax);
+        }
+
+        public boolean supportsRandomAccess(boolean forUpdate) {
+            return sel.supportsRandomAccess(forUpdate);
+        }
+
+        public boolean supportsLocking() {
+            return sel.supportsLocking();
+        }
+
+        public int getCount(JDBCStore store)
+            throws SQLException {
+            return sel.getCount(store);
+        }
+
+        public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
+            throws SQLException {
+            return sel.execute(store, fetch);
+        }
+
+        public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
+            int lockLevel)
+            throws SQLException {
+            return sel.execute(store, fetch, lockLevel);
+        }
+
+        public List getSubselects() {
+            return Collections.EMPTY_LIST;
+        }
+
+        public Select getParent() {
+            return null;
+        }
+
+        public String getSubselectPath() {
+            return null;
+        }
+
+        public void setParent(Select parent, String path) {
+            throw new UnsupportedException(_loc.get("union-element"));
+        }
+
+        public Select getFromSelect() {
+            return null;
+        }
+
+        public void setFromSelect(Select sel) {
+            throw new UnsupportedException(_loc.get("union-element"));
+        }
+
+        public boolean hasEagerJoin(boolean toMany) {
+            return sel.hasEagerJoin(toMany);
+        }
+
+        public boolean hasJoin(boolean toMany) {
+            return sel.hasJoin(toMany);
+        }
+
+        public boolean isSelected(Table table) {
+            return sel.isSelected(table);
+        }
+
+        public Collection getTableAliases() {
+            return sel.getTableAliases();
+        }
+
+        public List getSelectAliases() {
+            return sel.getSelectAliases();
+        }
+
+        public List getIdentifierAliases() {
+            return sel.getIdentifierAliases();
+        }
+
+        public SQLBuffer getOrdering() {
+            return sel.getOrdering();
+        }
+
+        public SQLBuffer getGrouping() {
+            return sel.getGrouping();
+        }
+
+        public SQLBuffer getWhere() {
+            return sel.getWhere();
+        }
+
+        public SQLBuffer getHaving() {
+            return sel.getHaving();
+        }
+
+        public Joins getJoins() {
+            return sel.getJoins();
+        }
+
+        public Iterator getJoinIterator() {
+            return sel.getJoinIterator();
+        }
+
+        public long getStartIndex() {
+            return sel.getStartIndex();
+        }
+
+        public long getEndIndex() {
+            return sel.getEndIndex();
+        }
+
+        public void setRange(long start, long end) {
+            sel.setRange(start, end);
+        }
+
+        public String getColumnAlias(Column col) {
+            return sel.getColumnAlias(col);
+        }
+
+        public String getColumnAlias(Column col, Joins joins) {
+            return sel.getColumnAlias(col, joins);
+        }
+
+        public String getColumnAlias(String col, Table table) {
+            return sel.getColumnAlias(col, table);
+        }
+
+        public String getColumnAlias(String col, Table table, Joins joins) {
+            return sel.getColumnAlias(col, table, joins);
+        }
+
+        public boolean isAggregate() {
+            return sel.isAggregate();
+        }
+
+        public void setAggregate(boolean agg) {
+            sel.setAggregate(agg);
+        }
+
+        public boolean isLob() {
+            return sel.isLob();
+        }
+
+        public void setLob(boolean lob) {
+            sel.setLob(lob);
+        }
+
+        public void selectPlaceholder(String sql) {
+            sel.selectPlaceholder(sql);
+        }
+
+        public void clearSelects() {
+            sel.clearSelects();
+        }
+
+        public boolean select(SQLBuffer sql, Object id) {
+            return sel.select(sql, id);
+        }
+
+        public boolean select(SQLBuffer sql, Object id, Joins joins) {
+            return sel.select(sql, id, joins);
+        }
+
+        public boolean select(String sql, Object id) {
+            return sel.select(sql, id);
+        }
+
+        public boolean select(String sql, Object id, Joins joins) {
+            return sel.select(sql, id, joins);
+        }
+
+        public boolean select(Column col) {
+            return sel.select(col);
+        }
+
+        public boolean select(Column col, Joins joins) {
+            return sel.select(col, joins);
+        }
+
+        public int select(Column[] cols) {
+            return sel.select(cols);
+        }
+
+        public int select(Column[] cols, Joins joins) {
+            return sel.select(cols, joins);
+        }
+
+        public void select(ClassMapping mapping, int subclasses,
+            JDBCStore store, JDBCFetchState fetchState, int eager) {
+            select(mapping, subclasses, store, fetchState, eager, null, false);
+        }
+
+        public void select(ClassMapping mapping, int subclasses,
+            JDBCStore store, JDBCFetchState fetchState, int eager,
+            Joins joins) {
+            select(mapping, subclasses, store, fetchState, eager, joins,
+                false);
+        }
+
+        private void select(ClassMapping mapping, int subclasses,
+            JDBCStore store, JDBCFetchState fetchState, int eager,
+            Joins joins, boolean identifier) {
+            // if this is the first (primary) mapping selected for this
+            // SELECT, record it so we can figure out what the result type is
+            // since the discriminator might not be selected
+            if (mappings[pos] == null)
+                mappings[pos] = mapping;
+
+            sel.select(this, mapping, subclasses, store, fetchState, eager,
+                joins, identifier);
+        }
+
+        public boolean selectIdentifier(Column col) {
+            return sel.selectIdentifier(col);
+        }
+
+        public boolean selectIdentifier(Column col, Joins joins) {
+            return sel.selectIdentifier(col, joins);
+        }
+
+        public int selectIdentifier(Column[] cols) {
+            return sel.selectIdentifier(cols);
+        }
+
+        public int selectIdentifier(Column[] cols, Joins joins) {
+            return sel.selectIdentifier(cols, joins);
+        }
+
+        public void selectIdentifier(ClassMapping mapping, int subclasses,
+            JDBCStore store, JDBCFetchState fetchState, int eager) {
+            select(mapping, subclasses, store, fetchState, eager, null, true);
+        }
+
+        public void selectIdentifier(ClassMapping mapping, int subclasses,
+            JDBCStore store, JDBCFetchState fetchState, int eager,
+            Joins joins) {
+            select(mapping, subclasses, store, fetchState, eager, joins, true);
+        }
+
+        public int selectPrimaryKey(ClassMapping mapping) {
+            return sel.selectPrimaryKey(mapping);
+        }
+
+        public int selectPrimaryKey(ClassMapping mapping, Joins joins) {
+            return sel.selectPrimaryKey(mapping, joins);
+        }
+
+        public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
+            boolean select) {
+            return orderByPrimaryKey(mapping, asc, null, select);
+        }
+
+        public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
+            Joins joins, boolean select) {
+            ClassMapping pks = mapping;
+            while (!pks.isPrimaryKeyObjectId(true))
+                pks = pks.getJoinablePCSuperclassMapping();
+            Column[] cols = pks.getPrimaryKeyColumns();
+            recordOrderColumns(cols, asc);
+            return sel.orderByPrimaryKey(mapping, asc, joins, select,
+                isUnion());
+        }
+
+        /**
+         * Record that we're ordering by a SQL expression.
+         */
+        protected void recordOrder(Object ord, boolean asc) {
+            if (ord == null)
+                return;
+            orderIdxs = null;
+
+            int idx = orders++;
+            if (desc.get(idx) && asc)
+                throw new UserException(_loc.get("incompat-ordering"));
+            if (!asc)
+                desc.set(idx);
+        }
+
+        /**
+         * Record that we're ordering by the given columns.
+         */
+        protected void recordOrderColumns(Column[] cols, boolean asc) {
+            for (int i = 0; i < cols.length; i++)
+                recordOrder(cols[i], asc);
+        }
+
+        public boolean orderBy(Column col, boolean asc, boolean select) {
+            return orderBy(col, asc, null, select);
+        }
+
+        public boolean orderBy(Column col, boolean asc, Joins joins,
+            boolean select) {
+            recordOrder(col, asc);
+            return sel.orderBy(col, asc, joins, select, isUnion());
+        }
+
+        public int orderBy(Column[] cols, boolean asc, boolean select) {
+            return orderBy(cols, asc, null, select);
+        }
+
+        public int orderBy(Column[] cols, boolean asc, Joins joins,
+            boolean select) {
+            recordOrderColumns(cols, asc);
+            return sel.orderBy(cols, asc, joins, select, isUnion());
+        }
+
+        public boolean orderBy(SQLBuffer sql, boolean asc, boolean select) {
+            return orderBy(sql, asc, null, select);
+        }
+
+        public boolean orderBy(SQLBuffer sql, boolean asc, Joins joins,
+            boolean select) {
+            recordOrder(sql.getSQL(false), asc);
+            return sel.orderBy(sql, asc, joins, select, isUnion());
+        }
+
+        public boolean orderBy(String sql, boolean asc, boolean select) {
+            return orderBy(sql, asc, null, select);
+        }
+
+        public boolean orderBy(String sql, boolean asc, Joins joins,
+            boolean select) {
+            recordOrder(sql, asc);
+            return sel.orderBy(sql, asc, joins, select, isUnion());
+        }
+
+        public void clearOrdering() {
+            sel.clearOrdering();
+        }
+
+        public void wherePrimaryKey(Object oid, ClassMapping mapping,
+            JDBCStore store) {
+            sel.wherePrimaryKey(oid, mapping, store);
+        }
+
+        public void whereForeignKey(ForeignKey fk, Object oid,
+            ClassMapping mapping, JDBCStore store) {
+            sel.whereForeignKey(fk, oid, mapping, store);
+        }
+
+        public void where(Joins joins) {
+            sel.where(joins);
+        }
+
+        public void where(SQLBuffer sql) {
+            sel.where(sql);
+        }
+
+        public void where(SQLBuffer sql, Joins joins) {
+            sel.where(sql, joins);
+        }
+
+        public void where(String sql) {
+            sel.where(sql);
+        }
+
+        public void where(String sql, Joins joins) {
+            sel.where(sql, joins);
+        }
+
+        public void having(SQLBuffer sql) {
+            sel.having(sql);
+        }
+
+        public void having(SQLBuffer sql, Joins joins) {
+            sel.having(sql, joins);
+        }
+
+        public void having(String sql) {
+            sel.having(sql);
+        }
+
+        public void having(String sql, Joins joins) {
+            sel.having(sql, joins);
+        }
+
+        public boolean groupBy(SQLBuffer sql, boolean select) {
+            return sel.groupBy(sql, null, select);
+        }
+
+        public boolean groupBy(SQLBuffer sql, Joins joins, boolean select) {
+            return sel.groupBy(sql, joins, select);
+        }
+
+        public boolean groupBy(String sql, boolean select) {
+            return sel.groupBy(sql, null, select);
+        }
+
+        public boolean groupBy(String sql, Joins joins, boolean select) {
+            return sel.groupBy(sql, joins, select);
+        }
+
+        public boolean groupBy(Column col, boolean select) {
+            return sel.groupBy(col, null, select);
+        }
+
+        public boolean groupBy(Column col, Joins joins, boolean select) {
+            return sel.groupBy(col, joins, select);
+        }
+
+        public int groupBy(Column[] cols, boolean select) {
+            return sel.groupBy(cols, null, select);
+        }
+
+        public int groupBy(Column[] cols, Joins joins, boolean select) {
+            return sel.groupBy(cols, joins, select);
+        }
+
+        public SelectExecutor whereClone(int sels) {
+            return sel.whereClone(sels);
+        }
+
+        public SelectExecutor fullClone(int sels) {
+            return sel.fullClone(sels);
+        }
+
+        public SelectExecutor eagerClone(FieldMapping key, int eagerType,
+            boolean toMany, int sels) {
+            SelectExecutor ex = sel.eagerClone(key, eagerType, toMany, sels);
+            return (ex == sel) ? this : ex;
+        }
+
+        public SelectExecutor getEager(FieldMapping key) {
+            SelectExecutor ex = sel.getEager(key);
+            return (ex == sel) ? this : ex;
+        }
+
+        public Joins newJoins() {
+            return sel.newJoins();
+        }
+
+        public void append(SQLBuffer buf, Joins joins) {
+            sel.append(buf, joins);
+        }
+
+        public Joins and(Joins joins1, Joins joins2) {
+            return sel.and(joins1, joins2);
+        }
+
+        public Joins or(Joins joins1, Joins joins2) {
+            return sel.or(joins1, joins2);
+        }
+
+        public Joins outer(Joins joins) {
+            return sel.outer(joins);
+        }
+
+        public String toString() {
+            return sel.toString();
+        }
+    }
+
+    /**
+     * Comparator for collating ordered results when faking a union.
+     */
+    private static class ResultComparator
+        implements MergedResult.ResultComparator {
+
+        private final List[] _orders;
+        private final BitSet _desc;
+        private final DBDictionary _dict;
+
+        public ResultComparator(List[] orders, BitSet desc, DBDictionary dict) {
+            _orders = orders;
+            _desc = desc;
+            _dict = dict;
+        }
+
+        public Object getOrderingValue(Result res, int idx) {
+            // if one value just return it
+            ResultSet rs = ((ResultSetResult) res).getResultSet();
+            if (_orders[idx].size() == 1)
+                return getOrderingValue(rs, _orders[idx].get(0));
+
+            // return array of values
+            Object[] vals = new Object[_orders[idx].size()];
+            for (int i = 0; i < vals.length; i++)
+                vals[i] = getOrderingValue(rs, _orders[idx].get(i));
+            return vals;
+        }
+
+        /**
+         * Extract value at given index from result set.
+         */
+        private Object getOrderingValue(ResultSet rs, Object i) {
+            try {
+                return _dict.getObject(rs, ((Integer) i).intValue() + 1, null);
+            } catch (SQLException se) {
+                throw SQLExceptions.getStore(se, _dict);
+            }
+        }
+
+        public int compare(Object o1, Object o2) {
+            if (o1 == o2)
+                return 0;
+            if (o1 == null)
+                return (_desc.get(0)) ? -1 : 1;
+            if (o2 == null)
+                return (_desc.get(0)) ? 1 : -1;
+
+            int cmp;
+            if (!(o1 instanceof Object[])) {
+                if (!(o2 instanceof Object[])) {
+                    cmp = ((Comparable) o1).compareTo(o2);
+                    return (_desc.get(0)) ? -cmp : cmp;
+                }
+
+                cmp = ((Comparable) o1).compareTo(((Object[]) o2)[0]);
+                if (cmp != 0)
+                    return (_desc.get(0)) ? -cmp : cmp;
+                return -1;
+            }
+
+            if (!(o2 instanceof Object[])) {
+                cmp = ((Comparable) ((Object[]) o1)[0]).compareTo(o2);
+                if (cmp != 0)
+                    return (_desc.get(0)) ? -cmp : cmp;
+                return 1;
+            }
+
+            Object[] a1 = (Object[]) o1;
+            Object[] a2 = (Object[]) o2;
+            for (int i = 0; i < a1.length; i++) {
+                cmp = ((Comparable) a1[i]).compareTo(a2[i]);
+                if (cmp != 0)
+                    return (_desc.get(i)) ? -cmp : cmp;
+            }
+            return a1.length - a2.length;
+        }
+    }
+}

Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.sql;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.openjpa.jdbc.kernel.JDBCFetchState;
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.util.UnsupportedException;
+
+/**
+ * Result that merges multiple result delegates. Support exists for
+ * maintaining ordering of the internally-held results, provided that each
+ * of the individual results is itself ordered.
+ *
+ * @author Abe White
+ */
+public class MergedResult
+    implements Result {
+
+    private static final byte NEXT = 0;
+    private static final byte CURRENT = 1;
+    private static final byte DONE = 2;
+
+    private final Result[] _res;
+    private final byte[] _status;
+    private final ResultComparator _comp;
+    private final Object[] _order;
+    private int _idx = 0;
+    private boolean _pushedBack = false;
+
+    /**
+     * Constructor; supply delegates.
+     */
+    public MergedResult(Result[] res) {
+        this(res, null);
+    }
+
+    /**
+     * Constructor; supply delegates and comparator for ordering results.
+     */
+    public MergedResult(Result[] res, ResultComparator comp) {
+        _res = res;
+        _comp = comp;
+        _order = (comp == null) ? null : new Object[res.length];
+        _status = (comp == null) ? null : new byte[res.length];
+    }
+
+    public Object getEager(FieldMapping key) {
+        return _res[_idx].getEager(key);
+    }
+
+    public void putEager(FieldMapping key, Object res) {
+        _res[_idx].putEager(key, res);
+    }
+
+    public Joins newJoins() {
+        return _res[_idx].newJoins();
+    }
+
+    public void close() {
+        for (int i = 0; i < _res.length; i++)
+            _res[i].close();
+    }
+
+    public boolean isLocking() {
+        return _res[_idx].isLocking();
+    }
+
+    public boolean supportsRandomAccess()
+        throws SQLException {
+        return false;
+    }
+
+    public boolean absolute(int row)
+        throws SQLException {
+        throw new UnsupportedException();
+    }
+
+    public boolean next()
+        throws SQLException {
+        if (_pushedBack) {
+            _pushedBack = false;
+            return true;
+        }
+
+        if (_comp == null) {
+            while (!_res[_idx].next()) {
+                if (_idx == _res.length - 1)
+                    return false;
+                _idx++;
+            }
+            return true;
+        }
+
+        // ordering is involved; extract order values from each result
+        boolean hasValue = false;
+        for (int i = 0; i < _status.length; i++) {
+            switch (_status[i]) {
+                case NEXT:
+                    if (_res[i].next()) {
+                        hasValue = true;
+                        _status[i] = CURRENT;
+                        _order[i] = _comp.getOrderingValue(_res[i], i);
+                    } else
+                        _status[i] = DONE;
+                    break;
+                case CURRENT:
+                    hasValue = true;
+                    break;
+            }
+        }
+
+        // all results exhausted
+        if (!hasValue)
+            return false;
+
+        // for all results with values, find the 'least' one according to
+        // the comparator
+        int least = -1;
+        Object orderVal = null;
+        int cmp;
+        for (int i = 0; i < _order.length; i++) {
+            if (_status[i] != CURRENT)
+                continue;
+            if (least == -1 || _comp.compare(_order[i], orderVal) < 0) {
+                least = i;
+                orderVal = _order[i];
+            }
+        }
+
+        // make the current result the one with the least value, and clear
+        // the cached value for that result
+        _idx = least;
+        _order[least] = null;
+        _status[least] = NEXT;
+        return true;
+    }
+
+    public void pushBack()
+        throws SQLException {
+        _pushedBack = true;
+    }
+
+    public int size()
+        throws SQLException {
+        int size = 0;
+        for (int i = 0; i < _res.length; i++)
+            size += _res[i].size();
+        return size;
+    }
+
+    public boolean contains(Object obj)
+        throws SQLException {
+        return _res[_idx].contains(obj);
+    }
+
+    public boolean containsAll(Object[] objs)
+        throws SQLException {
+        return _res[_idx].containsAll(objs);
+    }
+
+    public boolean contains(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].contains(col, joins);
+    }
+
+    public boolean containsAll(Column[] cols, Joins joins)
+        throws SQLException {
+        return _res[_idx].containsAll(cols, joins);
+    }
+
+    public ClassMapping getBaseMapping() {
+        return _res[_idx].getBaseMapping();
+    }
+
+    public void setBaseMapping(ClassMapping mapping) {
+        _res[_idx].setBaseMapping(mapping);
+    }
+
+    public int indexOf() {
+        return _res[_idx].indexOf();
+    }
+
+    public Object load(ClassMapping mapping, JDBCStore store,
+        JDBCFetchState fetchState)
+        throws SQLException {
+        return _res[_idx].load(mapping, store, fetchState);
+    }
+
+    public Object load(ClassMapping mapping, JDBCStore store,
+        JDBCFetchState fetchState, Joins joins)
+        throws SQLException {
+        return _res[_idx].load(mapping, store, fetchState, joins);
+    }
+
+    public Array getArray(Object obj)
+        throws SQLException {
+        return _res[_idx].getArray(obj);
+    }
+
+    public InputStream getAsciiStream(Object obj)
+        throws SQLException {
+        return _res[_idx].getAsciiStream(obj);
+    }
+
+    public BigDecimal getBigDecimal(Object obj)
+        throws SQLException {
+        return _res[_idx].getBigDecimal(obj);
+    }
+
+    public BigInteger getBigInteger(Object obj)
+        throws SQLException {
+        return _res[_idx].getBigInteger(obj);
+    }
+
+    public InputStream getBinaryStream(Object obj)
+        throws SQLException {
+        return _res[_idx].getBinaryStream(obj);
+    }
+
+    public Blob getBlob(Object obj)
+        throws SQLException {
+        return _res[_idx].getBlob(obj);
+    }
+
+    public boolean getBoolean(Object obj)
+        throws SQLException {
+        return _res[_idx].getBoolean(obj);
+    }
+
+    public byte getByte(Object obj)
+        throws SQLException {
+        return _res[_idx].getByte(obj);
+    }
+
+    public byte[] getBytes(Object obj)
+        throws SQLException {
+        return _res[_idx].getBytes(obj);
+    }
+
+    public Calendar getCalendar(Object obj)
+        throws SQLException {
+        return _res[_idx].getCalendar(obj);
+    }
+
+    public char getChar(Object obj)
+        throws SQLException {
+        return _res[_idx].getChar(obj);
+    }
+
+    public Reader getCharacterStream(Object obj)
+        throws SQLException {
+        return _res[_idx].getCharacterStream(obj);
+    }
+
+    public Clob getClob(Object obj)
+        throws SQLException {
+        return _res[_idx].getClob(obj);
+    }
+
+    public Date getDate(Object obj)
+        throws SQLException {
+        return _res[_idx].getDate(obj);
+    }
+
+    public java.sql.Date getDate(Object obj, Calendar cal)
+        throws SQLException {
+        return _res[_idx].getDate(obj, cal);
+    }
+
+    public double getDouble(Object obj)
+        throws SQLException {
+        return _res[_idx].getDouble(obj);
+    }
+
+    public float getFloat(Object obj)
+        throws SQLException {
+        return _res[_idx].getFloat(obj);
+    }
+
+    public int getInt(Object obj)
+        throws SQLException {
+        return _res[_idx].getInt(obj);
+    }
+
+    public Locale getLocale(Object obj)
+        throws SQLException {
+        return _res[_idx].getLocale(obj);
+    }
+
+    public long getLong(Object obj)
+        throws SQLException {
+        return _res[_idx].getLong(obj);
+    }
+
+    public Number getNumber(Object obj)
+        throws SQLException {
+        return _res[_idx].getNumber(obj);
+    }
+
+    public Object getObject(Object obj, int metaType, Object arg)
+        throws SQLException {
+        return _res[_idx].getObject(obj, metaType, arg);
+    }
+
+    public Object getSQLObject(Object obj, Map map)
+        throws SQLException {
+        return _res[_idx].getSQLObject(obj, map);
+    }
+
+    public Ref getRef(Object obj, Map map)
+        throws SQLException {
+        return _res[_idx].getRef(obj, map);
+    }
+
+    public short getShort(Object obj)
+        throws SQLException {
+        return _res[_idx].getShort(obj);
+    }
+
+    public String getString(Object obj)
+        throws SQLException {
+        return _res[_idx].getString(obj);
+    }
+
+    public Time getTime(Object obj, Calendar cal)
+        throws SQLException {
+        return _res[_idx].getTime(obj, cal);
+    }
+
+    public Timestamp getTimestamp(Object obj, Calendar cal)
+        throws SQLException {
+        return _res[_idx].getTimestamp(obj, cal);
+    }
+
+    public Array getArray(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getArray(col, joins);
+    }
+
+    public InputStream getAsciiStream(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getAsciiStream(col, joins);
+    }
+
+    public BigDecimal getBigDecimal(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getBigDecimal(col, joins);
+    }
+
+    public BigInteger getBigInteger(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getBigInteger(col, joins);
+    }
+
+    public InputStream getBinaryStream(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getBinaryStream(col, joins);
+    }
+
+    public Blob getBlob(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getBlob(col, joins);
+    }
+
+    public boolean getBoolean(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getBoolean(col, joins);
+    }
+
+    public byte getByte(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getByte(col, joins);
+    }
+
+    public byte[] getBytes(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getBytes(col, joins);
+    }
+
+    public Calendar getCalendar(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getCalendar(col, joins);
+    }
+
+    public char getChar(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getChar(col, joins);
+    }
+
+    public Reader getCharacterStream(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getCharacterStream(col, joins);
+    }
+
+    public Clob getClob(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getClob(col, joins);
+    }
+
+    public Date getDate(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getDate(col, joins);
+    }
+
+    public java.sql.Date getDate(Column col, Calendar cal, Joins joins)
+        throws SQLException {
+        return _res[_idx].getDate(col, cal, joins);
+    }
+
+    public double getDouble(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getDouble(col, joins);
+    }
+
+    public float getFloat(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getFloat(col, joins);
+    }
+
+    public int getInt(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getInt(col, joins);
+    }
+
+    public Locale getLocale(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getLocale(col, joins);
+    }
+
+    public long getLong(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getLong(col, joins);
+    }
+
+    public Number getNumber(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getNumber(col, joins);
+    }
+
+    public Object getObject(Column col, Object arg, Joins joins)
+        throws SQLException {
+        return _res[_idx].getObject(col, arg, joins);
+    }
+
+    public Object getSQLObject(Column col, Map map, Joins joins)
+        throws SQLException {
+        return _res[_idx].getSQLObject(col, map, joins);
+    }
+
+    public Ref getRef(Column col, Map map, Joins joins)
+        throws SQLException {
+        return _res[_idx].getRef(col, map, joins);
+    }
+
+    public short getShort(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getShort(col, joins);
+    }
+
+    public String getString(Column col, Joins joins)
+        throws SQLException {
+        return _res[_idx].getString(col, joins);
+    }
+
+    public Time getTime(Column col, Calendar cal, Joins joins)
+        throws SQLException {
+        return _res[_idx].getTime(col, cal, joins);
+    }
+
+    public Timestamp getTimestamp(Column col, Calendar cal, Joins joins)
+        throws SQLException {
+        return _res[_idx].getTimestamp(col, cal, joins);
+    }
+
+    public boolean wasNull()
+        throws SQLException {
+        return _res[_idx].wasNull();
+    }
+
+    public void startDataRequest(Object mapping) {
+        for (int i = 0; i < _res.length; i++)
+            _res[i].startDataRequest(mapping);
+    }
+
+    public void endDataRequest() {
+        for (int i = 0; i < _res.length; i++)
+            _res[i].endDataRequest();
+    }
+
+    /**
+     * Comparator for ordering result rows.
+     */
+    public static interface ResultComparator
+        extends Comparator {
+
+        /**
+         * Return the ordering value of the current row of the given result.
+         */
+        public Object getOrderingValue(Result res, int idx);
+    }
+}

Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.sql;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Arrays;
+
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Index;
+import org.apache.openjpa.jdbc.schema.PrimaryKey;
+import org.apache.openjpa.jdbc.schema.Table;
+
+/**
+ * Dictionary for MySQL.
+ */
+public class MySQLDictionary
+    extends DBDictionary {
+
+    /**
+     * The MySQL table type to use when creating tables; defaults to innodb.
+     */
+    public String tableType = "innodb";
+
+    /**
+     * Whether to use clobs. Some older versions of MySQL do not handle
+     * clobs properly so we default to false here.
+     */
+    public boolean useClobs = true;
+
+    /**
+     * Whether the driver automatically deserializes blobs.
+     */
+    public boolean driverDeserializesBlobs = true;
+
+    public MySQLDictionary() {
+        platform = "MySQL";
+        validationSQL = "SELECT NOW()";
+        distinctCountColumnSeparator = ",";
+
+        supportsDeferredConstraints = false;
+        constraintNameMode = CONS_NAME_MID;
+        supportsMultipleNontransactionalResultSets = false;
+        supportsSubselect = false; // old versions
+        requiresAliasForSubselect = true; // new versions
+        supportsSelectStartIndex = true;
+        supportsSelectEndIndex = true;
+        allowsAliasInBulkClause = false;
+
+        concatenateFunction = "CONCAT({0},{1})";
+
+        maxTableNameLength = 64;
+        maxColumnNameLength = 64;
+        maxIndexNameLength = 64;
+        maxConstraintNameLength = 64;
+        maxIndexesPerTable = 32;
+        schemaCase = SCHEMA_CASE_PRESERVE;
+
+        supportsAutoAssign = true;
+        lastGeneratedKeyQuery = "SELECT LAST_INSERT_ID()";
+        autoAssignClause = "AUTO_INCREMENT";
+
+        clobTypeName = "TEXT";
+        longVarcharTypeName = "TEXT";
+        longVarbinaryTypeName = "LONG VARBINARY";
+        timestampTypeName = "DATETIME";
+        fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
+            "BOOL", "LONG VARBINARY", "MEDIUMBLOB", "LONGBLOB",
+            "TINYBLOB", "LONG VARCHAR", "MEDIUMTEXT", "LONGTEXT", "TEXT",
+            "TINYTEXT", "DOUBLE PRECISION", "ENUM", "SET", "DATETIME",
+        }));
+        reservedWordSet.addAll(Arrays.asList(new String[]{
+            "INT1", "INT2", "INT4", "FLOAT1", "FLOAT2", "FLOAT4",
+            "AUTO_INCREMENT", "BINARY", "BLOB", "CHANGE", "ENUM", "INFILE",
+            "LOAD", "MEDIUMINT", "OPTION", "OUTFILE", "REPLACE",
+            "SET", "STARTING", "TEXT", "UNSIGNED", "ZEROFILL",
+        }));
+    }
+
+    public String[] getCreateTableSQL(Table table) {
+        String[] sql = super.getCreateTableSQL(table);
+        if (tableType != null && tableType.length() > 0)
+            sql[0] = sql[0] + " TYPE = " + tableType;
+        return sql;
+    }
+
+    public String[] getDropIndexSQL(Index index) {
+        return new String[]{ "DROP INDEX " + getFullName(index) + " ON "
+            + getFullName(index.getTable(), false) };
+    }
+
+    public String[] getAddPrimaryKeySQL(PrimaryKey pk) {
+        String[] sql = super.getAddPrimaryKeySQL(pk);
+
+        // mysql requires that a column be declared NOT NULL before
+        // it can be made a primary key.
+        Column[] cols = pk.getColumns();
+        String[] ret = new String[cols.length + sql.length];
+        for (int i = 0; i < cols.length; i++) {
+            ret[i] = "ALTER TABLE " + getFullName(cols[i].getTable(), false)
+                + " CHANGE " + cols[i].getName()
+                + " " + cols[i].getName() // name twice
+                + " " + getTypeName(cols[i]) + " NOT NULL";
+        }
+
+        System.arraycopy(sql, 0, ret, cols.length, sql.length);
+        return ret;
+    }
+
+    protected String getForeignKeyConstraintSQL(ForeignKey fk) {
+        // mysql does not support composite foreign keys
+        if (fk.getColumns().length > 1)
+            return null;
+        return super.getForeignKeyConstraintSQL(fk);
+    }
+
+    protected void appendSelectRange(SQLBuffer buf, long start, long end) {
+        buf.append(" LIMIT ").appendValue(start).append(", ");
+        if (end == Long.MAX_VALUE)
+            buf.appendValue(Long.MAX_VALUE);
+        else
+            buf.appendValue(end - start);
+    }
+
+    protected Column newColumn(ResultSet colMeta)
+        throws SQLException {
+        Column col = super.newColumn(colMeta);
+        if (col.isNotNull() && "0".equals(col.getDefaultString()))
+            col.setDefaultString(null);
+        return col;
+    }
+
+    public Object getBlobObject(ResultSet rs, int column, JDBCStore store)
+        throws SQLException {
+        // if the user has set a get-blob strategy explicitly or the driver
+        // does not automatically deserialize, delegate to super
+        if (useGetBytesForBlobs || useGetObjectForBlobs
+            || !driverDeserializesBlobs)
+            return super.getBlobObject(rs, column, store);
+
+        // most mysql drivers deserialize on getObject
+        return rs.getObject(column);
+    }
+
+    public int getPreferredType(int type) {
+        if (type == Types.CLOB && !useClobs)
+            return Types.LONGVARCHAR;
+        return super.getPreferredType(type);
+    }
+}

Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java
------------------------------------------------------------------------------
    svn:executable = *