All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.scudata.dw.RowPhyTable Maven / Gradle / Ivy

Go to download

SPL(Structured Process Language) A programming language specially for structured data computing.

There is a newer version: 20240823
Show newest version
package com.scudata.dw;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import com.scudata.common.MessageManager;
import com.scudata.common.RQException;
import com.scudata.dm.Context;
import com.scudata.dm.DataStruct;
import com.scudata.dm.LongArray;
import com.scudata.dm.ObjectReader;
import com.scudata.dm.ObjectWriter;
import com.scudata.dm.Record;
import com.scudata.dm.Sequence;
import com.scudata.dm.Table;
import com.scudata.dm.cursor.ConjxCursor;
import com.scudata.dm.cursor.ICursor;
import com.scudata.dm.cursor.MemoryCursor;
import com.scudata.dm.cursor.MergesCursor;
import com.scudata.dm.cursor.MultipathCursors;
import com.scudata.expression.Constant;
import com.scudata.expression.Expression;
import com.scudata.expression.Node;
import com.scudata.expression.UnknownSymbol;
import com.scudata.expression.operator.Equals;
import com.scudata.expression.operator.Greater;
import com.scudata.expression.operator.NotEquals;
import com.scudata.expression.operator.NotGreater;
import com.scudata.expression.operator.NotSmaller;
import com.scudata.expression.operator.Smaller;
import com.scudata.resources.EngineMessage;
import com.scudata.util.Variant;

/**
 * ?д??????
 * @author runqian
 *
 */
public class RowPhyTable extends PhyTable {
	protected transient String []sortedColNames; // ?????ֶ?(ά?ֶ?)
	private transient String []allSortedColNames; // ?????ֶ?(ά?ֶ?) ??????
	protected transient String []allKeyColNames; // key?ֶ?
	protected BlockLink dataBlockLink; // ?п???????

	protected transient BlockLinkWriter colWriter;
	protected transient ObjectWriter objectWriter;
	protected int sortedColStartIndex;////??????ά?ֶθ???
	
	protected boolean[] isDim;//?Ƿ???ά?ֶ?
	protected boolean[] isKey;//?Ƿ???key?ֶ?
	protected int []serialBytesLen;//ÿ???е??źų??ȣ?0??ʾ?????ź?????
	
	/**
	 * ???????л?
	 * @param groupTable
	 */
	public RowPhyTable(ComTable groupTable) {
		this.groupTable = groupTable;
		dataBlockLink = new BlockLink(groupTable);
		segmentBlockLink = new BlockLink(groupTable);
		this.modifyBlockLink1 = new BlockLink(groupTable);
		this.modifyBlockLink2 = new BlockLink(groupTable);
	}
	
	/**
	 * ???????л?
	 * @param groupTable
	 * @param parent
	 */
	public RowPhyTable(ComTable groupTable, RowPhyTable parent) {
		this.groupTable = groupTable;
		this.parent = parent;
		dataBlockLink = new BlockLink(groupTable);
		segmentBlockLink = new BlockLink(groupTable);
		this.modifyBlockLink1 = new BlockLink(groupTable);
		this.modifyBlockLink2 = new BlockLink(groupTable);
	}
	
	/**
	 * ?½?????
	 * @param groupTable
	 * @param colNames
	 * @throws IOException
	 */
	public RowPhyTable(ComTable groupTable, String []colNames) throws IOException {
		// ?????????飬?????ļ???ʱ????Ӱ?쵽????????????Ĵ???
		String []tmp = new String[colNames.length];
		System.arraycopy(colNames, 0, tmp, 0, colNames.length);
		colNames = tmp;

		this.groupTable = groupTable;
		this.tableName = "";
		dataBlockLink = new BlockLink(groupTable);
		this.segmentBlockLink = new BlockLink(groupTable);
		this.modifyBlockLink1 = new BlockLink(groupTable);
		this.modifyBlockLink2 = new BlockLink(groupTable);
		
		int len = colNames.length;
		isDim = new boolean[len];
		isKey = new boolean[len];
		serialBytesLen = new int[len];
		
		int keyStart = -1; // ????????ʼ?ֶ?
		
		// ??????ʼ?ֶ?ǰ????ֶ???Ϊ???????ֶ?
		for (int i = 0; i < len; ++i) {
			if (colNames[i].startsWith(KEY_PREFIX)) {
				keyStart = i;
				break;
			}
		}
		
		for (int i = 0; i < len; ++i) {
			if (colNames[i].startsWith(KEY_PREFIX)) {
				colNames[i] = colNames[i].substring(KEY_PREFIX.length());
				isDim[i] = true;
				isKey[i] = true;
			} else if (i < keyStart) {
				isDim[i] = true;
			}
		}

		this.colNames = colNames;
		init();
		
		if (sortedColNames == null) {
			hasPrimaryKey = false;
			isSorted = false;
		}
		tableList = new ArrayList();
	}
	
	/**
	 * ?????Ĵ???
	 * @param groupTable Ҫ?????????????
	 * @param colNames ??????
	 * @param serialBytesLen ?źų???
	 * @param tableName ??????
	 * @param parent ????????
	 * @throws IOException
	 */
	public RowPhyTable(ComTable groupTable, String []colNames, int []serialBytesLen,
			String tableName, RowPhyTable parent) throws IOException {
		// ?????????飬?????ļ???ʱ????Ӱ?쵽????????????Ĵ???
		String []tmp = new String[colNames.length];
		System.arraycopy(colNames, 0, tmp, 0, colNames.length);
		colNames = tmp;

		this.groupTable = groupTable;
		this.tableName = tableName;
		this.parent = parent;
		dataBlockLink = new BlockLink(groupTable);
		this.segmentBlockLink = new BlockLink(groupTable);
		this.modifyBlockLink1 = new BlockLink(groupTable);
		this.modifyBlockLink2 = new BlockLink(groupTable);
		
		this.serialBytesLen = serialBytesLen;
		int len = colNames.length;
		isDim = new boolean[len];
		isKey = new boolean[len];
		for (int i = 0; i < len; ++i) {
			String name = colNames[i];
			if (name.startsWith("#")) {
				colNames[i] = name.substring(1);
				isDim[i] = isKey[i] = true;
			} else {
				isDim[i] = isKey[i] = false;
			}
		}
		this.colNames = colNames;
		init();
		
		if (getAllSortedColNames() == null) {
			hasPrimaryKey = false;
			isSorted = false;
		}
		
		if (parent != null) {
			//Ŀǰ????ֻ????һ??
			if (parent.parent != null) {
				MessageManager mm = EngineMessage.get();
				throw new RQException(mm.getMessage("ds.dsNotMatch"));
			}
			
			PhyTable primaryTable = parent;
			String []primarySortedColNames = primaryTable.getSortedColNames();
			String []primaryColNames = primaryTable.getColNames();
			ArrayList collist = new ArrayList();
			for (String name : primaryColNames) {
				collist.add(name);
			}
			
			//?ֶβ??????????ֶ??ظ?
			for (int i = 0; i < len; i++) {
				if (collist.contains(colNames[i])) {
					MessageManager mm = EngineMessage.get();
					throw new RQException(colNames[i] + mm.getMessage("dw.fieldSameToPrimaryTable"));
				}
			}
			
			for (String name : primarySortedColNames) {
				collist.remove(name);
			}

			sortedColStartIndex = primarySortedColNames.length;
		}
		
		tableList = new ArrayList();
	}
	
	/**
	 * ????src????һ??ͬ???Ļ???
	 * @param groupTable Ҫ?????????????
	 * @param parent ????????
	 * @param src ?ṩ?ṹ??Դ????
	 * @throws IOException
	 */
	public RowPhyTable(ComTable groupTable, RowPhyTable parent, RowPhyTable src) throws IOException {
		this.groupTable = groupTable;
		this.parent = parent;
		
		System.arraycopy(src.reserve, 0, reserve, 0, reserve.length);
		segmentCol = src.segmentCol;
		segmentSerialLen = src.segmentSerialLen;
		tableName = src.tableName;
		colNames = src.colNames;

		isDim = src.isDim;
		isKey = src.isKey;
		serialBytesLen = src.serialBytesLen;
		
		dataBlockLink = new BlockLink(groupTable);
		this.segmentBlockLink = new BlockLink(groupTable);
		this.modifyBlockLink1 = new BlockLink(groupTable);
		this.modifyBlockLink2 = new BlockLink(groupTable);
		
		init();
		
		if (getAllSortedColNames() == null) {
			hasPrimaryKey = false;
			isSorted = false;
		}
		
		tableList = new ArrayList();
		for (PhyTable srcSub : src.tableList) {
			tableList.add(new RowPhyTable(groupTable, this, (RowPhyTable)srcSub));
		}
	}

	/**
	 * ??ʼ??????ȡά???????Ȼ?????Ϣ
	 */
	protected void init() {
		String[] col = colNames;
		int dimCount = getDimCount();
		int keyCount = getKeyCount();
		int count = col.length;

		if (dimCount > 0) {
			sortedColNames = new String[dimCount];
			int j = 0;
			for (int i = 0; i < count; ++i) {
				if (isDim(col[i])) {
					sortedColNames[j++] = col[i];
				}
			}
		}
		
		if (keyCount > 0) {
			allKeyColNames = new String[keyCount];
			int j = 0;
			for (int i = 0; i < count; ++i) {
				if (isKey(col[i])) {
					allKeyColNames[j++] = col[i];
				}
			}
		}
		
		if (parent != null) {
			PhyTable primaryTable = parent;
			String []primarySortedColNames = primaryTable.getAllSortedColNames();
			sortedColStartIndex = primarySortedColNames.length;

			allColNames = new String[sortedColStartIndex + colNames.length];
			int i = 0;
			for (String colName : primarySortedColNames) {
				allColNames[i++] = colName;
			}
			for (String colName : colNames) {
				allColNames[i++] = colName;
			}
			
			allSortedColNames = new String[sortedColStartIndex + dimCount];
			i = 0;
			if (primarySortedColNames != null) {
				for (String colName : primarySortedColNames) {
					allSortedColNames[i++] = colName;
				}
			}
			if (sortedColNames != null) {
				for (String colName : sortedColNames) {
					allSortedColNames[i++] = colName;
				}
			}
			ds = new DataStruct(getAllColNames());
		} else {
			ds = new DataStruct(colNames);
		}
	}
	
	/**
	 * ????????ά??????
	 * @return
	 */
	public String[] getSortedColNames() {
		return sortedColNames;
	}

	/**
	 * ????????key??????
	 * @return
	 */
	public String[] getAllKeyColNames() {
		return allKeyColNames;
	}
	
	/**
	 * ?????һ??ռ?
	 */
	protected void applyFirstBlock() throws IOException {
		if (segmentBlockLink.isEmpty()) {
			segmentBlockLink.setFirstBlockPos(groupTable.applyNewBlock());
			
			if (dataBlockLink.isEmpty()) {
				dataBlockLink.setFirstBlockPos(groupTable.applyNewBlock());
			}
		}
	}
	
	/**
	 * ׼??д????׷?ӡ?ɾ?????޸?????ǰ???á?
	 * ???ú??Թؼ???Ϣ???б??ݣ???ֹдһ???????ʱ????????
	 */
	protected void prepareAppend() throws IOException {
		applyFirstBlock();
		
		colWriter = new BlockLinkWriter(dataBlockLink, true);
		segmentWriter = new BlockLinkWriter(segmentBlockLink, true);
		objectWriter = new ObjectWriter(segmentWriter, groupTable.getBlockSize() - ComTable.POS_SIZE);
	}
	
	/**
	 * ????д
	 */
	protected void finishAppend() throws IOException {
		colWriter.finishWrite();
		colWriter = null;
		
		objectWriter.flush();
		segmentWriter.finishWrite();
		segmentWriter = null;
		objectWriter = null;
		
		groupTable.save();
		updateIndex();
	}
	
