Package org.lealone.hbase.dbobject.index

Source Code of org.lealone.hbase.dbobject.index.HBaseSecondaryIndex$BufferPool

/*
* Copyright 2011 The Apache Software Foundation
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.lealone.hbase.dbobject.index;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.hadoop.hbase.util.Bytes;
import org.lealone.command.Prepared;
import org.lealone.constant.ErrorCode;
import org.lealone.dbobject.index.BaseIndex;
import org.lealone.dbobject.index.Cursor;
import org.lealone.dbobject.index.IndexType;
import org.lealone.dbobject.table.Column;
import org.lealone.dbobject.table.IndexColumn;
import org.lealone.dbobject.table.Table;
import org.lealone.dbobject.table.TableFilter;
import org.lealone.engine.Session;
import org.lealone.hbase.metadata.MetaDataAdmin;
import org.lealone.hbase.result.HBaseRow;
import org.lealone.hbase.util.HBaseUtils;
import org.lealone.message.DbException;
import org.lealone.result.ResultInterface;
import org.lealone.result.Row;
import org.lealone.result.SearchRow;
import org.lealone.result.SortOrder;
import org.lealone.value.Value;
import org.lealone.value.ValueBytes;
import org.lealone.value.ValueNull;
import org.lealone.value.ValueString;

public class HBaseSecondaryIndex extends BaseIndex {

    public synchronized static void createIndexTableIfNotExists(Session session, String indexName) {
        StringBuilder buff = new StringBuilder("CREATE HBASE TABLE IF NOT EXISTS ");
        buff.append(indexName).append(" (COLUMN FAMILY ").append(Bytes.toString(MetaDataAdmin.DEFAULT_COLUMN_FAMILY));
        buff.append("(C char))");

        Prepared p = session.prepare(buff.toString(), true);
        p.setExecuteDirec(true);
        p.update();
    }

    public synchronized static void dropIndexTableIfExists(Session session, String indexName) {
        Prepared p = session.prepare("DROP TABLE IF EXISTS " + indexName, true);
        p.setExecuteDirec(true);
        p.update();
    }

    private static void checkIndexColumnTypes(IndexColumn[] columns) {
        for (IndexColumn c : columns) {
            int type = c.column.getType();
            if (type == Value.CLOB || type == Value.BLOB) {
                throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1,
                        "Index on BLOB or CLOB column: " + c.column.getCreateSQL());
            }
        }
    }

    //组成索引key的列个数
    private final int keyColumns;
    private final byte[] indexTableNameAsBytes;

    private final String select;
    private final String insert;
    private final String delete;

    public HBaseSecondaryIndex(Table table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
        initBaseIndex(table, id, indexName, columns, indexType);
        if (!database.isStarting()) {
            checkIndexColumnTypes(columns);
        }
        keyColumns = columns.length + 1; //多加了一列,最后一列对应rowKey
        indexTableNameAsBytes = Bytes.toBytes(indexName);

        select = "select _rowkey_ from " + indexName + " where _rowkey_>=?";
        insert = "insert into " + indexName + "(_rowkey_, c) values(?,'0')";
        delete = "delete from " + indexName + " where _rowkey_=?";
    }

    public byte[] getTableNameAsBytes() {
        return indexTableNameAsBytes;
    }

    @Override
    public void add(Session session, Row row) {
        if (indexType.isUnique()) {
            byte[] key = getStartKey(row);
            Prepared p = session.prepare(select, true);
            p.getParameters().get(0).setValue(ValueString.get(Bytes.toString(key)));
            ResultInterface r = p.query(1);
            try {
                if (r.next()) {
                    SearchRow r2 = getRow(new Buffer(Bytes.toBytes(r.currentRow()[0].getString())));
                    if (compareRows(row, r2) == 0) {
                        if (!containsNullAndAllowMultipleNull(r2)) {
                            throw getDuplicateKeyException();
                        }
                    }
                }
            } finally {
                r.close();
            }
        }

        Prepared p = session.prepare(insert, true);
        p.getParameters().get(0).setValue(ValueString.get(Bytes.toString(getKey(row))));
        p.update();
    }

    //参数row是主表的记录,并不是索引表的记录
    @Override
    public void remove(Session session, Row row) {
        //Update这种类型的SQL不需要先删除再insert,只需直接insert即可
        if (((HBaseRow) row).isForUpdate())
            return;

        //删除操作转成insert null操作
        Prepared p = session.prepare(delete, true);
        p.getParameters().get(0).setValue(ValueString.get(Bytes.toString(getKey(row))));
        p.update();
    }

    @Override
    public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
        byte[] startRow = getKey(first);
        byte[] stopRow = getLastKey(last);
        return new HBaseSecondaryIndexCursor(this, filter, startRow, stopRow);
    }

    @Override
    public Cursor find(Session session, SearchRow first, SearchRow last) {
        throw DbException.getUnsupportedException("find(Session, SearchRow, SearchRow)");
    }

    @Override
    public void close(Session session) {
        // nothing to do
    }

    public byte[] getKey(SearchRow r) {
        return getKey(r, false);
    }

    private byte[] getKey(SearchRow r, boolean isStartKey) {
        if (r == null) {
            return null;
        }

        Value[] array = new Value[keyColumns];
        Value v;
        for (int i = 0; i < columns.length; i++) {
            v = r.getValue(columns[i].getColumnId());
            if (v != null)
                array[i] = columns[i].convert(v);
        }
        if (isStartKey)
            array[keyColumns - 1] = null;
        else
            array[keyColumns - 1] = r.getRowKey();

        try {
            return encode(array);
        } catch (IOException e) {
            throw DbException.convert(e);
        }
    }

    //用于检查唯一约束是否违反
    private byte[] getStartKey(SearchRow r) {
        return getKey(r, true);
    }

    //因为HBase在进行scan时查询的记录范围是startKey <= row < endKey(也就是不包含endKey)
    //而SQL是startKey <= row <= endKey
    //所以需要在原有的endKey上面多加一些额外的字节才会返回endKey
    private byte[] getLastKey(SearchRow r) {
        if (r == null) {
            return null;
        }
        byte[] bytes;
        Buffer buffer = BufferPool.getBuffer();
        try {
            Value[] array = new Value[columns.length];
            Value v;
            for (int i = 0; i < columns.length; i++) {
                v = r.getValue(columns[i].getColumnId());
                if (v != null)
                    array[i] = columns[i].convert(v);
                if (array[i] == null || array[i] == ValueNull.INSTANCE) {
                    buffer.writeInt(Integer.MAX_VALUE); //lastKey查询不用0,而是用最大值
                } else {
                    bytes = HBaseUtils.toBytes(array[i]);
                    buffer.writeInt(bytes.length);
                    buffer.write(bytes);
                }
            }
            buffer.writeInt(Integer.MAX_VALUE);
            bytes = buffer.toByteArray();
            return bytes;
        } catch (IOException e) {
            throw DbException.convert(e);
        } finally {
            BufferPool.pushBuffer(buffer);
        }
    }

    SearchRow getRow(Buffer buffer) {
        try {
            return getRow(decode(buffer));
        } catch (IOException e) {
            throw DbException.convert(e);
        }
    }

    SearchRow getRow(Value[] array) {
        SearchRow searchRow = getTable().getTemplateRow();
        searchRow.setRowKey((array[array.length - 1]));
        Column[] cols = getColumns();
        for (int i = 0, size = array.length - 1; i < size; i++) {
            Column c = cols[i];
            int idx = c.getColumnId();
            Value v = array[i];
            searchRow.setValue(idx, v);
        }
        return searchRow;
    }

    private byte[] encode(Value[] array) throws IOException {
        Buffer buffer = BufferPool.getBuffer();
        try {
            byte[] bytes;
            for (int i = 0; i < array.length; i++) {
                Value v = array[i];
                if (v == null || v == ValueNull.INSTANCE) {
                    buffer.writeInt(0);
                } else {
                    bytes = HBaseUtils.toBytes(v);
                    buffer.writeInt(bytes.length);
                    buffer.write(bytes);
                }
            }
            buffer.writeInt(Integer.MAX_VALUE);
            bytes = buffer.toByteArray();
            return bytes;
        } catch (IOException e) {
            throw DbException.convert(e);
        } finally {
            BufferPool.pushBuffer(buffer);
        }
    }

    private Value[] decode(Buffer buff) throws IOException {
        int length;
        Value[] array = new Value[keyColumns];

        for (int i = 0; i < columns.length; i++) {
            length = buff.getInt();
            if (length == 0) {
                array[i] = ValueNull.INSTANCE;
            } else {
                byte[] bytes = new byte[length];
                buff.get(bytes);
                array[i] = HBaseUtils.toValue(bytes, columns[i].getType());
            }
        }

        length = buff.getInt();
        if (length == 0) {
            array[keyColumns - 1] = ValueNull.INSTANCE;
        } else {
            byte[] bytes = new byte[length];
            buff.get(bytes);
            array[keyColumns - 1] = ValueBytes.getNoCopy(bytes);
        }
        return array;
    }

    @Override
    public double getCost(Session session, int[] masks, SortOrder sortOrder) {
        return 10 * getCostRangeIndex(masks, 100, sortOrder);
    }

    @Override
    public void remove(Session session) {
        try {
            HBaseSecondaryIndex.dropIndexTableIfExists(session, getName());
        } catch (Exception e) {
            throw DbException.convert(e);
        }
    }

    @Override
    public void truncate(Session session) {
        try {
            HBaseSecondaryIndex.dropIndexTableIfExists(session, getName());
            HBaseSecondaryIndex.createIndexTableIfNotExists(session, getName());
        } catch (Exception e) {
            throw DbException.convert(e);
        }
    }

    @Override
    public boolean canGetFirstOrLast() {
        return false;
    }

    @Override
    public Cursor findFirstOrLast(Session session, boolean first) {
        throw DbException.getUnsupportedException("findFirstOrLast");
    }

    @Override
    public boolean needRebuild() {
        return false;
    }

    @Override
    public long getRowCount(Session session) {
        return 0;
    }

    @Override
    public long getRowCountApproximation() {
        return 0;
    }

    @Override
    public void checkRename() {
    }

    @Override
    public long getDiskSpaceUsed() {
        return 0;
    }

    private static class BufferPool {
        private static BlockingQueue<Buffer> pool = new LinkedBlockingQueue<Buffer>();

        public static Buffer getBuffer() {
            Buffer buffer = pool.poll();
            if (buffer != null) {
                buffer.reset();
                return buffer;
            }
            return new Buffer();
        }

        public static void pushBuffer(Buffer buffer) {
            pool.add(buffer);
        }
    }

    public static class Buffer extends DataOutputStream {
        private ByteArrayOutputStream baos;
        private DataInputStream in;

        public Buffer() {
            super(new ByteArrayOutputStream(256));
            baos = (ByteArrayOutputStream) this.out;
        }

        public Buffer(byte[] bytes) {
            super(null);
            in = new DataInputStream(new ByteArrayInputStream(bytes));
        }

        public byte[] toByteArray() {
            return baos.toByteArray();
        }

        public void reset() {
            baos.reset();
        }

        public void get(byte[] b) throws IOException {
            in.read(b);
        }

        public int getInt() throws IOException {
            return in.readInt();
        }
    }
}
TOP

Related Classes of org.lealone.hbase.dbobject.index.HBaseSecondaryIndex$BufferPool

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.
ers. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.