Package org.hsqldb.index

Source Code of org.hsqldb.index.IndexAVL$IndexRowIterator

/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Hypersonic SQL Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Hypersonic SQL Group.
*
*
* For work added by the HSQL Development Group:
*
* Copyright (c) 2001-2009, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


package org.hsqldb.index;

import java.util.NoSuchElementException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.hsqldb.Error;
import org.hsqldb.ErrorCode;
import org.hsqldb.HsqlNameManager;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.OpTypes;
import org.hsqldb.Row;
import org.hsqldb.RowAVL;
import org.hsqldb.SchemaObject;
import org.hsqldb.Session;
import org.hsqldb.Table;
import org.hsqldb.TableBase;
import org.hsqldb.Tokens;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.navigator.RowIterator;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.rights.Grantee;
import org.hsqldb.types.Type;

// fredt@users 20020221 - patch 513005 by sqlbob@users - corrections
// fredt@users 20020225 - patch 1.7.0 - changes to support cascading deletes
// tony_lai@users 20020820 - patch 595052 - better error message
// fredt@users 20021205 - patch 1.7.2 - changes to method signature
// fredt@users - patch 1.8.0 - reworked the interface and comparison methods
// fredt@users - patch 1.8.0 - improved reliability for cached indexes
// fredt@users - patch 1.9.0 - iterators and concurrency

/**
* Implementation of an AVL tree with parent pointers in nodes. Subclasses
* of Node implement the tree node objects for memory or disk storage. An
* Index has a root Node that is linked with other nodes using Java Object
* references or file pointers, depending on Node implementation.<p>
* An Index object also holds information on table columns (in the form of int
* indexes) that are covered by it.<p>
*
*  New class derived from Hypersonic SQL code and enhanced in HSQLDB. <p>
*
* @author Thomas Mueller (Hypersonic SQL Group)
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since Hypersonic SQL
*/
public class IndexAVL implements Index {

    // fields
    private final long      persistenceId;
    private final HsqlName  name;
    private final boolean[] colCheck;
    private final int[]     colIndex;
    private final int[]     defaultColMap;
    private final Type[]    colTypes;
    private final boolean[] colDesc;
    private final boolean[] nullsLast;
    private final int[]     pkCols;
    private final Type[]    pkTypes;
    private final boolean   isUnique;    // DDL uniqueness
    private final boolean   useRowId;
    private final boolean   isConstraint;
    private final boolean   isForward;
    private int             depth;
    private static final IndexRowIterator emptyIterator =
        new IndexRowIterator(null, (PersistentStore) null, null, null);
    private final TableBase table;
    private int             position;

    //
    ReadWriteLock lock      = new ReentrantReadWriteLock();
    Lock          readLock  = lock.readLock();
    Lock          writeLock = lock.writeLock();

    //
    public static final Index[] emptyArray = new Index[]{};

    /**
     * Set a node as child of another
     *
     * @param x parent node
     * @param isleft boolean
     * @param n child node
     *
     */
    private static NodeAVL set(PersistentStore store, NodeAVL x,
                               boolean isleft, NodeAVL n) {

        if (isleft) {
            x = x.setLeft(store, n);
        } else {
            x = x.setRight(store, n);
        }

        if (n != null) {
            n.setParent(store, x);
        }

        return x;
    }

    /**
     * Returns either child node
     *
     * @param x node
     * @param isleft boolean
     *
     * @return child node
     *
     */
    private static NodeAVL child(PersistentStore store, NodeAVL x,
                                 boolean isleft) {
        return isleft ? x.getLeft(store)
                      : x.getRight(store);
    }

    private static void getColumnList(Table t, int[] col, int len,
                                      StringBuffer a) {

        a.append('(');

        for (int i = 0; i < len; i++) {
            a.append(t.getColumn(col[i]).getName().statementName);

            if (i < len - 1) {
                a.append(',');
            }
        }

        a.append(')');
    }

    /**
     * compares two full table rows based on a set of columns
     *
     * @param a a full row
     * @param b a full row
     * @param cols array of column indexes to compare
     * @param coltypes array of column types for the full row
     *
     * @return comparison result, -1,0,+1
     */
    public static int compareRows(Object[] a, Object[] b, int[] cols,
                                  Type[] coltypes) {

        int fieldcount = cols.length;

        for (int j = 0; j < fieldcount; j++) {
            int i = coltypes[cols[j]].compare(a[cols[j]], b[cols[j]]);

            if (i != 0) {
                return i;
            }
        }

        return 0;
    }