	/**
	 * ??ȡ??ͷ????
	 */
	public void readExternal(BufferReader reader) throws IOException {
		reader.read(reserve);
		tableName = reader.readUTF();
		colNames = reader.readStrings();
		dataBlockCount = reader.readInt32();
		totalRecordCount = reader.readLong40();
		segmentBlockLink.readExternal(reader);
		dataBlockLink.readExternal(reader);
		curModifyBlock = reader.readByte();
		modifyBlockLink1.readExternal(reader);
		modifyBlockLink2.readExternal(reader);
		
		int count = reader.readInt();
		colNames = new String[count];
		for (int i = 0; i < count; ++i) {
			colNames[i] = reader.readUTF();
		}
		serialBytesLen = new int[count];
		for (int i = 0; i < count; ++i) {
			serialBytesLen[i] = reader.readInt();
		}
		isDim = new boolean[count];
		for (int i = 0; i < count; ++i) {
			isDim[i] = reader.readBoolean();
		}
		
		if (reserve[0] > 0) {
			isKey = new boolean[count];
			for (int i = 0; i < count; ++i) {
				isKey[i] = reader.readBoolean();
			}
		}
		
		count = reader.readInt();
		if (count > 0) {
			maxValues = new Object[count];
			for (int i = 0; i < count; ++i) {
				maxValues[i] = reader.readObject();
			}
		}
		
		hasPrimaryKey = reader.readBoolean();
		isSorted = reader.readBoolean();
		
		indexNames = reader.readStrings();
		if (indexNames == null) {
			indexFields = null;
			indexValueFields = null;
		} else {
			int indexCount = indexNames.length;
			indexFields = new String[indexCount][];
			for (int i = 0; i < indexCount; i++) {
				indexFields[i] = reader.readStrings();
			}
			indexValueFields = new String[indexCount][];
			for (int i = 0; i < indexCount; i++) {
				indexValueFields[i] = reader.readStrings();
			}
		}
		
		if (groupTable.reserve[0] > 2) {
			cuboids = reader.readStrings();//?汾3????
		}
		segmentCol = (String)reader.readObject();
		segmentSerialLen = reader.readInt();
		init();
		
		count = reader.readInt();
		tableList = new ArrayList(count);
		for (int i = 0; i < count; ++i) {
			PhyTable table = new RowPhyTable(groupTable, this);
			table.readExternal(reader);
			tableList.add(table);
		}
	}
	
	/**
	 * д????ͷ????
	 */
	public void writeExternal(BufferWriter writer) throws IOException {
		reserve[0] = 1;
		writer.write(reserve);
		writer.writeUTF(tableName);
		writer.writeStrings(colNames);
		writer.writeInt32(dataBlockCount);
		writer.writeLong40(totalRecordCount);
		segmentBlockLink.writeExternal(writer);
		dataBlockLink.writeExternal(writer);
		writer.writeByte(curModifyBlock);
		modifyBlockLink1.writeExternal(writer);
		modifyBlockLink2.writeExternal(writer);
		
		String []colNames = this.colNames;
		int count = colNames.length;
		writer.writeInt(count);
		for (int i = 0; i < count; ++i) {
			writer.writeUTF(colNames[i]);
		}
		for (int i = 0; i < count; ++i) {
			writer.writeInt(serialBytesLen[i]);
		}
		for (int i = 0; i < count; ++i) {
			writer.writeBoolean(isDim[i]);
		}
		
		//?汾1????
		for (int i = 0; i < count; ++i) {
			writer.writeBoolean(isKey[i]);
		}
		
		if (maxValues == null) {
			writer.writeInt(0);
		} else {
			writer.writeInt(maxValues.length);
			for (Object val : maxValues) {
				writer.writeObject(val);
			}
			
			writer.flush();
		}
		
		writer.writeBoolean(hasPrimaryKey);
		writer.writeBoolean(isSorted);
		
		writer.writeStrings(indexNames);
		if (indexNames != null) {
			for (int i = 0, indexCount = indexNames.length; i < indexCount; i++) {
				writer.writeStrings(indexFields[i]);
			}
			for (int i = 0, indexCount = indexNames.length; i < indexCount; i++) {
				writer.writeStrings(indexValueFields[i]);
			}
		}
		
		writer.writeStrings(cuboids);//?汾3????
		
		writer.writeObject(segmentCol);
		writer.flush();
		writer.writeInt(segmentSerialLen);
		
		ArrayList tableList = this.tableList;
		count = tableList.size();
		writer.writeInt(count);
		for (int i = 0; i < count; ++i) {
			tableList.get(i).writeExternal(writer);
		}
	}
	
	/**
	 * ׷?Ӹ?????һ??????
	 * @param data
	 * @param start ??ʼ????
	 * @param recList ????????
	 * @throws IOException
	 */
	private void appendAttachedDataBlock(Sequence data, boolean []isMyCol, LongArray recList) throws IOException {
		Record r;
		int count = allColNames.length;
		boolean isDim[] = getDimIndex();
		Object []minValues = null;//һ?????Сάֵ
		Object []maxValues = null;//һ??????άֵ
		
		if (sortedColNames != null) {
			minValues = new Object[count];
			maxValues = new Object[count];
		}
		
		RowBufferWriter bufferWriter= new RowBufferWriter(groupTable.getStructManager());
		long recNum = totalRecordCount;
		
		int end = data.length();
		for (int i = 1; i <= end; ++i) {
			//дα??
			bufferWriter.writeObject(++recNum);
			//д????
			bufferWriter.writeObject(recList.get(i - 1));
			
			r = (Record) data.get(i);
			Object[] vals = r.getFieldValues();
			//дһ????buffer
			for (int j = 0; j < count; j++) {
				if (!isMyCol[j]) continue;
				Object obj = vals[j];
				bufferWriter.writeObject(obj);
			}
			for (int j = 0; j < count; j++) {
				if (!isMyCol[j]) continue;
				Object obj = vals[j];
				if (isDim[j]) {
					if (Variant.compare(obj, maxValues[j], true) > 0)
						maxValues[j] = obj;
					if (i == 1)
						minValues[j] = obj;//??һ??Ҫ??ֵ????Ϊnull??ʾ??С
					if (Variant.compare(obj, minValues[j], true) < 0)
						minValues[j] = obj;
				}
			}
		}

		if (recList.size() == 0) {
			//????ǿտ飬??дһ??null
			bufferWriter.writeObject(null);
		}
		
		if (sortedColNames == null) {
			//?ύbuffer???п?
			long pos = colWriter.writeDataBuffer(bufferWriter.finish());
			//???·ֶ???Ϣ
			appendSegmentBlock(end);
			objectWriter.writeLong40(pos);
		} else {
			//?ύbuffer???п?
			long pos = colWriter.writeDataBuffer(bufferWriter.finish());
			//???·ֶ???Ϣ
			appendSegmentBlock(end);
			objectWriter.writeLong40(pos);
			for (int i = 0; i < count; ++i) {
				if (!isMyCol[i]) continue;
				if (isDim[i]) {
					objectWriter.writeObject(minValues[i]);
					objectWriter.writeObject(maxValues[i]);
				}
			}
		}
	}
	
	/**
	 * ??data???е?ָ????Χ??????д??
	 * @param data ????????
	 * @param start ??ʼλ??
	 * @param end ????λ??
	 * @throws IOException
	 */
	protected void appendDataBlock(Sequence data, int start, int end) throws IOException {
		Record r;
		int count = colNames.length;
		boolean isDim[] = getDimIndex();
		Object []minValues = null;//һ?????Сάֵ
		Object []maxValues = null;//һ??????άֵ

		if (sortedColNames != null) {
			minValues = new Object[count];
			maxValues = new Object[count];
		}

		RowBufferWriter bufferWriter= new RowBufferWriter(groupTable.getStructManager());
		long recNum = totalRecordCount;
		
		for (int i = start; i <= end; ++i) {
			r = (Record) data.get(i);
			Object[] vals = r.getFieldValues();
			//??һ??????????д??buffer
			bufferWriter.writeObject(++recNum);//?д?Ҫ??дһ??α??
			for (int j = 0; j < count; j++) {
				Object obj = vals[j];
				bufferWriter.writeObject(obj);
				if (isDim[j]) {
					if (Variant.compare(obj, maxValues[j], true) > 0)
						maxValues[j] = obj;
					if (i == start)
						minValues[j] = obj;//??һ??Ҫ??ֵ????Ϊnull??ʾ??С
					if (Variant.compare(obj, minValues[j], true) < 0)
						minValues[j] = obj;
				}
			}
		}
		
		//д????ʱ??ѹ??
		if (sortedColNames == null) {
			//?ύbuffer???п?
			long pos = colWriter.writeDataBuffer(bufferWriter.finish());
			//???·ֶ???Ϣ
			appendSegmentBlock(end - start + 1);
			objectWriter.writeLong40(pos);
		} else {
			//?ύbuffer???п?
			long pos = colWriter.writeDataBuffer(bufferWriter.finish());
			//???·ֶ???Ϣ
			appendSegmentBlock(end - start + 1);
			objectWriter.writeLong40(pos);
			for (int i = 0; i < count; ++i) {
				if (isDim[i]) {
					objectWriter.writeObject(minValues[i]);
					objectWriter.writeObject(maxValues[i]);
				}
			}
		}
	}

	/**
	 * ???α??????д??
	 * @param cursor
	 * @throws IOException
	 */
	private void appendNormal(ICursor cursor) throws IOException {
		Sequence data = cursor.fetch(MIN_BLOCK_RECORD_COUNT);
		while (data != null && data.length() > 0) {
			appendDataBlock(data, 1, data.length());
			data = cursor.fetch(MIN_BLOCK_RECORD_COUNT);
		}
	}
	
