1 /*
2  * For work developed by the HSQL Development Group:
3  *
4  * Copyright (c) 2001-2016, The HSQL Development Group
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * Redistributions of source code must retain the above copyright notice, this
11  * list of conditions and the following disclaimer.
12  *
13  * Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * Neither the name of the HSQL Development Group nor the names of its
18  * contributors may be used to endorse or promote products derived from this
19  * software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
25  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  *
34  *
35  * For work originally developed by the Hypersonic SQL Group:
36  *
37  * Copyright (c) 1995-2000, The Hypersonic SQL Group.
38  * All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions are met:
42  *
43  * Redistributions of source code must retain the above copyright notice, this
44  * list of conditions and the following disclaimer.
45  *
46  * Redistributions in binary form must reproduce the above copyright notice,
47  * this list of conditions and the following disclaimer in the documentation
48  * and/or other materials provided with the distribution.
49  *
50  * Neither the name of the Hypersonic SQL Group nor the names of its
51  * contributors may be used to endorse or promote products derived from this
52  * software without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
55  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57  * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
58  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
59  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
60  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
61  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
62  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
63  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65  *
66  * This software consists of voluntary contributions made by many individuals
67  * on behalf of the Hypersonic SQL Group.
68  */
69 
70 
71 package org.hsqldb.index;
72 
73 import org.hsqldb.Constraint;
74 import org.hsqldb.HsqlNameManager.HsqlName;
75 import org.hsqldb.OpTypes;
76 import org.hsqldb.Row;
77 import org.hsqldb.RowAVL;
78 import org.hsqldb.SchemaObject;
79 import org.hsqldb.Session;
80 import org.hsqldb.Table;
81 import org.hsqldb.TableBase;
82 import org.hsqldb.Tokens;
83 import org.hsqldb.TransactionManager;
84 import org.hsqldb.error.Error;
85 import org.hsqldb.error.ErrorCode;
86 import org.hsqldb.lib.ArrayUtil;
87 import org.hsqldb.lib.OrderedHashSet;
88 import org.hsqldb.navigator.RowIterator;
89 import org.hsqldb.persist.PersistentStore;
90 import org.hsqldb.rights.Grantee;
91 import org.hsqldb.types.Type;
92 
93 // fredt@users 20020221 - patch 513005 by sqlbob@users - corrections
94 // fredt@users - patch 1.8.0 - reworked the interface and comparison methods
95 // fredt@users - patch 1.8.0 - improved reliability for cached indexes
96 // fredt@users - patch 1.9.0 - iterators and concurrency
97 // fredt@users - patch 2.0.0 - enhanced selection and iterators
98 
99 /**
100  * Implementation of an AVL tree with parent pointers in nodes. Subclasses
101  * of Node implement the tree node objects for memory or disk storage. An
102  * Index has a root Node that is linked with other nodes using Java Object
103  * references or file pointers, depending on Node implementation.<p>
104  * An Index object also holds information on table columns (in the form of int
105  * indexes) that are covered by it.<p>
106  *
107  * New class derived from Hypersonic SQL code and enhanced in HSQLDB. <p>
108  *
109  * @author Thomas Mueller (Hypersonic SQL Group)
110  * @author Fred Toussi (fredt@users dot sourceforge.net)
111  * @version 2.3.3
112  * @since Hypersonic SQL
113  */
114 public class IndexAVL implements Index {
115 
116     private static final IndexRowIterator emptyIterator =
117         new IndexRowIterator(null, (PersistentStore) null, null, null, 0,
118                              false, false);
119 
120     // fields
121     private final long       persistenceId;
122     protected final HsqlName name;
123     private final boolean[]  colCheck;
124     final int[]              colIndex;
125     private final int[]      defaultColMap;
126     final Type[]             colTypes;
127     private final boolean[]  colDesc;
128     private final boolean[]  nullsLast;
129     final boolean            isSimpleOrder;
130     final boolean            isSimple;
131     protected final boolean  isPK;        // PK with or without columns
132     protected final boolean  isUnique;    // DDL uniqueness
133     protected final boolean  isConstraint;
134     private final boolean    isForward;
135     private boolean          isClustered;
136     protected TableBase      table;
137     int                      position;
138     private IndexUse[]       asArray;
139 
140     //
141     Object[] nullData;
142 
143     /**
144      * Constructor declaration
145      *
146      * @param name HsqlName of the index
147      * @param id persistnece id
148      * @param table table of the index
149      * @param columns array of column indexes
150      * @param descending boolean[]
151      * @param nullsLast boolean[]
152      * @param colTypes array of column types
153      * @param pk if index is for a primary key
154      * @param unique is this a unique index
155      * @param constraint does this index belonging to a constraint
156      * @param forward is this an auto-index for an FK that refers to a table
157      *   defined after this table
158      */
IndexAVL(HsqlName name, long id, TableBase table, int[] columns, boolean[] descending, boolean[] nullsLast, Type[] colTypes, boolean pk, boolean unique, boolean constraint, boolean forward)159     public IndexAVL(HsqlName name, long id, TableBase table, int[] columns,
160                     boolean[] descending, boolean[] nullsLast,
161                     Type[] colTypes, boolean pk, boolean unique,
162                     boolean constraint, boolean forward) {
163 
164         this.persistenceId = id;
165         this.name          = name;
166         this.colIndex      = columns;
167         this.colTypes      = colTypes;
168         this.colDesc       = descending == null ? new boolean[columns.length]
169                                                 : descending;
170         this.nullsLast     = nullsLast == null ? new boolean[columns.length]
171                                                : nullsLast;
172         this.isPK          = pk;
173         this.isUnique      = unique;
174         this.isConstraint  = constraint;
175         this.isForward     = forward;
176         this.table         = table;
177         this.colCheck      = table.getNewColumnCheckList();
178         this.asArray = new IndexUse[]{ new IndexUse(this, colIndex.length) };
179 
180         ArrayUtil.intIndexesToBooleanArray(colIndex, colCheck);
181 
182         this.defaultColMap = new int[columns.length];
183 
184         ArrayUtil.fillSequence(defaultColMap);
185 
186         boolean simpleOrder = colIndex.length > 0;
187 
188         for (int i = 0; i < colDesc.length; i++) {
189             if (this.colDesc[i] || this.nullsLast[i]) {
190                 simpleOrder = false;
191             }
192         }
193 
194         isSimpleOrder = simpleOrder;
195         isSimple      = isSimpleOrder && colIndex.length == 1;
196         nullData      = new Object[colIndex.length];
197     }
198 
199     // SchemaObject implementation
getType()200     public int getType() {
201         return SchemaObject.INDEX;
202     }
203 
getName()204     public HsqlName getName() {
205         return name;
206     }
207 
getCatalogName()208     public HsqlName getCatalogName() {
209         return name.schema.schema;
210     }
211 
getSchemaName()212     public HsqlName getSchemaName() {
213         return name.schema;
214     }
215 
getOwner()216     public Grantee getOwner() {
217         return name.schema.owner;
218     }
219 
getReferences()220     public OrderedHashSet getReferences() {
221         return new OrderedHashSet();
222     }
223 
getComponents()224     public OrderedHashSet getComponents() {
225         return null;
226     }
227 
compile(Session session, SchemaObject parentObject)228     public void compile(Session session, SchemaObject parentObject) {}
229 
getSQL()230     public String getSQL() {
231 
232         StringBuffer sb = new StringBuffer(128);
233 
234         sb.append(Tokens.T_CREATE).append(' ');
235 
236         if (isUnique()) {
237             sb.append(Tokens.T_UNIQUE).append(' ');
238         }
239 
240         sb.append(Tokens.T_INDEX).append(' ');
241         sb.append(getName().statementName);
242         sb.append(' ').append(Tokens.T_ON).append(' ');
243         sb.append(((Table) table).getName().getSchemaQualifiedStatementName());
244         sb.append(((Table) table).getColumnListSQL(colIndex, colIndex.length));
245 
246         return sb.toString();
247     }
248 
getChangeTimestamp()249     public long getChangeTimestamp() {
250         return 0;
251     }
252 
253     // IndexInterface
asArray()254     public IndexUse[] asArray() {
255         return asArray;
256     }
257 
emptyIterator()258     public RowIterator emptyIterator() {
259         return emptyIterator;
260     }
261 
getPosition()262     public int getPosition() {
263         return position;
264     }
265 
setPosition(int position)266     public void setPosition(int position) {
267         this.position = position;
268     }
269 
getPersistenceId()270     public long getPersistenceId() {
271         return persistenceId;
272     }
273 
274     /**
275      * Returns the count of visible columns used
276      */
getColumnCount()277     public int getColumnCount() {
278         return colIndex.length;
279     }
280 
281     /**
282      * Is this a UNIQUE index?
283      */
isUnique()284     public boolean isUnique() {
285         return isUnique;
286     }
287 
288     /**
289      * Does this index belong to a constraint?
290      */
isConstraint()291     public boolean isConstraint() {
292         return isConstraint;
293     }
294 
295     /**
296      * Returns the array containing column indexes for index
297      */
getColumns()298     public int[] getColumns() {
299         return colIndex;
300     }
301 
302     /**
303      * Returns the array containing column indexes for index
304      */
getColumnTypes()305     public Type[] getColumnTypes() {
306         return colTypes;
307     }
308 
getColumnDesc()309     public boolean[] getColumnDesc() {
310         return colDesc;
311     }
312 
getDefaultColumnMap()313     public int[] getDefaultColumnMap() {
314         return this.defaultColMap;
315     }
316 
317     /**
318      * Returns a value indicating the order of different types of index in
319      * the list of indexes for a table. The position of the groups of Indexes
320      * in the list in ascending order is as follows:
321      *
322      * primary key index
323      * unique constraint indexes
324      * autogenerated foreign key indexes for FK's that reference this table or
325      *  tables created before this table
326      * user created indexes (CREATE INDEX)
327      * autogenerated foreign key indexes for FK's that reference tables created
328      *  after this table
329      *
330      * Among a group of indexes, the order is based on the order of creation
331      * of the index.
332      *
333      * @return ordinal value
334      */
getIndexOrderValue()335     public int getIndexOrderValue() {
336 
337         if (isPK) {
338             return 0;
339         }
340 
341         if (isConstraint) {
342             return isForward ? 4
343                              : isUnique ? 0
344                                         : 1;
345         } else {
346             return 2;
347         }
348     }
349 
isForward()350     public boolean isForward() {
351         return isForward;
352     }
353 
setTable(TableBase table)354     public void setTable(TableBase table) {
355         this.table = table;
356     }
357 
getTable()358     public TableBase getTable() {
359         return table;
360     }
361 
setClustered(boolean clustered)362     public void setClustered(boolean clustered) {
363         isClustered = clustered;
364     }
365 
isClustered()366     public boolean isClustered() {
367         return isClustered;
368     }
369 
370     /**
371      * Returns the node count.
372      */
size(Session session, PersistentStore store)373     public long size(Session session, PersistentStore store) {
374         return store.elementCount(session);
375     }
376 
sizeUnique(PersistentStore store)377     public long sizeUnique(PersistentStore store) {
378         return store.elementCountUnique(this);
379     }
380 
searchCost(Session session, PersistentStore store)381     public double[] searchCost(Session session, PersistentStore store) {
382 
383         boolean  probeDeeper = false;
384         int      counter     = 1;
385         double[] changes     = new double[colIndex.length];
386         int      depth       = 0;
387         int[]    depths      = new int[1];
388 
389         store.readLock();
390 
391         try {
392             NodeAVL node = getAccessor(store);
393             NodeAVL temp = node;
394 
395             if (node == null) {
396                 return changes;
397             }
398 
399             while (true) {
400                 node = temp;
401                 temp = node.getLeft(store);
402 
403                 if (temp == null) {
404                     break;
405                 }
406 
407                 if (depth == Index.probeDepth) {
408                     probeDeeper = true;
409 
410                     break;
411                 }
412 
413                 depth++;
414             }
415 
416             while (true) {
417                 temp  = next(store, node, depth, probeDepth, depths);
418                 depth = depths[0];
419 
420                 if (temp == null) {
421                     break;
422                 }
423 
424                 compareRowForChange(session, node.getData(store),
425                                     temp.getData(store), changes);
426 
427                 node = temp;
428 
429                 counter++;
430             }
431 
432             if (probeDeeper) {
433                 double[] factors = new double[colIndex.length];
434                 int extras = probeFactor(session, store, factors, true)
435                              + probeFactor(session, store, factors, false);
436 
437                 for (int i = 0; i < colIndex.length; i++) {
438                     factors[i] /= 2.0;
439 
440                     for (int j = 0; j < factors[i]; j++) {
441                         changes[i] *= 2;
442                     }
443                 }
444             }
445 
446             long rowCount = store.elementCount();
447 
448             for (int i = 0; i < colIndex.length; i++) {
449                 if (changes[i] == 0) {
450                     changes[i] = 1;
451                 }
452 
453                 changes[i] = rowCount / changes[i];
454 
455                 if (changes[i] < 2) {
456                     changes[i] = 2;
457                 }
458             }
459 
460 /*
461             StringBuffer s = new StringBuffer();
462 
463             s.append("count " + rowCount + " columns " + colIndex.length
464                      + " selectivity " + changes[0]);
465             System.out.println(s);
466 */
467             return changes;
468         } finally {
469             store.readUnlock();
470         }
471     }
472 
probeFactor(Session session, PersistentStore store, double[] changes, boolean left)473     int probeFactor(Session session, PersistentStore store, double[] changes,
474                     boolean left) {
475 
476         int     depth = 0;
477         NodeAVL x     = getAccessor(store);
478         NodeAVL n     = x;
479 
480         if (x == null) {
481             return 0;
482         }
483 
484         while (n != null) {
485             x = n;
486             n = left ? x.getLeft(store)
487                      : x.getRight(store);
488 
489             depth++;
490 
491             if (depth > probeDepth && n != null) {
492                 compareRowForChange(session, x.getData(store),
493                                     n.getData(store), changes);
494             }
495         }
496 
497         return depth - probeDepth;
498     }
499 
getNodeCount(Session session, PersistentStore store)500     public long getNodeCount(Session session, PersistentStore store) {
501 
502         long        count = 0;
503         RowIterator it    = firstRow(session, store, 0, null);
504 
505         while (it.hasNext()) {
506             it.getNextRow();
507 
508             count++;
509         }
510 
511         return count;
512     }
513 
isEmpty(PersistentStore store)514     public boolean isEmpty(PersistentStore store) {
515 
516         store.readLock();
517 
518         try {
519             return getAccessor(store) == null;
520         } finally {
521             store.readUnlock();
522         }
523     }
524 
525     /**
526      * Removes all links between memory nodes
527      */
unlinkNodes(NodeAVL primaryRoot)528     public void unlinkNodes(NodeAVL primaryRoot) {
529 
530         NodeAVL x = primaryRoot;
531         NodeAVL l = x;
532 
533         while (l != null) {
534             x = l;
535             l = x.getLeft(null);
536         }
537 
538         while (x != null) {
539             NodeAVL n = nextUnlink(x);
540 
541             x = n;
542         }
543     }
544 
nextUnlink(NodeAVL x)545     private NodeAVL nextUnlink(NodeAVL x) {
546 
547         NodeAVL temp = x.getRight(null);
548 
549         if (temp != null) {
550             x    = temp;
551             temp = x.getLeft(null);
552 
553             while (temp != null) {
554                 x    = temp;
555                 temp = x.getLeft(null);
556             }
557 
558             return x;
559         }
560 
561         temp = x;
562         x    = x.getParent(null);
563 
564         while (x != null && x.isRight(temp)) {
565             x.nRight = null;
566 
567             temp.getRow(null).destroy();
568             temp.delete();
569 
570             //
571             temp = x;
572             x    = x.getParent(null);
573         }
574 
575         if (x != null) {
576             x.nLeft = null;
577         }
578 
579         temp.getRow(null).destroy();
580         temp.delete();
581 
582         return x;
583     }
584 
checkIndex(PersistentStore store)585     public void checkIndex(PersistentStore store) {
586 
587         store.readLock();
588 
589         try {
590             NodeAVL p = getAccessor(store);
591             NodeAVL f = null;
592 
593             while (p != null) {
594                 f = p;
595 
596                 checkNodes(store, p);
597 
598                 p = p.getLeft(store);
599             }
600 
601             p = f;
602 
603             while (f != null) {
604                 checkNodes(store, f);
605 
606                 f = next(store, f);
607             }
608         } finally {
609             store.readUnlock();
610         }
611     }
612 
checkNodes(PersistentStore store, NodeAVL p)613     void checkNodes(PersistentStore store, NodeAVL p) {
614 
615         NodeAVL l = p.getLeft(store);
616         NodeAVL r = p.getRight(store);
617 
618         if (l != null && l.getBalance(store) == -2) {
619             System.out.print("broken index - deleted");
620         }
621 
622         if (r != null && r.getBalance(store) == -2) {
623             System.out.print("broken index -deleted");
624         }
625 
626         if (l != null && !p.equals(l.getParent(store))) {
627             System.out.print("broken index - no parent");
628         }
629 
630         if (r != null && !p.equals(r.getParent(store))) {
631             System.out.print("broken index - no parent");
632         }
633     }
634 
635     /**
636      * Compares two table rows based on the columns of this index. The rowColMap
637      * parameter specifies which columns of the other table are to be compared
638      * with the colIndex columns of this index. The rowColMap can cover all or
639      * only some columns of this index.
640      *
641      * @param session Session
642      * @param a row from another table
643      * @param rowColMap column indexes in the other table
644      * @param b a full row in this table
645      * @return comparison result, -1,0,+1
646      */
compareRowNonUnique(Session session, Object[] a, Object[] b, int[] rowColMap)647     public int compareRowNonUnique(Session session, Object[] a, Object[] b,
648                                    int[] rowColMap) {
649 
650         int fieldcount = rowColMap.length;
651 
652         for (int j = 0; j < fieldcount; j++) {
653             int i = colTypes[j].compare(session, a[colIndex[j]],
654                                         b[rowColMap[j]]);
655 
656             if (i != 0) {
657                 return i;
658             }
659         }
660 
661         return 0;
662     }
663 
compareRowNonUnique(Session session, Object[] a, Object[] b, int[] rowColMap, int fieldCount)664     public int compareRowNonUnique(Session session, Object[] a, Object[] b,
665                                    int[] rowColMap, int fieldCount) {
666 
667         for (int j = 0; j < fieldCount; j++) {
668             int i = colTypes[j].compare(session, a[colIndex[j]],
669                                         b[rowColMap[j]]);
670 
671             if (i != 0) {
672                 return i;
673             }
674         }
675 
676         return 0;
677     }
678 
679     /**
680      * As above but use the index column data
681      */
compareRowNonUnique(Session session, Object[] a, Object[] b, int fieldCount)682     public int compareRowNonUnique(Session session, Object[] a, Object[] b,
683                                    int fieldCount) {
684 
685         for (int j = 0; j < fieldCount; j++) {
686             int i = colTypes[j].compare(session, a[colIndex[j]],
687                                         b[colIndex[j]]);
688 
689             if (i != 0) {
690                 return i;
691             }
692         }
693 
694         return 0;
695     }
696 
compareRowForChange(Session session, Object[] a, Object[] b, double[] changes)697     public void compareRowForChange(Session session, Object[] a, Object[] b,
698                                     double[] changes) {
699 
700         for (int j = 0; j < colIndex.length; j++) {
701             int i = colTypes[j].compare(session, a[colIndex[j]],
702                                         b[colIndex[j]]);
703 
704             if (i != 0) {
705                 for (; j < colIndex.length; j++) {
706                     changes[j]++;
707                 }
708             }
709         }
710     }
711 
compareRow(Session session, Object[] a, Object[] b)712     public int compareRow(Session session, Object[] a, Object[] b) {
713 
714         for (int j = 0; j < colIndex.length; j++) {
715             int i = colTypes[j].compare(session, a[colIndex[j]],
716                                         b[colIndex[j]]);
717 
718             if (i != 0) {
719                 if (isSimpleOrder) {
720                     return i;
721                 }
722 
723                 boolean nulls = a[colIndex[j]] == null
724                                 || b[colIndex[j]] == null;
725 
726                 if (colDesc[j] && !nulls) {
727                     i = -i;
728                 }
729 
730                 if (nullsLast[j] && nulls) {
731                     i = -i;
732                 }
733 
734                 return i;
735             }
736         }
737 
738         return 0;
739     }
740 
741     /**
742      * Compare two rows of the table for inserting rows into unique indexes
743      * Supports descending columns.
744      *
745      * @param session Session
746      * @param newRow data
747      * @param existingRow data
748      * @param useRowId boolean
749      * @param start int
750      * @return comparison result, -1,0,+1
751      */
compareRowForInsertOrDelete(Session session, Row newRow, Row existingRow, boolean useRowId, int start)752     int compareRowForInsertOrDelete(Session session, Row newRow,
753                                     Row existingRow, boolean useRowId,
754                                     int start) {
755 
756         Object[] a = newRow.getData();
757         Object[] b = existingRow.getData();
758 
759         for (int j = start; j < colIndex.length; j++) {
760             int i = colTypes[j].compare(session, a[colIndex[j]],
761                                         b[colIndex[j]]);
762 
763             if (i != 0) {
764                 if (isSimpleOrder) {
765                     return i;
766                 }
767 
768                 boolean nulls = a[colIndex[j]] == null
769                                 || b[colIndex[j]] == null;
770 
771                 if (colDesc[j] && !nulls) {
772                     i = -i;
773                 }
774 
775                 if (nullsLast[j] && nulls) {
776                     i = -i;
777                 }
778 
779                 return i;
780             }
781         }
782 
783         if (useRowId) {
784             long diff = newRow.getPos() - existingRow.getPos();
785 
786             return diff == 0L ? 0
787                               : diff > 0L ? 1
788                                           : -1;
789         }
790 
791         return 0;
792     }
793 
compareObject(Session session, Object[] a, Object[] b, int[] rowColMap, int position, int opType)794     int compareObject(Session session, Object[] a, Object[] b,
795                       int[] rowColMap, int position, int opType) {
796         return colTypes[position].compare(session, a[colIndex[position]],
797                                           b[rowColMap[position]], opType);
798     }
799 
hasNulls(Session session, Object[] rowData)800     boolean hasNulls(Session session, Object[] rowData) {
801 
802         boolean uniqueNulls = session == null
803                               || session.database.sqlUniqueNulls;
804         boolean compareId = false;
805 
806         for (int j = 0; j < colIndex.length; j++) {
807             if (rowData[colIndex[j]] == null) {
808                 compareId = true;
809 
810                 if (uniqueNulls) {
811                     break;
812                 }
813             } else if (!uniqueNulls) {
814                 compareId = false;
815 
816                 break;
817             }
818         }
819 
820         return compareId;
821     }
822 
823     /**
824      * Insert a node into the index
825      */
insert(Session session, PersistentStore store, Row row)826     public void insert(Session session, PersistentStore store, Row row) {
827 
828         NodeAVL n;
829         NodeAVL x;
830         boolean isleft       = true;
831         int     compare      = -1;
832         boolean compareRowId = !isUnique || hasNulls(session, row.getData());
833 
834         n = getAccessor(store);
835         x = n;
836 
837         if (n == null) {
838             store.setAccessor(this, ((RowAVL) row).getNode(position));
839 
840             return;
841         }
842 
843         while (true) {
844             Row currentRow = n.getRow(store);
845 
846             compare = compareRowForInsertOrDelete(session, row, currentRow,
847                                                   compareRowId, 0);
848 
849             // after the first match and check, all compares are with row id
850             if (compare == 0 && session != null && !compareRowId
851                     && session.database.txManager.isMVRows()) {
852                 if (!isEqualReadable(session, store, n)) {
853                     compareRowId = true;
854                     compare = compareRowForInsertOrDelete(session, row,
855                                                           currentRow,
856                                                           compareRowId,
857                                                           colIndex.length);
858                 }
859             }
860 
861             if (compare == 0) {
862                 Constraint c = null;
863 
864                 if (isConstraint) {
865                     c = ((Table) table).getUniqueConstraintForIndex(this);
866                 }
867 
868                 if (c == null) {
869                     throw Error.error(ErrorCode.X_23505, name.statementName);
870                 } else {
871                     throw c.getException(row.getData());
872                 }
873             }
874 
875             isleft = compare < 0;
876             x      = n;
877             n      = x.child(store, isleft);
878 
879             if (n == null) {
880                 break;
881             }
882         }
883 
884         x = x.set(store, isleft, ((RowAVL) row).getNode(position));
885 
886         balance(store, x, isleft);
887     }
888 
889     public void delete(Session session, PersistentStore store, Row row) {
890 
891         row = (Row) store.get(row, false);
892 
893         NodeAVL x = ((RowAVL) row).getNode(position);
894 
895         if (x == null) {
896             return;
897         }
898 
899         NodeAVL n;
900 
901         if (x.getLeft(store) == null) {
902             n = x.getRight(store);
903         } else if (x.getRight(store) == null) {
904             n = x.getLeft(store);
905         } else {
906             NodeAVL d = x;
907 
908             x = x.getLeft(store);
909 
910             while (true) {
911                 NodeAVL temp = x.getRight(store);
912 
913                 if (temp == null) {
914                     break;
915                 }
916 
917                 x = temp;
918             }
919 
920             // x will be replaced with n later
921             n = x.getLeft(store);
922 
923             // swap d and x
924             int b = x.getBalance(store);
925 
926             x = x.setBalance(store, d.getBalance(store));
927             d = d.setBalance(store, b);
928 
929             // set x.parent
930             NodeAVL xp = x.getParent(store);
931             NodeAVL dp = d.getParent(store);
932 
933             if (d.isRoot(store)) {
934                 store.setAccessor(this, x);
935             }
936 
937             x = x.setParent(store, dp);
938 
939             if (dp != null) {
940                 if (dp.isRight(d)) {
941                     dp = dp.setRight(store, x);
942                 } else {
943                     dp = dp.setLeft(store, x);
944                 }
945             }
946 
947             // relink d.parent, x.left, x.right
948             if (d.equals(xp)) {
949                 d = d.setParent(store, x);
950 
951                 if (d.isLeft(x)) {
952                     x = x.setLeft(store, d);
953 
954                     NodeAVL dr = d.getRight(store);
955 
956                     x = x.setRight(store, dr);
957                 } else {
958                     x = x.setRight(store, d);
959 
960                     NodeAVL dl = d.getLeft(store);
961 
962                     x = x.setLeft(store, dl);
963                 }
964             } else {
965                 d  = d.setParent(store, xp);
966                 xp = xp.setRight(store, d);
967 
968                 NodeAVL dl = d.getLeft(store);
969                 NodeAVL dr = d.getRight(store);
970 
971                 x = x.setLeft(store, dl);
972                 x = x.setRight(store, dr);
973             }
974 
975             x.getRight(store).setParent(store, x);
976             x.getLeft(store).setParent(store, x);
977 
978             // set d.left, d.right
979             d = d.setLeft(store, n);
980 
981             if (n != null) {
982                 n = n.setParent(store, d);
983             }
984 
985             d = d.setRight(store, null);
986             x = d;
987         }
988 
989         boolean isleft = x.isFromLeft(store);
990 
991         x.replace(store, this, n);
992 
993         n = x.getParent(store);
994 
995         x.delete();
996 
997         while (n != null) {
998             x = n;
999 
1000             int sign = isleft ? 1
1001                               : -1;
1002 
1003             switch (x.getBalance(store) * sign) {
1004 
1005                 case -1 :
1006                     x = x.setBalance(store, 0);
1007                     break;
1008 
1009                 case 0 :
1010                     x = x.setBalance(store, sign);
1011 
1012                     return;
1013 
1014                 case 1 :
1015                     NodeAVL r = x.child(store, !isleft);
1016                     int     b = r.getBalance(store);
1017 
1018                     if (b * sign >= 0) {
1019                         x.replace(store, this, r);
1020 
1021                         NodeAVL child = r.child(store, isleft);
1022 
1023                         x = x.set(store, !isleft, child);
1024                         r = r.set(store, isleft, x);
1025 
1026                         if (b == 0) {
1027                             x = x.setBalance(store, sign);
1028                             r = r.setBalance(store, -sign);
1029 
1030                             return;
1031                         }
1032 
1033                         x = x.setBalance(store, 0);
1034                         r = r.setBalance(store, 0);
1035                         x = r;
1036                     } else {
1037                         NodeAVL l = r.child(store, isleft);
1038 
1039                         x.replace(store, this, l);
1040 
1041                         b = l.getBalance(store);
1042                         r = r.set(store, isleft, l.child(store, !isleft));
1043                         l = l.set(store, !isleft, r);
1044                         x = x.set(store, !isleft, l.child(store, isleft));
1045                         l = l.set(store, isleft, x);
1046                         x = x.setBalance(store, (b == sign) ? -sign
1047                                                             : 0);
1048                         r = r.setBalance(store, (b == -sign) ? sign
1049                                                              : 0);
1050                         l = l.setBalance(store, 0);
1051                         x = l;
1052                     }
1053             }
1054 
1055             isleft = x.isFromLeft(store);
1056             n      = x.getParent(store);
1057         }
1058     }
1059 
existsParent(Session session, PersistentStore store, Object[] rowdata, int[] rowColMap)1060     public boolean existsParent(Session session, PersistentStore store,
1061                                 Object[] rowdata, int[] rowColMap) {
1062 
1063         NodeAVL node = findNode(session, store, rowdata, rowColMap,
1064                                 rowColMap.length, OpTypes.EQUAL,
1065                                 TransactionManager.ACTION_REF, false);
1066 
1067         return node != null;
1068     }
1069 
1070     /**
1071      * Return the first node equal to the indexdata object. The rowdata has the
1072      * same column mapping as this index.
1073      *
1074      * @param session session object
1075      * @param store store object
1076      * @param rowdata array containing index column data
1077      * @param matchCount count of columns to match
1078      * @param compareType int
1079      * @param reversed boolean
1080      * @param map boolean[]
1081      * @return iterator
1082      */
findFirstRow(Session session, PersistentStore store, Object[] rowdata, int matchCount, int distinctCount, int compareType, boolean reversed, boolean[] map)1083     public RowIterator findFirstRow(Session session, PersistentStore store,
1084                                     Object[] rowdata, int matchCount,
1085                                     int distinctCount, int compareType,
1086                                     boolean reversed, boolean[] map) {
1087 
1088         NodeAVL node = findNode(session, store, rowdata, defaultColMap,
1089                                 matchCount, compareType,
1090                                 TransactionManager.ACTION_READ, reversed);
1091 
1092         if (node == null) {
1093             return emptyIterator;
1094         }
1095 
1096         return new IndexRowIterator(session, store, this, node, distinctCount,
1097                                     false, reversed);
1098     }
1099 
1100     /**
1101      * Return the first node equal to the rowdata object.
1102      * The rowdata has the same column mapping as this table.
1103      *
1104      * @param session session object
1105      * @param store store object
1106      * @param rowdata array containing table row data
1107      * @return iterator
1108      */
findFirstRow(Session session, PersistentStore store, Object[] rowdata)1109     public RowIterator findFirstRow(Session session, PersistentStore store,
1110                                     Object[] rowdata) {
1111 
1112         NodeAVL node = findNode(session, store, rowdata, colIndex,
1113                                 colIndex.length, OpTypes.EQUAL,
1114                                 TransactionManager.ACTION_READ, false);
1115 
1116         if (node == null) {
1117             return emptyIterator;
1118         }
1119 
1120         return new IndexRowIterator(session, store, this, node, 0, false,
1121                                     false);
1122     }
1123 
1124     /**
1125      * Return the first node equal to the rowdata object. The rowdata has the
1126      * column mapping provided in rowColMap.
1127      *
1128      * @param session session object
1129      * @param store store object
1130      * @param rowdata array containing table row data
1131      * @param rowColMap int[]
1132      * @return iterator
1133      */
findFirstRow(Session session, PersistentStore store, Object[] rowdata, int[] rowColMap)1134     public RowIterator findFirstRow(Session session, PersistentStore store,
1135                                     Object[] rowdata, int[] rowColMap) {
1136 
1137         NodeAVL node = findNode(session, store, rowdata, rowColMap,
1138                                 rowColMap.length, OpTypes.EQUAL,
1139                                 TransactionManager.ACTION_READ, false);
1140 
1141         if (node == null) {
1142             return emptyIterator;
1143         }
1144 
1145         return new IndexRowIterator(session, store, this, node, 0, false,
1146                                     false);
1147     }
1148 
1149     /**
1150      * Finds the first node where the data is not null.
1151      *
1152      * @return iterator
1153      */
findFirstRowNotNull(Session session, PersistentStore store)1154     public RowIterator findFirstRowNotNull(Session session,
1155                                            PersistentStore store) {
1156 
1157         NodeAVL node = findNode(session, store, nullData, this.defaultColMap,
1158                                 1, OpTypes.NOT,
1159                                 TransactionManager.ACTION_READ, false);
1160 
1161         if (node == null) {
1162             return emptyIterator;
1163         }
1164 
1165         return new IndexRowIterator(session, store, this, node, 0, false,
1166                                     false);
1167     }
1168 
1169     /**
1170      * Returns the row for the first node of the index
1171      *
1172      * @return Iterator for first row
1173      */
firstRow(Session session, PersistentStore store, int distinctCount, boolean[] map)1174     public RowIterator firstRow(Session session, PersistentStore store,
1175                                 int distinctCount, boolean[] map) {
1176 
1177         store.readLock();
1178 
1179         try {
1180             NodeAVL x = getAccessor(store);
1181             NodeAVL l = x;
1182 
1183             while (l != null) {
1184                 x = l;
1185                 l = x.getLeft(store);
1186             }
1187 
1188             while (session != null && x != null) {
1189                 Row row = x.getRow(store);
1190 
1191                 if (session.database.txManager.canRead(
1192                         session, store, row, TransactionManager.ACTION_READ,
1193                         null)) {
1194                     break;
1195                 }
1196 
1197                 x = next(store, x);
1198             }
1199 
1200             if (x == null) {
1201                 return emptyIterator;
1202             }
1203 
1204             return new IndexRowIterator(session, store, this, x,
1205                                         distinctCount, false, false);
1206         } finally {
1207             store.readUnlock();
1208         }
1209     }
1210 
firstRow(PersistentStore store)1211     public RowIterator firstRow(PersistentStore store) {
1212 
1213         store.readLock();
1214 
1215         try {
1216             NodeAVL x = getAccessor(store);
1217             NodeAVL l = x;
1218 
1219             while (l != null) {
1220                 x = l;
1221                 l = x.getLeft(store);
1222             }
1223 
1224             if (x == null) {
1225                 return emptyIterator;
1226             }
1227 
1228             return new IndexRowIterator(null, store, this, x, 0, false, false);
1229         } finally {
1230             store.readUnlock();
1231         }
1232     }
1233 
1234     /**
1235      * Returns the row for the last node of the index
1236      *
1237      * @return last row
1238      */
lastRow(Session session, PersistentStore store, int distinctCount, boolean[] map)1239     public RowIterator lastRow(Session session, PersistentStore store,
1240                                int distinctCount, boolean[] map) {
1241 
1242         store.readLock();
1243 
1244         try {
1245             NodeAVL x = getAccessor(store);
1246             NodeAVL l = x;
1247 
1248             while (l != null) {
1249                 x = l;
1250                 l = x.getRight(store);
1251             }
1252 
1253             while (session != null && x != null) {
1254                 Row row = x.getRow(store);
1255 
1256                 if (session.database.txManager.canRead(
1257                         session, store, row, TransactionManager.ACTION_READ,
1258                         null)) {
1259                     break;
1260                 }
1261 
1262                 x = last(store, x);
1263             }
1264 
1265             if (x == null) {
1266                 return emptyIterator;
1267             }
1268 
1269             return new IndexRowIterator(session, store, this, x,
1270                                         distinctCount, false, true);
1271         } finally {
1272             store.readUnlock();
1273         }
1274     }
1275 
1276     /**
1277      * Returns the node after the given one
1278      */
next(Session session, PersistentStore store, NodeAVL x, int distinctCount)1279     NodeAVL next(Session session, PersistentStore store, NodeAVL x,
1280                  int distinctCount) {
1281 
1282         if (x == null) {
1283             return null;
1284         }
1285 
1286         if (distinctCount != 0) {
1287             return findDistinctNode(session, store, x, distinctCount, false);
1288         }
1289 
1290         while (true) {
1291             x = next(store, x);
1292 
1293             if (x == null) {
1294                 return x;
1295             }
1296 
1297             if (session == null) {
1298                 return x;
1299             }
1300 
1301             Row row = x.getRow(store);
1302 
1303             if (session.database.txManager.canRead(
1304                     session, store, row, TransactionManager.ACTION_READ,
1305                     null)) {
1306                 return x;
1307             }
1308         }
1309     }
1310 
last(Session session, PersistentStore store, NodeAVL x, int distinctCount)1311     NodeAVL last(Session session, PersistentStore store, NodeAVL x,
1312                  int distinctCount) {
1313 
1314         if (x == null) {
1315             return null;
1316         }
1317 
1318         if (distinctCount != 0) {
1319             return findDistinctNode(session, store, x, distinctCount, true);
1320         }
1321 
1322         while (true) {
1323             x = last(store, x);
1324 
1325             if (x == null) {
1326                 return x;
1327             }
1328 
1329             if (session == null) {
1330                 return x;
1331             }
1332 
1333             Row row = x.getRow(store);
1334 
1335             if (session.database.txManager.canRead(
1336                     session, store, row, TransactionManager.ACTION_READ,
1337                     null)) {
1338                 return x;
1339             }
1340         }
1341     }
1342 
next(PersistentStore store, NodeAVL x)1343     NodeAVL next(PersistentStore store, NodeAVL x) {
1344 
1345         if (x == null) {
1346             return null;
1347         }
1348 
1349         RowAVL row = x.getRow(store);
1350 
1351         x = row.getNode(position);
1352 
1353         NodeAVL temp = x.getRight(store);
1354 
1355         if (temp != null) {
1356             x    = temp;
1357             temp = x.getLeft(store);
1358 
1359             while (temp != null) {
1360                 x    = temp;
1361                 temp = x.getLeft(store);
1362             }
1363 
1364             return x;
1365         }
1366 
1367         temp = x;
1368         x    = x.getParent(store);
1369 
1370         while (x != null && x.isRight(temp)) {
1371             temp = x;
1372             x    = x.getParent(store);
1373         }
1374 
1375         return x;
1376     }
1377 
next(PersistentStore store, NodeAVL x, int depth, int maxDepth, int[] depths)1378     NodeAVL next(PersistentStore store, NodeAVL x, int depth, int maxDepth,
1379                  int[] depths) {
1380 
1381         NodeAVL temp = depth == maxDepth ? null
1382                                          : x.getRight(store);
1383 
1384         if (temp != null) {
1385             depth++;
1386 
1387             x    = temp;
1388             temp = depth == maxDepth ? null
1389                                      : x.getLeft(store);
1390 
1391             while (temp != null) {
1392                 depth++;
1393 
1394                 x = temp;
1395 
1396                 if (depth == maxDepth) {
1397                     temp = null;
1398                 } else {
1399                     temp = x.getLeft(store);
1400                 }
1401             }
1402 
1403             depths[0] = depth;
1404 
1405             return x;
1406         }
1407 
1408         temp = x;
1409         x    = x.getParent(store);
1410 
1411         depth--;
1412 
1413         while (x != null && x.isRight(temp)) {
1414             temp = x;
1415             x    = x.getParent(store);
1416 
1417             depth--;
1418         }
1419 
1420         depths[0] = depth;
1421 
1422         return x;
1423     }
1424 
last(PersistentStore store, NodeAVL x)1425     NodeAVL last(PersistentStore store, NodeAVL x) {
1426 
1427         if (x == null) {
1428             return null;
1429         }
1430 
1431         RowAVL row = x.getRow(store);
1432 
1433         x = row.getNode(position);
1434 
1435         NodeAVL temp = x.getLeft(store);
1436 
1437         if (temp != null) {
1438             x    = temp;
1439             temp = x.getRight(store);
1440 
1441             while (temp != null) {
1442                 x    = temp;
1443                 temp = x.getRight(store);
1444             }
1445 
1446             return x;
1447         }
1448 
1449         temp = x;
1450         x    = x.getParent(store);
1451 
1452         while (x != null && x.isLeft(temp)) {
1453             temp = x;
1454             x    = x.getParent(store);
1455         }
1456 
1457         return x;
1458     }
1459 
isEqualReadable(Session session, PersistentStore store, NodeAVL node)1460     boolean isEqualReadable(Session session, PersistentStore store,
1461                             NodeAVL node) {
1462 
1463         NodeAVL  c = node;
1464         Object[] data;
1465         Object[] nodeData;
1466         Row      row;
1467 
1468         row = node.getRow(store);
1469 
1470         session.database.txManager.setTransactionInfo(store, row);
1471 
1472         if (session.database.txManager.canRead(session, store, row,
1473                                                TransactionManager.ACTION_DUP,
1474                                                null)) {
1475             return true;
1476         }
1477 
1478         data = node.getData(store);
1479 
1480         while (true) {
1481             c = last(store, c);
1482 
1483             if (c == null) {
1484                 break;
1485             }
1486 
1487             nodeData = c.getData(store);
1488 
1489             if (compareRow(session, data, nodeData) == 0) {
1490                 row = c.getRow(store);
1491 
1492                 session.database.txManager.setTransactionInfo(store, row);
1493 
1494                 if (session.database.txManager.canRead(
1495                         session, store, row, TransactionManager.ACTION_DUP,
1496                         null)) {
1497                     return true;
1498                 }
1499 
1500                 continue;
1501             }
1502 
1503             break;
1504         }
1505 
1506         while (true) {
1507             c = next(session, store, node, 0);
1508 
1509             if (c == null) {
1510                 break;
1511             }
1512 
1513             nodeData = c.getData(store);
1514 
1515             if (compareRow(session, data, nodeData) == 0) {
1516                 row = c.getRow(store);
1517 
1518                 session.database.txManager.setTransactionInfo(store, row);
1519 
1520                 if (session.database.txManager.canRead(
1521                         session, store, row, TransactionManager.ACTION_DUP,
1522                         null)) {
1523                     return true;
1524                 }
1525 
1526                 continue;
1527             }
1528 
1529             break;
1530         }
1531 
1532         return false;
1533     }
1534 
1535     /**
1536      * Finds a match with a row from a different table
1537      *
1538      * @param session Session
1539      * @param store PersistentStore
1540      * @param rowdata array containing data for the index columns
1541      * @param rowColMap map of the data to columns
1542      * @param fieldCount int
1543      * @param compareType int
1544      * @param readMode int
1545      * @param reversed
1546      * @return matching node or null
1547      */
findNode(Session session, PersistentStore store, Object[] rowdata, int[] rowColMap, int fieldCount, int compareType, int readMode, boolean reversed)1548     NodeAVL findNode(Session session, PersistentStore store, Object[] rowdata,
1549                      int[] rowColMap, int fieldCount, int compareType,
1550                      int readMode, boolean reversed) {
1551 
1552         store.readLock();
1553 
1554         try {
1555             NodeAVL x          = getAccessor(store);
1556             NodeAVL n          = null;
1557             NodeAVL result     = null;
1558             Row     currentRow = null;
1559 
1560             if (compareType != OpTypes.EQUAL
1561                     && compareType != OpTypes.IS_NULL) {
1562                 fieldCount--;
1563 
1564                 if (compareType == OpTypes.SMALLER
1565                         || compareType == OpTypes.SMALLER_EQUAL
1566                         || compareType == OpTypes.MAX) {
1567                     reversed = true;
1568                 }
1569             }
1570 
1571             while (x != null) {
1572                 currentRow = x.getRow(store);
1573 
1574                 int i = 0;
1575 
1576                 if (fieldCount > 0) {
1577                     i = compareRowNonUnique(session, currentRow.getData(),
1578                                             rowdata, rowColMap, fieldCount);
1579                 }
1580 
1581                 if (i == 0) {
1582                     switch (compareType) {
1583 
1584                         case OpTypes.MAX :
1585                         case OpTypes.IS_NULL :
1586                         case OpTypes.EQUAL : {
1587                             result = x;
1588 
1589                             if (reversed) {
1590                                 n = x.getRight(store);
1591                             } else {
1592                                 n = x.getLeft(store);
1593                             }
1594 
1595                             break;
1596                         }
1597                         case OpTypes.NOT :
1598                         case OpTypes.GREATER : {
1599                             i = compareObject(session, currentRow.getData(),
1600                                               rowdata, rowColMap, fieldCount,
1601                                               compareType);
1602 
1603                             if (i <= 0) {
1604                                 n = x.getRight(store);
1605                             } else {
1606                                 result = x;
1607                                 n      = x.getLeft(store);
1608                             }
1609 
1610                             break;
1611                         }
1612                         case OpTypes.GREATER_EQUAL_PRE :
1613                         case OpTypes.GREATER_EQUAL : {
1614                             i = compareObject(session, currentRow.getData(),
1615                                               rowdata, rowColMap, fieldCount,
1616                                               compareType);
1617 
1618                             if (i < 0) {
1619                                 n = x.getRight(store);
1620                             } else {
1621                                 result = x;
1622                                 n      = x.getLeft(store);
1623                             }
1624 
1625                             break;
1626                         }
1627                         case OpTypes.SMALLER : {
1628                             i = compareObject(session, currentRow.getData(),
1629                                               rowdata, rowColMap, fieldCount,
1630                                               compareType);
1631 
1632                             if (i < 0) {
1633                                 result = x;
1634                                 n      = x.getRight(store);
1635                             } else {
1636                                 n = x.getLeft(store);
1637                             }
1638 
1639                             break;
1640                         }
1641                         case OpTypes.SMALLER_EQUAL : {
1642                             i = compareObject(session, currentRow.getData(),
1643                                               rowdata, rowColMap, fieldCount,
1644                                               compareType);
1645 
1646                             if (i <= 0) {
1647                                 result = x;
1648                                 n      = x.getRight(store);
1649                             } else {
1650                                 n = x.getLeft(store);
1651                             }
1652 
1653                             break;
1654                         }
1655                         default :
1656                             throw Error.runtimeError(ErrorCode.U_S0500,
1657                                                      "Index");
1658                     }
1659                 } else if (i < 0) {
1660                     n = x.getRight(store);
1661                 } else if (i > 0) {
1662                     n = x.getLeft(store);
1663                 }
1664 
1665                 if (n == null) {
1666                     break;
1667                 }
1668 
1669                 x = n;
1670             }
1671 
1672             // MVCC 190
1673             if (session == null) {
1674                 return result;
1675             }
1676 
1677             while (result != null) {
1678                 currentRow = result.getRow(store);
1679 
1680                 if (session.database.txManager.canRead(session, store,
1681                                                        currentRow, readMode,
1682                                                        colIndex)) {
1683                     break;
1684                 }
1685 
1686                 result = reversed ? last(store, result)
1687                                   : next(store, result);
1688 
1689                 if (result == null) {
1690                     break;
1691                 }
1692 
1693                 currentRow = result.getRow(store);
1694 
1695                 if (fieldCount > 0
1696                         && compareRowNonUnique(
1697                             session, currentRow.getData(), rowdata, rowColMap,
1698                             fieldCount) != 0) {
1699                     result = null;
1700 
1701                     break;
1702                 }
1703             }
1704 
1705             return result;
1706         } finally {
1707             store.readUnlock();
1708         }
1709     }
1710 
findDistinctNode(Session session, PersistentStore store, NodeAVL node, int fieldCount, boolean reversed)1711     NodeAVL findDistinctNode(Session session, PersistentStore store,
1712                              NodeAVL node, int fieldCount, boolean reversed) {
1713 
1714         store.readLock();
1715 
1716         try {
1717             NodeAVL  x          = getAccessor(store);
1718             NodeAVL  n          = null;
1719             NodeAVL  result     = null;
1720             Row      currentRow = null;
1721             Object[] rowData    = node.getData(store);
1722 
1723             while (x != null) {
1724                 currentRow = x.getRow(store);
1725 
1726                 int i = 0;
1727 
1728                 i = compareRowNonUnique(session, currentRow.getData(),
1729                                         rowData, colIndex, fieldCount);
1730 
1731                 if (reversed) {
1732                     if (i < 0) {
1733                         result = x;
1734                         n      = x.getRight(store);
1735                     } else {
1736                         n = x.getLeft(store);
1737                     }
1738                 } else {
1739                     if (i <= 0) {
1740                         n = x.getRight(store);
1741                     } else {
1742                         result = x;
1743                         n      = x.getLeft(store);
1744                     }
1745                 }
1746 
1747                 if (n == null) {
1748                     break;
1749                 }
1750 
1751                 x = n;
1752             }
1753 
1754             // MVCC 190
1755             if (session == null) {
1756                 return result;
1757             }
1758 
1759             while (result != null) {
1760                 currentRow = result.getRow(store);
1761 
1762                 if (session.database.txManager.canRead(
1763                         session, store, currentRow,
1764                         TransactionManager.ACTION_READ, colIndex)) {
1765                     break;
1766                 }
1767 
1768                 result = reversed ? last(store, result)
1769                                   : next(store, result);
1770             }
1771 
1772             return result;
1773         } finally {
1774             store.readUnlock();
1775         }
1776     }
1777 
1778     /**
1779      * Balances part of the tree after an alteration to the index.
1780      */
balance(PersistentStore store, NodeAVL x, boolean isleft)1781     void balance(PersistentStore store, NodeAVL x, boolean isleft) {
1782 
1783         while (true) {
1784             int sign = isleft ? 1
1785                               : -1;
1786 
1787             switch (x.getBalance(store) * sign) {
1788 
1789                 case 1 :
1790                     x = x.setBalance(store, 0);
1791 
1792                     return;
1793 
1794                 case 0 :
1795                     x = x.setBalance(store, -sign);
1796                     break;
1797 
1798                 case -1 :
1799                     NodeAVL l = x.child(store, isleft);
1800 
1801                     if (l.getBalance(store) == -sign) {
1802                         x.replace(store, this, l);
1803 
1804                         x = x.set(store, isleft, l.child(store, !isleft));
1805                         l = l.set(store, !isleft, x);
1806                         x = x.setBalance(store, 0);
1807                         l = l.setBalance(store, 0);
1808                     } else {
1809                         NodeAVL r = l.child(store, !isleft);
1810 
1811                         x.replace(store, this, r);
1812 
1813                         l = l.set(store, !isleft, r.child(store, isleft));
1814                         r = r.set(store, isleft, l);
1815                         x = x.set(store, isleft, r.child(store, !isleft));
1816                         r = r.set(store, !isleft, x);
1817 
1818                         int rb = r.getBalance(store);
1819 
1820                         x = x.setBalance(store, (rb == -sign) ? sign
1821                                                               : 0);
1822                         l = l.setBalance(store, (rb == sign) ? -sign
1823                                                              : 0);
1824                         r = r.setBalance(store, 0);
1825                     }
1826 
1827                     return;
1828             }
1829 
1830             if (x.isRoot(store)) {
1831                 return;
1832             }
1833 
1834             isleft = x.isFromLeft(store);
1835             x      = x.getParent(store);
1836         }
1837     }
1838 
getAccessor(PersistentStore store)1839     NodeAVL getAccessor(PersistentStore store) {
1840 
1841         NodeAVL node = (NodeAVL) store.getAccessor(this);
1842 
1843         return node;
1844     }
1845 
getIterator(Session session, PersistentStore store, NodeAVL x, boolean single, boolean reversed)1846     IndexRowIterator getIterator(Session session, PersistentStore store,
1847                                  NodeAVL x, boolean single, boolean reversed) {
1848 
1849         if (x == null) {
1850             return emptyIterator;
1851         } else {
1852             IndexRowIterator it = new IndexRowIterator(session, store, this,
1853                 x, 0, single, reversed);
1854 
1855             return it;
1856         }
1857     }
1858 
1859     public static final class IndexRowIterator implements RowIterator {
1860 
1861         final Session         session;
1862         final PersistentStore store;
1863         final IndexAVL        index;
1864         NodeAVL               nextnode;
1865         Row                   lastrow;
1866         int                   distinctCount;
1867         boolean               single;
1868         boolean               reversed;
1869 
1870         /**
1871          * When session == null, rows from all sessions are returned
1872          */
IndexRowIterator(Session session, PersistentStore store, IndexAVL index, NodeAVL node, int distinctCount, boolean single, boolean reversed)1873         public IndexRowIterator(Session session, PersistentStore store,
1874                                 IndexAVL index, NodeAVL node,
1875                                 int distinctCount, boolean single,
1876                                 boolean reversed) {
1877 
1878             this.session       = session;
1879             this.store         = store;
1880             this.index         = index;
1881             this.distinctCount = distinctCount;
1882             this.single        = single;
1883             this.reversed      = reversed;
1884 
1885             if (index == null) {
1886                 return;
1887             }
1888 
1889             nextnode = node;
1890         }
1891 
hasNext()1892         public boolean hasNext() {
1893             return nextnode != null;
1894         }
1895 
getNextRow()1896         public Row getNextRow() {
1897 
1898             if (nextnode == null) {
1899                 release();
1900 
1901                 return null;
1902             }
1903 
1904             NodeAVL lastnode = nextnode;
1905 
1906             if (single) {
1907                 nextnode = null;
1908             } else {
1909                 store.readLock();
1910 
1911                 try {
1912                     while (true) {
1913                         if (reversed) {
1914                             nextnode = index.last(session, store, nextnode,
1915                                                   distinctCount);
1916                         } else {
1917                             nextnode = index.next(session, store, nextnode,
1918                                                   distinctCount);
1919                         }
1920 
1921                         if (nextnode == null) {
1922                             break;
1923                         }
1924 
1925                         Row row = nextnode.getRow(store);
1926 
1927                         if (session == null
1928                                 || store.canRead(
1929                                     session, row,
1930                                     TransactionManager.ACTION_READ, null)) {
1931                             break;
1932                         }
1933                     }
1934                 } finally {
1935                     store.readUnlock();
1936                 }
1937             }
1938 
1939             lastrow = lastnode.getRow(store);
1940 
1941             return lastrow;
1942         }
1943 
getNext()1944         public Object[] getNext() {
1945 
1946             Row row = getNextRow();
1947 
1948             return row == null ? null
1949                                : row.getData();
1950         }
1951 
removeCurrent()1952         public void removeCurrent() {
1953             store.delete(session, lastrow);
1954             store.remove(lastrow);
1955         }
1956 
release()1957         public void release() {}
1958 
getRowId()1959         public long getRowId() {
1960             return nextnode.getPos();
1961         }
1962 
getCurrentTable()1963         public TableBase getCurrentTable() {
1964             return index.table;
1965         }
1966     }
1967 }
1968