/* * Copyright 2014-2015 the original author or authors * * Licensed 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. */ // Created on 2015年2月3日 // $Id$ package com.wplatform.ddal.dispatch; import java.util.List; import java.util.Map; import com.wplatform.ddal.command.expression.Comparison; import com.wplatform.ddal.dbobject.index.IndexCondition; import com.wplatform.ddal.dbobject.table.Column; import com.wplatform.ddal.dbobject.table.TableMate; import com.wplatform.ddal.dispatch.function.PartitionFunction; import com.wplatform.ddal.dispatch.rule.*; import com.wplatform.ddal.engine.Database; import com.wplatform.ddal.engine.Session; import com.wplatform.ddal.message.DbException; import com.wplatform.ddal.result.ResultInterface; import com.wplatform.ddal.result.SearchRow; import com.wplatform.ddal.util.New; import com.wplatform.ddal.value.Value; import com.wplatform.ddal.value.ValueLong; import com.wplatform.ddal.value.ValueNull; /** * @author <a href="mailto:jorgie.mail@gmail.com">jorgie li</a> */ public class RoutingHandlerImpl implements RoutingHandler { private Database database; private RoutingCalculator trc; public RoutingHandlerImpl(Database database) { this.database = database; this.trc = new RoutingCalculatorImpl(); } @Override public RoutingResult doRoute(TableMate table, SearchRow row) { TableRouter tr = table.getTableRouter(); if (tr != null) { Map<String, List<Value>> args = getRuleColumnArgs(table, row); RoutingResult rr = trc.calculate(tr, args); if (rr.isMultipleNode()) { throw new TableRoutingException(table.getName() + " routing error."); } return rr; } else { return fixedRoutingResult(table.getShards()); } } @Override public RoutingResult doRoute(TableMate table, SearchRow first, SearchRow last) { TableRouter tr = table.getTableRouter(); if (tr == null) { return fixedRoutingResult(table.getShards()); } else { Map<String, List<Value>> routingArgs = New.hashMap(); exportRangeArg(table, first, last, routingArgs); RoutingResult rr = trc.calculate(tr, routingArgs); return rr; } } @Override public RoutingResult doRoute(TableMate table, Session session, List<IndexCondition> indexConditions) { TableRouter tr = table.getTableRouter(); if (tr == null) { return fixedRoutingResult(table.getShards()); } else { Map<String, List<Value>> routingArgs = New.hashMap(); List<RuleColumn> ruleCols = tr.getRuleColumns(); SearchRow start = null, end = null; for (IndexCondition condition : indexConditions) { Column column = condition.getColumn(); String colName = column.getName(); RuleColumn matched = null; for (RuleColumn ruleColumn : ruleCols) { if (colName.equalsIgnoreCase(ruleColumn.getName())) { matched = ruleColumn; } } if (matched == null) { continue; } List<Value> values = routingArgs.get(matched.getName()); if (values == null) { values = New.arrayList(); routingArgs.put(matched.getName(), values); } if (condition.getCompareType() == Comparison.IN_LIST) { Value[] inList = condition.getCurrentValueList(session); for (Value value : inList) { values.add(value); } } else if (condition.getCompareType() == Comparison.IN_QUERY) { ResultInterface result = condition.getCurrentResult(); while (result.next()) { Value v = result.currentRow()[0]; if (v != ValueNull.INSTANCE) { v = column.convert(v); values.add(v); } } } else { int columnId = column.getColumnId(); Value v = condition.getCurrentValue(session); boolean isStart = condition.isStart(); boolean isEnd = condition.isEnd(); if (isStart) { start = getSearchRow(table, session, start, columnId, v, true); } if (isEnd) { end = getSearchRow(table, session, end, columnId, v, false); } } } exportRangeArg(table, start, end, routingArgs); RoutingResult rr = trc.calculate(tr, routingArgs); return rr; } } private Map<String, List<Value>> getRuleColumnArgs(TableMate table, SearchRow row) { Map<String, List<Value>> args = New.hashMap(); TableRouter tableRouter = table.getTableRouter(); for (RuleColumn ruleCol : tableRouter.getRuleColumns()) { Column[] columns = table.getColumns(); Column matched = null; for (Column column : columns) { String colName = column.getName(); if (colName.equalsIgnoreCase(ruleCol.getName())) { matched = column; break; } } if (matched == null) { throw DbException.getInvalidValueException("RuleColumn", ruleCol); } Value value = row.getValue(matched.getColumnId()); if (value != null && value != ValueNull.INSTANCE) { List<Value> values = New.arrayList(1); values.add(value); args.put(ruleCol.getName(), values); } } return args; } /** * @param table */ private RoutingResult fixedRoutingResult(TableNode ... tableNode) { RoutingResult result = RoutingResult.fixedResult(tableNode); return result; } /** * @param table * @param first * @param last * @param routingArgs */ private void exportRangeArg(TableMate table, SearchRow first, SearchRow last, Map<String, List<Value>> routingArgs) { TableRouter tr = table.getTableRouter(); List<RuleColumn> ruleCols = tr.getRuleColumns(); if (first != null && last != null) { for (int i = 0; first != null && i < first.getColumnCount(); i++) { Value firstV = first.getValue(i); Value listV = last.getValue(i); if (firstV == null || listV == null || firstV == ValueNull.INSTANCE || listV == ValueNull.INSTANCE) { continue; } Column col = table.getColumn(i); String colName = col.getName(); RuleColumn matched = null; for (RuleColumn ruleColumn : ruleCols) { if (colName.equalsIgnoreCase(ruleColumn.getName())) { matched = ruleColumn; } } if (matched == null) { continue; } List<Value> values = routingArgs.get(matched.getName()); if (values == null) { values = New.arrayList(); routingArgs.put(matched.getName(), values); } int compare = database.compare(firstV, listV); if (compare == 0) { values.add(firstV); } else if (compare < 0) { List<Value> enumValue = enumRange(firstV, listV); if (enumValue != null) { values.addAll(enumValue); } } else { throw new TableRoutingException(table.getName() + " routing error. The conidition " + matched.getName() + " is alwarys false."); } } } } private List<Value> enumRange(Value firstV, Value listV) { if (firstV.getType() != listV.getType()) { return null; } int type = firstV.getType(); switch (type) { case Value.BYTE: case Value.INT: case Value.LONG: case Value.SHORT: if (listV.subtract(firstV).getLong() > 200) { return null; } List<Value> enumValues = New.arrayList(10); Value enumValue = firstV; while (database.compare(enumValue, listV) <= 0) { enumValues.add(enumValue); Value increase = ValueLong.get(1).convertTo(enumValue.getType()); enumValue = enumValue.add(increase); } return enumValues; default: return null; } } private SearchRow getSearchRow(TableMate table, Session s, SearchRow row, int columnId, Value v, boolean max) { if (row == null) { row = table.getTemplateRow(); } else { v = getMax(table, s, row.getValue(columnId), v, max); } if (columnId < 0) { row.setKey(v.getLong()); } else { row.setValue(columnId, v); } return row; } private Value getMax(TableMate table, Session s, Value a, Value b, boolean bigger) { if (a == null) { return b; } else if (b == null) { return a; } if (s.getDatabase().getSettings().optimizeIsNull) { // IS NULL must be checked later if (a == ValueNull.INSTANCE) { return b; } else if (b == ValueNull.INSTANCE) { return a; } } int comp = a.compareTo(b, table.getDatabase().getCompareMode()); if (comp == 0) { return a; } if (a == ValueNull.INSTANCE || b == ValueNull.INSTANCE) { if (s.getDatabase().getSettings().optimizeIsNull) { // column IS NULL AND column <op> <not null> is always false return null; } } if (!bigger) { comp = -comp; } return comp > 0 ? a : b; } @Override public PartitionFunction getPartitionFunction(TableMate table) { RuleExpression ruleExpression = table.getTableRouter().getRuleExpression(); return ruleExpression.getFunction(); } }