/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package org.opencloudb.mpp; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.opencloudb.MycatServer; import org.opencloudb.backend.BackendConnection; import org.opencloudb.config.model.SystemConfig; import org.opencloudb.mpp.controller.NodeExcutionController; import org.opencloudb.mpp.model.NodeRowDataPacket; import org.opencloudb.mysql.nio.handler.MultiNodeQueryWithLimitHandler; import org.opencloudb.mysql.nio.handler.ResponseHandler; import org.opencloudb.net.mysql.RowDataPacket; import org.opencloudb.route.RouteResultset; import org.opencloudb.route.RouteResultsetNode; import org.opencloudb.server.NonBlockingSession; /** * Data merge service handle data Min,Max,AVG group 、order by 、limit * * @author wuzhih * */ public class MutiDataMergeService extends DataMergeService implements ResponseHandler { private static final Logger LOGGER = Logger .getLogger(MutiDataMergeService.class); private RowDataPacketGrouper grouper = null; private RangRowDataPacketSorter sorter = null; private NodeExcutionController dataController; private MultiNodeQueryWithLimitHandler limitExcution = null; private Map<String, NodeRowDataPacket> result = new HashMap<String, NodeRowDataPacket>(); private int pagePatchSize = 100; public MutiDataMergeService(RouteResultset rrs) { super(rrs); this.rrs = rrs; this.dataController = new NodeExcutionController(this.rrs, this.result, this); RouteResultsetNode[] nodeArr = this.rrs.getNodes(); this.pagePatchSize = this.rrs.getLimitSize(); SystemConfig sysConfig = MycatServer.getInstance().getConfig().getSystem(); int mutiNodePatchSize = sysConfig.getMutiNodePatchSize(); int size = ((mutiNodePatchSize - (mutiNodePatchSize % this.pagePatchSize))/this.pagePatchSize + 1)*this.pagePatchSize; if (pagePatchSize < size) { pagePatchSize = size; } for (RouteResultsetNode node : nodeArr) { NodeRowDataPacket packet = new NodeRowDataPacket(node, pagePatchSize); result.put(node.getName(), packet); } } public void setLimitExcution(MultiNodeQueryWithLimitHandler limitExcution) { this.limitExcution = limitExcution; } public void initHandler(NonBlockingSession session) { this.dataController.initHandler(session); } private void appendNodeRang(String dataNode) { NodeRowDataPacket packet = result.get(dataNode); packet.newRang(); } public void execute(BackendConnection conn, RouteResultsetNode node) { this.appendNodeRang(node.getName()); this.dataController._execute(conn, node); } public void releaseAllBackend() { this.dataController.releaseAllBackend(); } public long loadTrimTotal() { long trimTotal = 0; Set<String> dataNodeNameSet = result.keySet(); for (Iterator<String> iter = dataNodeNameSet.iterator(); iter.hasNext();) { String dataNodeName = iter.next(); NodeRowDataPacket nodePacket = result.get(dataNodeName); trimTotal += nodePacket.loadTrimTotal(); } return trimTotal; } public void dataOk(String dataNode, byte[] eof, BackendConnection conn) { this.dataController.dataOk(dataNode, eof, conn); } /** * return merged data * * @return */ public Collection<RowDataPacket> getResults() { Collection<RowDataPacket> tmpResult = new LinkedList<RowDataPacket>(); Set<String> dataNodeNameSet = result.keySet(); for (Iterator<String> iter = dataNodeNameSet.iterator(); iter.hasNext();) { String dataNodeName = iter.next(); NodeRowDataPacket nodePacket = result.get(dataNodeName); List<RowDataPacket> list = nodePacket.loadData(); tmpResult.addAll(list); } if (this.grouper != null) { tmpResult = grouper.getResult(); grouper = null; } if (sorter != null) { Iterator<RowDataPacket> itor = tmpResult.iterator(); while (itor.hasNext()) { sorter.addRow(itor.next()); itor.remove(); } tmpResult = sorter.getSortedResult(); sorter = null; } return tmpResult; } public void setFieldCount(int fieldCount) { this.fieldCount = fieldCount; } public RouteResultset getRrs() { return rrs; } private int fieldCount; private final RouteResultset rrs; public void onRowMetaData(Map<String, ColMeta> columToIndx, int fieldCount) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("field metadata inf:" + Arrays.toString(columToIndx.entrySet().toArray())); } int[] groupColumnIndexs = null; this.fieldCount = fieldCount; if (rrs.getGroupByCols() != null) { groupColumnIndexs = (toColumnIndex(rrs.getGroupByCols(), columToIndx)); } if (rrs.isHasAggrColumn()) { List<MergeCol> mergCols = new LinkedList<MergeCol>(); if (rrs.getMergeCols() != null) { for (Map.Entry<String, Integer> mergEntry : rrs.getMergeCols() .entrySet()) { String colName = mergEntry.getKey().toUpperCase(); ColMeta colMeta = columToIndx.get(colName); mergCols.add(new MergeCol(colMeta, mergEntry.getValue())); } } // add no alias merg column for (Map.Entry<String, ColMeta> fieldEntry : columToIndx.entrySet()) { String colName = fieldEntry.getKey(); int result = MergeCol.tryParseAggCol(colName); if (result != MergeCol.MERGE_UNSUPPORT && result != MergeCol.MERGE_NOMERGE) { mergCols.add(new MergeCol(fieldEntry.getValue(), result)); } } grouper = new RowDataPacketGrouper(groupColumnIndexs, mergCols.toArray(new MergeCol[mergCols.size()])); } if (rrs.getOrderByCols() != null) { LinkedHashMap<String, Integer> orders = rrs.getOrderByCols(); OrderCol[] orderCols = new OrderCol[orders.size()]; int i = 0; for (Map.Entry<String, Integer> entry : orders.entrySet()) { orderCols[i++] = new OrderCol(columToIndx.get(entry.getKey() .toUpperCase()), entry.getValue()); } sorter = new RangRowDataPacketSorter(orderCols); this.dataController.setSorter(sorter); } } public void onNewRangRecord(String dataNode) { NodeRowDataPacket nodePacket = this.result.get(dataNode); nodePacket.newRang(); } /** * process new record (mysql binary data),if data can output to client * ,return true * * @param dataNode * DN's name (data from this dataNode) * @param rowData * raw data */ public boolean onNewRecord(String dataNode, byte[] rowData) { NodeRowDataPacket nodePacket = this.result.get(dataNode); RowDataPacket rowDataPkg = new RowDataPacket(fieldCount); rowDataPkg.read(rowData); if (grouper != null) { grouper.addRow(rowDataPkg); } else { nodePacket.addPacket(rowDataPkg); this.dataController.newRecord(dataNode); } return false; } private static int[] toColumnIndex(String[] columns, Map<String, ColMeta> toIndexMap) { int[] result = new int[columns.length]; ColMeta curColMeta = null; for (int i = 0; i < columns.length; i++) { curColMeta = toIndexMap.get(columns[i].toUpperCase()); if (curColMeta == null) { throw new java.lang.IllegalArgumentException( "can't find column in select fields " + columns[i]); } result[i] = curColMeta.colIndex; } return result; } /** * release resources */ public void clear() { grouper = null; sorter = null; result = null; } @Override public void connectionAcquired(BackendConnection conn) { this.limitExcution.connectionAcquired(conn); } @Override public void connectionClose(BackendConnection conn, String reason) { this.limitExcution.connectionClose(conn, reason); } @Override public void connectionError(Throwable e, BackendConnection conn) { this.limitExcution.connectionError(e, conn); } @Override public void errorResponse(byte[] err, BackendConnection conn) { this.limitExcution.errorResponse(err, conn); } @Override public void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) { this.limitExcution.fieldEofResponse(header, fields, eof, conn); } @Override public void okResponse(byte[] ok, BackendConnection conn) { this.limitExcution.okResponse(ok, conn); } @Override public void rowEofResponse(byte[] eof, BackendConnection conn) { this.limitExcution.rowEofResponse(eof, conn); } @Override public void rowResponse(byte[] row, BackendConnection conn) { this.limitExcution.rowResponse(row, conn); } @Override public void writeQueueAvailable() { this.limitExcution.writeQueueAvailable(); } public int getPagePatchSize() { return pagePatchSize; } }