package com.tesora.dve.distribution; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.datatype.XMLGregorianCalendar; import com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl; import com.tesora.dve.common.PEStringUtils; import com.tesora.dve.common.catalog.DistributionModel; import com.tesora.dve.common.catalog.PersistentColumn; import com.tesora.dve.common.catalog.PersistentTable; import com.tesora.dve.common.catalog.StorageGroup; import com.tesora.dve.common.catalog.UserTable; import com.tesora.dve.distribution.compare.ComparatorCache; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.resultset.ResultColumn; import com.tesora.dve.resultset.ResultRow; import com.tesora.dve.sql.schema.ConnectionValues; import com.tesora.dve.sql.util.UnaryFunction; @SuppressWarnings("restriction") @XmlRootElement public class KeyValue extends LinkedHashMap<String, ColumnDatum> implements IKeyValue { private static final long serialVersionUID = 1L; private PersistentTable userTable; private Integer rangeID; public KeyValue(PersistentTable ut, Integer rangeID) { userTable = ut; this.rangeID = rangeID; } public KeyValue(KeyValue k) { for (String key : k.keySet()) put(key, new ColumnDatum(k.get(key))); userTable = k.userTable; rangeID = k.rangeID; } public KeyValue(PersistentTable targetTable, Integer rangeID, List<String> distColumns) throws PEException { userTable = targetTable; this.rangeID = rangeID; for (String colName : distColumns) { PersistentColumn col = userTable.getUserColumn(colName); if (col == null) throw new PEException("Column " + colName + " not found in table " + userTable.displayName()); addColumnTemplate(col); } } @Override public DistributionModel getDistributionModel() { return userTable.getDistributionModel(); } @Override public StorageGroup getPersistentGroup() { return userTable.getPersistentGroup(); } @Override public int getPersistentGroupId() { return getPersistentGroup().getId(); } public PersistentTable getUserTable() { return userTable; } @Override public int getUserTableId() { return userTable.getId(); } @Override public String getQualifiedTableName() { return userTable.getQualifiedName(); } public void addColumnTemplate(PersistentColumn col) { ColumnDatum cd = new ColumnDatum(col); this.put(col.getPersistentName(), cd); } public void populateFromResultSet(ResultSet results) throws SQLException { for (ColumnDatum cd : this.values()) { cd.setValue(results.getObject(cd.getColumn().getPersistentName())); } } @Override public boolean equals(Object o) { return equals((IKeyValue)this,(IKeyValue)o); } public static boolean equals(IKeyValue left, IKeyValue right) { boolean isEqual; try { isEqual = compare(left, right) == 0; } catch (Throwable t) { throw new RuntimeException("Cannot compare values for equality", t); } return isEqual; } @Override public int hashCode() { return buildHashCode(this); } public static int buildHashCode(IKeyValue ikv) { int code = 0; for(IColumnDatum icd : ikv.getValues().values()) { code = code * 31 + icd.hashCode(); } return code; } public KeyValue populateFromResultSet(ResultRow row, Map<String, Integer> colMap) { for (ColumnDatum cd : this.values()) { String colName = cd.userColumn.getAliasName(); int colIdx = colMap.get(colName); ResultColumn resultCol = row.getResultColumn(colIdx+1); cd.value = resultCol.getColumnValue(); } return this; } @SuppressWarnings({ "unchecked", "rawtypes" }) private static <T extends IColumnDatum> int compareCol(T c1, T c2) { int compareResult; if (c1.getComparatorClassName() != null) compareResult = ComparatorCache.get(c1.getComparatorClassName()).compare(c1.getValue(), c2.getValue()); else { final Comparable value1 = c1.getValueForComparison(); final Comparable value2 = c2.getValueForComparison(); compareResult = value1.compareTo(value2); } return compareResult; } @Override public int compare(IKeyValue other) throws PEException { return compare(this,other); } public static int compare(IKeyValue left, IKeyValue right) throws PEException { int c = 0; Map<String,? extends IColumnDatum> leftValues = left.getValues(); Map<String,? extends IColumnDatum> rightValues = right.getValues(); if (leftValues.size() == rightValues.size()) { Iterator<? extends IColumnDatum> iThis = leftValues.values().iterator(); Iterator<? extends IColumnDatum> iOther = rightValues.values().iterator(); while (c == 0 && iThis.hasNext()) { c = compareCol(iThis.next(), iOther.next()); } } else { throw new PEException("Cannot compare "+PEStringUtils.toString(left.getQualifiedTableName(), leftValues.keySet()) +" to "+PEStringUtils.toString(right.getQualifiedTableName(), rightValues.keySet())); } return c; } public void setUserTable(UserTable ut) { userTable = ut; } @Override public String toString() { return PEStringUtils.toString(this.userTable.getPersistentName(), this); } public String orderedKeyNames() { List<String> keyNames = Arrays.asList(keySet().toArray(new String[]{})); Collections.sort(keyNames); String okn = keyNames.toString(); return okn.substring(1, okn.length()-1); } @Override public Map<String, ? extends IColumnDatum> getValues() { return this; } @Override public DistributionModel getContainerDistributionModel() { if (userTable.getContainer() == null) return null; return userTable.getContainer().getDistributionModel(); } private static Map<Object, UnaryFunction<Object, Object>> typePromotionMap = new HashMap<Object, UnaryFunction<Object,Object>>() { private static final long serialVersionUID = 1L; { put(Long.class, new UnaryFunction<Object,Object>() { @Override public Object evaluate(Object o) { return new Long((Long)o); } }); put(Integer.class, new UnaryFunction<Object,Object>() { @Override public Object evaluate(Object o) { return new Long((Integer)o); } }); put(Short.class, new UnaryFunction<Object,Object>() { @Override public Object evaluate(Object o) { return new Long(((Short)o).longValue()); } }); put(Byte.class, new UnaryFunction<Object,Object>() { @Override public Object evaluate(Object o) { return new Long(((Byte) o).longValue()); } }); put(Float.class, new UnaryFunction<Object,Object>() { @Override public Object evaluate(Object o) { return new Double((Float)o); } }); // this is to handle the class name as returned from the xml in the generation_key_range table put(XMLGregorianCalendarImpl.class, new UnaryFunction<Object,Object>() { @Override public Object evaluate(Object o) { return ((XMLGregorianCalendar)o).toGregorianCalendar().getTime(); } }); }}; @Override public int compare(RangeLimit rangeLimit) throws PEException { return compare(this, rangeLimit); } @SuppressWarnings({ "unchecked", "rawtypes" }) private static <T extends IColumnDatum> int compareCol(T c1, Object c2) { int compareResult; if (c1.getComparatorClassName() != null) compareResult = ComparatorCache.get(c1.getComparatorClassName()).compare(c1.getValue(), c2); else { final Comparable value1 = c1.getValueForComparison(); if (value1.getClass() != c2.getClass() && typePromotionMap.containsKey(c2.getClass())) compareResult = value1.compareTo(typePromotionMap.get(c2.getClass()).evaluate(c2)); else compareResult = value1.compareTo(c2); } return compareResult; } public static int compare(IKeyValue keyValue, RangeLimit rangeLimit) throws PEException { int result = 0; Map<String,? extends IColumnDatum> keyValueMap = keyValue.getValues(); if (keyValueMap.size() == rangeLimit.size()) { Iterator<? extends IColumnDatum> iKeyValue = keyValueMap.values().iterator(); Iterator<Object> iRangeLimit = rangeLimit.iterator(); while (result == 0 && iKeyValue.hasNext()) { result = compareCol(iKeyValue.next(), iRangeLimit.next()); } } else { throw new PEException("Cannot compare "+PEStringUtils.toString(keyValue.getQualifiedTableName(), keyValueMap.keySet()) +" to "+ rangeLimit.toString()); } return result; } @Override public Integer getRangeId() { return rangeID; } @Override public IKeyValue rebind(ConnectionValues cv) { return this; } }