    /**
     * Constructor declaration
     *
     * @param name HsqlName of the index
     * @param id persistnece id
     * @param table table of the index
     * @param columns array of column indexes
     * @param descending boolean[]
     * @param nullsLast boolean[]
     * @param colTypes array of column types
     * @param unique is this a unique index
     * @param constraint does this index belonging to a constraint
     * @param forward is this an auto-index for an FK that refers to a table
     *   defined after this table
     */
    public IndexAVL(HsqlName name, long id, TableBase table, int[] columns,
                    boolean[] descending, boolean[] nullsLast,
                    Type[] colTypes, boolean pk, boolean unique,
                    boolean constraint, boolean forward) {

        persistenceId  = id;
        this.name      = name;
        this.colIndex  = columns;
        this.colTypes  = colTypes;
        this.colDesc   = descending == null ? new boolean[columns.length]
                                            : descending;
        this.nullsLast = nullsLast == null ? new boolean[columns.length]
                                           : nullsLast;
        isUnique       = unique;
        isConstraint   = constraint;
        isForward      = forward;
        this.table     = table;
        this.pkCols    = table.getPrimaryKey();
        this.pkTypes   = table.getPrimaryKeyTypes();
        useRowId = (!isUnique && pkCols.length == 0) || (colIndex.length == 0);
        colCheck       = table.getNewColumnCheckList();

        ArrayUtil.intIndexesToBooleanArray(colIndex, colCheck);

        defaultColMap = new int[columns.length];

        ArrayUtil.fillSequence(defaultColMap);
    }

    // SchemaObject implementation
    public int getType() {
        return SchemaObject.INDEX;
    }

    public HsqlName getName() {
        return name;
    }

    public HsqlName getCatalogName() {
        return name.schema.schema;
    }

    public HsqlName getSchemaName() {
        return name.schema;
    }

    public Grantee getOwner() {
        return name.schema.owner;
    }

    public OrderedHashSet getReferences() {
        return new OrderedHashSet();
    }

    public OrderedHashSet getComponents() {
        return null;
    }

    public void compile(Session session) {}

    public String getSQL() {

        StringBuffer sb = new StringBuffer();

        sb = new StringBuffer(64);

        sb.append(Tokens.T_CREATE).append(' ');

        if (isUnique()) {
            sb.append(Tokens.T_UNIQUE).append(' ');
        }

        sb.append(Tokens.T_INDEX).append(' ');
        sb.append(getName().statementName);
        sb.append(' ').append(Tokens.T_ON).append(' ');
        sb.append(((Table) table).getName().getSchemaQualifiedStatementName());

        int[] col = getColumns();
        int   len = getVisibleColumns();

        getColumnList(((Table) table), col, len, sb);

        return sb.toString();
    }

