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.ResultSet;
22 import java.sql.SQLException;
23 import java.util.BitSet;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Iterator;
27 import java.util.List;
28
29 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
30 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
31 import org.apache.openjpa.jdbc.kernel.JDBCStore;
32 import org.apache.openjpa.jdbc.meta.ClassMapping;
33 import org.apache.openjpa.jdbc.meta.FieldMapping;
34 import org.apache.openjpa.jdbc.schema.Column;
35 import org.apache.openjpa.jdbc.schema.ForeignKey;
36 import org.apache.openjpa.jdbc.schema.Table;
37 import org.apache.openjpa.lib.util.Localizer;
38 import org.apache.openjpa.util.InternalException;
39 import org.apache.openjpa.util.UnsupportedException;
40 import org.apache.openjpa.util.UserException;
41
42 /**
43 * A logical union made up of multiple distinct selects whose results are
44 * combined in memory.
45 *
46 * @author Abe White
47 */
48 public class LogicalUnion
49 implements Union {
50
51 private static final Localizer _loc = Localizer.forPackage
52 (LogicalUnion.class);
53
54 protected final UnionSelect[] sels;
55 protected final DBDictionary dict;
56 protected final ClassMapping[] mappings;
57 protected final BitSet desc = new BitSet();
58 private boolean _distinct = true;
59
60
61 /**
62 * Constructor.
63 *
64 * @param conf system configuration
65 * @param sels the number of SQL selects to union together
66 */
67 public LogicalUnion(JDBCConfiguration conf, int sels) {
68 this(conf, sels, null);
69 }
70
71 /**
72 * Constructor used to seed the internal selects.
73 */
74 public LogicalUnion(JDBCConfiguration conf, Select[] seeds) {
75 this(conf, seeds.length, seeds);
76 }
77
78 /**
79 * Delegate constructor.
80 */
81 protected LogicalUnion(JDBCConfiguration conf, int sels, Select[] seeds) {
82 if (sels == 0)
83 throw new InternalException("sels == 0");
84
85 dict = conf.getDBDictionaryInstance();
86 mappings = new ClassMapping[sels];
87 this.sels = new UnionSelect[sels];
88
89 SelectImpl seed;
90 for (int i = 0; i < sels; i++) {
91 seed = (seeds == null)
92 ? (SelectImpl) conf.getSQLFactoryInstance().newSelect()
93 : (SelectImpl) seeds[i];
94 this.sels[i] = newUnionSelect(seed, i);
95 }
96 }
97
98 /**
99 * Create a new union select with the given delegate and union position.
100 */
101 protected UnionSelect newUnionSelect(SelectImpl seed, int pos) {
102 return new UnionSelect(seed, pos);
103 }
104
105 public Select[] getSelects() {
106 return sels;
107 }
108
109 public boolean isUnion() {
110 return false;
111 }
112
113 public void abortUnion() {
114 }
115
116 public String getOrdering() {
117 return null;
118 }
119
120 public JDBCConfiguration getConfiguration() {
121 return sels[0].getConfiguration();
122 }
123
124 public DBDictionary getDBDictionary() {
125 return dict;
126 }
127
128 public SQLBuffer toSelect(boolean forUpdate, JDBCFetchConfiguration fetch) {
129 return dict.toSelect(sels[0], forUpdate, fetch);
130 }
131
132 public SQLBuffer toSelectCount() {
133 return dict.toSelectCount(sels[0]);
134 }
135
136 public boolean getAutoDistinct() {
137 return sels[0].getAutoDistinct();
138 }
139
140 public void setAutoDistinct(boolean distinct) {
141 for (int i = 0; i < sels.length; i++)
142 sels[i].setAutoDistinct(distinct);
143 }
144
145 public boolean isDistinct() {
146 return _distinct;
147 }
148
149 public void setDistinct(boolean distinct) {
150 _distinct = distinct;
151 }
152
153 public boolean isLRS() {
154 return sels[0].isLRS();
155 }
156
157 public void setLRS(boolean lrs) {
158 for (int i = 0; i < sels.length; i++)
159 sels[i].setLRS(lrs);
160 }
161
162 public int getExpectedResultCount() {
163 return sels[0].getExpectedResultCount();
164 }
165
166 public void setExpectedResultCount(int expectedResultCount,
167 boolean force) {
168 for (int i = 0; i < sels.length; i++)
169 sels[i].setExpectedResultCount(expectedResultCount, force);
170 }
171
172 public int getJoinSyntax() {
173 return sels[0].getJoinSyntax();
174 }
175
176 public void setJoinSyntax(int syntax) {
177 for (int i = 0; i < sels.length; i++)
178 sels[i].setJoinSyntax(syntax);
179 }
180
181 public boolean supportsRandomAccess(boolean forUpdate) {
182 if (sels.length == 1)
183 return sels[0].supportsRandomAccess(forUpdate);
184 return false;
185 }
186
187 public boolean supportsLocking() {
188 if (sels.length == 1)
189 return sels[0].supportsLocking();
190 for (int i = 0; i < sels.length; i++)
191 if (!sels[i].supportsLocking())
192 return false;
193 return true;
194 }
195
196 public int getCount(JDBCStore store)
197 throws SQLException {
198 int count = 0;
199 for (int i = 0; i < sels.length; i++)
200 count += sels[i].getCount(store);
201 return count;
202 }
203
204 public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
205 throws SQLException {
206 if (fetch == null)
207 fetch = store.getFetchConfiguration();
208 return execute(store, fetch, fetch.getReadLockLevel());
209 }
210
211 public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
212 int lockLevel)
213 throws SQLException {
214 if (fetch == null)
215 fetch = store.getFetchConfiguration();
216
217 if (sels.length == 1) {
218 Result res = sels[0].execute(store, fetch, lockLevel);
219 ((AbstractResult) res).setBaseMapping(mappings[0]);
220 return res;
221 }
222
223 if (getExpectedResultCount() == 1) {
224 AbstractResult res;
225 for (int i = 0; i < sels.length; i++) {
226 res = (AbstractResult) sels[i].execute(store, fetch,
227 lockLevel);
228 res.setBaseMapping(mappings[i]);
229 res.setIndexOf(i);
230
231 // if we get to the last select, just return its result
232 if (i == sels.length - 1)
233 return res;
234
235 // return the first result that has a row
236 try {
237 if (!res.next())
238 res.close();
239 else {
240 res.pushBack();
241 return res;
242 }
243 }
244 catch (SQLException se) {
245 res.close();
246 throw se;
247 }
248 }
249 }
250
251 // create a single result from each select in our fake union, merging
252 // them as needed
253 AbstractResult[] res = new AbstractResult[sels.length];
254 List[] orderIdxs = null;
255 try {
256 List l;
257 for (int i = 0; i < res.length; i++) {
258 res[i] = (AbstractResult) sels[i].execute(store, fetch,
259 lockLevel);
260 res[i].setBaseMapping(mappings[i]);
261 res[i].setIndexOf(i);
262
263 l = sels[i].getSelectedOrderIndexes();
264 if (l != null) {
265 if (orderIdxs == null)
266 orderIdxs = new List[sels.length];
267 orderIdxs[i] = l;
268 }
269 }
270 } catch (SQLException se) {
271 for (int i = 0; res[i] != null; i++)
272 res[i].close();
273 throw se;
274 }
275
276 // if multiple selects have ordering, use a comparator to collate
277 ResultComparator comp = null;
278 if (orderIdxs != null)
279 comp = new ResultComparator(orderIdxs, desc, dict);
280 return new MergedResult(res, comp);
281 }
282
283 public void select(Union.Selector selector) {
284 for (int i = 0; i < sels.length; i++)
285 selector.select(sels[i], i);
286 }
287
288 public String toString() {
289 return toSelect(false, null).getSQL();
290 }
291
292 /**
293 * A callback used to create the selects in a SQL union.
294 */
295 public static interface Selector {
296
297 /**
298 * Populate the <code>i</code>th select in the union.
299 */
300 public void select(Select sel, int i);
301 }
302
303 /**
304 * A select that is part of a logical union.
305 */
306 protected class UnionSelect
307 implements Select {
308
309 protected final SelectImpl sel;
310 protected final int pos;
311 protected int orders = 0;
312 protected List orderIdxs = null;
313
314 public UnionSelect(SelectImpl sel, int pos) {
315 this.sel = sel;
316 this.pos = pos;
317 sel.setRecordOrderedIndexes(true);
318 }
319
320 /**
321 * Delegate select.
322 */
323 public SelectImpl getDelegate() {
324 return sel;
325 }
326
327 /**
328 * Return the indexes of the data in the select clause this query is
329 * ordered by.
330 */
331 public List getSelectedOrderIndexes() {
332 if (orderIdxs == null)
333 orderIdxs = sel.getOrderedIndexes();
334 return orderIdxs;
335 }
336
337 public JDBCConfiguration getConfiguration() {
338 return sel.getConfiguration();
339 }
340
341 public int indexOf() {
342 return pos;
343 }
344
345 public SQLBuffer toSelect(boolean forUpdate,
346 JDBCFetchConfiguration fetch) {
347 return sel.toSelect(forUpdate, fetch);
348 }
349
350 public SQLBuffer toSelectCount() {
351 return sel.toSelectCount();
352 }
353
354 public boolean getAutoDistinct() {
355 return sel.getAutoDistinct();
356 }
357
358 public void setAutoDistinct(boolean distinct) {
359 sel.setAutoDistinct(distinct);
360 }
361
362 public boolean isDistinct() {
363 return sel.isDistinct();
364 }
365
366 public void setDistinct(boolean distinct) {
367 sel.setDistinct(distinct);
368 }
369
370 public boolean isLRS() {
371 return sel.isLRS();
372 }
373
374 public void setLRS(boolean lrs) {
375 sel.setLRS(lrs);
376 }
377
378 public int getJoinSyntax() {
379 return sel.getJoinSyntax();
380 }
381
382 public void setJoinSyntax(int joinSyntax) {
383 sel.setJoinSyntax(joinSyntax);
384 }
385
386 public boolean supportsRandomAccess(boolean forUpdate) {
387 return sel.supportsRandomAccess(forUpdate);
388 }
389
390 public boolean supportsLocking() {
391 return sel.supportsLocking();
392 }
393
394 public int getCount(JDBCStore store)
395 throws SQLException {
396 return sel.getCount(store);
397 }
398
399 public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
400 throws SQLException {
401 return sel.execute(store, fetch);
402 }
403
404 public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
405 int lockLevel)
406 throws SQLException {
407 return sel.execute(store, fetch, lockLevel);
408 }
409
410 public List getSubselects() {
411 return Collections.EMPTY_LIST;
412 }
413
414 public Select getParent() {
415 return null;
416 }
417
418 public String getSubselectPath() {
419 return null;
420 }
421
422 public void setParent(Select parent, String path) {
423 throw new UnsupportedException(_loc.get("union-element"));
424 }
425
426 public Select getFromSelect() {
427 return null;
428 }
429
430 public void setFromSelect(Select sel) {
431 throw new UnsupportedException(_loc.get("union-element"));
432 }
433
434 public boolean hasEagerJoin(boolean toMany) {
435 return sel.hasEagerJoin(toMany);
436 }
437
438 public boolean hasJoin(boolean toMany) {
439 return sel.hasJoin(toMany);
440 }
441
442 public boolean isSelected(Table table) {
443 return sel.isSelected(table);
444 }
445
446 public Collection getTableAliases() {
447 return sel.getTableAliases();
448 }
449
450 public List getSelects() {
451 return sel.getSelects();
452 }
453
454 public List getSelectAliases() {
455 return sel.getSelectAliases();
456 }
457
458 public List getIdentifierAliases() {
459 return sel.getIdentifierAliases();
460 }
461
462 public SQLBuffer getOrdering() {
463 return sel.getOrdering();
464 }
465
466 public SQLBuffer getGrouping() {
467 return sel.getGrouping();
468 }
469
470 public SQLBuffer getWhere() {
471 return sel.getWhere();
472 }
473
474 public SQLBuffer getHaving() {
475 return sel.getHaving();
476 }
477
478 public void addJoinClassConditions() {
479 sel.addJoinClassConditions();
480 }
481
482 public Joins getJoins() {
483 return sel.getJoins();
484 }
485
486 public Iterator getJoinIterator() {
487 return sel.getJoinIterator();
488 }
489
490 public long getStartIndex() {
491 return sel.getStartIndex();
492 }
493
494 public long getEndIndex() {
495 return sel.getEndIndex();
496 }
497
498 public void setRange(long start, long end) {
499 sel.setRange(start, end);
500 }
501
502 public String getColumnAlias(Column col) {
503 return sel.getColumnAlias(col);
504 }
505
506 public String getColumnAlias(Column col, Joins joins) {
507 return sel.getColumnAlias(col, joins);
508 }
509
510 public String getColumnAlias(String col, Table table) {
511 return sel.getColumnAlias(col, table);
512 }
513
514 public String getColumnAlias(String col, Table table, Joins joins) {
515 return sel.getColumnAlias(col, table, joins);
516 }
517
518 public boolean isAggregate() {
519 return sel.isAggregate();
520 }
521
522 public void setAggregate(boolean agg) {
523 sel.setAggregate(agg);
524 }
525
526 public boolean isLob() {
527 return sel.isLob();
528 }
529
530 public void setLob(boolean lob) {
531 sel.setLob(lob);
532 }
533
534 public void selectPlaceholder(String sql) {
535 sel.selectPlaceholder(sql);
536 }
537
538 public void clearSelects() {
539 sel.clearSelects();
540 }
541
542 public boolean select(SQLBuffer sql, Object id) {
543 return sel.select(sql, id);
544 }
545
546 public boolean select(SQLBuffer sql, Object id, Joins joins) {
547 return sel.select(sql, id, joins);
548 }
549
550 public boolean select(String sql, Object id) {
551 return sel.select(sql, id);
552 }
553
554 public boolean select(String sql, Object id, Joins joins) {
555 return sel.select(sql, id, joins);
556 }
557
558 public boolean select(Column col) {
559 return sel.select(col);
560 }
561
562 public boolean select(Column col, Joins joins) {
563 return sel.select(col, joins);
564 }
565
566 public int select(Column[] cols) {
567 return sel.select(cols);
568 }
569
570 public int select(Column[] cols, Joins joins) {
571 return sel.select(cols, joins);
572 }
573
574 public void select(ClassMapping mapping, int subclasses,
575 JDBCStore store, JDBCFetchConfiguration fetch, int eager) {
576 select(mapping, subclasses, store, fetch, eager, null, false);
577 }
578
579 public void select(ClassMapping mapping, int subclasses,
580 JDBCStore store, JDBCFetchConfiguration fetch, int eager,
581 Joins joins) {
582 select(mapping, subclasses, store, fetch, eager, joins, false);
583 }
584
585 private void select(ClassMapping mapping, int subclasses,
586 JDBCStore store, JDBCFetchConfiguration fetch, int eager,
587 Joins joins, boolean identifier) {
588 // if this is the first (primary) mapping selected for this
589 // SELECT, record it so we can figure out what the result type is
590 // since the discriminator might not be selected
591 if (mappings[pos] == null)
592 mappings[pos] = mapping;
593
594 sel.select(this, mapping, subclasses, store, fetch, eager,
595 joins, identifier);
596 }
597
598 public boolean selectIdentifier(Column col) {
599 return sel.selectIdentifier(col);
600 }
601
602 public boolean selectIdentifier(Column col, Joins joins) {
603 return sel.selectIdentifier(col, joins);
604 }
605
606 public int selectIdentifier(Column[] cols) {
607 return sel.selectIdentifier(cols);
608 }
609
610 public int selectIdentifier(Column[] cols, Joins joins) {
611 return sel.selectIdentifier(cols, joins);
612 }
613
614 public void selectIdentifier(ClassMapping mapping, int subclasses,
615 JDBCStore store, JDBCFetchConfiguration fetch, int eager) {
616 select(mapping, subclasses, store, fetch, eager, null, true);
617 }
618
619 public void selectIdentifier(ClassMapping mapping, int subclasses,
620 JDBCStore store, JDBCFetchConfiguration fetch, int eager,
621 Joins joins) {
622 select(mapping, subclasses, store, fetch, eager, joins, true);
623 }
624
625 public int selectPrimaryKey(ClassMapping mapping) {
626 return sel.selectPrimaryKey(mapping);
627 }
628
629 public int selectPrimaryKey(ClassMapping mapping, Joins joins) {
630 return sel.selectPrimaryKey(mapping, joins);
631 }
632
633 public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
634 boolean select) {
635 return orderByPrimaryKey(mapping, asc, null, select);
636 }
637
638 public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
639 Joins joins, boolean select) {
640 ClassMapping pks = mapping;
641 while (!pks.isPrimaryKeyObjectId(true))
642 pks = pks.getJoinablePCSuperclassMapping();
643 Column[] cols = pks.getPrimaryKeyColumns();
644 recordOrderColumns(cols, asc);
645 return sel.orderByPrimaryKey(mapping, asc, joins, select,
646 isUnion());
647 }
648
649 /**
650 * Record that we're ordering by a SQL expression.
651 */
652 protected void recordOrder(Object ord, boolean asc) {
653 if (ord == null)
654 return;
655 orderIdxs = null;
656
657 int idx = orders++;
658 if (desc.get(idx) && asc)
659 throw new UserException(_loc.get("incompat-ordering"));
660 if (!asc)
661 desc.set(idx);
662 }
663
664 /**
665 * Record that we're ordering by the given columns.
666 */
667 protected void recordOrderColumns(Column[] cols, boolean asc) {
668 for (int i = 0; i < cols.length; i++)
669 recordOrder(cols[i], asc);
670 }
671
672 public boolean orderBy(Column col, boolean asc, boolean select) {
673 return orderBy(col, asc, null, select);
674 }
675
676 public boolean orderBy(Column col, boolean asc, Joins joins,
677 boolean select) {
678 recordOrder(col, asc);
679 return sel.orderBy(col, asc, joins, select, isUnion());
680 }
681
682 public int orderBy(Column[] cols, boolean asc, boolean select) {
683 return orderBy(cols, asc, null, select);
684 }
685
686 public int orderBy(Column[] cols, boolean asc, Joins joins,
687 boolean select) {
688 recordOrderColumns(cols, asc);
689 return sel.orderBy(cols, asc, joins, select, isUnion());
690 }
691
692 public boolean orderBy(SQLBuffer sql, boolean asc, boolean select) {
693 return orderBy(sql, asc, null, select);
694 }
695
696 public boolean orderBy(SQLBuffer sql, boolean asc, Joins joins,
697 boolean select) {
698 recordOrder(sql.getSQL(false), asc);
699 return sel.orderBy(sql, asc, joins, select, isUnion());
700 }
701
702 public boolean orderBy(String sql, boolean asc, boolean select) {
703 return orderBy(sql, asc, null, select);
704 }
705
706 public boolean orderBy(String sql, boolean asc, Joins joins,
707 boolean select) {
708 recordOrder(sql, asc);
709 return sel.orderBy(sql, asc, joins, select, isUnion());
710 }
711
712 public void clearOrdering() {
713 sel.clearOrdering();
714 }
715
716 public void wherePrimaryKey(Object oid, ClassMapping mapping,
717 JDBCStore store) {
718 sel.wherePrimaryKey(oid, mapping, store);
719 }
720
721 public void whereForeignKey(ForeignKey fk, Object oid,
722 ClassMapping mapping, JDBCStore store) {
723 sel.whereForeignKey(fk, oid, mapping, store);
724 }
725
726 public void where(Joins joins) {
727 sel.where(joins);
728 }
729
730 public void where(SQLBuffer sql) {
731 sel.where(sql);
732 }
733
734 public void where(SQLBuffer sql, Joins joins) {
735 sel.where(sql, joins);
736 }
737
738 public void where(String sql) {
739 sel.where(sql);
740 }
741
742 public void where(String sql, Joins joins) {
743 sel.where(sql, joins);
744 }
745
746 public void having(SQLBuffer sql) {
747 sel.having(sql);
748 }
749
750 public void having(SQLBuffer sql, Joins joins) {
751 sel.having(sql, joins);
752 }
753
754 public void having(String sql) {
755 sel.having(sql);
756 }
757
758 public void having(String sql, Joins joins) {
759 sel.having(sql, joins);
760 }
761
762 public void groupBy(SQLBuffer sql) {
763 sel.groupBy(sql);
764 }
765
766 public void groupBy(SQLBuffer sql, Joins joins) {
767 sel.groupBy(sql, joins);
768 }
769
770 public void groupBy(String sql) {
771 sel.groupBy(sql);
772 }
773
774 public void groupBy(String sql, Joins joins) {
775 sel.groupBy(sql, joins);
776 }
777
778 public void groupBy(Column col) {
779 sel.groupBy(col);
780 }
781
782 public void groupBy(Column col, Joins joins) {
783 sel.groupBy(col, joins);
784 }
785
786 public void groupBy(Column[] cols) {
787 sel.groupBy(cols);
788 }
789
790 public void groupBy(Column[] cols, Joins joins) {
791 sel.groupBy(cols, joins);
792 }
793
794 public void groupBy(ClassMapping mapping, int subclasses,
795 JDBCStore store, JDBCFetchConfiguration fetch) {
796 sel.groupBy(mapping, subclasses, store, fetch);
797 }
798
799 public void groupBy(ClassMapping mapping, int subclasses,
800 JDBCStore store, JDBCFetchConfiguration fetch, Joins joins) {
801 sel.groupBy(mapping, subclasses, store, fetch, joins);
802 }
803
804 public SelectExecutor whereClone(int sels) {
805 return sel.whereClone(sels);
806 }
807
808 public SelectExecutor fullClone(int sels) {
809 return sel.fullClone(sels);
810 }
811
812 public SelectExecutor eagerClone(FieldMapping key, int eagerType,
813 boolean toMany, int sels) {
814 SelectExecutor ex = sel.eagerClone(key, eagerType, toMany, sels);
815 return (ex == sel) ? this : ex;
816 }
817
818 public SelectExecutor getEager(FieldMapping key) {
819 SelectExecutor ex = sel.getEager(key);
820 return (ex == sel) ? this : ex;
821 }
822
823 public Joins newJoins() {
824 return sel.newJoins();
825 }
826
827 public Joins newOuterJoins() {
828 return sel.newOuterJoins();
829 }
830
831 public void append(SQLBuffer buf, Joins joins) {
832 sel.append(buf, joins);
833 }
834
835 public Joins and(Joins joins1, Joins joins2) {
836 return sel.and(joins1, joins2);
837 }
838
839 public Joins or(Joins joins1, Joins joins2) {
840 return sel.or(joins1, joins2);
841 }
842
843 public Joins outer(Joins joins) {
844 return sel.outer(joins);
845 }
846
847 public String toString() {
848 return sel.toString();
849 }
850
851 public int getExpectedResultCount() {
852 return sel.getExpectedResultCount();
853 }
854
855 public void setExpectedResultCount(int expectedResultCount,
856 boolean force) {
857 sel.setExpectedResultCount(expectedResultCount, force);
858 }
859 }
860
861 /**
862 * Comparator for collating ordered results when faking a union.
863 */
864 private static class ResultComparator
865 implements MergedResult.ResultComparator {
866
867 private final List[] _orders;
868 private final BitSet _desc;
869 private final DBDictionary _dict;
870
871 public ResultComparator(List[] orders, BitSet desc, DBDictionary dict) {
872 _orders = orders;
873 _desc = desc;
874 _dict = dict;
875 }
876
877 public Object getOrderingValue(Result res, int idx) {
878 // if one value just return it
879 ResultSet rs = ((ResultSetResult) res).getResultSet();
880 if (_orders[idx].size() == 1)
881 return getOrderingValue(rs, _orders[idx].get(0));
882
883 // return array of values
884 Object[] vals = new Object[_orders[idx].size()];
885 for (int i = 0; i < vals.length; i++)
886 vals[i] = getOrderingValue(rs, _orders[idx].get(i));
887 return vals;
888 }
889
890 /**
891 * Extract value at given index from result set.
892 */
893 private Object getOrderingValue(ResultSet rs, Object i) {
894 try {
895 return _dict.getObject(rs, ((Integer) i).intValue() + 1, null);
896 } catch (SQLException se) {
897 throw SQLExceptions.getStore(se, _dict);
898 }
899 }
900
901 public int compare(Object o1, Object o2) {
902 if (o1 == o2)
903 return 0;
904 if (o1 == null)
905 return (_desc.get(0)) ? -1 : 1;
906 if (o2 == null)
907 return (_desc.get(0)) ? 1 : -1;
908
909 int cmp;
910 if (!(o1 instanceof Object[])) {
911 if (!(o2 instanceof Object[])) {
912 cmp = ((Comparable) o1).compareTo(o2);
913 return (_desc.get(0)) ? -cmp : cmp;
914 }
915
916 cmp = ((Comparable) o1).compareTo(((Object[]) o2)[0]);
917 if (cmp != 0)
918 return (_desc.get(0)) ? -cmp : cmp;
919 return -1;
920 }
921
922 if (!(o2 instanceof Object[])) {
923 cmp = ((Comparable) ((Object[]) o1)[0]).compareTo(o2);
924 if (cmp != 0)
925 return (_desc.get(0)) ? -cmp : cmp;
926 return 1;
927 }
928
929 Object[] a1 = (Object[]) o1;
930 Object[] a2 = (Object[]) o2;
931 for (int i = 0; i < a1.length; i++) {
932 cmp = ((Comparable) a1[i]).compareTo(a2[i]);
933 if (cmp != 0)
934 return (_desc.get(i)) ? -cmp : cmp;
935 }
936 return a1.length - a2.length;
937 }
938 }
939 }