package com.taobao.tddl.executor.cursor.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import com.taobao.tddl.common.exception.TddlException; import com.taobao.tddl.common.utils.GeneralUtil; import com.taobao.tddl.common.utils.TStringUtil; import com.taobao.tddl.executor.codec.CodecFactory; import com.taobao.tddl.executor.common.ExecutionContext; import com.taobao.tddl.executor.common.KVPair; import com.taobao.tddl.executor.common.TransactionConfig.Isolation; import com.taobao.tddl.executor.cursor.ICursorMeta; import com.taobao.tddl.executor.cursor.ISchematicCursor; import com.taobao.tddl.executor.cursor.ITempTableSortCursor; import com.taobao.tddl.executor.record.CloneableRecord; import com.taobao.tddl.executor.rowset.IRowSet; import com.taobao.tddl.executor.rowset.RowSetWrapper; import com.taobao.tddl.executor.spi.ICursorFactory; import com.taobao.tddl.executor.spi.IRepository; import com.taobao.tddl.executor.spi.ITable; import com.taobao.tddl.executor.utils.ExecUtils; import com.taobao.tddl.optimizer.config.table.ColumnMeta; import com.taobao.tddl.optimizer.config.table.IndexMeta; import com.taobao.tddl.optimizer.config.table.IndexType; import com.taobao.tddl.optimizer.config.table.Relationship; import com.taobao.tddl.optimizer.config.table.TableMeta; import com.taobao.tddl.optimizer.core.datatype.DataType; import com.taobao.tddl.optimizer.core.expression.IOrderBy; import com.taobao.tddl.optimizer.core.expression.ISelectable; import com.taobao.tddl.common.utils.logger.Logger; import com.taobao.tddl.common.utils.logger.LoggerFactory; /** * 用于临时表排序,需要依赖bdb * * @author mengshi.sunmengshi 2013-12-3 上午11:01:15 * @since 5.0.0 */ public class TempTableSortCursor extends SortCursor implements ITempTableSortCursor { private final static Logger logger = LoggerFactory.getLogger(TempTableSortCursor.class); protected static AtomicLong seed = new AtomicLong(0); protected long sizeProtection = 100000; protected ICursorFactory cursorFactory; boolean sortedDuplicates; private static final String identity = "__identity__".toUpperCase(); private boolean inited = false; private final IRepository repo; protected ISchematicCursor tempTargetCursor = null; ITable targetTable = null; protected ICursorMeta returnMeta = null; private final long requestID; ExecutionContext executionContext = null; /** * @param cursorFactory * @param repo * @param cursor * @param orderBys 按照何列排序 * @param sortedDuplicates 是否允许重复 * @throws FetchException * @throws TddlException */ public TempTableSortCursor(ICursorFactory cursorFactory, IRepository repo, ISchematicCursor cursor, List<IOrderBy> orderBys, boolean sortedDuplicates, long requestID, ExecutionContext executionContext) throws TddlException, TddlException{ super(cursor, orderBys); this.sortedDuplicates = sortedDuplicates; setCursorFactory(cursorFactory); tempTargetCursor = cursor; this.repo = repo; this.requestID = requestID; this.executionContext = executionContext; } private void initTTSC() throws TddlException { if (!inited) { prepare(repo, tempTargetCursor, orderBys); inited = true; } } protected ISchematicCursor prepare(IRepository repo, ISchematicCursor cursor, List<IOrderBy> orderBys) throws TddlException { List<ColumnMeta> columns = new ArrayList<ColumnMeta>(); List<ColumnMeta> values = new ArrayList<ColumnMeta>(); // 用来生成CursorMeta的 列 List<ColumnMeta> metaColumns = new ArrayList<ColumnMeta>(); List<ColumnMeta> metaValues = new ArrayList<ColumnMeta>(); // 遍历cursor的keyColumn,如果某个列是order by的条件,那么放到temp // cursor的key里面,否则放到value里面 IRowSet rowSet = cursor.next(); if (rowSet != null) { // 希望通过kv中的列来构造meta data,因为底层讲avg(pk),解释成了count,和 sum buildColumnMeta(cursor, orderBys, columns, values, rowSet, metaValues, metaColumns); } else { // 如果没值返回空 ,什么都不做 return null; } String oldTableName = rowSet.getParentCursorMeta().getColumns().iterator().next().getTableName(); String tableName; synchronized (this.getClass()) { seed.addAndGet(1); // 因为tempTable里面的表名不是外部所用的表名,而是使用tmp作为表名,所以需要先变成外部表名. 这里非常hack.. // 因为实际做cursor的表名匹配的时候,使用的是截取法。。取第一个"."之前的作为匹配标志。 tableName = oldTableName + ".tmp." + System.currentTimeMillis() + "." + seed + "requestID." + requestID; if (logger.isDebugEnabled()) { logger.warn("tempTableName:\n" + tableName); } } IndexMeta primary_meta = new IndexMeta(tableName, columns, values, IndexType.BTREE, Relationship.ONE_TO_ONE, true, true, null); TableMeta tmpSchema = new TableMeta(tableName, new ArrayList(), primary_meta, null); tmpSchema.setTmp(true); tmpSchema.setSortedDuplicates(sortedDuplicates); // 增加临时表的判定 targetTable = repo.getTempTable(tmpSchema); CloneableRecord key = CodecFactory.getInstance(CodecFactory.FIXED_LENGTH).getCodec(columns).newEmptyRecord(); CloneableRecord value = null; if (values != null && values.size() != 0) { value = CodecFactory.getInstance(CodecFactory.FIXED_LENGTH).getCodec(values).newEmptyRecord(); } int i = 0; long size = 0; boolean protection = false; // 建立临时表,将老数据插入新表中。 if (rowSet != null) { do { size++; if (size > sizeProtection) { protection = true; break; } for (ColumnMeta column : columns) { String colName = column.getName(); if (colName.contains(".")) { String[] sp = TStringUtil.split(colName, "."); colName = sp[sp.length - 1]; } /** * 在临时表的时候,来自不同2个表的相同的列,比如 a.id和b.id * 会在这里被合并,导致后面取值有问题。现在准备将临时表中的columnName改成 * tableName.columnName的形式。顺序不变可以将后面的取值完成 有点hack.. */ Object o = ExecUtils.getValueByTableAndName(rowSet, column.getTableName(), colName); key.put(column.getName(), o); } if (values != null && values.size() != 0) { for (ColumnMeta valueColumn : values) { String colName = valueColumn.getName(); if ("__IDENTITY__".equals(colName)) { continue; } if (colName.contains(".")) { // String[] sp = StringUtil.split(colName, "."); // colName = sp[sp.length-1]; int m = colName.indexOf("."); colName = colName.substring(m + 1, colName.length()); } /** * 在临时表的时候,来自不同2个表的相同的列,比如 a.id和b.id * 会在这里被合并,导致后面取值有问题。现在准备将临时表中的columnName改成 * tableName.columnName的形式。顺序不变可以将后面的取值完成 有点hack.. */ Object o = ExecUtils.getValueByTableAndName(rowSet, valueColumn.getTableName(), colName); value.put(valueColumn.getName(), o); } } // value内加入唯一索引key。 if (sortedDuplicates) { value.put(identity, i++); } // System.out.println("TempTableSortCursor: "+key+" "+value); targetTable.put(this.executionContext, key, value, primary_meta, tableName); } while ((rowSet = cursor.next()) != null); } ExecutionContext tmpContext = new ExecutionContext(); tmpContext.setIsolation(Isolation.READ_UNCOMMITTED); ISchematicCursor ret = targetTable.getCursor(tmpContext, primary_meta, tableName); // 去除唯一标志 List<ColumnMeta> retColumns = new ArrayList<ColumnMeta>(); // 将唯一标志,从返回数据内排除 retColumns.addAll(metaColumns); for (ColumnMeta cm : metaValues) { if (!identity.equals(cm.getName())) { retColumns.add(cm); } } if (!orderBys.get(0).getDirection()) { ret = cursorFactory.reverseOrderCursor(executionContext, ret); } List<TddlException> exs = new ArrayList(); exs = cursor.close(exs); if (!exs.isEmpty()) { throw GeneralUtil.mergeException(exs); } if (protection) { exs = this.close(exs); if (!exs.isEmpty()) { throw GeneralUtil.mergeException(exs); } throw new IllegalStateException("temp table size protection , check your sql or enlarge the limination size . "); } this.cursor = ret; returnMeta = CursorMetaImp.buildNew(retColumns); return ret; } private void buildColumnMeta(ISchematicCursor cursor, List<IOrderBy> orderBys, List<ColumnMeta> columns, List<ColumnMeta> values, IRowSet kv, List<ColumnMeta> metaValues, List<ColumnMeta> metaColumns) { ICursorMeta iCursorMeta = kv.getParentCursorMeta(); List<ColumnMeta> columnMeta = iCursorMeta.getColumns(); Set<IOrderBy> hashOrderBys = new HashSet<IOrderBy>(); for (ColumnMeta cm : columnMeta) { if (findOrderByInKey(orderBys, columns, cm, hashOrderBys)) { if (!metaColumns.contains(cm)) metaColumns.add(cm); continue; } else { // 列名与order by not match ,放到value里 ColumnMeta cm2 = new ColumnMeta(cm.getTableName(), cm.getTableName() + "." + cm.getName(), cm.getDataType(), cm.getAlias(), cm.isNullable()); if (!values.contains(cm2)) { values.add(cm2); } if (!metaValues.contains(cm)) { metaValues.add(cm); } } } // 是否针对重复的value进行排序 if (sortedDuplicates) {// identity if (columns.size() < orderBys.size()) { // throw new RuntimeException("should not be here"); for (IOrderBy ob : orderBys) { if (hashOrderBys.contains(ob)) { continue; } else { ISelectable cm = ob.getColumn(); ColumnMeta cm2 = new ColumnMeta(cm.getTableName(), cm.getTableName() + "." + cm.getColumnName(), cm.getDataType(), cm.getAlias(), true); columns.add(cm2); metaColumns.add(cm2); } } } values.add(new ColumnMeta(columns.get(0).getTableName(), identity, DataType.IntegerType, null, true)); } } private boolean findOrderByInKey(List<IOrderBy> orderBys, List<ColumnMeta> columns, ColumnMeta cm, Set<IOrderBy> hashOrderBys) { for (IOrderBy ob : orderBys) { ISelectable iSelectable = ob.getColumn(); String orderByTable = iSelectable.getTableName(); orderByTable = ExecUtils.getLogicTableName(orderByTable); if (cm != null && TStringUtil.equals(ExecUtils.getLogicTableName(cm.getTableName()), orderByTable)) { if (TStringUtil.equals(cm.getName(), iSelectable.getColumnName())) { ColumnMeta cm2 = new ColumnMeta(cm.getTableName(), cm.getTableName() + "." + cm.getName(), cm.getDataType(), cm.getAlias(), cm.isNullable()); // 列名与order by Match.放到key里 columns.add(cm2); hashOrderBys.add(ob); return true; } } } return false; } public ICursorFactory getCursorFactory() { return cursorFactory; } public void setCursorFactory(ICursorFactory cursorFactory) { this.cursorFactory = cursorFactory; } @Override public IRowSet next() throws TddlException { initTTSC(); IRowSet next = parentCursorNext(); next = wrap(next); // System.out.println("TempTableSortCursor: next "+next); return next; } private IRowSet wrap(IRowSet next) { if (next != null) { next = new RowSetWrapper(returnMeta, next); } return next; } @Override public boolean skipTo(CloneableRecord key) throws TddlException { initTTSC(); return parentCursorSkipTo(key); } @Override public boolean skipTo(KVPair key) throws TddlException { initTTSC(); return parentCursorSkipTo(key); } @Override public IRowSet current() throws TddlException { initTTSC(); IRowSet current = parentCursorCurrent(); current = wrap(current); return current; } @Override public IRowSet first() throws TddlException { initTTSC(); IRowSet first = parentCursorFirst(); first = wrap(first); return first; } @Override public IRowSet last() throws TddlException { initTTSC(); IRowSet last = parentCursorPrev(); last = wrap(last); return last; } @Override public IRowSet prev() throws TddlException { initTTSC(); IRowSet prev = parentCursorPrev(); prev = wrap(prev); return prev; } @Override public List<TddlException> close(List<TddlException> exs) { exs = parentCursorClose(exs); if (targetTable != null) try { targetTable.close(); } catch (TddlException e) { exs.add(e); } return exs; } @Override public String toString() { return toStringWithInden(0); } @Override public String toStringWithInden(int inden) { String tabTittle = GeneralUtil.getTab(inden); String tabContent = GeneralUtil.getTab(inden + 1); StringBuilder sb = new StringBuilder(); sb.append(tabTittle).append("TempTableCursor").append("\n"); GeneralUtil.printAFieldToStringBuilder(sb, "orderBy", this.orderBys, tabContent); if (this.cursor != null) { sb.append(this.tempTargetCursor.toStringWithInden(inden + 1)); } return sb.toString(); } @Override public List<ColumnMeta> getReturnColumns() throws TddlException { initTTSC(); return this.returnMeta.getColumns(); } }