    // IndexInterface
    public RowIterator emptyIterator() {
        return emptyIterator;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    public long getPersistenceId() {
        return persistenceId;
    }

    /**
     * Returns the count of visible columns used
     */
    public int getVisibleColumns() {
        return colIndex.length;
    }

    /**
     * Returns the count of visible columns used
     */
    public int getColumnCount() {
        return colIndex.length;
    }

    /**
     * Is this a UNIQUE index?
     */
    public boolean isUnique() {
        return isUnique;
    }

    /**
     * Does this index belong to a constraint?
     */
    public boolean isConstraint() {
        return isConstraint;
    }

    /**
     * Returns the array containing column indexes for index
     */
    public int[] getColumns() {
        return colIndex;
    }

    /**
     * Returns the array containing column indexes for index
     */
    public Type[] getColumnTypes() {
        return colTypes;
    }

    public boolean[] getColumnDesc() {
        return colDesc;
    }

    /**
     * Returns a value indicating the order of different types of index in
     * the list of indexes for a table. The position of the groups of Indexes
     * in the list in ascending order is as follows:
     *
     * primary key index
     * unique constraint indexes
     * autogenerated foreign key indexes for FK's that reference this table or
     *  tables created before this table
     * user created indexes (CREATE INDEX)
     * autogenerated foreign key indexes for FK's that reference tables created
     *  after this table
     *
     * Among a group of indexes, the order is based on the order of creation
     * of the index.
     *
     * @return ordinal value
     */
    public int getIndexOrderValue() {

        if (isConstraint) {
            return isForward ? 4
                             : isUnique ? 0
                                        : 1;
        } else {
            return 2;
        }
    }

    public boolean isForward() {
        return isForward;
    }

    /**
     * Returns the node count.
     */
    public int size(PersistentStore store) {

        int count = 0;

        readLock.lock();

        try {
            RowIterator it = firstRow(null, store);

            while (it.hasNext()) {
                it.getNextRow();

                count++;
            }

            return count;
        } finally {
            readLock.unlock();
        }
    }

    public int sizeEstimate(PersistentStore store) {
        return (int) (1L << depth);
    }

    public boolean isEmpty(PersistentStore store) {

        readLock.lock();

        try {
            return getAccessor(store) == null;
        } finally {
            readLock.unlock();
        }
    }

    public void checkIndex(PersistentStore store) {

        readLock.lock();

        try {
            NodeAVL p = getAccessor(store);
            NodeAVL f = null;

            while (p != null) {
                f = p;

                checkNodes(store, p);

                p = p.getLeft(store);
            }

            p = f;

            while (f != null) {
                checkNodes(store, f);

                f = next(store, f);
            }
        } finally {
            readLock.unlock();
        }
    }

    private void checkNodes(PersistentStore store, NodeAVL p) {

        NodeAVL l = p.getLeft(store);
        NodeAVL r = p.getRight(store);

        if (l != null && l.getBalance() == -2) {
            System.out.print("broken index - deleted");
        }

        if (r != null && r.getBalance() == -2) {
            System.out.print("broken index -deleted");
        }

        if (l != null && !p.equals(l.getParent(store))) {
            System.out.print("broken index - no parent");
        }

        if (r != null && !p.equals(r.getParent(store))) {
            System.out.print("broken index - no parent");
        }
    }

    /**
     * Insert a node into the index
     */
    public void insert(Session session, PersistentStore store, Row row) {

        NodeAVL n;
        NodeAVL x;
        boolean isleft  = true;
        int     compare = -1;

        writeLock.lock();

        try {
            n = getAccessor(store);
            x = n;

            if (n == null) {
                store.setAccessor(this, ((RowAVL) row).getNode(position));

                return;
            }

            while (true) {
                Row currentRow = n.getRow(store);

                compare = compareRowForInsertOrDelete(session, row,
                                                      currentRow);

                if (compare == 0) {
                    throw Error.error(ErrorCode.X_23505);
                }

                isleft = compare < 0;
                x      = n;
                n      = child(store, x, isleft);

                if (n == null) {
                    break;
                }
            }

            x = set(store, x, isleft, ((RowAVL) row).getNode(position));

            balance(store, x, isleft);
        } finally {
            writeLock.unlock();
        }
    }

    public void delete(PersistentStore store, Row row) {

        if (!row.isInMemory()) {
            row = (Row) store.get(row, false);
        }

        NodeAVL node = ((RowAVL) row).getNode(position);

        delete(store, node);
    }

    public void delete(PersistentStore store, NodeAVL x) {

        if (x == null) {
            return;
        }

        NodeAVL n;

        writeLock.lock();

        try {
            if (x.getLeft(store) == null) {
                n = x.getRight(store);
            } else if (x.getRight(store) == null) {
                n = x.getLeft(store);
            } else {
                NodeAVL d = x;

                x = x.getLeft(store);

                while (true) {
                    NodeAVL temp = x.getRight(store);

                    if (temp == null) {
                        break;
                    }

                    x = temp;
                }

                // x will be replaced with n later
                n = x.getLeft(store);

                // swap d and x
                int b = x.getBalance();

                x = x.setBalance(store, d.getBalance());
                d = d.setBalance(store, b);

                // set x.parent
                NodeAVL xp = x.getParent(store);
                NodeAVL dp = d.getParent(store);

                if (d.isRoot()) {
                    store.setAccessor(this, x);
                }

                x = x.setParent(store, dp);

                if (dp != null) {
                    if (dp.isRight(d)) {
                        dp = dp.setRight(store, x);
                    } else {
                        dp = dp.setLeft(store, x);
                    }
                }

                // relink d.parent, x.left, x.right
                if (d.equals(xp)) {
                    d = d.setParent(store, x);

                    if (d.isLeft(x)) {
                        x = x.setLeft(store, d);

                        NodeAVL dr = d.getRight(store);

                        x = x.setRight(store, dr);
                    } else {
                        x = x.setRight(store, d);

                        NodeAVL dl = d.getLeft(store);

                        x = x.setLeft(store, dl);
                    }
                } else {
                    d  = d.setParent(store, xp);
                    xp = xp.setRight(store, d);

                    NodeAVL dl = d.getLeft(store);
                    NodeAVL dr = d.getRight(store);

                    x = x.setLeft(store, dl);
                    x = x.setRight(store, dr);
                }

                // apprently no-ops
                x.getRight(store).setParent(store, x);
                x.getLeft(store).setParent(store, x);

                // set d.left, d.right
                d = d.setLeft(store, n);

                if (n != null) {
                    n = n.setParent(store, d);
                }

                d = d.setRight(store, null);
                x = d;
            }

            boolean isleft = x.isFromLeft(store);

            replace(store, x, n);

            n = x.getParent(store);

            x.delete();

            while (n != null) {
                x = n;

                int sign = isleft ? 1
                                  : -1;

                switch (x.getBalance() * sign) {

                    case -1 :
                        x = x.setBalance(store, 0);
                        break;

                    case 0 :
                        x = x.setBalance(store, sign);

                        return;

                    case 1 :
                        NodeAVL r = child(store, x, !isleft);
                        int     b = r.getBalance();

                        if (b * sign >= 0) {
                            replace(store, x, r);

                            x = set(store, x, !isleft,
                                    child(store, r, isleft));
                            r = set(store, r, isleft, x);

                            if (b == 0) {
                                x = x.setBalance(store, sign);
                                r = r.setBalance(store, -sign);

                                return;
                            }

                            x = x.setBalance(store, 0);
                            r = r.setBalance(store, 0);
                            x = r;
                        } else {
                            NodeAVL l = child(store, r, isleft);

                            replace(store, x, l);

                            b = l.getBalance();
                            r = set(store, r, isleft,
                                    child(store, l, !isleft));
                            l = set(store, l, !isleft, r);
                            x = set(store, x, !isleft,
                                    child(store, l, isleft));
                            l = set(store, l, isleft, x);
                            x = x.setBalance(store, (b == sign) ? -sign
                                                                : 0);
                            r = r.setBalance(store, (b == -sign) ? sign
                                                                 : 0);
                            l = l.setBalance(store, 0);
                            x = l;
                        }
                }

                isleft = x.isFromLeft(store);
                n      = x.getParent(store);
            }
        } finally {
            writeLock.unlock();
        }
    }

    public boolean exists(Session session, PersistentStore store,
                          Object[] rowdata, int[] rowColMap) {
        return findNode(session, store, rowdata, rowColMap, rowColMap.length)
               != null;
    }

    /**
     * Return the first node equal to the indexdata object. The rowdata has
     * the same column mapping as this index.
     *
     * @param session session object
     * @param store store object
     * @param rowdata array containing index column data
     * @param match count of columns to match
     * @return iterator
     */
    public RowIterator findFirstRow(Session session, PersistentStore store,
                                    Object[] rowdata, int match) {

        NodeAVL node = findNode(session, store, rowdata, defaultColMap, match);

        return getIterator(session, store, node);
    }

    /**
     * Return the first node equal to the rowdata object.
     * The rowdata has the same column mapping as this table.
     *
     * @param session session object
     * @param store store object
     * @param rowdata array containing table row data
     * @return iterator
     */
    public RowIterator findFirstRow(Session session, PersistentStore store,
                                    Object[] rowdata) {

        NodeAVL node = findNode(session, store, rowdata, colIndex,
                                colIndex.length);

        return getIterator(session, store, node);
    }

    /**
     * Return the first node equal to the rowdata object.
     * The rowdata has the column mapping privided in rowColMap.
     *
     * @param session session object
     * @param store store object
     * @param rowdata array containing table row data
     * @return iterator
     */
    public RowIterator findFirstRow(Session session, PersistentStore store,
                                    Object[] rowdata, int[] rowColMap) {

        NodeAVL node = findNode(session, store, rowdata, rowColMap,
                                rowColMap.length);

        return getIterator(session, store, node);
    }

    /**
     * Finds the first node that is larger or equal to the given one based
     * on the first column of the index only.
     *
     * @param session session object
     * @param store store object
     * @param value value to match
     * @param compare comparison Expression type
     *
     * @return iterator
     */
    public RowIterator findFirstRow(Session session, PersistentStore store,
                                    Object value, int compare) {

        readLock.lock();

        try {
            if (compare == OpTypes.SMALLER
                    || compare == OpTypes.SMALLER_EQUAL) {
                return findFirstRowNotNull(session, store);
            }

            boolean isEqual = compare == OpTypes.EQUAL
                              || compare == OpTypes.IS_NULL;
            NodeAVL x     = getAccessor(store);
            int     iTest = 1;

            if (compare == OpTypes.GREATER) {
                iTest = 0;
            }

            if (value == null && !isEqual) {
                return emptyIterator;
            }

            // this method returns the correct node only with the following conditions
            boolean check = compare == OpTypes.GREATER
                            || compare == OpTypes.EQUAL
                            || compare == OpTypes.GREATER_EQUAL;

            if (!check) {
                Error.runtimeError(ErrorCode.U_S0500, "Index.findFirst");
            }

            while (x != null) {
                boolean t = colTypes[0].compare(
                    value, x.getRow(store).getData()[colIndex[0]]) >= iTest;

                if (t) {
                    NodeAVL r = x.getRight(store);

                    if (r == null) {
                        break;
                    }

                    x = r;
                } else {
                    NodeAVL l = x.getLeft(store);

                    if (l == null) {
                        break;
                    }

                    x = l;
                }
            }

/*
        while (x != null
                && Column.compare(value, x.getData()[colIndex_0], colType_0)
                   >= iTest) {
            x = next(x);
        }
*/
            while (x != null) {
                Object colvalue = x.getRow(store).getData()[colIndex[0]];
                int    result   = colTypes[0].compare(value, colvalue);

                if (result >= iTest) {
                    x = next(store, x);
                } else {
                    if (isEqual) {
                        if (result != 0) {
                            x = null;
                        }
                    } else if (colvalue == null) {
                        x = next(store, x);

                        continue;
                    }

                    break;
                }
            }

// MVCC
            if (session == null || x == null) {
                return getIterator(session, store, x);
            }

            while (x != null) {
                Row row = x.getRow(store);

                if (compare == OpTypes.EQUAL
                        && colTypes[0].compare(
                            value, row.getData()[colIndex[0]]) != 0) {
                    x = null;

                    break;
                }

                if (session.database.txManager.canRead(session, row)) {
                    break;
                }

                x = next(store, x);
            }

            return getIterator(session, store, x);
        } finally {
            readLock.unlock();
        }
    }

    /**
     * Finds the first node where the data is not null.
     *
     * @return iterator
     */
    public RowIterator findFirstRowNotNull(Session session,
                                           PersistentStore store) {

        readLock.lock();

        try {
            NodeAVL x = getAccessor(store);

            while (x != null) {
                boolean t = colTypes[0].compare(
                    null, x.getRow(store).getData()[colIndex[0]]) >= 0;

                if (t) {
                    NodeAVL r = x.getRight(store);

                    if (r == null) {
                        break;
                    }

                    x = r;
                } else {
                    NodeAVL l = x.getLeft(store);

                    if (l == null) {
                        break;
                    }

                    x = l;
                }
            }

            while (x != null) {
                Object colvalue = x.getRow(store).getData()[colIndex[0]];

                if (colvalue == null) {
                    x = next(store, x);
                } else {
                    break;
                }
            }

// MVCC
            while (session != null && x != null) {
                Row row = x.getRow(store);

                if (session.database.txManager.canRead(session, row)) {
                    break;
                }

                x = next(store, x);
            }

            return getIterator(session, store, x);
        } finally {
            readLock.unlock();
        }
    }

    /**
     * Returns the row for the first node of the index
     *
     * @return Iterator for first row
     */
    public RowIterator firstRow(Session session, PersistentStore store) {

        int tempDepth = 0;

        readLock.lock();

        try {
            NodeAVL x = getAccessor(store);
            NodeAVL l = x;

            while (l != null) {
                x = l;
                l = x.getLeft(store);

                tempDepth++;
            }

            while (session != null && x != null) {
                Row row = x.getRow(store);

                if (session.database.txManager.canRead(session, row)) {
                    break;
                }

                x = next(store, x);
            }

            return getIterator(session, store, x);
        } finally {
            depth = tempDepth;

            readLock.unlock();
        }
    }

    public RowIterator firstRow(PersistentStore store) {

        int tempDepth = 0;

        readLock.lock();

        try {
            NodeAVL x = getAccessor(store);
            NodeAVL l = x;

            while (l != null) {
                x = l;
                l = x.getLeft(store);

                tempDepth++;
            }

            return getIterator(null, store, x);
        } finally {
            depth = tempDepth;

            readLock.unlock();
        }
    }

    /**
     * Returns the row for the last node of the index
     *
     * @return last row
     */
    public Row lastRow(Session session, PersistentStore store) {

        readLock.lock();

        try {
            NodeAVL x = getAccessor(store);
            NodeAVL l = x;

            while (l != null) {
                x = l;
                l = x.getRight(store);
            }

            while (session != null && x != null) {
                Row row = x.getRow(store);

                if (session.database.txManager.canRead(session, row)) {
                    break;
                }

                x = last(store, x);
            }

            return x == null ? null
                             : x.getRow(store);
        } finally {
            readLock.unlock();
        }
    }

    /**
     * Returns the node after the given one
     *
     * @param x node
     *
     * @return next node
     */
    private NodeAVL next(Session session, PersistentStore store, NodeAVL x) {

        if (x == null) {
            return null;
        }

        readLock.lock();

        try {
            while (true) {
                x = next(store, x);

                if (x == null) {
                    return x;
                }

                if (session == null) {
                    return x;
                }

                Row row = x.getRow(store);

                if (session.database.txManager.canRead(session, row)) {
                    return x;
                }
            }
        } finally {
            readLock.unlock();
        }
    }

    private NodeAVL next(PersistentStore store, NodeAVL x) {

        NodeAVL r = x.getRight(store);

        if (r != null) {
            x = r;

            NodeAVL l = x.getLeft(store);

            while (l != null) {
                x = l;
                l = x.getLeft(store);
            }

            return x;
        }

        NodeAVL ch = x;

        x = x.getParent(store);

        while (x != null && ch.equals(x.getRight(store))) {
            ch = x;
            x  = x.getParent(store);
        }

        return x;
    }

    private NodeAVL last(PersistentStore store, NodeAVL x) {

        if (x == null) {
            return null;
        }

        readLock.lock();

        try {
            NodeAVL left = x.getLeft(store);

            if (left != null) {
                x = left;

                NodeAVL right = x.getRight(store);

                while (right != null) {
                    x     = right;
                    right = x.getRight(store);
                }

                return x;
            }

            NodeAVL ch = x;

            x = x.getParent(store);

            while (x != null && ch.equals(x.getLeft(store))) {
                ch = x;
                x  = x.getParent(store);
            }

            return x;
        } finally {
            readLock.unlock();
        }
    }

    /**
     * Replace x with n
     *
     * @param x node
     * @param n node
     */
    private void replace(PersistentStore store, NodeAVL x, NodeAVL n) {

        if (x.isRoot()) {
            if (n != null) {
                n = n.setParent(store, null);
            }

            store.setAccessor(this, n);
        } else {
            set(store, x.getParent(store), x.isFromLeft(store), n);
        }
    }

    /**
     * Compares two table rows based on the columns of this index. The rowColMap
     * parameter specifies which columns of the other table are to be compared
     * with the colIndex columns of this index. The rowColMap can cover all
     * or only some columns of this index.
     *
     * @param a row from another table
     * @param rowColMap column indexes in the other table
     * @param b a full row in this table
     *
     * @return comparison result, -1,0,+1
     */
    public int compareRowNonUnique(Object[] a, int[] rowColMap, Object[] b) {

        int fieldcount = rowColMap.length;

        for (int j = 0; j < fieldcount; j++) {
            int i = colTypes[j].compare(a[rowColMap[j]], b[colIndex[j]]);

            if (i != 0) {
                return i;
            }
        }

        return 0;
    }

    public int compareRowNonUnique(Object[] a, int[] rowColMap, Object[] b,
                                   int fieldCount) {

        for (int j = 0; j < fieldCount; j++) {
            int i = colTypes[j].compare(a[rowColMap[j]], b[colIndex[j]]);

            if (i != 0) {
                return i;
            }
        }

        return 0;
    }

    /**
     * As above but use the index column data
     */
    public int compareRowNonUnique(Object[] a, Object[] b, int fieldcount) {

        for (int j = 0; j < fieldcount; j++) {
            int i = colTypes[j].compare(a[j], b[colIndex[j]]);

            if (i != 0) {
                return i;
            }
        }

        return 0;
    }

    /**
     * Compare two rows of the table for inserting rows into unique indexes
     * Supports descending columns.
     *
     * @param newRow data
     * @param existingRow data
     * @return comparison result, -1,0,+1
     */
    private int compareRowForInsertOrDelete(Session session, Row newRow,
            Row existingRow) {

        Object[] a       = newRow.getData();
        Object[] b       = existingRow.getData();
        int      j       = 0;
        boolean  hasNull = false;

        for (; j < colIndex.length; j++) {
            Object  currentvalue = a[colIndex[j]];
            Object  othervalue   = b[colIndex[j]];
            int     i = colTypes[j].compare(currentvalue, othervalue);
            boolean nulls        = currentvalue == null || othervalue == null;

            if (i != 0) {
                if (colDesc[j] && !nulls) {
                    i = -i;
                }

                if (nullsLast[j] && nulls) {
                    i = -i;
                }

                return i;
            }

            if (currentvalue == null) {
                hasNull = true;
            }
        }

        if (isUnique && !useRowId && !hasNull) {
            if (session == null
                    || session.database.txManager.canRead(session,
                        existingRow)) {

                //* debug 190
//                session.database.txManager.canRead(session, existingRow);
                return 0;
            } else {
                int difference = newRow.getPos() - existingRow.getPos();

                return difference;
            }
        }

        for (j = 0; j < pkCols.length; j++) {
            Object currentvalue = a[pkCols[j]];
            int    i = pkTypes[j].compare(currentvalue, b[pkCols[j]]);

            if (i != 0) {
                return i;
            }
        }

        if (useRowId) {
            int difference = newRow.getPos() - existingRow.getPos();

            if (difference < 0) {
                difference = -1;
            } else if (difference > 0) {
                difference = 1;
            }

            return difference;
        }

        if (session == null
                || session.database.txManager.canRead(session, existingRow)) {
            return 0;
        } else {
            int difference = newRow.getPos() - existingRow.getPos();

            if (difference < 0) {
                difference = -1;
            } else if (difference > 0) {
                difference = 1;
            }

            return difference;
        }
    }

    /**
     * Finds a match with a row from a different table
     *
     * @param rowdata array containing data for the index columns
     * @param rowColMap map of the data to columns
     * @param first true if the first matching node is required, false if any node
     * @return matching node or null
     */
    private NodeAVL findNode(Session session, PersistentStore store,
                             Object[] rowdata, int[] rowColMap,
                             int fieldCount) {

        readLock.lock();

        try {
            NodeAVL x = getAccessor(store);
            NodeAVL n;
            NodeAVL result = null;

            while (x != null) {
                int i = this.compareRowNonUnique(rowdata, rowColMap,
                                                 x.getRow(store).getData(),
                                                 fieldCount);

                if (i == 0) {
                    result = x;
                    n      = x.getLeft(store);
                } else if (i > 0) {
                    n = x.getRight(store);
                } else {
                    n = x.getLeft(store);
                }

                if (n == null) {
                    break;
                }

                x = n;
            }

            // MVCC 190
            if (session == null) {
                return result;
            }

            while (result != null) {
                Row row = result.getRow(store);

                if (compareRowNonUnique(
                        rowdata, rowColMap, row.getData(), fieldCount) != 0) {
                    result = null;

                    break;
                }

                if (session.database.txManager.canRead(session, row)) {
                    break;
                }

                result = next(store, result);
            }

            return result;
        } finally {
            readLock.unlock();
        }
    }

    /**
     * Balances part of the tree after an alteration to the index.
     */
    private void balance(PersistentStore store, NodeAVL x, boolean isleft) {

        while (true) {
            int sign = isleft ? 1
                              : -1;

            switch (x.getBalance() * sign) {

                case 1 :
                    x = x.setBalance(store, 0);

                    return;

                case 0 :
                    x = x.setBalance(store, -sign);
                    break;

                case -1 :
                    NodeAVL l = child(store, x, isleft);

                    if (l.getBalance() == -sign) {
                        replace(store, x, l);

                        x = set(store, x, isleft, child(store, l, !isleft));
                        l = set(store, l, !isleft, x);
                        x = x.setBalance(store, 0);
                        l = l.setBalance(store, 0);
                    } else {
                        NodeAVL r = child(store, l, !isleft);

                        replace(store, x, r);

                        l = set(store, l, !isleft, child(store, r, isleft));
                        r = set(store, r, isleft, l);
                        x = set(store, x, isleft, child(store, r, !isleft));
                        r = set(store, r, !isleft, x);

                        int rb = r.getBalance();

                        x = x.setBalance(store, (rb == -sign) ? sign
                                                              : 0);
                        l = l.setBalance(store, (rb == sign) ? -sign
                                                             : 0);
                        r = r.setBalance(store, 0);
                    }

                    return;
            }

            if (x.isRoot()) {
                return;
            }

            isleft = x.isFromLeft(store);
            x      = x.getParent(store);
        }
    }

    private NodeAVL getAccessor(PersistentStore store) {

        NodeAVL node = (NodeAVL) store.getAccessor(this);

        return node;
    }

    private IndexRowIterator getIterator(Session session,
                                         PersistentStore store, NodeAVL x) {

        if (x == null) {
            return emptyIterator;
        } else {
            IndexRowIterator it = new IndexRowIterator(session, store, this,
                x);

            return it;
        }
    }

    public static final class IndexRowIterator implements RowIterator {

        final Session         session;
        final PersistentStore store;
        final IndexAVL        index;
        NodeAVL               nextnode;
        Row                   lastrow;
        IndexRowIterator      last;
        IndexRowIterator      next;
        IndexRowIterator      lastInSession;
        IndexRowIterator      nextInSession;

        /**
         * When session == null, rows from all sessions are returned
         */
        public IndexRowIterator(Session session, PersistentStore store,
                                IndexAVL index, NodeAVL node) {

            this.session = session;
            this.store   = store;
            this.index   = index;

            if (index == null) {
                return;
            }

            nextnode = node;
        }

        public boolean hasNext() {
            return nextnode != null;
        }

        public Row getNextRow() {

            if (nextnode == null) {
                release();

                return null;
            }

            lastrow  = nextnode.getRow(store);
            nextnode = index.next(session, store, nextnode);

            if (nextnode == null) {
                release();
            }

            return lastrow;
        }

        public void remove() {
            store.delete(lastrow);
        }

        public void release() {}

        public boolean setRowColumns(boolean[] columns) {
            return false;
        }

        public long getPos() {
            return nextnode.getPos();
        }
    }
   
    /*************** VOLTDB *********************/
   
    String getColumnNameList() {

        String columnNameList = "";
        Table t2 = (Table) table;

        for (int j = 0; j < colIndex.length; ++j) {
            columnNameList +=
                t2.getColumn(colIndex[j]).getName().statementName;

            if (j < colIndex.length - 1) {
                columnNameList += ",";
            }
        }

        return columnNameList;
    }
   
    /**
     * VoltDB added method to get a non-catalog-dependent
     * representation of this HSQLDB object.
     * @param session The current Session object may be needed to resolve
     * some names.
     * @param indent A string of whitespace to be prepended to every line
     * in the resulting XML.
     * @return XML, correctly indented, representing this object.
     */
    @Override
    public String voltGetXML(Session session, String indent) {
        StringBuilder sb = new StringBuilder();
       
        sb.append(indent).append("<index");
        sb.append(" name='").append(getName().name).append("'");
        sb.append(" columns='").append(getColumnNameList()).append("'");
        sb.append(" unique='").append(isUnique() ? "true" : "false").append("'");
        sb.append(">\n");
   
        sb.append(indent).append("</index>\n");
       
        return sb.toString();
    }
}
TOP

Related Classes of org.hsqldb.index.IndexAVL$IndexRowIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.