Package org.lealone.hbase.command.dml

Source Code of org.lealone.hbase.command.dml.InsertOrMergeSupport

/*
* 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.command.dml;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.hadoop.hbase.client.Put;
import org.lealone.command.CommandInterface;
import org.lealone.command.CommandRemote;
import org.lealone.command.Prepared;
import org.lealone.command.dml.Query;
import org.lealone.command.dml.TransactionCommand;
import org.lealone.dbobject.table.Column;
import org.lealone.dbobject.table.Table;
import org.lealone.engine.Session;
import org.lealone.engine.SessionInterface;
import org.lealone.expression.Expression;
import org.lealone.hbase.command.CommandParallel;
import org.lealone.hbase.dbobject.table.HBaseTable;
import org.lealone.hbase.engine.HBaseSession;
import org.lealone.hbase.engine.SessionRemotePool;
import org.lealone.hbase.result.HBaseRow;
import org.lealone.hbase.util.HBaseRegionInfo;
import org.lealone.hbase.util.HBaseUtils;
import org.lealone.message.DbException;
import org.lealone.result.Row;
import org.lealone.util.New;
import org.lealone.util.StatementBuilder;
import org.lealone.util.StringUtils;
import org.lealone.value.Value;
import org.lealone.value.ValueNull;
import org.lealone.value.ValueString;
import org.lealone.value.ValueUuid;

public class InsertOrMergeSupport {
    private final Map<String, Map<String, List<String>>> servers = New.hashMap();
    private final HBaseSession session;
    private final InsertOrMerge iom;
    private final boolean isInsert;
    private Query query;

    private StatementBuilder alterTable;
    private ArrayList<Column> alterColumns;
    private int rowKeyColumnIndex = -1;

    private HBaseTable table;
    private ArrayList<Expression[]> list;
    private Column[] columns;
    private Column[] keys;

    public InsertOrMergeSupport(Session session, InsertOrMerge iom, boolean isInsert) {
        this.session = (HBaseSession) session;
        this.iom = iom;
        this.isInsert = isInsert;
    }

    public void postPrepare(Table table, Query query, ArrayList<Expression[]> list, Column[] columns, Column[] keys) {
        this.table = (HBaseTable) table;
        this.list = list;
        this.columns = columns;
        this.keys = keys;
        this.query = query;

        if (query != null) {
            int index = -1;
            for (Column c : columns) {
                index++;
                if (c.isRowKeyColumn()) {
                    rowKeyColumnIndex = index;
                    break;
                }
            }
        }
    }

    public int update(boolean insertFromSelect, boolean sortedInsertMode, Prepared prepared) {

        boolean isTopTransaction = false;
        boolean isNestedTransaction = false;

        if (query != null || list.size() > 1 || table.doesSecondaryIndexExist()) {
            if (session.getAutoCommit()) {
                session.setAutoCommit(false);
                isTopTransaction = true;
            } else {
                isNestedTransaction = true;
                session.addSavepoint(TransactionCommand.INTERNAL_SAVEPOINT);
            }
        }

        //当在Parser中解析insert语句时,如果insert中的一些字段是新的,那么会标注字段列表已修改了,
        //并且新字段的类型是未知的,只有在执行insert时再由字段值的实际类型确定字段的类型。
        if (table.isColumnsModified()) {
            alterTable = new StatementBuilder("ALTER TABLE ");
            //不能使用ALTER TABLE t ADD COLUMN(f1 int, f2 int)这样的语法,因为有可能多个RS都在执行这种语句,在Master那会有冲突
            alterTable.append(table.getSQL()).append(" ADD COLUMN IF NOT EXISTS ");

            alterColumns = New.arrayList();
        }

        try {
            int updateCount = iom.internalUpdate();

            if (!servers.isEmpty()) {
                List<CommandInterface> commands = New.arrayList(servers.size());
                for (Map.Entry<String, Map<String, List<String>>> e : servers.entrySet()) {
                    CommandRemote c = SessionRemotePool.getCommandRemote(session, prepared, e.getKey(), //
                            getPlanSQL(insertFromSelect, sortedInsertMode, e.getValue().entrySet()));

                    commands.add(c);
                }

                updateCount += CommandParallel.executeUpdate(commands);
            }

            if (table.isColumnsModified()) {
                table.setColumnsModified(false);
                SessionInterface si = SessionRemotePool.getMasterSessionRemote(session.getOriginalProperties());
                for (Column c : alterColumns) {
                    CommandInterface ci = si.prepareCommand(alterTable + c.getCreateSQL(true), 1);
                    ci.executeUpdate();
                }
            }
            if (isTopTransaction)
                session.commit(false);
            return updateCount;
        } catch (Exception e) {
            if (isTopTransaction)
                session.rollback();

            //嵌套事务出错时提前rollback
            if (isNestedTransaction)
                session.rollbackToSavepoint(TransactionCommand.INTERNAL_SAVEPOINT);

            throw DbException.convert(e);
        } finally {
            if (isTopTransaction)
                session.setAutoCommit(true);
            servers.clear();
        }
    }

    private String getPlanSQL(boolean insertFromSelect, boolean sortedInsertMode, Set<Map.Entry<String, List<String>>> regions) {
        StatementBuilder buff = new StatementBuilder();
        boolean first = true;
        for (Map.Entry<String, List<String>> entry : regions) {
            if (!first) {
                buff.append(";");
                buff.append('\n');
            } else {
                first = false;
            }
            buff.append("IN THE REGION ");
            buff.append(StringUtils.quoteStringSQL(entry.getKey()));
            buff.append('\n');
            if (isInsert)
                buff.append("INSERT INTO ");
            else
                buff.append("MERGE INTO ");
            buff.append(table.getSQL()).append('(');
            buff.resetCount();
            for (Column c : columns) {
                buff.appendExceptFirst(", ");
                buff.append(c.getSQL());
            }
            buff.append(")\n");

            if (isInsert) {
                if (insertFromSelect) {
                    buff.append("DIRECT ");
                }
                if (sortedInsertMode) {
                    buff.append("SORTED ");
                }
            } else {
                if (keys != null) {
                    buff.append(" KEY(");
                    buff.resetCount();
                    for (Column c : keys) {
                        buff.appendExceptFirst(", ");
                        buff.append(c.getSQL());
                    }
                    buff.append(")\n");
                }
            }
            buff.append("VALUES ");
            int row = 0;
            if (entry.getValue().size() > 1) {
                buff.append('\n');
            }
            for (String value : entry.getValue()) {
                if (row++ > 0) {
                    buff.append(",\n");
                }
                buff.append(value);
            }
        }
        return buff.toString();
    }

    private static String getPlanSQL(Value[] values) {
        StatementBuilder buff = new StatementBuilder();
        buff.append('(');
        for (Value v : values) {
            buff.appendExceptFirst(", ");
            if (v == null) {
                buff.append("NULL");
            } else {
                buff.append(v.getSQL());
            }
        }
        buff.append(')');
        return buff.toString();
    }

    private static String getPlanSQL(Expression[] list) {
        StatementBuilder buff = new StatementBuilder();
        buff.append('(');
        for (Expression e : list) {
            buff.appendExceptFirst(", ");
            if (e == null) {
                buff.append("DEFAULT");
            } else {
                buff.append(e.getSQL());
            }
        }
        buff.append(')');
        return buff.toString();
    }

    private HBaseRow createRow(Value rowKey, String value) {
        rowKey = ValueString.get(rowKey.getString());
        byte[] rowKeyAsBytes = HBaseUtils.toBytes(rowKey);

        HBaseRegionInfo hri = HBaseUtils.getHBaseRegionInfo(getTableNameAsBytes(), rowKeyAsBytes);
        if (!HBaseUtils.isLocal(session, hri)) {
            Map<String, List<String>> regions = servers.get(hri.getRegionServerURL());
            if (regions == null) {
                regions = New.hashMap();
                servers.put(hri.getRegionServerURL(), regions);
            }

            List<String> values = regions.get(hri.getRegionName());
            if (values == null) {
                values = New.arrayList();
                regions.put(hri.getRegionName(), values);
            }
            values.add(value);

            return null;
        }

        HBaseRow row = (HBaseRow) table.getTemplateRow();
        row.setRowKey(rowKey);
        row.setRegionName(hri.getRegionNameAsBytes());
        row.setPut(session.getTransaction().createHBasePut(table.getDefaultColumnFamilyNameAsBytes(), rowKey));

        return row;
    }

    protected Row createRow(Expression[] expr, int rowId) {
        HBaseRow row = createRow(getRowKey(rowId), getPlanSQL(expr));
        if (row == null)
            return null;

        Put put = row.getPut();
        Column c;
        Value v;
        Expression e;
        for (int i = 0, len = columns.length; i < len; i++) {
            c = columns[i];
            e = expr[i];
            if (e != null) {
                // e can be null (DEFAULT)
                e = e.optimize(session);
                v = e.getValue(session);
                if (c.isTypeUnknown()) {
                    c.setType(v.getType());
                    //alterTable.appendExceptFirst(", ");
                    //alterTable.append(c.getCreateSQL());
                    if (alterColumns != null)
                        alterColumns.add(c);
                }
                v = c.convert(e.getValue(session));
                row.setValue(c.getColumnId(), v);

                if (!c.isRowKeyColumn())
                    put.add(c.getColumnFamilyNameAsBytes(), c.getNameAsBytes(), HBaseUtils.toBytes(v));
            } else {
                if (!c.isRowKeyColumn())
                    put.add(c.getColumnFamilyNameAsBytes(), c.getNameAsBytes(), HBaseUtils.toBytes(ValueNull.INSTANCE));
            }
        }
        return row;
    }

    protected Row createRow(Value[] values) {
        HBaseRow row = createRow(getRowKey(values), getPlanSQL(values));
        if (row == null)
            return null;

        Put put = row.getPut();
        Column c;
        Value v;

        for (int j = 0, len = columns.length; j < len; j++) {
            c = columns[j];
            int index = c.getColumnId();
            v = values[j];
            if (c.isTypeUnknown()) {
                c.setType(v.getType());
                if (alterColumns != null)
                    alterColumns.add(c);
            }
            v = c.convert(values[j]);
            row.setValue(index, v);

            if (!c.isRowKeyColumn())
                put.add(c.getColumnFamilyNameAsBytes(), c.getNameAsBytes(), HBaseUtils.toBytes(v));
        }

        return row;
    }

    private byte[] getTableNameAsBytes() {
        return ((HBaseTable) table).getTableNameAsBytes();
    }

    private Value getRowKey(Value[] values) {
        if (rowKeyColumnIndex == -1)
            return getUuidRowKey();
        else
            return values[rowKeyColumnIndex];
    }

    private Value getRowKey(int rowIndex) {
        if (!list.isEmpty() && list.get(rowIndex).length > 0) {
            int columnIndex = 0;
            for (Column c : columns) {
                if (c.isRowKeyColumn()) {
                    return list.get(rowIndex)[columnIndex].getValue(session);
                }
                columnIndex++;
            }
        }
        return getUuidRowKey();
    }

    private Value getUuidRowKey() {
        return ValueUuid.getNewRandom();
    }
}
TOP

Related Classes of org.lealone.hbase.command.dml.InsertOrMergeSupport

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.