	/**
	 * ???α??????д?? ????????
	 * @param cursor
	 * @throws IOException
	 */
	private void appendAttached(ICursor cursor) throws IOException {
		PhyTable primaryTable = parent;
		int pBlockCount = primaryTable.getDataBlockCount();//???????????ܿ???
		int curBlockCount = dataBlockCount;//Ҫ׷?ӵĿ?ʼ???
		int pkeyEndIndex = sortedColStartIndex;
		
		String []primaryTableKeys = primaryTable.getSortedColNames();
		ArrayList primaryTableKeyList = new ArrayList();
		for (String name : primaryTableKeys) {
			primaryTableKeyList.add(name);
		}
		String []colNames = getAllColNames();
		int fcount = colNames.length;
		boolean []isMyCol = new boolean[fcount];
		for (int i = 0; i < fcount; i++) {
			if (primaryTableKeyList.contains(colNames[i])) {
				isMyCol[i] = false;
			} else {
				isMyCol[i] = true;
			}
		}
		
		RowCursor cs;
		if (primaryTable.totalRecordCount == 0) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("dw.baseTableNull"));
		}
		cs = (RowCursor) primaryTable.cursor(primaryTableKeys);
		cs.setSegment(curBlockCount, curBlockCount + 1);
		Sequence pkeyData = cs.fetch(ICursor.MAXSIZE);
		int pkeyIndex = 1;

		int pkeyDataLen = pkeyData.length();
		ComTableRecord curPkey = (ComTableRecord) pkeyData.get(1);
		Object []curPkeyVals = curPkey.getFieldValues();

		String []allSortedColNames = getAllSortedColNames();
		int sortedColCount = allSortedColNames.length;
		Object []tableMaxValues = this.maxValues;
		Object []lastValues = new Object[sortedColCount];//??һ??ά??ֵ
		
		LongArray guideCol = new LongArray(MIN_BLOCK_RECORD_COUNT);
		Sequence seq = new Sequence(MIN_BLOCK_RECORD_COUNT);
		Sequence data = cursor.fetch(ICursor.FETCHCOUNT);
		Record r;
		Object []vals = new Object[sortedColCount];
		int []findex = new int[sortedColCount];
		DataStruct ds = data.dataStruct();
		for (int f = 0; f < sortedColCount; ++f) {
			findex[f] = ds.getFieldIndex(allSortedColNames[f]);
		}
		
		while (data != null && data.length() > 0) {
			int len = data.length();
			for (int i = 1; i <= len; ++i) {
				r = (Record) data.get(i);
				for (int f = 0; f < sortedColCount; ++f) {
					Object obj = r.getNormalFieldValue(findex[f]);
					vals[f] = obj;
				}
				
				//?ұ???????????Ӧ?ļ?¼
				while (true) {
					int cmp = Variant.compareArrays(curPkeyVals, vals, pkeyEndIndex);
					if (cmp == 0) {
						break;
					} else if (cmp < 0) {
						pkeyIndex++;
						if (pkeyIndex > pkeyDataLen) {
							//ע?⣺??ʱ?п???seq??û?м?¼?????Ҫ׷??һ???տ?
							//??????һ???˾??ύһ??
							appendAttachedDataBlock(seq, isMyCol, guideCol);
							seq.clear();
							guideCol = new LongArray(MIN_BLOCK_RECORD_COUNT);
							
							//ȡ??һ??????????
							curBlockCount++;
							if (curBlockCount >= pBlockCount) {
								//????ȡ??????ˣ??????ﲻӦ?û??????ݣ????쳣
								MessageManager mm = EngineMessage.get();
								throw new RQException(mm.getMessage("dw.appendNotMatch") + r.toString(null));
							}
							cs = (RowCursor) primaryTable.cursor(primaryTableKeys);
							cs.setSegment(curBlockCount, curBlockCount + 1);
							pkeyData = cs.fetch(ICursor.MAXSIZE);
							pkeyIndex = 1;
							pkeyDataLen = pkeyData.length();
						}
						curPkey = (ComTableRecord) pkeyData.get(pkeyIndex);
						curPkeyVals = curPkey.getFieldValues();
					} else if (cmp > 0) {
						//??Ӧ?ó??֣????쳣
						MessageManager mm = EngineMessage.get();
						throw new RQException(mm.getMessage("dw.appendNotMatch") + r.toString(null));
					}
				}
				
				//?????????ȷ??Ҫ׷??һ????
				guideCol.add(curPkey.getRecordSeq());//???ӵ???
				
				//??????????????
				if (isSorted) {
					if (tableMaxValues != null) {
						int cmp = Variant.compareArrays(vals, tableMaxValues, sortedColCount);
						if (cmp < 0) {
							//????׷?ӵ????ݱ???????
							MessageManager mm = EngineMessage.get();
							throw new RQException(mm.getMessage("dw.appendAttachedTable"));
						} else if (cmp == 0){
							if (hasPrimaryKey) hasPrimaryKey = false;
						} else {
							System.arraycopy(vals, 0, tableMaxValues, 0, sortedColCount);
						}
					} else {
						tableMaxValues = maxValues = new Object[sortedColCount];
						System.arraycopy(vals, 0, tableMaxValues, 0, sortedColCount);
					}
				}
				
				seq.add(r);//???????ݴ?				
				System.arraycopy(vals, 0, lastValues, 0, sortedColCount);//????һ??άֵ
			}
			data = cursor.fetch(ICursor.FETCHCOUNT);
		}
		
		//???????һ???п? (??????ȡ??????ˣ??????ﻹ?еĻ????Ͳ?????)
		if (seq.length() > 0) {
			appendAttachedDataBlock(seq, isMyCol, guideCol);
		}
		
	}
	
	/**
	 * ???α??????д????д??ʱ??Ҫ???зֶΡ?
	 * @param cursor
	 * @throws IOException
	 */
	private void appendSegment(ICursor cursor) throws IOException {
		int recCount = 0;
		int sortedColCount = sortedColNames.length;
		Object []tableMaxValues = this.maxValues;

		String []sortedColNames = this.sortedColNames;
		String segmentCol = groupTable.baseTable.getSegmentCol();
		int segmentSerialLen = groupTable.baseTable.getSegmentSerialLen();
		int segmentIndex = 0;

		for (int i = 0; i < sortedColCount; i++) {
			if (segmentCol.equals(sortedColNames[i])) {
				segmentIndex = i;
				break;
			}
		}
		int cmpLen = segmentIndex + 1;
		int serialBytesLen = getSerialBytesLen(segmentIndex);
		if (segmentSerialLen == 0 || segmentSerialLen > serialBytesLen) {
			segmentSerialLen = serialBytesLen;
		}
		Object []lastValues = new Object[cmpLen];//??һ??ά??ֵ
		Object []curValues = new Object[cmpLen];//??ǰ??ά??ֵ
		
		Sequence seq = new Sequence(MIN_BLOCK_RECORD_COUNT);
		Sequence data = cursor.fetch(ICursor.FETCHCOUNT);
		Record r;
		Object []vals = new Object[sortedColCount];
		int []findex = getSortedColIndex();
		
		while (data != null && data.length() > 0) {
			int len = data.length();
			for (int i = 1; i <= len; ++i) {
				r = (Record) data.get(i);
				for (int f = 0; f < sortedColCount; ++f) {
					vals[f] = r.getNormalFieldValue(findex[f]);
				}

				//?????ж??Ƿ?һ???п???
				if (recCount >= MIN_BLOCK_RECORD_COUNT){
					if (serialBytesLen < 1) {
						System.arraycopy(vals, 0, curValues, 0, cmpLen);
					} else {
						System.arraycopy(vals, 0, curValues, 0, segmentIndex);
						Long val;
						Object  obj = vals[segmentIndex];
						if (obj instanceof Integer) {
							val = (Integer)obj & 0xFFFFFFFFL;
						} else {
							val = (Long) obj;
						}
						curValues[segmentIndex] = val >>> (serialBytesLen - segmentSerialLen) * 8;
					}
					if (0 != Variant.compareArrays(lastValues, curValues, cmpLen)) {
						appendDataBlock(seq, 1, seq.length());
						seq.clear();
						recCount = 0;
					}
				}
				
				//??????????????
				if (isSorted) {
					if (tableMaxValues != null) {
						int cmp = Variant.compareArrays(vals, tableMaxValues, sortedColCount);
						if (cmp < 0) {
							//hasPrimaryKey = false;//???ٴ???????
							isSorted = false;
							maxValues = null;
						} else if (cmp == 0){
							//if (hasPrimaryKey) hasPrimaryKey = false;//???ٴ???????
						} else {
							System.arraycopy(vals, 0, tableMaxValues, 0, sortedColCount);
						}
						if (tableList.size() > 0 && !hasPrimaryKey) {
							//???ڸ???ʱ??????׷?ӵ????ݱ???????Ψһ
							MessageManager mm = EngineMessage.get();
							throw new RQException(mm.getMessage("dw.appendPrimaryTable"));
						}
					} else {
						tableMaxValues = maxValues = new Object[sortedColCount];
						System.arraycopy(vals, 0, tableMaxValues, 0, sortedColCount);
					}
				}
				
				//???????ݴ?
				seq.add(r);
				
				if (serialBytesLen < 1) {
					System.arraycopy(vals, 0, lastValues, 0, cmpLen);
				} else {
					System.arraycopy(vals, 0, lastValues, 0, segmentIndex);//????һ??άֵ
					Long val;
					Object  obj = vals[segmentIndex];
					if (obj instanceof Integer) {
						val = (Integer)obj & 0xFFFFFFFFL;
					} else {
						val = (Long) obj;
					}
					lastValues[segmentIndex] = val >>> (serialBytesLen - segmentSerialLen) * 8;
				}
				recCount++;
			}
			data = cursor.fetch(ICursor.FETCHCOUNT);
		}
		
		//???????һ???п?
		if (seq.length() > 0) {
			appendDataBlock(seq, 1, seq.length());
		}
		
	}
	
	/**
	 * ???α??????д????д??ʱ??Ҫ?ж??????Ƿ??ά????
	 * @param cursor
	 * @throws IOException
	 */
	private void appendSorted(ICursor cursor) throws IOException {
		int recCount = 0;
		int sortedColCount = sortedColNames.length;
		Object []tableMaxValues = this.maxValues;
		Object []lastValues = new Object[sortedColCount];//??һ??ά??ֵ
		
		Sequence seq = new Sequence(MIN_BLOCK_RECORD_COUNT);
		Sequence data = cursor.fetch(ICursor.FETCHCOUNT);
		Record r;
		Object []vals = new Object[sortedColCount];
		int []findex = getSortedColIndex();

		while (data != null && data.length() > 0) {
			int len = data.length();
			for (int i = 1; i <= len; ++i) {
				r = (Record) data.get(i);
				for (int f = 0; f < sortedColCount; ++f) {
					vals[f] = r.getNormalFieldValue(findex[f]);
				}

				//?????ж??Ƿ?һ???п???
				if (recCount >= MAX_BLOCK_RECORD_COUNT) {
					//??ʱ?ύһ??
					appendDataBlock(seq, 1, MAX_BLOCK_RECORD_COUNT/2);
					seq = (Sequence) seq.get(MAX_BLOCK_RECORD_COUNT/2 + 1, seq.length() + 1);
					recCount = seq.length(); 
				} else if (recCount >= MIN_BLOCK_RECORD_COUNT){
					boolean doAppend = true;
					int segLen;
					if (sortedColCount > 1) {
						segLen = sortedColCount / 2;
					} else {
						segLen = 1;
					}
					for (int c = 0; c < segLen; c++) {
						int cmp = Variant.compare(lastValues[c], vals[c], true);
						if (cmp == 0) {
							doAppend = false;
							break;
						}
					}
					if (doAppend) {
						appendDataBlock(seq, 1, seq.length());
						seq.clear();
						recCount = 0;
					}
				}
				
				//??????????????
				if (isSorted) {
					if (tableMaxValues != null) {
						int cmp = Variant.compareArrays(vals, tableMaxValues, sortedColCount);
						if (cmp < 0) {
							//hasPrimaryKey = false;//???ٴ???????
							isSorted = false;
							maxValues = null;
						} else if (cmp == 0){
							//if (hasPrimaryKey) hasPrimaryKey = false;//???ٴ???????
						} else {
							System.arraycopy(vals, 0, tableMaxValues, 0, sortedColCount);
						}
						if (tableList.size() > 0 && !hasPrimaryKey) {
							//???ڸ???ʱ??????׷?ӵ????ݱ???????Ψһ
							MessageManager mm = EngineMessage.get();
							throw new RQException(mm.getMessage("dw.appendPrimaryTable"));
						}
					} else {
						tableMaxValues = maxValues = new Object[sortedColCount];
						System.arraycopy(vals, 0, tableMaxValues, 0, sortedColCount);
					}
				}
				
				seq.add(r);//???????ݴ?				
				System.arraycopy(vals, 0, lastValues, 0, sortedColCount);//????һ??άֵ				
				recCount++;
			}
			data = cursor.fetch(ICursor.FETCHCOUNT);
		}
		
		//???????һ???п?
		if (seq.length() > 0) {
			appendDataBlock(seq, 1, seq.length());
		}
		
	}
	
	/**
	 * ׷??????
	 */
	public void append(ICursor cursor) throws IOException {
		// ???û??ά?ֶ???ȡGroupTable.MIN_BLOCK_RECORD_COUNT????¼
		// ?????????3??ά?ֶ?d1??d2??d3??????ά?ֶε?ֵȡ??????MIN_BLOCK_RECORD_COUNT????¼
		// ???[d1,d2,d3]????????Ҫ??[d1,d2]ֵ??ͬ?ĸ????????????֮??Ҫ??[d1,d2,d3]ֵ??ͬ?IJ?????????
		// ?????ͬ?ij?????MAX_BLOCK_RECORD_COUNT??????MAX_BLOCK_RECORD_COUNT / 2??Ϊһ??
		// ??ÿһ?е?????д??BufferWriterȻ?????finish?õ??ֽ????飬?ٵ???compressѹ?????ݣ????д??ColumnMetaData
		// ??ά?ֶ?ʱҪ????maxValues??hasPrimaryKey??????Ա?????hasPrimaryKeyΪfalse???ٸ???
		if (cursor == null) {
			return;
		}

		getGroupTable().checkWritable();
		Sequence data = cursor.peek(MIN_BLOCK_RECORD_COUNT);		
		if (data == null || data.length() <= 0) {
			return;
		}
		
		//?жϽṹƥ??
		DataStruct ds = data.dataStruct();
		if (ds == null) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.needPurePmt"));
		}
		
		String []colNames = getAllColNames();
		int count = colNames.length;
		for (int i = 0; i < count; i++) {
			if (!ds.getFieldName(i).equals(colNames[i])) {
				MessageManager mm = EngineMessage.get();
				throw new RQException(mm.getMessage("engine.dsNotMatch"));
			}
		}
		
		//????α????ݲ???1??
		if (data.length() < MIN_BLOCK_RECORD_COUNT) {
			if (appendCache == null) {
				appendCache = data;
			} else {
				appendCache.addAll(data);
			}
			data = null;
			cursor.close();
			if (appendCache.length() >= MIN_BLOCK_RECORD_COUNT) {
				appendCache();
			}
			return;
		}
		
		//????л???????
		if (appendCache != null) {
			ICursor []cursorArray = new ICursor[2];
			cursorArray[0] = new MemoryCursor(appendCache);
			cursorArray[1] = cursor;
			cursor = new ConjxCursor(cursorArray);
			appendCache = null;
		}
		
		// ׼??д????
		prepareAppend();
		
		if (parent != null) {
			parent.appendCache();
			appendAttached(cursor);
		} else if (sortedColNames == null) {
			appendNormal(cursor);
		} else if (groupTable.baseTable.getSegmentCol() == null) {
			appendSorted(cursor);
		} else {
			appendSegment(cursor);
		}
		
		// ????д???ݣ????浽?ļ?
		finishAppend();
	}

	protected void appendSegmentBlock(int recordCount) throws IOException {
		dataBlockCount++;
		totalRecordCount += recordCount;
		objectWriter.writeInt32(recordCount);
	}
	
	/**
	 * ȡ?ֶι??????ȼ?
	 * @param name
	 * @return
	 */
	int getColumnFilterPriority(String name) {
		if (sortedColNames != null) {
			int len = sortedColNames.length;
			for (int i = 0; i < len; ++i) {
				if (sortedColNames[i] == name) {
					return i;
				}
			}
			
			return len;
		} else {
			return 0;
		}
	}
	
	/**
	 * ??????????????α?
	 */
	public ICursor cursor() {
		getGroupTable().checkReadable();
		ICursor cs = new RowCursor(this);
		PhyTable tmd = getSupplementTable(false);
		if (tmd == null) {
			return cs;
		} else {
			ICursor cs2 = tmd.cursor();
			return merge(cs, cs2);
		}
	}
	
	public ICursor cursor(String []fields) {
		getGroupTable().checkReadable();
		ICursor cs = new RowCursor(this, fields);
		PhyTable tmd = getSupplementTable(false);
		if (tmd == null) {
			return cs;
		} else {
			ICursor cs2 = tmd.cursor(fields);
			return merge(cs, cs2);
		}
	}
	
	public ICursor cursor(String []fields, Expression exp, Context ctx) {
		getGroupTable().checkReadable();
		ICursor cs = new RowCursor(this, fields, exp, ctx);
		PhyTable tmd = getSupplementTable(false);
		if (tmd == null) {
			return cs;
		} else {
			ICursor cs2 = tmd.cursor(fields, exp, ctx);
			return merge(cs, cs2);
		}
	}

	public ICursor cursor(Expression[] exps, String[] fields, Expression filter, Context ctx) {
		getGroupTable().checkReadable();
		ICursor cs = new RowCursor(this, null, filter, exps, fields, ctx);
		PhyTable tmd = getSupplementTable(false);
		if (tmd == null) {
			return cs;
		} else {
			ICursor cs2 = tmd.cursor(exps, fields, filter, null, null, null, null, ctx);
			return merge(cs, cs2);
		}
	}

	public ICursor cursor(String []fields, Expression filter, Context ctx, int segSeq, int segCount) {
		getGroupTable().checkReadable();
		return cursor(null, fields, filter, ctx, segSeq, segCount);
	}
	
	public ICursor cursor(Expression []exps, String []fields, Expression filter, Context ctx, int segSeq, int segCount) {
		getGroupTable().checkReadable();
		if (filter != null) {
			filter = filter.newExpression(ctx); // ?ֶβ??ж?ȡʱ??Ҫ???Ʊ???ʽ??ͬһ??????ʽ??֧?ֲ???????
		}
		
		String []fetchFields;
		if (exps != null) {
			int colCount = exps.length;
			fetchFields = new String[colCount];
			for (int i = 0; i < colCount; ++i) {
				if (exps[i] == null) {
					exps[i] = Expression.NULL;
				}
				
				if (exps[i].getHome() instanceof UnknownSymbol) {
					fetchFields[i] = exps[i].getIdentifierName();
				}
			}
		} else {
			fetchFields = fields;
			fields = null;
		}
		
		RowCursor cursor = new RowCursor(this, fetchFields, filter, exps, fields, ctx);
		if (segCount < 2) {
			return cursor;
		}
		
		int startBlock = 0;
		int endBlock = -1;
		int avg = dataBlockCount / segCount;
		
		if (avg < 1) {
			// ÿ?β???һ??
			if (segSeq <= dataBlockCount) {
				startBlock = segSeq - 1;
				endBlock = segSeq;
			}
		} else {
			if (segSeq > 1) {
				endBlock = segSeq * avg;
				startBlock = endBlock - avg;
				
				// ʣ??Ŀ?????ÿ?ζ?һ??
				int mod = dataBlockCount % segCount;
				int n = mod - (segCount - segSeq);
				if (n > 0) {
					endBlock += n;
					startBlock += n - 1;
				}
			} else {
				endBlock = avg;
			}
		}

		cursor.setSegment(startBlock, endBlock);
		return cursor;
	
	}
	public ICursor cursor(String []fields, Expression exp, MultipathCursors mcs, Context ctx) {
		getGroupTable().checkReadable();
		//?д治֧?ֶ?·
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}
	
	public ICursor cursor(String []fields, Expression filter, String []fkNames, Sequence []codes, String []opts, Context ctx) {
		if (fkNames == null) {
			return cursor(fields, filter, ctx);
		} else {
			throw new RQException("K:T param is unimplemented in row group table!");
		}
	}

	public ICursor cursor(String []fields, Expression filter, String []fkNames, Sequence []codes, 
			String []opts, Context ctx, int pathCount) {
		if (fkNames == null) {
			return cursor(fields, filter, ctx, pathCount);
		} else {
			throw new RQException("K:T param is unimplemented in row group table!");
		}
	}
	
	public ICursor cursor(String []fields, Expression filter, String []fkNames, Sequence []codes, 
			String []opts, Context ctx, int pathSeq, int pathCount) {
		if (fkNames == null) {
			return cursor(fields, filter, ctx, pathSeq, pathCount);
		} else {
			throw new RQException("K:T param is unimplemented in row group table!");
		}
	}
	
	public ICursor cursor(Expression []exps, String []fields, Expression filter, String []fkNames, Sequence []codes,
			String []opts, String opt, Context ctx) {
		if (fkNames == null) {
			return cursor(exps, fields, filter, ctx);
		} else {
			throw new RQException("K:T param is unimplemented in row group table!");
		}
	}
	
	public ICursor cursor(Expression []exps, String []fields, Expression filter, String []fkNames, Sequence []codes,
			String []opts, Context ctx, int pathCount) {
		if (fkNames == null) {
			return cursor(exps, fields, filter, ctx, pathCount);
		} else {
			throw new RQException("K:T param is unimplemented in row group table!");
		}
	}
	
	public ICursor cursor(Expression []exps, String []fields, Expression filter, String []fkNames, Sequence []codes,
			String []opts, Context ctx, int pathSeq, int pathCount) {
		if (fkNames == null) {
			return cursor(exps, fields, filter, ctx, pathSeq, pathCount);
		} else {
			throw new RQException("K:T param is unimplemented in row group table!");
		}
	}
	
	public ICursor cursor(String []fields, Expression exp, String []fkNames, Sequence []codes, 
			String []opts, MultipathCursors mcs, String opt, Context ctx) {
		if (fkNames == null) {
			return cursor(fields, exp, mcs, ctx);
		} else {
			throw new RQException("K:T param is unimplemented in row group table!");
		}
	}

	/**
	 * ?в??ļ?ʱ?????ݸ???
	 * @param stmd
	 * @param data
	 * @param opt
	 * @return
	 * @throws IOException
	 */
	private Sequence update(PhyTable stmd, Sequence data, String opt) throws IOException {
		boolean isUpdate = true, isInsert = true;
		Sequence result = null;
		if (opt != null) {
			if (opt.indexOf('i') != -1) isUpdate = false;
			if (opt.indexOf('u') != -1) {
				if (!isUpdate) {
					MessageManager mm = EngineMessage.get();
					throw new RQException(opt + mm.getMessage("engine.optConflict"));
				}
				
				isInsert = false;
			}
			
			if (opt.indexOf('n') != -1) result = new Sequence();
		}
		
		DataStruct ds = data.dataStruct();
		if (ds == null) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.needPurePmt"));
		}
		
		if (!ds.isCompatible(this.ds)) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.dsNotMatch"));
		}
		
		// ?Ը??????ݽ???????
		data.sortFields(getAllSortedColNames());
		appendCache();
		
		String[] columns = getAllSortedColNames();
		int keyCount = columns.length;
		int []keyIndex = new int[keyCount];
		for (int k = 0; k < keyCount; ++k) {
			keyIndex[k] = ds.getFieldIndex(columns[k]);
			if (keyIndex[k] < 0) {
				MessageManager mm = EngineMessage.get();
				throw new RQException(columns[k] + mm.getMessage("ds.fieldNotExist"));
			}
		}
		
		boolean isPrimaryTable = parent == null;
		int len = data.length();
		long []seqs = new long[len + 1];
		int []block = new int[len + 1];//?Ƿ???һ???εĵײ?insert(?ӱ?)
		long []recNum = null;
		int []temp = new int[1];
		
		if (isPrimaryTable) {
			RowRecordSeqSearcher searcher = new RowRecordSeqSearcher(this);
			if (keyCount == 1) {
				int k = keyIndex[0];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					seqs[i] = searcher.findNext(r.getFieldValue(k));
				}
			} else {
				Object []keyValues = new Object[keyCount];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					for (int k = 0; k < keyCount; ++k) {
						keyValues[k] = r.getFieldValue(keyIndex[k]);
					}
					
					seqs[i] = searcher.findNext(keyValues);
				}
			}
		} else {
			recNum  = new long[len + 1];//?ӱ???Ӧ????????α?ţ?0??ʾ??????????
			RowPhyTable baseTable = (RowPhyTable) this.groupTable.baseTable;
			RowRecordSeqSearcher baseSearcher = new RowRecordSeqSearcher(baseTable);
			RowRecordSeqSearcher2 searcher = new RowRecordSeqSearcher2(this);
			if (keyCount == 1) {
				int k = keyIndex[0];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					seqs[i] = searcher.findNext(r.getFieldValue(k), temp);
					block[i] = temp[0];
					if (seqs[i] < 0) {
						//????Dz??룬Ҫ?ж?һ???Ƿ?????????????
						long seq = baseSearcher.findNext(r.getFieldValue(k));
						if (seq > 0) {
							recNum[i] = seq;
						} else {
							if (baseSearcher.isEnd()) {
								//?ӱ?????????ݱ?????????
								MessageManager mm = EngineMessage.get();
								throw new RQException(r.toString(null) + mm.getMessage("grouptable.invalidData"));	
							}
							recNum[i] = 0;//???????????????????ᴦ??
						}
					} else {
						recNum[i] = searcher.getRecNum();
					}
				}
			} else {
				Object []keyValues = new Object[keyCount];
				int baseKeyCount = sortedColStartIndex;
				Object []baseKeyValues = new Object[baseKeyCount];
				
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					for (int k = 0; k < keyCount; ++k) {
						keyValues[k] = r.getFieldValue(keyIndex[k]);
						if (k < baseKeyCount) {
							baseKeyValues[k] = keyValues[k]; 
						}
					}
					
					seqs[i] = searcher.findNext(keyValues, temp);
					block[i] = temp[0];
					if (seqs[i] < 0 || block[i] > 0) {
						//????Dz??룬Ҫ?ж?һ???Ƿ?????????????
						long seq = baseSearcher.findNext(baseKeyValues);
						if (seq > 0) {
							recNum[i] = seq;
						} else {
							if (baseSearcher.isEnd()) {
								//?ӱ?????????ݱ?????????
								MessageManager mm = EngineMessage.get();
								throw new RQException(r.toString(null) + mm.getMessage("grouptable.invalidData"));	
							}
							recNum[i] = 0;//???????????????????ᴦ??
						}
					} else {
						recNum[i] = searcher.getRecNum();
					}
				}
			}
		}
		
		// ??Ҫ????????ĵ???append׷??
		Sequence append = new Sequence();
		ArrayList modifyRecords = getModifyRecords();
		boolean needUpdateSubTable = false;
		
		if (modifyRecords == null) {
			modifyRecords = new ArrayList(len);
			this.modifyRecords = modifyRecords;
			for (int i = 1; i <= len; ++i) {
				Record sr = (Record)data.getMem(i);
				if (seqs[i] > 0) {
					if (isUpdate) {
						ModifyRecord r = new ModifyRecord(seqs[i], ModifyRecord.STATE_UPDATE, sr);
						modifyRecords.add(r);
						if (result != null) {
							result.add(sr);
						}
					}
				} else {
					append.add(sr);
				}
			}
		} else {
			int srcLen = modifyRecords.size();
			ArrayList tmp = new ArrayList(len + srcLen);
			int s = 0;
			int t = 1;
			
			while (s < srcLen && t <= len) {
				ModifyRecord mr = modifyRecords.get(s);
				long seq1 = mr.getRecordSeq();
				long seq2 = seqs[t];
				if (seq2 > 0) {
					if (seq1 < seq2) {
						s++;
						tmp.add(mr);
					} else if (seq1 == seq2) {
						if (mr.getState() == ModifyRecord.STATE_INSERT) {
							s++;
							tmp.add(mr);
						} else {
							if ((mr.getState() == ModifyRecord.STATE_UPDATE && isUpdate) || 
									(mr.getState() == ModifyRecord.STATE_DELETE && isInsert)) {
								// ״̬????update
								Record sr = (Record)data.getMem(t);
								mr.setRecord(sr, ModifyRecord.STATE_UPDATE);
								if (result != null) {
									result.add(sr);
								}
							}

							s++;
							t++;
							tmp.add(mr);
						}
					} else {
						if (isUpdate) {
							Record sr = (Record)data.getMem(t);
							mr = new ModifyRecord(seq2, ModifyRecord.STATE_UPDATE, sr);
							tmp.add(mr);
							
							if (result != null) {
								result.add(sr);
							}
						}
						
						t++;
					}
				} else {
					seq2 = -seq2;
					if (seq1 < seq2) {
						s++;
						tmp.add(mr);
					} else if (seq1 == seq2) {
						if (mr.getState() == ModifyRecord.STATE_INSERT) {
							int cmp = mr.getRecord().compare((Record)data.getMem(t), keyIndex);
							if (cmp < 0) {
								s++;
								tmp.add(mr);
							} else if (cmp == 0) {
								if (isUpdate) {
									Record sr = (Record)data.getMem(t);
									mr.setRecord(sr);
									if (result != null) {
										result.add(sr);
									}
								}
								
								tmp.add(mr);
								s++;
								t++;
							} else {
								append.add(data.getMem(t));
								t++;
							}
						} else {
							append.add(data.getMem(t));
							t++;
						}
					} else {
						append.add(data.getMem(t));
						t++;
					}
				}
			}
			
			for (; s < srcLen; ++s) {
				tmp.add(modifyRecords.get(s));
			}
			
			for (; t <= len; ++t) {
				Record sr = (Record)data.getMem(t);
				if (seqs[t] > 0) {
					if (isUpdate) {
						ModifyRecord r = new ModifyRecord(seqs[t], ModifyRecord.STATE_UPDATE, sr);
						tmp.add(r);
						if (result != null) {
							result.add(sr);
						}
					}
				} else {
					append.add(sr);
				}
			}
			
			this.modifyRecords = tmp;
			if (srcLen != tmp.size()) {
				needUpdateSubTable = true;
			}
		}
		
		if (!isPrimaryTable) {
			//?ӱ????????Ҫ?????????????޸?
			update(parent.getModifyRecords());
			
			for (ModifyRecord r : modifyRecords) {
				if (r.getState() == ModifyRecord.STATE_INSERT) {
					if (r.getParentRecordSeq() == 0) {
						this.modifyRecords = null;
						this.modifyRecords = getModifyRecords();
						//?ӱ?????????ݱ?????????
						MessageManager mm = EngineMessage.get();
						throw new RQException(r.getRecord().toString(null) + mm.getMessage("grouptable.invalidData"));
					}
				}
			}
			
		}
		
		saveModifyRecords();
		
		if (isPrimaryTable && needUpdateSubTable) {
			//??????insert???ͱ???????????ӱ?????
			ArrayList tableList = getTableList();
			for (int i = 0, size = tableList.size(); i < size; ++i) {
				RowPhyTable t = ((RowPhyTable)tableList.get(i));
				boolean needSave = t.update(modifyRecords);
				if (needSave) {
					t.saveModifyRecords();
				}
			}
		}
		
		if (append.length() > 0) {
			Sequence seq = stmd.update(append, opt);
			if (result != null) {
				result.addAll(seq);
			}
		}
		
		groupTable.save();
		return result;
	}
	
	/**
	 * ????
	 */
	public Sequence update(Sequence data, String opt) throws IOException {
		if (!hasPrimaryKey) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("dw.lessKey"));
		}
		
		ComTable groupTable = getGroupTable();
		groupTable.checkWritable();
		PhyTable tmd = getSupplementTable(false);
		if (tmd != null) {
			return update(tmd, data, opt);
		}
		
		boolean isInsert = true,isUpdate = true;
		Sequence result = null;
		if (opt != null) {
			if (opt.indexOf('i') != -1) isUpdate = false;
			if (opt.indexOf('u') != -1) {
				if (!isUpdate) {
					MessageManager mm = EngineMessage.get();
					throw new RQException(opt + mm.getMessage("engine.optConflict"));
				}
				
				isInsert = false;
			}
			
			if (opt.indexOf('n') != -1) result = new Sequence();
		}
		
		long totalRecordCount = this.totalRecordCount;
		if (totalRecordCount == 0) {
			if (isInsert) {
				ICursor cursor = new MemoryCursor(data);
				append(cursor);
				appendCache();
				if (result != null) {
					result.addAll(data);
				}
			}
			
			return result;
		}
		
		DataStruct ds = data.dataStruct();
		if (ds == null) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.needPurePmt"));
		}
		
		if (!ds.isCompatible(this.ds)) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.dsNotMatch"));
		}
		
		// ?Ը??????ݽ???????
		data.sortFields(getAllSortedColNames());
		appendCache();
		
		String[] columns = getAllSortedColNames();
		int keyCount = columns.length;
		int []keyIndex = new int[keyCount];
		for (int k = 0; k < keyCount; ++k) {
			keyIndex[k] = ds.getFieldIndex(columns[k]);
			if (keyIndex[k] < 0) {
				MessageManager mm = EngineMessage.get();
				throw new RQException(columns[k] + mm.getMessage("ds.fieldNotExist"));
			}
		}
		
		boolean isPrimaryTable = parent == null;
		int len = data.length();
		long []seqs = new long[len + 1];
		int []block = new int[len + 1];//?Ƿ???һ???εĵײ?insert(?ӱ?)
		long []recNum = null;
		int []temp = new int[1];
		
		if (isPrimaryTable) {
			RowRecordSeqSearcher searcher = new RowRecordSeqSearcher(this);
			
			if (keyCount == 1) {
				int k = keyIndex[0];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					seqs[i] = searcher.findNext(r.getFieldValue(k));
				}
			} else {
				Object []keyValues = new Object[keyCount];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					for (int k = 0; k < keyCount; ++k) {
						keyValues[k] = r.getFieldValue(keyIndex[k]);
					}
					
					seqs[i] = searcher.findNext(keyValues);
				}
			}
		} else {
			recNum  = new long[len + 1];//?ӱ???Ӧ????????α?ţ?0??ʾ??????????
			RowPhyTable baseTable = (RowPhyTable) this.groupTable.baseTable;
			RowRecordSeqSearcher baseSearcher = new RowRecordSeqSearcher(baseTable);
			RowRecordSeqSearcher2 searcher = new RowRecordSeqSearcher2(this);
			if (keyCount == 1) {
				int k = keyIndex[0];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					seqs[i] = searcher.findNext(r.getFieldValue(k), temp);
					block[i] = temp[0];
					if (seqs[i] < 0) {
						//????Dz??룬Ҫ?ж?һ???Ƿ?????????????
						long seq = baseSearcher.findNext(r.getFieldValue(k));
						if (seq > 0) {
							recNum[i] = seq;
						} else {
							if (baseSearcher.isEnd()) {
								//?ӱ?????????ݱ?????????
								MessageManager mm = EngineMessage.get();
								throw new RQException(r.toString(null) + mm.getMessage("grouptable.invalidData"));	
							}
							recNum[i] = 0;//???????????????????ᴦ??
						}
					} else {
						recNum[i] = searcher.getRecNum();
					}
				}
			} else {
				Object []keyValues = new Object[keyCount];
				int baseKeyCount = sortedColStartIndex;
				Object []baseKeyValues = new Object[baseKeyCount];
				
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					for (int k = 0; k < keyCount; ++k) {
						keyValues[k] = r.getFieldValue(keyIndex[k]);
						if (k < baseKeyCount) {
							baseKeyValues[k] = keyValues[k]; 
						}
					}
					
					seqs[i] = searcher.findNext(keyValues, temp);
					block[i] = temp[0];
					if (seqs[i] < 0 || block[i] > 0) {
						//????Dz??룬Ҫ?ж?һ???Ƿ?????????????
						long seq = baseSearcher.findNext(baseKeyValues);
						if (seq > 0) {
							recNum[i] = seq;
						} else {
							if (baseSearcher.isEnd()) {
								//?ӱ?????????ݱ?????????
								MessageManager mm = EngineMessage.get();
								throw new RQException(r.toString(null) + mm.getMessage("grouptable.invalidData"));	
							}
							recNum[i] = 0;//???????????????????ᴦ??
						}
					} else {
						recNum[i] = searcher.getRecNum();
					}
				}
			}
		}
		
		// ??Ҫ????????ĵ???append׷??
		Sequence append = new Sequence();
		ArrayList modifyRecords = getModifyRecords();
		boolean needUpdateSubTable = false;
		
		if (modifyRecords == null) {
			modifyRecords = new ArrayList(len);
			this.modifyRecords = modifyRecords;
			for (int i = 1; i <= len; ++i) {
				Record sr = (Record)data.getMem(i);
				if (seqs[i] > 0) {
					if (isUpdate) {
						ModifyRecord r = new ModifyRecord(seqs[i], ModifyRecord.STATE_UPDATE, sr);
						if (!isPrimaryTable) {
							r.setParentRecordSeq(recNum[i]);
						}
						modifyRecords.add(r);
						if (result != null) {
							result.add(sr);
						}
					}
				} else if (isInsert) {
					long seq = -seqs[i];
					if (seq <= totalRecordCount || block[i] > 0) {
						ModifyRecord r = new ModifyRecord(seq, ModifyRecord.STATE_INSERT, sr);
						r.setBlock(block[i]);
						//??????ӱ?insert Ҫ????parentRecordSeq????Ϊ?ӱ?insert?Ŀ???ָ??????????
						//??????????Ϊָ??????α?ţ?????????????????޸?
						if (!isPrimaryTable) {
							r.setParentRecordSeq(recNum[i]);
						}
						modifyRecords.add(r);
					} else {
						append.add(sr);
					}
					
					if (result != null) {
						result.add(sr);
					}
				}
			}
		} else {
			int srcLen = modifyRecords.size();
			ArrayList tmp = new ArrayList(len + srcLen);
			int s = 0;
			int t = 1;
			
			while (s < srcLen && t <= len) {
				ModifyRecord mr = modifyRecords.get(s);
				long seq1 = mr.getRecordSeq();
				long seq2 = seqs[t];
				if (seq2 > 0) {
					if (seq1 < seq2) {
						s++;
						tmp.add(mr);
					} else if (seq1 == seq2) {
						if (mr.getState() == ModifyRecord.STATE_INSERT) {
							s++;
							tmp.add(mr);
						} else {
							if ((mr.getState() == ModifyRecord.STATE_UPDATE && isUpdate) || 
									(mr.getState() == ModifyRecord.STATE_DELETE && isInsert)) {
								// ״̬????update
								Record sr = (Record)data.getMem(t);
								mr.setRecord(sr, ModifyRecord.STATE_UPDATE);
								if (!isPrimaryTable) {
									mr.setParentRecordSeq(recNum[t]);
								}
								if (result != null) {
									result.add(sr);
								}
							}

							s++;
							t++;
							tmp.add(mr);
						}
					} else {
						if (isUpdate) {
							Record sr = (Record)data.getMem(t);
							mr = new ModifyRecord(seq2, ModifyRecord.STATE_UPDATE, sr);
							if (!isPrimaryTable) {
								mr.setParentRecordSeq(recNum[t]);
							}
							tmp.add(mr);
							
							if (result != null) {
								result.add(sr);
							}
						}
						
						t++;
					}
				} else {
					seq2 = -seq2;
					if (seq1 < seq2) {
						s++;
						tmp.add(mr);
					} else if (seq1 == seq2) {
						if (mr.getState() == ModifyRecord.STATE_INSERT) {
							int cmp = mr.getRecord().compare((Record)data.getMem(t), keyIndex);
							if (cmp < 0) {
								s++;
								tmp.add(mr);
							} else if (cmp == 0) {
								if (isUpdate) {
									Record sr = (Record)data.getMem(t);
									mr.setRecord(sr);
									if (result != null) {
										result.add(sr);
									}
								}
								
								tmp.add(mr);
								s++;
								t++;
							} else {
								if (isInsert) {
									Record sr = (Record)data.getMem(t);
									mr = new ModifyRecord(seq2, ModifyRecord.STATE_INSERT, sr);
									mr.setBlock(block[t]);
									//??????ӱ?insert Ҫ????parentRecordSeq????Ϊ?ӱ?insert?Ŀ???ָ??????????
									//??????????Ϊָ??????α?ţ?????????????????޸?
									if (!isPrimaryTable) {
										mr.setParentRecordSeq(recNum[t]);
									}
									modifyRecords.add(mr);
									tmp.add(mr);
									if (result != null) {
										result.add(sr);
									}
								}
								
								t++;
							}
						} else {
							if (isInsert) {
								Record sr = (Record)data.getMem(t);
								mr = new ModifyRecord(seq2, ModifyRecord.STATE_INSERT, sr);
								mr.setBlock(block[t]);
								//??????ӱ?insert Ҫ????parentRecordSeq????Ϊ?ӱ?insert?Ŀ???ָ??????????
								//??????????Ϊָ??????α?ţ?????????????????޸?
								if (!isPrimaryTable) {
									mr.setParentRecordSeq(recNum[t]);
								}
								modifyRecords.add(mr);
								tmp.add(mr);
								if (result != null) {
									result.add(sr);
								}
							}
							
							t++;
						}
					} else {
						if (isInsert) {
							Record sr = (Record)data.getMem(t);
							mr = new ModifyRecord(seq2, ModifyRecord.STATE_INSERT, sr);
							mr.setBlock(block[t]);
							//??????ӱ?insert Ҫ????parentRecordSeq????Ϊ?ӱ?insert?Ŀ???ָ??????????
							//??????????Ϊָ??????α?ţ?????????????????޸?
							if (!isPrimaryTable) {
								mr.setParentRecordSeq(recNum[t]);
							}
							modifyRecords.add(mr);
							tmp.add(mr);
							if (result != null) {
								result.add(sr);
							}
						}
						
						t++;
					}
				}
			}
			
			for (; s < srcLen; ++s) {
				tmp.add(modifyRecords.get(s));
			}
			
			for (; t <= len; ++t) {
				Record sr = (Record)data.getMem(t);
				if (seqs[t] > 0) {
					if (isUpdate) {
						ModifyRecord r = new ModifyRecord(seqs[t], ModifyRecord.STATE_UPDATE, sr);
						tmp.add(r);
						if (result != null) {
							result.add(sr);
						}
					}
				} else if (isInsert) {
					long seq = -seqs[t];
					if (seq <= totalRecordCount) {
						ModifyRecord r = new ModifyRecord(seq, ModifyRecord.STATE_INSERT, sr);
						r.setBlock(block[t]);
						//??????ӱ?insert Ҫ????parentRecordSeq????Ϊ?ӱ?insert?Ŀ???ָ??????????
						//??????????Ϊָ??????α?ţ?????????????????޸?
						if (!isPrimaryTable) {
							r.setParentRecordSeq(recNum[t]);
						}
						modifyRecords.add(r);
						tmp.add(r);
					} else {
						append.add(sr);
					}
					
					if (result != null) {
						result.add(sr);
					}
				}
			}
			
			this.modifyRecords = tmp;
			if (srcLen != tmp.size()) {
				needUpdateSubTable = true;
			}
		}
		
		if (!isPrimaryTable) {
			//?ӱ????????Ҫ?????????????޸?
			update(parent.getModifyRecords());
			
			for (ModifyRecord r : modifyRecords) {
				if (r.getState() == ModifyRecord.STATE_INSERT) {
					if (r.getParentRecordSeq() == 0) {
						this.modifyRecords = null;
						this.modifyRecords = getModifyRecords();
						//?ӱ?????????ݱ?????????
						MessageManager mm = EngineMessage.get();
						throw new RQException(r.getRecord().toString(null) + mm.getMessage("grouptable.invalidData"));
					}
				}
			}
			
		}
		
		saveModifyRecords();
		
		if (isPrimaryTable && needUpdateSubTable) {
			//??????insert???ͱ???????????ӱ?????
			ArrayList tableList = getTableList();
			for (int i = 0, size = tableList.size(); i < size; ++i) {
				RowPhyTable t = ((RowPhyTable)tableList.get(i));
				boolean needSave = t.update(modifyRecords);
				if (needSave) {
					t.saveModifyRecords();
				}
			}
		}
		
		if (append.length() > 0) {
			ICursor cursor = new MemoryCursor(append);
			append(cursor);
		} else {
			groupTable.save();
		}
		
		return result;
	}
	
	public Sequence delete(Sequence data, String opt) throws IOException {
		if (!hasPrimaryKey) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("dw.lessKey"));
		}
		
		ComTable groupTable = getGroupTable();
		groupTable.checkWritable();
		
		Sequence result1 = null;
		PhyTable tmd = getSupplementTable(false);
		if (tmd != null) {
			// ?в??ļ?ʱ??ɾ?????ļ??е????ݣ????ļ??в????ڵ?????Դ?ļ???ɾ??
			result1 = tmd.delete(data, "n");
			data = (Sequence) data.diff(result1, false);
		}
		
		appendCache();
		boolean nopt = opt != null && opt.indexOf('n') != -1;
		long totalRecordCount = this.totalRecordCount;
		if (totalRecordCount == 0 || data == null || data.length() == 0) {
			return nopt ? result1 : null;
		}
		
		Sequence result = null;
		if (nopt) {
			result = new Sequence();
		}
		
		boolean deleteByBaseKey = false;//ֻ?????ڲ?ɾ???ӱ???????????ʱ??????@n
		if (opt != null && opt.indexOf('s') != -1) {
			deleteByBaseKey = true;
		}
		
		DataStruct ds = data.dataStruct();
		if (ds == null) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.needPurePmt"));
		}
				
		String[] columns = getAllSortedColNames();
		int keyCount = columns.length;
		if (deleteByBaseKey) {
			keyCount = sortedColStartIndex;
		}
		int []keyIndex = new int[keyCount];
		for (int k = 0; k < keyCount; ++k) {
			keyIndex[k] = ds.getFieldIndex(columns[k]);
			if (keyIndex[k] < 0) {
				MessageManager mm = EngineMessage.get();
				throw new RQException(columns[k] + mm.getMessage("ds.fieldNotExist"));
			}
		}
		
		boolean isPrimaryTable = parent == null;
		if (deleteByBaseKey) {
			data.sortFields(parent.getSortedColNames());
		} else {
			data.sortFields(getAllSortedColNames());
		}
		int len = data.length();
		long []seqs = new long[len + 1];
		int temp[] = new int[1];
		LongArray seqList = null;
		Sequence seqListData = null;
		if (deleteByBaseKey) {
			seqList = new LongArray(len * 10);
			seqList.add(0);
			seqListData = new Sequence(len);
		}
		
		if (isPrimaryTable) {
			RowRecordSeqSearcher searcher = new RowRecordSeqSearcher(this);
			
			if (keyCount == 1) {
				int k = keyIndex[0];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					seqs[i] = searcher.findNext(r.getFieldValue(k));
				}
			} else {
				Object []keyValues = new Object[keyCount];
				for (int i = 1; i <= len; ++i) {
					Record r = (Record)data.getMem(i);
					for (int k = 0; k < keyCount; ++k) {
						keyValues[k] = r.getFieldValue(keyIndex[k]);
					}
					
					seqs[i] = searcher.findNext(keyValues);
				}
			}
		} else {
			RowRecordSeqSearcher2 searcher = new RowRecordSeqSearcher2(this);
			
			Object []keyValues = new Object[keyCount];
			int baseKeyCount = sortedColStartIndex;
			Object []baseKeyValues = new Object[baseKeyCount];
			
			for (int i = 1; i <= len;) {
				Record r = (Record)data.getMem(i);
				for (int k = 0; k < keyCount; ++k) {
					keyValues[k] = r.getFieldValue(keyIndex[k]);
					if (k < baseKeyCount) {
						baseKeyValues[k] = keyValues[k]; 
					}
				}
				
				if (deleteByBaseKey) {
					long s = searcher.findNext(keyValues, keyCount);
					if (s <= 0) {
						i++;//???Ҳ???ʱ??++
					} else {
						seqList.add(s);
						seqListData.add(r);
					}

				} else {
					seqs[i] = searcher.findNext(keyValues, temp);
					i++;
				}
				
			}
		}
		
		if (deleteByBaseKey) {
			len = seqList.size() - 1;
			if (0 == len) {
				return result;
			}
			seqs = seqList.toArray();
			data = seqListData;
		}
		
		ArrayList modifyRecords = getModifyRecords();
		boolean modified = true;
		
		if (modifyRecords == null) {
			modifyRecords = new ArrayList(len);
			for (int i = 1; i <= len; ++i) {
				if (seqs[i] > 0) {
					ModifyRecord r = new ModifyRecord(seqs[i]);
					modifyRecords.add(r);
					
					if (result != null) {
						result.add(data.getMem(i));
					}
				}
			}
			
			if (modifyRecords.size() > 0) {
				this.modifyRecords = modifyRecords;
			} else {
				modified = false;
			}
		} else {
			int srcLen = modifyRecords.size();
			ArrayList tmp = new ArrayList(len + srcLen);
			int s = 0;
			int t = 1;
			
			while (s < srcLen && t <= len) {
				ModifyRecord mr = modifyRecords.get(s);
				long seq1 = mr.getRecordSeq();
				long seq2 = seqs[t];
				if (seq2 > 0) {
					if (seq1 < seq2) {
						s++;
					} else if (seq1 == seq2) {
						if (mr.getState() == ModifyRecord.STATE_INSERT) {
							int cmp = mr.getRecord().compare((Record)data.getMem(t), keyIndex);
							if (cmp < 0) {
								tmp.add(mr);
							} else if (cmp == 0) {
								if (result != null) {
									result.add(data.getMem(t));
								}
							} else {
								if (result != null) {
									result.add(data.getMem(t));
								}

								ModifyRecord r = new ModifyRecord(seqs[t]);
								tmp.add(r);
								tmp.add(mr);
								t++;
							}
							s++;
							continue;
						} else {
							if (result != null && mr.getState() == ModifyRecord.STATE_UPDATE) {
								result.add(data.getMem(t));
							}
							
							mr.setDelete();
							s++;
							t++;
						}
					} else {
						mr = new ModifyRecord(seq2);
						if (result != null) {
							result.add(data.getMem(t));
						}
	
						t++;
					}
					
					tmp.add(mr);
				} else {
					seq2 = -seq2;
					if (seq1 < seq2) {
						s++;
						tmp.add(mr);
					} else if (seq1 == seq2) {
						if (mr.getState() == ModifyRecord.STATE_INSERT) {
							int cmp = mr.getRecord().compare((Record)data.getMem(t), keyIndex);
							if (cmp < 0) {
								s++;
								tmp.add(mr);
							} else if (cmp == 0) {
								if (result != null) {
									result.add(data.getMem(t));
								}
	
								s++;
								t++;
							} else {
								t++;
							}
						} else {
							s++;
							t++;
							tmp.add(mr);
						}
					} else {
						t++;
					}
				}
			}
			
			for (; s < srcLen; ++s) {
				tmp.add(modifyRecords.get(s));
			}
			
			for (; t <= len; ++t) {
				if (seqs[t] > 0) {
					if (result != null) {
						result.add(data.getMem(t));
					}

					ModifyRecord r = new ModifyRecord(seqs[t]);
					tmp.add(r);
				}
			}
			
			this.modifyRecords = tmp;
		}
		
		if (modified) {
			if (isPrimaryTable) {
				//??????delete???ͱ???ͬ??delete?ӱ?
				ArrayList tableList = getTableList();
				int size = tableList.size();
				for (int i = 0; i < size; ++i) {
					RowPhyTable t = ((RowPhyTable)tableList.get(i));
					t.delete(data, "s");//ɾ???ӱ?????
					t.delete(data);//ɾ???ӱ?????
				}
				
				//??????ɾ??????????λ?û?仯????Ҫͬ???ӱ?????
				for (int i = 0; i < size; ++i) {
					RowPhyTable t = ((RowPhyTable)tableList.get(i));
					t.update(this.modifyRecords);
					t.saveModifyRecords();
				}
			}
			
			if (!deleteByBaseKey) {
				saveModifyRecords();
			}
		}
		
		if (!deleteByBaseKey) {
			groupTable.save();
		}
		
		if (nopt) {
			result.addAll(result1);
		}
		
		return result;
	}
	
	/**
	 * ???ݻ????IJ?????ͬ???????Լ??IJ???
	 * @param baseModifyRecords ?????IJ?????¼
	 * @return
	 * @throws IOException
	 */
	private boolean update(ArrayList baseModifyRecords) throws IOException {
		getGroupTable().checkWritable();
		
		if (baseModifyRecords == null) return false;
		ArrayList modifyRecords = getModifyRecords();
		if (modifyRecords == null) {
			return false;
		}
		//int fieldsLen = columns.length;
		int len = sortedColStartIndex;
		int []index = new int[len];
		int []findex = getSortedColIndex();
		for (int i = 0; i < len; ++i) {
			index[i] = findex[i];
		}
		
		boolean find = false;
		int parentRecordSeq = 0;
		for (ModifyRecord mr : baseModifyRecords) {
			parentRecordSeq++;
			Record mrec = mr.getRecord();
			
			if (mr.getState() != ModifyRecord.STATE_DELETE) {
				for (ModifyRecord r : modifyRecords) {
					if (r.getState() == ModifyRecord.STATE_DELETE) {
						continue;
					}
					
					Record rec = r.getRecord();
					int cmp = rec.compare(mrec, index);
					if (cmp == 0) {
						if (mr.getState() == ModifyRecord.STATE_INSERT) {
							r.setParentRecordSeq(-parentRecordSeq);
						} else {
							r.setParentRecordSeq(mr.getRecordSeq());
						}
						find = true;
					}
				}
			}
		}
		return find;
	}
	
	/**
	 * ????dataɾ???ӱ??IJ???
	 * @param data
	 * @return
	 * @throws IOException
	 */
	private boolean delete(Sequence data) throws IOException {
		ArrayList tmp = new ArrayList();
		ArrayList srcModifyRecords = new ArrayList();
		ArrayList modifyRecords = getModifyRecords();
		if (modifyRecords == null) {
			return false;
		}
		tmp.addAll(modifyRecords);
		
		int len = sortedColStartIndex;
		int []index = new int[len];
		int []findex = getSortedColIndex();
		for (int i = 0; i < len; ++i) {
			index[i] = findex[i];
		}
		
		len = data.length();
		boolean delete = false;
		for (int i = 1; i <= len; i++) {
			Record mrec = (Record) data.get(i);
			srcModifyRecords.clear();
			srcModifyRecords.addAll(tmp);
			tmp.clear();
			for (ModifyRecord r : srcModifyRecords) {
				int state = r.getState();
				if (state == ModifyRecord.STATE_UPDATE) {
					Record rec = r.getRecord();
					int cmp = rec.compare(mrec, index);
					if (cmp == 0) {
						r.setDelete();
						r.setRecord(null);
						delete = true;
					}
					tmp.add(r);
				} else if (state == ModifyRecord.STATE_INSERT) {
					Record rec = r.getRecord();
					int cmp = rec.compare(mrec, index);
					if (cmp == 0) {
						delete = true;
					} else {
						tmp.add(r);
					}
				} else {
					tmp.add(r);
				}
			}
		}
		
		if (delete) {
			this.modifyRecords = tmp;
		}
		return delete;
	}
	
	/**
	 * ????reader????ѡ?Ƿ????һ?飩
	 * @param isLoadFirstBlock
	 * @return
	 */
	public BlockLinkReader getRowReader(boolean isLoadFirstBlock) {
		BlockLinkReader reader = new BlockLinkReader(dataBlockLink);
		reader.setDecompressBufferSize(4096);
		
		if (isLoadFirstBlock) {
			try {
				reader.loadFirstBlock();
			} catch (IOException e) {
				throw new RQException(e.getMessage(), e);
			}
		}
		
		return reader;
	}

	public ObjectReader getSegmentObjectReader() {
		BlockLinkReader segmentReader = new BlockLinkReader(segmentBlockLink);
		try {
			segmentReader.loadFirstBlock();
			return new ObjectReader(segmentReader, groupTable.getBlockSize() - ComTable.POS_SIZE);
		} catch (IOException e) {
			segmentReader.close();
			throw new RQException(e.getMessage(), e);
		}
	}
	
	/**
	 * ???ά?ֶε?λ??
	 * @return
	 */
	public boolean[] getDimIndex() {
		String[] col = getAllColNames();
		boolean []isDim = new boolean[col.length];

		for (int i = 0, len = col.length; i < len; i++) {
			if (isDim(col[i])) {
				isDim[i] = true;
			} else {
				isDim[i] = false;
			}
		}
		return isDim;
	}

	/**
	 * ?ж??Ƿ???ά
	 * @param colName
	 * @return
	 */
	protected boolean isDim(String colName) {
		boolean isDim[] = this.isDim;
		String colNames[] = this.colNames;
		int len = colNames.length;
		
		for (int i = 0; i < len; ++i) {
			if (colName.equals(colNames[i])) {
				return isDim[i];
			}
		}
		return false;
	}
	
	/**
	 * ?ж??Ƿ???key
	 * @param colName
	 * @return
	 */
	protected boolean isKey(String colName) {
		boolean isKey[] = this.isDim;
		String colNames[] = this.colNames;
		int len = colNames.length;
		
		for (int i = 0; i < len; ++i) {
			if (colName.equals(colNames[i])) {
				return isKey[i];
			}
		}
		return false;
	}
	
	/**
	 * ?õ?ά?ֶθ???
	 * @return
	 */
	protected int getDimCount() {
		boolean isDim[] = this.isDim; 
		int len = isDim.length;
		int count = 0;
		
		for (int i = 0; i < len; ++i) {
			if (isDim[i]) {
				count++;
			}
		}
		return count;
	}
	
	/**
	 * ?õ?ά?ֶθ???
	 * @return
	 */
	protected int getKeyCount() {
		boolean isKey[] = this.isKey; 
		int len = isKey.length;
		int count = 0;
		
		for (int i = 0; i < len; ++i) {
			if (isKey[i]) {
				count++;
			}
		}
		return count;
	}
	
	protected int getSerialBytesLen(int index) {
		return serialBytesLen[index];
	}
	
	public int[] getSerialBytesLen() {
		return serialBytesLen;
	}
	
	public String[] getAllSortedColNames() {
		if (parent == null) return getSortedColNames();
		return allSortedColNames;
	}
	
	/**
	 * ?????????ֶ????ƣ????????????ӱ??????ֶ?
	 * @return
	 */
	public String[] getTotalColNames() {
		if (parent == null) return colNames;
		int len = getTotalColCount();
		int baseColCount = parent.colNames.length;
		String[] names = new String[len];
		System.arraycopy(parent.colNames, 0, names, 0, baseColCount);
		System.arraycopy(colNames, 0, names, baseColCount, colNames.length);		
		return names;
	}
	
	/**
	 * ???ֶ???
	 * @return
	 */
	public int getTotalColCount() {
		if (parent == null) return colNames.length;
		return parent.colNames.length + colNames.length;
	}
	
	/**
	 * ?????ֶε??źų??ȣ????????????ӱ??????ֶΣ?
	 * @return
	 */
	public int[] getTotalSerialBytesLen() {
		if (parent == null) return serialBytesLen;
		int len = getTotalColCount();
		int []baseSerialBytesLen = parent.getSerialBytesLen();
		int baseColCount = baseSerialBytesLen.length;
		int[] serialBytesLen = new int[len];
		System.arraycopy(baseSerialBytesLen, 0, serialBytesLen, 0, baseColCount);
		System.arraycopy(this.serialBytesLen, 0, serialBytesLen, baseColCount, this.serialBytesLen.length);		
		return serialBytesLen;
	}
	
	/**
	 * ?д治֧?ֶ?·
	 * @param fields
	 * @param filter
	 * @param ctx
	 * @param pathCount
	 * @return
	 */
	public ICursor cursor(String[] fields, Expression filter, Context ctx, int pathCount) {
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}
	
	public ICursor cursor(Expression[] exps, String[] fields, Expression filter, Context ctx, int pathCount) {
		return cursor(exps, fields, filter, null, null, null, pathCount, null, ctx);
	}
	
	public Table finds(Sequence values) throws IOException {
		return finds(values, null);
	}

	public Table finds(Sequence values, String[] selFields) throws IOException {
		getGroupTable().checkReadable();
		
		if (!hasPrimaryKey()) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("dw.lessKey"));
		}
		
		String []keys = getAllSortedColNames();
		int keyCount = keys.length;
		Expression exp;
		Sequence keyValues = values;
		
		if (keyCount == 1) {
			exp = new Expression("null.contain(" + keys[0] + ")"); 
			Object obj = values.getMem(1);
			int valueLen = values.length();
			if (valueLen == 0) {
				return null;
			}
			if (obj instanceof Sequence) {
				Sequence seq = (Sequence)obj;
				int dimCount = seq.length();
				if (dimCount > keyCount) {
					MessageManager mm = EngineMessage.get();
					throw new RQException("find" + mm.getMessage("function.invalidParam"));
				}
				
				keyValues = new Sequence();
				for (int i = 1; i <= valueLen; ++i) {
					seq = (Sequence)values.getMem(i);
					keyValues.add(seq.getMem(1));
				}
			}
		} else {
			String str = "null.contain([";
			for (int i = 0; i < keyCount; i++) {
				str += keys[i];
				if (i != keyCount - 1) {
					str += ",";
				}
			}
			str += "])";
			exp = new Expression(str); 
		}

		Context ctx = new Context();
		exp.getHome().setLeft(new Constant(keyValues));
		Sequence result = cursor(selFields, exp, ctx).fetch();
		if (result == null) return null;
		Table table = new Table(result.dataStruct());
		table.addAll(result);
		return table;
	}

	public int getFirstBlockFromModifyRecord() {
		// TODO Auto-generated method stub
		return 0;
	}

	public long resetByBlock(int block) {
		// TODO Auto-generated method stub
		return 0;
	}
	
	long[] checkDim(String field, Node node, Context ctx) {
		if (!isDim(field) || (parent != null)) {
			return null;
		}
		
		int operator = 0;
		if (node instanceof Equals) {
			operator = IFilter.EQUAL;
		} else if (node instanceof Greater) {
			operator = IFilter.GREATER;
		} else if (node instanceof NotSmaller) {
			operator = IFilter.GREATER_EQUAL;
		} else if (node instanceof Smaller) {
			operator = IFilter.LESS;
		} else if (node instanceof NotGreater) {
			operator = IFilter.LESS_EQUAL;
		} else if (node instanceof NotEquals) {
			operator = IFilter.NOT_EQUAL;
		} else {
			return null;
		}
		
		Object value;
		if (node.getRight() instanceof UnknownSymbol) {
			value = node.getLeft().calculate(ctx);
		} else {
			value = node.getRight().calculate(ctx);
		}
		
		int blockCount = dataBlockCount;
		ColumnFilter filter = new ColumnFilter(field, 0, operator, value);
		LongArray intervals = new LongArray();
		ObjectReader segmentReader = getSegmentObjectReader();
		int index = -1;
		String []colNames = this.colNames;
		for (int i = 0; i < colNames.length; ++i) {
			if (field.equals(colNames[i])) {
				index = i;
				break;
			}
		}
		
		try {
			boolean flag = false;
			Object maxValue = null, minValue = null;
			int keyCount = getAllSortedColNamesLength();
			
			for (int i = 0; i < blockCount; ++i) {
				int recordCount = segmentReader.readInt32();
				long pos = segmentReader.readLong40();
				if (flag) {
					intervals.add(pos - 1);
				}
				flag = false;
				for (int k = 0; k < keyCount; ++k) {
					if (k == index) {
						minValue = segmentReader.readObject();
						maxValue = segmentReader.readObject();
					} else {
						segmentReader.skipObject();
						segmentReader.skipObject();
					}
				}
				if (filter.match(minValue, maxValue) && recordCount != 1) {
					intervals.add(pos);
					flag = true;
				}
			}
			if (flag) {
				intervals.add(getGroupTable().fileSize);
			}
		} catch (IOException e) {
			throw new RQException(e.getMessage(), e);
		}
		if (intervals.size() == 0) {
			return null;
		}
		return intervals.toArray();
	}

	// @m ?Թ鲢??ʽ׷?ӣ??ݲ?֧???и????????
	public void append(ICursor cursor, String opt) throws IOException {
		if (isSorted && opt != null) {
			if (opt.indexOf('a') != -1) {
				RowPhyTable ctmd = (RowPhyTable)getSupplementTable(true);
				ctmd.mergeAppend(cursor, opt);
			} else if (opt.indexOf('m') != -1) {
				mergeAppend(cursor, opt);
			} else {
				append(cursor);
				if (opt.indexOf('i') != -1) {
					appendCache();
				}
			}
		} else {
			append(cursor);
		}
	}
	
	private void mergeAppend(ICursor cursor, String opt) throws IOException {
		// ??֧?ִ???????????鲢׷??
		if (!isSingleTable()) {
			throw new RQException("'append@m' is unimplemented in annex table!");
		}
		// ??????ݽṹ?Ƿ????
		Sequence data = cursor.peek(ICursor.FETCHCOUNT);		
		if (data == null || data.length() <= 0) {
			return;
		}
		
		//?жϽṹƥ??
		DataStruct ds = data.dataStruct();
		if (ds == null) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.needPurePmt"));
		}
		
		String []columns = this.colNames;
		int colCount = columns.length;
		if (colCount != ds.getFieldCount()) {
			MessageManager mm = EngineMessage.get();
			throw new RQException(mm.getMessage("engine.dsNotMatch"));
		}
		
		for (int i = 0; i < colCount; i++) {
			if (!ds.getFieldName(i).equals(columns[i])) {
				MessageManager mm = EngineMessage.get();
				throw new RQException(mm.getMessage("engine.dsNotMatch"));
			}
		}
				
		// ?鲢?????????ȱ??浽??ʱ?ļ?
		RowComTable groupTable = (RowComTable)getGroupTable();
		File srcFile = groupTable.getFile();
		File tmpFile = File.createTempFile("tmpdata", "", srcFile.getParentFile());
		try {
			Context ctx = new Context();
			String colNames[] = this.colNames.clone();
			for (int i = 0; i < colNames.length; i++) {
				if (isDim[i]) {
					colNames[i] = "#" + colNames[i];
				}
			}
			RowComTable tmpGroupTable = new RowComTable(tmpFile, colNames, groupTable.getDistribute(), null, ctx);
			tmpGroupTable.writePswHash = groupTable.writePswHash;
			tmpGroupTable.readPswHash = groupTable.readPswHash;
			
			PhyTable baseTable = tmpGroupTable.getBaseTable();
			if (segmentCol != null) {
				baseTable.setSegmentCol(segmentCol, segmentSerialLen);
			}
			
			int dcount = sortedColNames.length;
			Expression []mergeExps = new Expression[dcount];
			for (int i = 0; i < dcount; ++i) {
				mergeExps[i] = new Expression(sortedColNames[i]);
			}
			
			// ???鲢
			RowCursor srcCursor = new RowCursor(this);
			ICursor []cursors = new ICursor[]{srcCursor, cursor};
			MergesCursor mergeCursor = new MergesCursor(cursors, mergeExps, ctx);
			baseTable.append(mergeCursor);
			baseTable.close();
			
			// ?رղ?ɾ??????ļ???????ʱ?ļ???????Ϊ????ļ???
			groupTable.raf.close();
			groupTable.file.delete();
			tmpFile.renameTo(groupTable.file);
			
			// ???´????
			groupTable.reopen();
		} finally {
			tmpFile.delete();
		}	
	}
	
	//д?뻺???????
	public void appendCache() throws IOException {
		if (appendCache == null) return;
		
		ICursor cursor = new MemoryCursor(appendCache);
		// ׼??д????
		prepareAppend();
		
		if (parent != null) {
			parent.appendCache();
			appendAttached(cursor);
		} else if (sortedColNames == null) {
			appendNormal(cursor);
		} else if (groupTable.baseTable.getSegmentCol() == null) {
			appendSorted(cursor);
		} else {
			appendSegment(cursor);
		}
		
		// ????д???ݣ????浽?ļ?
		finishAppend();
		appendCache = null;
	}

	public ICursor cursor(String[] fields, Expression filter, String[] fkNames, Sequence[] codes, 
			String []opts, Context ctx, int pathSeq, int pathCount, int pathCount2) {
		getGroupTable().checkReadable();
		//?д治֧?ֶ?·
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}

	public ICursor cursor(String[] fields, Expression exp, String[] fkNames, Sequence[] codes, 
			String []opts, ICursor cs, int seg, Object [][]endValues, Context ctx) {
		getGroupTable().checkReadable();
		//?д治֧?ֶ?·
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}

	public ICursor cursor(Expression[] exps, String[] fields, Expression filter, String[] fkNames, 
			Sequence[] codes,String []opts, int pathCount, String opt, Context ctx) {
		if (fkNames != null || codes != null) {
			MessageManager mm = EngineMessage.get();
			throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
		}
		
		if (pathCount < 2) {
			return cursor(exps, fields, filter, fkNames, codes, opts, opt, ctx);
		}
		
		PhyTable tmd = getSupplementTable(false);
		int blockCount = getDataBlockCount();
		if (blockCount == 0) {
			if (tmd == null) {
				return new MemoryCursor(null);
			} else {
				return tmd.cursor(exps, fields, filter, fkNames, codes,  opts, pathCount, opt, ctx);
			}
		}
		
		ICursor []cursors;

		int avg = blockCount / pathCount;
		if (avg < 1) {
			avg = 1;
			pathCount = blockCount;
		}
		
		// ǰ??Ŀ?ÿ?ζ?һ??
		int mod = blockCount % pathCount;
		cursors = new ICursor[pathCount];
		int start = 0;
		for (int i = 0; i < pathCount; ++i) {
			int end = start + avg;
			if (i < mod) {
				end++;
			}
			
			if (filter != null) {
				// ?ֶβ??ж?ȡʱ??Ҫ???Ʊ???ʽ??ͬһ??????ʽ??֧?ֲ???????
				filter = filter.newExpression(ctx);
			}
			
			RowCursor cursor = new RowCursor(this, null, filter, exps, fields, ctx);
			
			cursor.setSegment(start, end);

			cursors[i] = cursor;
			start = end;
		}
		
		MultipathCursors mcs = new MultipathCursors(cursors, ctx);
		if (tmd == null) {
			return mcs;
		}
		
		String []sortFields = ((IDWCursor)cursors[0]).getSortFields();
		if (sortFields != null) {
			ICursor cs2 = tmd.cursor(exps, fields, filter, fkNames, codes,  opts, mcs, opt, ctx);
			return merge(mcs, (MultipathCursors)cs2, sortFields);
		} else {
			ICursor cs2 = tmd.cursor(exps, fields, filter, fkNames, codes,  opts, pathCount, opt, ctx);
			return conj(mcs, cs2);
		}
	
	}

	public ICursor cursor(Expression[] exps, String[] fields, Expression filter, String[] fkNames, 
			Sequence[] codes,String []opts, int segSeq, int segCount, String opt, Context ctx) {

		getGroupTable().checkReadable();
		
		if (filter != null) {
			// ?ֶβ??ж?ȡʱ??Ҫ???Ʊ???ʽ??ͬһ??????ʽ??֧?ֲ???????
			filter = filter.newExpression(ctx);
		}
		
		RowCursor cursor = new RowCursor(this, null, filter, exps, fields, ctx);
		
		if (segCount < 2) {
			return cursor;
		}
		
		int startBlock = 0;
		int endBlock = -1;
		int avg = dataBlockCount / segCount;
		
		if (avg < 1) {
			// ÿ?β???һ??
			if (segSeq <= dataBlockCount) {
				startBlock = segSeq - 1;
				endBlock = segSeq;
			}
		} else {
			if (segSeq > 1) {
				endBlock = segSeq * avg;
				startBlock = endBlock - avg;
				
				// ʣ??Ŀ?????ÿ?ζ?һ??
				int mod = dataBlockCount % segCount;
				int n = mod - (segCount - segSeq);
				if (n > 0) {
					endBlock += n;
					startBlock += n - 1;
				}
			} else {
				endBlock = avg;
			}
		}

		cursor.setSegment(startBlock, endBlock);
		return cursor;
	}

	public ICursor cursor(Expression[] exps, String[] fields, Expression filter, String[] fkNames, 
			Sequence[] codes,String []opts, MultipathCursors mcs, String opt, Context ctx) {
		//?д治֧?ֶ?·
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}

	public ICursor cursor(Expression[] exps, String[] fields, Expression filter, String[] fkNames, 
			Sequence[] codes,String []opts, int pathSeq, int pathCount, int pathCount2, String opt, Context ctx) {
		//?д治֧?ֶ?·
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}

	public ICursor cursor(Expression[] exps, String[] fields, Expression filter, String[] fkNames, 
			Sequence[] codes,String []opts, ICursor cs, int seg, Object[][] endValues, Context ctx) {
		//?д治֧?ֶ?·
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}

	public void addColumn(String colName, Expression exp, Context ctx) {
		//?д治֧???޸???
		MessageManager mm = EngineMessage.get();
		throw new RQException("cursor" + mm.getMessage("dw.needMCursor"));
	}

	public void deleteColumn(String colName) {
	}
	
	/**
	 * ?????е??????Сֵ
	 * @param key
	 * @return
	 * @throws IOException
	 */
	public Object[] getMaxMinValue(String key) throws IOException {
		if (this.totalRecordCount == 0) {
			return null;
		}
		boolean isDim[] = this.isDim;
		String colNames[] = this.colNames;
		int len = colNames.length;
		
		boolean isColumn = false;
		int col = -1;
		for (int i = 0; i < len; ++i) {
			if (key.equals(colNames[i])) {
				isColumn = true;
				col = i;
				break;
			}
		}
		
		if (!isColumn) {
			return null;
		}
		
		if (!isDim[col]) {
			Expression max = new Expression("max(" + key +")");
			Expression min = new Expression("min(" + key +")");
			Expression[] exps = new Expression[] {max, min};
			Sequence seq = cursor(new String[] {key}).groups(null, null, exps, null, null, new Context());
			return ((Record)seq.get(1)).getFieldValues();
		}
		
		ObjectReader segmentReader = getSegmentObjectReader();
		
		int colCount = getAllColNames().length;
		int blockCount = getDataBlockCount();
		Object max = null, min = null;
		
		segmentReader.readLong40();
		for (int c = 0; c < colCount; c++) {
			Object minValue = segmentReader.readObject();
			Object maxValue = segmentReader.readObject();
			if (c == col) {
				min = minValue;
				max = maxValue;
			}
		}
		
		for (int i = 1; i < blockCount; ++i) {
			segmentReader.readLong40();
			for (int c = 0; c < colCount; c++) {
				Object minValue = segmentReader.readObject();
				Object maxValue = segmentReader.readObject();
				if (c == col) {
					if (Variant.compare(minValue, min) < 0) {
						min = minValue;
					}
					segmentReader.readObject();
					if (Variant.compare(maxValue, max) > 0) {
						max = maxValue;
					}
				}
			}
		}
		return new Object[] {max, min};
	}

	public void append(PhyTable table) throws IOException {
		throw new RuntimeException();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy