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

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

Go to download

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

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

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import com.scudata.common.DateFactory;
import com.scudata.common.MessageManager;
import com.scudata.common.RQException;
import com.scudata.dm.BaseRecord;
import com.scudata.dm.Context;
import com.scudata.dm.DataStruct;
import com.scudata.dm.Env;
import com.scudata.dm.FileObject;
import com.scudata.dm.ObjectReader;
import com.scudata.dm.Sequence;
import com.scudata.dm.Table;
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.IParam;
import com.scudata.expression.Node;
import com.scudata.expression.ParamInfo2;
import com.scudata.expression.UnknownSymbol;
import com.scudata.expression.fn.Between;
import com.scudata.expression.fn.datetime.Month;
import com.scudata.expression.fn.datetime.Year;
import com.scudata.expression.mfn.sequence.Contain;
import com.scudata.expression.operator.And;
import com.scudata.expression.operator.DotOperator;
import com.scudata.expression.operator.Equals;
import com.scudata.expression.operator.Greater;
import com.scudata.expression.operator.NotGreater;
import com.scudata.expression.operator.NotSmaller;
import com.scudata.expression.operator.Or;
import com.scudata.expression.operator.Smaller;
import com.scudata.parallel.ClusterCursor;
import com.scudata.parallel.ClusterPhyTable;
import com.scudata.resources.EngineMessage;
import com.scudata.util.EnvUtil;
import com.scudata.util.Variant;

/**
 * ????????
 * ?̳????д?????????ڴ洢һ??????Ԥ???ܽ??
 * @author runqian
 *
 */
public class Cuboid extends RowComTable {
	public static final String CUBE_PREFIX = "_CUBOID@";//???????ļ?ǰ׺
	private static final int FIXED_OBJ_LEN = 9;//?????ֶεij???
	//?ļ?ͷ????????????
	protected String[] exps;//???????ʽ
	protected String[] newExps;//???ܱ???ʽ
	private long srcCount;//ԭ?????ѷ??????? 

	protected CuboidTable baseTable;
	
	/**
	 * ??,???鱸?ݱ?־
	 * @param file ???????ļ?
	 * @param ctx
	 * @throws IOException
	 */
	public Cuboid(File file, Context ctx) throws IOException {
		super();
		this.file = file;
		this.raf = new RandomAccessFile(file, "rw");
		this.ctx = ctx;
		if (ctx != null)
			ctx.addResource(this);
		readHeader();
	}

	/**
	 * ?½?һ????????
	 * @param file ?ļ?????
	 * @param colNames ??????
	 * @param serialBytesLen ÿ???е??źų???
	 * @param ctx
	 * @param writePsw д????
	 * @param readPsw ??????
	 * @param exps ???????ʽ
	 * @param newExps ???ܱ???ʽ
	 * @throws IOException
	 */
	public Cuboid(File file, String []colNames, int []serialBytesLen, Context ctx, 
			String writePsw, String readPsw, String[] exps, String[] newExps) throws IOException {
		super(file, colNames, null, null, ctx);
		this.exps = exps;
		this.newExps = newExps;
		srcCount = 0;
	}
	
	/**
	 * ???ļ?ͷ
	 */
	protected void readHeader() throws IOException {
		Object syncObj = getSyncObject();
		synchronized(syncObj) {
			restoreTransaction();
			raf.seek(0);
			byte []bytes = new byte[32];
			raf.read(bytes);
			if (bytes[0] != 'r' || bytes[1] != 'q' || bytes[2] != 'd' || bytes[3] != 'w' || bytes[4] != 'g' || bytes[5] != 't' || bytes[6] != 'r') {
				MessageManager mm = EngineMessage.get();
				throw new RQException(mm.getMessage("license.fileFormatError"));
			}
			
			BufferReader reader = new BufferReader(structManager, bytes, 7, 25);
			setBlockSize(reader.readInt32());
			headerBlockLink = new BlockLink(this);
			headerBlockLink.readExternal(reader);
			
			BlockLinkReader headerReader = new BlockLinkReader(headerBlockLink);
			bytes = headerReader.readBlocks();
			headerReader.close();
			reader = new BufferReader(structManager, bytes);
			reader.read(); // r
			reader.read(); // q
			reader.read(); // d
			reader.read(); // w
			reader.read(); // g
			reader.read(); // t
			reader.read(); // r
			
			blockSize = reader.readInt32();
			headerBlockLink.readExternal(reader);
			
			reader.read(reserve); // ????λ
			freePos = reader.readLong40();
			fileSize = reader.readLong40();
			
			if (reserve[0] > 0) {
				writePswHash = reader.readString();
				readPswHash = reader.readString();
				checkPassword(null);
				
				if (reserve[0] > 1) {
					distribute = reader.readString();
				}
			}
	
			int dsCount = reader.readInt();
			if (dsCount > 0) {
				ArrayList dsList = new ArrayList(dsCount);
				for (int i = 0; i < dsCount; ++i) {
					String []fieldNames = reader.readStrings();
					DataStruct ds = new DataStruct(fieldNames);
					dsList.add(ds);
				}
				
				structManager = new StructManager(dsList);
			} else {
				structManager = new StructManager();
			}

			baseTable = new CuboidTable(this, null);
			baseTable.readExternal(reader);
			super.baseTable = baseTable;
			
			exps = reader.readStrings();
			newExps = reader.readStrings();
			srcCount = reader.readLong40();
			
		}
	}
	
	/**
	 * д???ļ?ͷ
	 */
	public void writeHeader() throws IOException {
		Object syncObj = getSyncObject();
		synchronized(syncObj) {
			beginTransaction(null);
			BufferWriter writer = new BufferWriter(structManager);
			writer.write('r');
			writer.write('q');
			writer.write('d');
			writer.write('w');
			writer.write('g');
			writer.write('t');
			writer.write('r');
			
			writer.writeInt32(blockSize);
			headerBlockLink.writeExternal(writer);
			
			reserve[0] = 3; // 1???????룬2???ӷֲ???????3????Ԥ????
			writer.write(reserve); // ????λ
			
			writer.writeLong40(freePos);
			writer.writeLong40(fileSize);
			
			// ??????????Ա?汾1???ӵ?
			writer.writeString(writePswHash);
			writer.writeString(readPswHash);
			
			writer.writeString(distribute); // ?汾2????
	
			ArrayList dsList = structManager.getStructList();
			if (dsList != null) {
				writer.writeInt(dsList.size());
				for (DataStruct ds : dsList) {
					String []fieldNames = ds.getFieldNames();
					writer.writeStrings(fieldNames);
				}
			} else {
				writer.writeInt(0);
			}
			super.baseTable.writeExternal(writer);
			
			writer.writeStrings(exps);
			writer.writeStrings(newExps);
			writer.writeLong40(srcCount);
			
			BlockLinkWriter headerWriter = new BlockLinkWriter(headerBlockLink, false);
			headerWriter.rewriteBlocks(writer.finish());
			headerWriter.close();
			
			// ??дheaderBlockLink
			writer.write('r');
			writer.write('q');
			writer.write('d');
			writer.write('w');
			writer.write('g');
			writer.write('t');
			writer.write('r');
			
			writer.writeInt32(blockSize);
			headerBlockLink.writeExternal(writer);
			raf.seek(0);
			raf.write(writer.finish());
			raf.getChannel().force(true);
			commitTransaction(0);
		}
	}

	public void save() throws IOException {
		super.save();
	}
	
	public void setCount(long count) {
		this.setSrcCount(count);
	}

	/**
	 * ??ʽ??????ʽ???????Ԥ?????ֶ?ƥ?????''???????ƥ??????Ϊ?DZ???ʽ
	 * @param exps
	 * @param ds
	 */
	private static void formatExps(Expression exps[], DataStruct ds) {
		int expsLen = exps == null ? 0 : exps.length;
		for (int i = 0; i < expsLen; i++) {
			if (ds.getFieldIndex(exps[i].getIdentifierName()) >= 0) {
				//???ֱ?Ӿ???Ԥ??????ֶ?
				exps[i] = new Expression("'" + exps[i].getIdentifierName() + "'");
			}
		}
	}
	
	/**
	 * ȡ?÷??????ʽexps?;ۺϱ???ʽnewExps???õ????ֶΣ???????ԭ???α?
	 * @param srcTable ԭ??
	 * @param exps ???????ʽ
	 * @param newExps ?ۺϱ???ʽ
	 * @param w ???˱???ʽ
	 * @param hasM ?Ƿ??·
	 * @param n ????·??
	 * @param ctx ??????
	 * @return
	 */
	private static ICursor makeCursor(PhyTable srcTable, Expression []exps, Expression []newExps,
			Expression w, boolean hasM, int n, Context ctx) {
		List list = new ArrayList();
		if (exps != null) {
			for (Expression exp : exps) {
				exp.getUsedFields(ctx, list);
			}
		}
		if (newExps != null) {
			for (Expression exp : newExps) {
				exp.getUsedFields(ctx, list);
			}
		}
		String fields[] = new String[list.size()];
		list.toArray(fields);
		
		if (hasM) {
			return srcTable.cursor(null, fields, w, null, null, null, n, null, ctx);
		} else {
			return srcTable.cursor(fields, w, ctx);
		}
	}
	
	/**
	 * ????
	 * @param exps ???????ʽ
	 * @param names ???????ʽ??????
	 * @param newExps ???ܱ???ʽ
	 * @param newNames ???ܱ???ʽ??????
	 * @param srcTable Ҫ????????
	 * @param w ????????
	 * @param hasM ?Ƿ???
	 * @param n ??????
	 * @param option
	 * @param ctx
	 * @return
	 */
	public static Sequence cgroups(String []expNames, String []names, String []newExpNames, String []newNames,
			PhyTable srcTable, Expression w, boolean hasM, int n, String option,  Context ctx) {
		//ע?⣬cgroups_?õ????ǰ??????ļ?cuboid??
		
		return cgroups_(expNames, names, newExpNames, newNames, srcTable, w, hasM, n, option, null, ctx);
		
		/*TableMetaData tmd = srcTable.getSupplementTable(false);
		if (tmd == null) {
			return seq;
		} else {			
			//??֯???????ʽ
			int count = names.length;
			Expression exps[] = new Expression[count];
			for (int i = 0; i < count; i++) {
				exps[i] = new Expression(names[i]);
			}
			
			//??֯?پۺϱ???ʽ
			Expression newExps[] = new Expression[count = newNames.length];//?????洢Ҫ?ۺϵ?ԭ?ֶ?
			for (int i = 0, len = count; i < len; i++) {
				String str = newExpNames[i];
				//??count?پۺϣ?Ҫ??Ϊ?ۼ?
				if (str.indexOf("count(") != -1) {
					str = str.replaceFirst("count", "sum");
				}
				
				//?پۺ?ʱҪ?滻һ???ֶ???
				String sub = str.substring(str.indexOf('(') + 1, str.indexOf(')'));
				str = str.replaceAll(sub, "'" + newNames[i] + "'");
				newExps[i] = new Expression(str);
			}
			return seq.groups(exps, names, newExps, newNames, option, ctx);
		}*/
	}
	
	public static Sequence cgroups(String []expNames, String []names, String []newExpNames, String []newNames,
			PhyTable srcTable, Expression w, boolean hasM, int n, String option, FileObject[] files,  Context ctx) {
		return cgroups_(expNames, names, newExpNames, newNames, srcTable, w, hasM, n, option, files, ctx);
	}
	
	private static Sequence cgroups_(String []expNames, String []names, String []newExpNames, String []newNames,
			PhyTable srcTable,	Expression w, boolean hasM, int n, String option, FileObject[] files, Context ctx) {
		Expression []exps = null;
		if (expNames != null) {
			int len = expNames.length;
			exps = new Expression[len];
			for (int i = 0; i < len; i++) {
				exps[i] = new Expression(expNames[i]);
			}
		}
		
		Expression []newExps = null;
		Expression []newExpsBak = null;
		if (newExpNames != null) {
			int len = newExpNames.length;
			newExps = new Expression[len];
			newExpsBak = new Expression[len];
			for (int i = 0; i < len; i++) {
				newExps[i] = new Expression(newExpNames[i]);
				newExpsBak[i] = new Expression(newExpNames[i]);
			}
		}
		
		int fcount = exps == null ? 0 : exps.length;
		if (newExps != null) fcount += newExps.length;
		if (newExpNames != null) {
			for (int i = 0; i < newExpNames.length; i++) {
				String s = new String(newExpNames[i]);
				newExpNames[i] = s;// s.replaceAll(regex, "_");
			}
		}

		Object obj;
		if (files == null) {
			obj= findCuboid(srcTable, expNames, newExpNames, w, ctx);
		} else {
			obj= findCuboid(srcTable, expNames, newExpNames, w, files, ctx);
		}
		try {
			if (obj instanceof ArrayList) {
				//?ж??Ԥ??????????
				@SuppressWarnings("unchecked")
				ArrayList tableList = (ArrayList) obj;
				int size = tableList.size();
				if (size == 0) {
					//û??Ԥ??????????
					ICursor cs = makeCursor(srcTable, exps, newExps, w, hasM, n, ctx);
					return cs.groups(exps, names, newExps, newNames, option, ctx);
				} else {
					CuboidTable table = null;
					if (size == 1) {
						table = (CuboidTable) tableList.get(0);
					} else {
						//ѡһ????¼???????ٵ?
						int idx = 0;
						long cnt = tableList.get(0).getTotalRecordCount();
						for (int i = 1; i < size; i++) {
							long l = tableList.get(i).getTotalRecordCount();
							if (cnt < l) {
								idx = i;
								cnt = l;
							}
						}
						for (int i = 0; i < size; i++) {
							if (i != idx) {
								tableList.get(i).close();
							}
						}
						table = (CuboidTable) tableList.get(idx);
					}

					
					//Ԥ???鲻?ٸ?????????£?????ɾ??
//					???Ԥ?????Ƿ??????µ?
//					if (((Cuboid) table.groupTable).getSrcCount() != srcTable.getActualRecordCount()) {
//						try {
//							((Cuboid) table.groupTable).update(srcTable);
//						} catch (Exception e) {
//							if (table != null)
//								table.close();
//							for (PhyTable tbl : tableList) {
//								tbl.close();
//							}
//							throw new RQException(e.getMessage(), e);
//						}
//					}
					

					ArrayList fieldExpList = new ArrayList();
					Expression[] fieldExps;//?پۺ?ʱҪȡ???????ֶ?
					int expsLen = exps == null ? 0 : exps.length;
					DataStruct ds = table.getDataStruct();
					for (int i = 0; i < expsLen; i++) {
						if (ds.getFieldIndex(expNames[i]) >= 0) {
							//???ֱ?Ӿ???Ԥ??????ֶ?
							fieldExpList.add(new Expression("'" + expNames[i] + "'"));
						} else {
							//??ȡ?漰?????ֶ?
							Expression exp = new Expression(expNames[i]);
							ArrayList strList = new ArrayList();
							exp.getUsedFields(ctx, strList);
							for (String str : strList) {
								fieldExpList.add(new Expression(str));
							}
						}
					}
					for (int i = expsLen; i < fcount; i++) {
						fieldExpList.add(new Expression("'" + newExpNames[i - expsLen] + "'"));
					}
					fieldExps = new Expression[fieldExpList.size()];
					fieldExpList.toArray(fieldExps);

					Expression newSrcExps[] = new Expression[newExps.length];//?????洢Ҫ?ۺϵ?ԭ?ֶ?
					for (int i = 0, len = newExps.length; i < len; i++) {
						String str = new String(newExps[i].getIdentifierName());
						if (newNames[i] == null) {
							newNames[i] = str;
						}

						//??count?پۺϣ?Ҫ??Ϊ?ۼ?
						if (str.indexOf("count(") != -1) {
							str = str.replaceFirst("count", "sum");
						}

						//??Ԥ???????????پۺ?ʱҪ?滻һ???ֶ???
						String sub = str.substring(str.indexOf('(') + 1, str.indexOf(')'));
						str = str.replaceAll(sub, "'" + newExpNames[i] + "'");
						newExps[i] = new Expression(str);
						if (str.indexOf("top(") == 0) {
							sub = sub.substring(sub.indexOf(',') + 1);
						}
						newSrcExps[i] = new Expression(sub);
					}

					//????ʱ???????Ż?
					while (w != null) {
						//??????????ȡ???????ڷ?????ֶΣ??????ж??
						ArrayList list = new ArrayList();
						getDateFields(table, list);

						//????Щ?????ֶ?ȥƥ??w
						int count = list.size();
						if (count > 0) {
							Object[] interval = new Object[2];
							boolean[] isEQ = new boolean[2];
							ArrayList otherNodes = new ArrayList();
							for (int i = 0; i < count; i += 2) {
								String fname = (String) list.get(i);
								getDateInterval(fname, w.getHome(), interval, isEQ, otherNodes, (Integer) list.get(i + 1), ctx);
								if (interval[0] != null || interval[1] != null) {
									//???w????ʱ??????
									Node nodes[] = makeNode(fname, table.colNames, interval, isEQ,
											(Integer) list.get(i + 1));
									if (nodes != null) {
										if (nodes[0] == null) {
											//ֻ??Ҫ??????????
											otherNodes.add(nodes[1]);
											Expression w2 = conbineNodes(otherNodes);
											ICursor cursor2 = table.cursor(null, w2, ctx);
											return cursor2.groups(exps, names, newExps, newNames, null, ctx);
										}
										@SuppressWarnings("unchecked")
										ArrayList otherNodes2 = (ArrayList) otherNodes.clone();

										otherNodes.add(nodes[0]);
										Expression w1 = conbineNodes(otherNodes);
										Expression tempExps[] = fieldExps.clone();
										System.arraycopy(newSrcExps, 0, tempExps, expsLen, newSrcExps.length);
										ICursor cursor1 = srcTable.cursor(null, w1, ctx);

										ICursor tempCursor = new MemoryCursor(cursor1.groups(exps, names,
												newExpsBak, newExpNames, null, ctx));

										otherNodes2.add(nodes[1]);
										Expression w2 = conbineNodes(otherNodes2);
										ICursor cursor2 = table.cursor(null, w2, ctx);

										ICursor[] cursors = new ICursor[] { tempCursor, cursor2 };
										ICursor mcursor = new MultipathCursors(cursors, ctx);//ICursor mcursor = new ConjxCursor(cursors);
										return mcursor.groups(exps, names, newExps, newNames, null, ctx);
									}
								}

								interval[0] = null;
								interval[1] = null;
								otherNodes.clear();
							}
						} else {
							//û???????ֶ?
							break;
						}

						//?޷?ʹ??Ԥ??????????(??w????С???????????ĵ?λ?????粻??һ???꣬?Ż????е????ʵ????????????ټ????һ????????)
						for (int i = 0; i < newExps.length; i++) {
							newExps[i] = new Expression(newNames[i]);
						}
						ICursor cs = makeCursor(srcTable, exps, newExpsBak, w, hasM, n, ctx);
						return cs.groups(exps, names, newExpsBak, newNames, option, ctx);
					}

					try {
						ICursor cs;
						if (hasM) {
							cs = table.cursor(null, null, w, ctx, n);
						} else {
							cs = table.cursor(null, null, w, ctx);
						}
						formatExps(exps, ds);
						return cs.groups(exps, names, newExps, newNames, null, ctx);
					} catch (Exception e) {
						table.close();
						throw new RQException("cgroups:" + e.getMessage(), e);
					}
				}
			} else {
				PhyTable table = (PhyTable) obj;
				Expression[] fieldExps = new Expression[fcount];
				for (int i = exps.length; i < fcount; i++) {
					fieldExps[i] = new Expression("'" + newExpNames[i - exps.length] + "'");
				}

				String[] fieldNames = new String[fcount];
				for (int i = 0, len = names.length; i < len; i++) {
					if (names[i] == null) {
						names[i] = exps[i].getIdentifierName();
					}
					fieldExps[i] = new Expression(exps[i].getIdentifierName());
				}
				for (int i = 0, len = newNames.length; i < len; i++) {
					if (newNames[i] == null) {
						newNames[i] = newExps[i].getIdentifierName();
					}
				}
				System.arraycopy(names, 0, fieldNames, 0, names.length);
				System.arraycopy(newNames, 0, fieldNames, names.length, newNames.length);

				if (hasM && table.dataBlockCount < n) {
					hasM = false;
				}

				//ת??w
				if (w != null) {
					String str = w.getIdentifierName();
					for (int i = 0, len = names.length; i < len; i++) {
						str = str.replace(fieldNames[i], "'" + fieldNames[i] + "'");
					}
					w = new Expression(str);
				}
				Sequence result;
				if (hasM) {
					result = table.cursor(fieldExps, fieldNames, w, null, null, null, n, null, ctx).fetch();
				} else {
					result = table.cursor(fieldExps, fieldNames, w, null, null, null, null, ctx).fetch();
				}
				if (!(result instanceof Table)) {
					Table seq = new Table(result.dataStruct());
					seq.addAll(result);
					return seq;
				}
				return result;
			}
		} finally {
			if (obj != null) {
				if (obj instanceof ArrayList) {
					@SuppressWarnings("unchecked")
					ArrayList tableList = (ArrayList) obj;
					for (PhyTable table : tableList) {
						table.close();
					}
				} else {
					PhyTable table = (PhyTable) obj;
					table.close();
				}
				
			}
		}
	}
	
	/**
	 * ??Ⱥ????cgroups
	 * @param exps ???????ʽ
	 * @param names ???????ʽ??????
	 * @param newExps ???ܱ???ʽ
	 * @param newNames ???ܱ???ʽ??????
	 * @param srcTable Ҫ????????
	 * @param w ????????
	 * @param hasM ?Ƿ???
	 * @param n ????·??
	 * @param option ????????
	 * @param ctx ??????
	 * @return
	 */
	public static Sequence cgroups(String []expNames, String []names, String []newExpNames, String []newNames,
			ClusterPhyTable srcTable,	Expression w, boolean hasM, int n, String option,  Context ctx) {
		Expression []exps = null;
		if (expNames != null) {
			int len = expNames.length;
			exps = new Expression[len];
			for (int i = 0; i < len; i++) {
				exps[i] = new Expression(expNames[i]);
			}
		}
		
		Expression []newExps = null;
		Expression []newExpsBak = null;
		if (newExpNames != null) {
			int len = newExpNames.length;
			newExps = new Expression[len];
			newExpsBak = new Expression[len];
			for (int i = 0; i < len; i++) {
				newExps[i] = new Expression(newExpNames[i]);
				newExpsBak[i] = new Expression(newExpNames[i]);
			}
		}
		
		int fcount = exps == null ? 0 : exps.length;
		if (newExps != null) fcount += newExps.length;
		if (newExpNames != null) {
			for (int i = 0; i < newExpNames.length; i++) {
				String s = new String(newExpNames[i]);
				newExpNames[i] = s;
			}
		}

		Object obj = findCuboid(srcTable, expNames, newExpNames, w, ctx);
		try {
			if (obj instanceof ArrayList) {
				//?ж??Ԥ??????????
				@SuppressWarnings("unchecked")
				ArrayList tableList = (ArrayList) obj;
				int size = tableList.size();
				if (size == 0) {
					//û??Ԥ??????????
					ClusterCursor cc = srcTable.cursor(null, null, null, null, null, null, 0, null, ctx);
					return (Sequence) cc.groups(exps, names, newExps, newNames, option, ctx);
				} else {
					CuboidTable table = null;
					if (size == 1) {
						table = (CuboidTable) tableList.get(0);
					} else {
						//ѡһ????¼???????ٵ?
						int idx = 0;
						long cnt = tableList.get(0).getTotalRecordCount();
						for (int i = 1; i < size; i++) {
							long l = tableList.get(i).getTotalRecordCount();
							if (cnt > l) {
								idx = i;
								cnt = l;
							}
						}
						for (int i = 1; i < size; i++) {
							if (i != idx) {
								tableList.get(i).close();
							}
						}
						table = (CuboidTable) tableList.get(idx);
					}

					ArrayList fieldExpList = new ArrayList();
					Expression[] fieldExps;//?پۺ?ʱҪȡ???????ֶ?
					int expsLen = exps == null ? 0 : exps.length;
					DataStruct ds = table.getDataStruct();
					for (int i = 0; i < expsLen; i++) {
						if (ds.getFieldIndex(expNames[i]) > 0) {
							//???ֱ?Ӿ???Ԥ??????ֶ?
							fieldExpList.add(new Expression("'" + expNames[i] + "'"));
						} else {
							//??ȡ?漰?????ֶ?
							Expression exp = new Expression(expNames[i]);
							ArrayList strList = new ArrayList();
							exp.getUsedFields(ctx, strList);
							for (String str : strList) {
								fieldExpList.add(new Expression(str));
							}
						}
					}
					for (int i = expsLen; i < fcount; i++) {
						fieldExpList.add(new Expression("'" + newExpNames[i - expsLen] + "'"));
					}
					fieldExps = new Expression[fieldExpList.size()];
					fieldExpList.toArray(fieldExps);

					Expression newSrcExps[] = new Expression[newExps.length];//?????洢Ҫ?ۺϵ?ԭ?ֶ?
					for (int i = 0, len = newExps.length; i < len; i++) {
						String str = new String(newExps[i].getIdentifierName());
						if (newNames[i] == null) {
							newNames[i] = str;
						}

						//??count?پۺϣ?Ҫ??Ϊ?ۼ?
						if (str.indexOf("count(") != -1) {
							str = str.replaceFirst("count", "sum");
						}

						//??Ԥ???????????پۺ?ʱҪ?滻һ???ֶ???
						String sub = str.substring(str.indexOf('(') + 1, str.indexOf(')'));
						str = str.replaceAll(sub, "'" + newExpNames[i] + "'");
						newExps[i] = new Expression(str);
						if (str.indexOf("top(") == 0) {
							sub = sub.substring(sub.indexOf(',') + 1);
						}
						newSrcExps[i] = new Expression(sub);
					}
					try {
						ICursor cs = table.cursor(fieldExps, null, w, ctx);
						return cs.groups(exps, names, newExps, newNames, null, ctx);
					} catch (Exception e) {
						table.close();
						throw new RQException("cgroups:" + e.getMessage(), e);
					}
				}
			} else {
				PhyTable table = (PhyTable) obj;
				Expression[] fieldExps = new Expression[fcount];
				for (int i = exps.length; i < fcount; i++) {
					fieldExps[i] = new Expression("'" + newExpNames[i - exps.length] + "'");
				}

				String[] fieldNames = new String[fcount];
				for (int i = 0, len = names.length; i < len; i++) {
					if (names[i] == null) {
						names[i] = exps[i].getIdentifierName();
					}
					fieldExps[i] = new Expression("'" + names[i] + "'");
				}
				for (int i = 0, len = newNames.length; i < len; i++) {
					if (newNames[i] == null) {
						newNames[i] = newExps[i].getIdentifierName();
					}
				}
				System.arraycopy(names, 0, fieldNames, 0, names.length);
				System.arraycopy(newNames, 0, fieldNames, names.length, newNames.length);

				if (hasM && table.dataBlockCount < n) {
					hasM = false;
				}

				//ת??w
				if (w != null) {
					String str = w.getIdentifierName();
					for (int i = 0, len = names.length; i < len; i++) {
						str = str.replace(fieldNames[i], "'" + fieldNames[i] + "'");
					}
					w = new Expression(str);
				}
				Sequence result;
				if (hasM) {
					result = table.cursor(fieldExps, fieldNames, w, null, null, null, n, null, ctx).fetch();
				} else {
					result = table.cursor(fieldExps, fieldNames, w, null, null, null, null, ctx).fetch();
				}
				if (!(result instanceof Table)) {
					Table seq = new Table(result.dataStruct());
					seq.addAll(result);
					return seq;
				}
				return result;
			} 
		} finally {
			if (obj != null) {
				if (obj instanceof ArrayList) {
					@SuppressWarnings("unchecked")
					ArrayList tableList = (ArrayList) obj;
					for (PhyTable table : tableList) {
						table.close();
					}
				} else {
					PhyTable table = (PhyTable) obj;
					table.close();
				}
				
			}
		}
	}
	
	/**
	 * ????Ԥ????????ٷ???
	 * @param sub0 ?????ֶ?
	 * @param sub1 ???????ʽ
	 * @param srcTable Ҫ????????
	 * @param w ????????
	 * @param hasM ?Ƿ???
	 * @param n ??????
	 * @param opt
	 * @param ctx
	 * @return
	 */
	public static Sequence cgroups(IParam sub0, IParam sub1, PhyTable srcTable,
			Expression w, boolean hasM, int n, String opt, Context ctx) {
		String []expNames = null;
		String []names = null;
		String []newExpNames = null;
		String []newNames = null;
		
		if (sub0 != null) {
			ParamInfo2 pi0 = ParamInfo2.parse(sub0, "cuboid", true, false);
			names = pi0.getExpressionStrs2();
			expNames = pi0.getExpressionStrs1();
		}
		
		ParamInfo2 pi1 = null;
		if (sub1 != null) {
			pi1 = ParamInfo2.parse(sub1, "cuboid", true, false);
			newExpNames = pi1.getExpressionStrs1();
			newNames = pi1.getExpressionStrs2();
		}
		return cgroups(expNames, names, newExpNames, newNames, srcTable, w, hasM, n, opt, ctx);
	}
	
	private static Expression conbineNodes(ArrayList nodes) {
		int size = nodes.size();
		if (size == 0) {
			return null;
		}
		Node node = nodes.get(0);
		for (int i = 1; i < size; i++) {
			And and = new And();
			and.setLeft(node);
			and.setRight(nodes.get(i));
			node = and;
		}
		return new Expression(node);
	}
	
	/**
	 * ???Һ??ʵ???????
	 * @param srcTable ԭ??
	 * @param names Ҫ??????ֶ?????
	 * @param expNames ?ۺϱ???ʽ????
	 * @param w ???˱???ʽ
	 * @return
	 */
	public static Object findCuboid(PhyTable srcTable, String names[], String expNames[], Expression w) {
		return findCuboid(srcTable, names, expNames, w, null);
	}
	
	/**
	 * ???Һ??ʵ???????
	 * @param srcTable ԭ??
	 * @param names Ҫ??????ֶ?????
	 * @param expNames ?ۺϱ???ʽ????
	 * @param w ???˱???ʽ
	 * @param ctx
	 * @return
	 */
	public static Object findCuboid(PhyTable srcTable, String names[], String expNames[], 
			Expression w, Context ctx) {
		String dir = srcTable.getGroupTable().getFile().getAbsolutePath() + "_";
		String cuboids[] = srcTable.getCuboids();
		ArrayList tableList = new ArrayList();
		if (cuboids == null) return tableList;
		ArrayList tableList2 = new ArrayList();
		
		//???exps??avg???????پۺ?
		boolean flag = false;//??ʾ?????پۺ?
		for (String exp : expNames) {
			if (exp.indexOf("avg(") != -1) {
				flag = true;//return tableList;
			}
			if (exp.indexOf("top(") != -1) {
				flag = true;//return tableList;
			}
		}
		
		ArrayList filterFields = null;
		ArrayList fieldList = null;
		
		for (String cuboid: cuboids) {
			FileObject fo = new FileObject(dir + srcTable.getTableName() + CUBE_PREFIX + cuboid);
			if (!fo.isExists()) {
				continue;
			}
			File file = fo.getLocalFile().file();
			RowComTable table = null;
			try {
				table = new Cuboid(file, null);
				table.checkPassword("cuboid");
				PhyTable baseTable = table.getBaseTable();
				String fields[] = baseTable.getAllColNames();
				if (w != null) {
					filterFields = new ArrayList();
					fieldList = new ArrayList();
					for (String f : fields) {
						fieldList.add(f);
					}
					parseFilter(fieldList, w.getHome(), filterFields);
				}
				int match = check(fields, baseTable.getAllSortedColNames().length, names, expNames, filterFields, ctx);
				if (match == 1 && !flag) {
					tableList.add(baseTable);
				} else if ( match == 2 && !flag) {
					tableList2.add(baseTable);
				} else if (match == 3) {
					//??ƥ?䣬???ر?????
					for (PhyTable tbl : tableList) {
						tbl.close();
					}
					for (PhyTable tbl : tableList2) {
						tbl.close();
					}
					return baseTable;
				} else {
					table.close();
				}
			} catch (Exception e) {
				if (table != null) table.close();
				for (PhyTable tbl : tableList) {
					tbl.close();
				}
				throw new RQException(e.getMessage(), e);
			}
		}

		//ȥ??ƥ??ȵ͵ģ??????2?ģ???ȥ??1?ģ?
		if (tableList2.size() != 0) {
			for (PhyTable tbl : tableList) {
				tbl.close();
			}
			return tableList2;
		}
		return tableList;
	}
	
	/**
	 * ???Һ??ʵ???????
	 * @param srcTable ԭ??
	 * @param names Ҫ??????ֶ?????
	 * @param expNames ?ۺϱ???ʽ????
	 * @param w ???˱???ʽ
	 * @param files ???????ļ?
	 * @param ctx
	 * @return
	 */
	public static Object findCuboid(PhyTable srcTable, String names[], String expNames[], 
			Expression w, FileObject[] files, Context ctx) {
		//String dir = srcTable.getGroupTable().getFile().getAbsolutePath() + "_";
		//String cuboids[] = srcTable.getCuboids();
		ArrayList tableList = new ArrayList();
		if (files == null) return tableList;
		ArrayList tableList2 = new ArrayList();
		
		//???exps??avg???????پۺ?
		boolean flag = false;//??ʾ?????پۺ?
		for (String exp : expNames) {
			if (exp.indexOf("avg(") != -1) {
				flag = true;//return tableList;
			}
			if (exp.indexOf("top(") != -1) {
				flag = true;//return tableList;
			}
		}
		
		ArrayList filterFields = null;
		ArrayList fieldList = null;
		
		for (FileObject fo: files) {
			if (!fo.isExists()) {
				continue;
			}
			File file = fo.getLocalFile().file();
			RowComTable table = null;
			try {
				table = new Cuboid(file, null);
				table.checkPassword("cuboid");
				PhyTable baseTable = table.getBaseTable();
				String fields[] = baseTable.getAllColNames();
				if (w != null) {
					filterFields = new ArrayList();
					fieldList = new ArrayList();
					for (String f : fields) {
						fieldList.add(f);
					}
					parseFilter(fieldList, w.getHome(), filterFields);
				}
				int match = check(fields, baseTable.getAllSortedColNames().length, names, expNames, filterFields, ctx);
				if (match == 1 && !flag) {
					tableList.add(baseTable);
				} else if ( match == 2 && !flag) {
					tableList2.add(baseTable);
				} else if (match == 3) {
					//??ƥ?䣬???ر?????
					for (PhyTable tbl : tableList) {
						tbl.close();
					}
					for (PhyTable tbl : tableList2) {
						tbl.close();
					}
					return baseTable;
				} else {
					table.close();
				}
			} catch (Exception e) {
				if (table != null) table.close();
				for (PhyTable tbl : tableList) {
					tbl.close();
				}
				throw new RQException(e.getMessage(), e);
			}
		}

		//ȥ??ƥ??ȵ͵ģ??????2?ģ???ȥ??1?ģ?
		if (tableList2.size() != 0) {
			for (PhyTable tbl : tableList) {
				tbl.close();
			}
			return tableList2;
		}
		return tableList;
	}
	
	/**
	 * ???Һ??ʵ??????? (??Ⱥ)
	 * @param srcTable ԭ??
	 * @param names Ҫ??????ֶ?????
	 * @param expNames ?ۺϱ???ʽ????
	 * @param w ???˱???ʽ
	 * @param ctx
	 * @return
	 */
	public static Object findCuboid(ClusterPhyTable srcTable, String names[], String expNames[], 
			Expression w, Context ctx) {
		List cuboids = getCuboids(srcTable);
		ArrayList tableList = new ArrayList();
		if (cuboids == null) return tableList;
		ArrayList tableList2 = new ArrayList();
		
		//???exps??avg???????پۺ?
		for (String exp : expNames) {
			if (exp.indexOf("avg(") != -1) {
				return tableList;
			}
			if (exp.indexOf("top(") != -1) {
				return tableList;
			}
		}
		
		ArrayList filterFields = null;
		ArrayList fieldList = null;
		
		for (String cuboid: cuboids) {
			FileObject fo = new FileObject(cuboid);
			if (!fo.isExists()) {
				continue;
			}
			File file = fo.getLocalFile().file();
			RowComTable table = null;
			try {
				table = new Cuboid(file, null);
				table.checkPassword("cuboid");
				PhyTable baseTable = table.getBaseTable();
				String fields[] = baseTable.getAllColNames();
				if (w != null) {
					filterFields = new ArrayList();
					fieldList = new ArrayList();
					for (String f : fields) {
						fieldList.add(f);
					}
					parseFilter(fieldList, w.getHome(), filterFields);
				}
				int match = check(fields, baseTable.getAllSortedColNames().length, names, expNames, filterFields, ctx);
				if (match == 1) {
					tableList.add(baseTable);
				} else if ( match == 2) {
					tableList2.add(baseTable);
				} else if (match == 3) {
					//??ƥ?䣬???ر?????
					for (PhyTable tbl : tableList) {
						tbl.close();
					}
					for (PhyTable tbl : tableList2) {
						tbl.close();
					}
					return baseTable;
				} else {
					table.close();
				}
			} catch (Exception e) {
				if (table != null) table.close();
				for (PhyTable tbl : tableList) {
					tbl.close();
				}
				throw new RQException(e.getMessage(), e);
			}
		}
		
		//ȥ??ƥ??ȵ͵ģ??????2?ģ???ȥ??1?ģ?
		if (tableList2.size() != 0) {
			for (PhyTable tbl : tableList) {
				tbl.close();
			}
			return tableList2;
		}
		return tableList;
	}
	
	/**
	 * ???????ֶκ???????(????ļ?)?ֶε?ƥ????
	 * @param fields ????????ֶ?
	 * @param kcount ???????ά?ֶθ???
	 * @param names Ҫ??????ֶ?
	 * @param expNames ?ۺϱ???ʽ
	 * @param filterFields ???˱???ʽw????ֶ?
	 * @return 0 ??ƥ??,1 ƥ??,2 ?????????ֶ?ƥ??,3ȫƥ??
	 */
	private static int check(String fields[], int kcount, String names[], String expNames[], ArrayList filterFields, Context ctx) {
		int fcount = names == null ? 0 : names.length;
		int ecount = expNames.length;
		int size = fields.length;
		//boolean hasYearDate = false;//??ֱ??ƥ?䣬????ƥ?䵽year??date????ʽ
		
		//???w????ֶ??Ƿ??????????ֶ??????
		if (filterFields != null) {
			ArrayList flist = new ArrayList();
			for (int i = 0; i < kcount; i++) {
				flist.add(fields[i]);
			}
			for (String f : filterFields) {
				boolean flag = flist.contains(f);
				if (!flag) {
					//?????????????ֶ?,???Ǵ??ڰ???ʱ??????ı???ʽҲ????
					if (flist.contains("year(" + f + ")")) {
						//hasYearDate = true;
						flag = true;
					}
					if (flist.contains("month@y(" + f + ")")) {
						flag = true;
					}
					if (flist.contains("date(" + f + ")")) {
						//hasYearDate = true;
						flag = true;
					}
				}
				if (!flag) {
					return 0;
				}
			}
		}
		
		if (size >= fcount + ecount) {
			//????????????Ƿ???exp
			for (String exp : expNames) {
				boolean find = false;
				for (int i = 0; i < size; i++) {
					if (exp.equals(fields[i])) {
						find = true;
						break;
					}
				}
				if (!find) return 0;
			}
			
			//?ж??Dz???contain
			if (ctx != null && fcount == 1 && names[0].indexOf("contain") != -1) {
				//???contain????ʽ?漰?ֶ??Ƿ????????????Fi
				Expression exp = new Expression(names[0]);
				if (exp.getHome() instanceof DotOperator 
						&& exp.getHome().getRight() instanceof Contain) {
					ArrayList list = new ArrayList();
					exp.getUsedFields(ctx, list);
					//Ҫ??list????ֶζ?????fields???ҵ?
					boolean find = false;
					for (String str : list) {
						find = false;
						for (int i = 0; i < size; i++) {
							if (str.equals(fields[i])) {
								find = true;
								break;
							}
						}
						if (!find) {
							break;
						}
					}
					if (find) {
						return 1;
					}
				}
			}
			
			//??˳????Fi???????????Fi
			for (int i = 0; i < fcount; i++) {
				String name = names[i];
				if (! name.equals(fields[i])) {
					return 0;
				}
			}
			if (fcount == kcount && filterFields == null) {
				return 3;
			} else if (filterFields != null && filterFields.size() != 0) {
				return 2;//?????????ֶ?ƥ??
			} else {
				return 1;
			}
		}
		else {
			return 0;
		}
	}

	/**
	 * ȡ??table??ת??Ϊ?????յ????????͵?ԭ?ֶ?
	 * @param table
	 * @param list ???{?ֶ???????????} ??????:1 year ,2 month,3 day
	 */
	private static void getDateFields(PhyTable table, ArrayList list) {
		String[] fields = table.getAllSortedColNames();
		ArrayList flist = new ArrayList();
		for (String f : fields) {
			flist.add(f);
		}
		for (String f : fields) {
			if (f.indexOf("year(") == 0) {
				String s = f.substring(5);
				s = s.substring(0, s.length() - 1);
				list.add(s);
				list.add(1);
			} else if (f.indexOf("month@y(") == 0) {
				String s = f.substring(8);
				s = s.substring(0, s.length() - 1);
				list.add(s);
				list.add(2);
			} else if (f.indexOf("date(") == 0) {
				String s = f.substring(5);
				s = s.substring(0, s.length() - 1);
				list.add(s);
				list.add(3);
			}
		}
		return;
	}
	
	/**
	 * ?ӱ???ʽ????ȡ????
	 * @param field ?????ֶ???
	 * @param node ????ʽ?ڵ?
	 * @param interval ???????
	 * @param isEQ ????Ⱥű?־
	 * @param otherNodes ???漰?????????node
	 * @param ctx
	 */
	private static void getDateInterval(String field, Node node, Object[] interval,
			boolean[] isEQ, ArrayList otherNodes, Integer timeUnit, Context ctx) {
		if (node instanceof And) {
			getDateInterval(field, node.getLeft(), interval, isEQ, otherNodes, timeUnit, ctx);
			getDateInterval(field, node.getRight(), interval, isEQ, otherNodes, timeUnit, ctx);
			return;
		} else if (node instanceof Between) {
			Between bt = (Between) node;
			IParam sub0 = bt.getParam().getSub(0);
			IParam sub1 = bt.getParam().getSub(1);
			if (sub0 == null || sub1 == null) {
				otherNodes.add(node);
				return;
			}
			Expression f = sub0.getLeafExpression();
			if (field.equals(f.getIdentifierName())) {
				interval[0] = sub1.getSub(0).getLeafExpression().calculate(ctx);
				interval[1] = sub1.getSub(1).getLeafExpression().calculate(ctx);
				isEQ[0] = isEQ[1] = true;
				return;
			} else {
				otherNodes.add(node);
				return;
			}
		} else if (node instanceof Or) {
			otherNodes.add(node);
			return;
		} else {
			Node left = node.getLeft();
			Node right = node.getRight();
			String fname = null;
			if (left instanceof UnknownSymbol || left instanceof Year || left instanceof Month) {
				if (left instanceof UnknownSymbol) {
					fname = ((UnknownSymbol)left).getName();
				} else if (left instanceof Year && timeUnit == 1) {
					fname = ((Year)left).getParamString();
				} else if (left instanceof Month && timeUnit == 2){
					fname = ((Month)left).getParamString();
				}
				if (fname == null || !fname.equals(field)) {
					otherNodes.add(node);
					return;
				}
				
				if (node instanceof Equals) {
					interval[0] = interval[1] = right.calculate(ctx);
					isEQ[0] = isEQ[1] = true;
				} else if (node instanceof Greater) {
					interval[0] = right.calculate(ctx);
					isEQ[0] = false;
				} else if (node instanceof NotSmaller) {
					interval[0] = right.calculate(ctx);
					isEQ[0] = true;
				} else if (node instanceof Smaller) {
					interval[1] = right.calculate(ctx);
					isEQ[1] = false;
				} else if (node instanceof NotGreater) {
					interval[1] = right.calculate(ctx);
					isEQ[1] = true;
				} else {
					otherNodes.add(node);
					return;
				}
			} else if (right instanceof UnknownSymbol) {
				if (right instanceof UnknownSymbol) {
					fname = ((UnknownSymbol)right).getName();
				} else if (right instanceof Year && timeUnit == 1) {
					fname = ((Year)right).getParamString();
				} else if (right instanceof Month && timeUnit == 2){
					fname = ((Month)right).getParamString();
				}
				if (fname == null || !fname.equals(field)) {
					otherNodes.add(node);
					return;
				}
				
				if (node instanceof Equals) {
					interval[0] = interval[1] = left.calculate(ctx);
					isEQ[0] = isEQ[1] = true;
				} else if (node instanceof Greater) {
					interval[1] = left.calculate(ctx);
					isEQ[1] = false;
				} else if (node instanceof NotSmaller) {
					interval[1] = left.calculate(ctx);
					isEQ[1] = true;
				} else if (node instanceof Smaller) {
					interval[0] = left.calculate(ctx);
					isEQ[0] = false;
				} else if (node instanceof NotGreater) {
					interval[0] = left.calculate(ctx);
					isEQ[0] = true;
				} else {
					otherNodes.add(node);
					return;
				}
			} else {
				otherNodes.add(node);
				return;
			}
		}
	}
	
	/**
	 * ??ʱ??????ת??Ϊ????????ʽ??1 ????ԭ????? 2 ???????????
	 * @param field	ԭ????ֶ???
	 * @param field	??????ȡ???ֶ?
	 * @param array		ʱ??????
	 * @param isEQ		?Ƿ????
	 * @param timeUnit
	 * @return
	 */
	private static Node[] makeNode(String field, String fields[], Object array[], boolean isEQ[], Integer timeUnit) {
		Node nodes[] = null;
		
		//????????????ֶ???ת??Ϊ#i
		String year = "year(" + field + ")";
		String month = "month@y(" + field + ")";
		String date = "date(" + field + ")";
		String yearFieldName = null;
		String monthFieldName = null;
		String dateFieldName = null;

		for (int i = 0, len = fields.length; i < len; i++) {
			String f = fields[i];
			if (f.indexOf(year) != -1) {
				yearFieldName = "#" + (i + 1);
			} else if (f.indexOf(month) != -1) {
				monthFieldName = "#" + (i + 1);
			} else if (f.indexOf(date) != -1) {
				dateFieldName = "#" + (i + 1);
			}
		}
		
		if (timeUnit == 1) {//year
			if ((array[0] != null && array[0] instanceof Integer) ||
					(array[1] != null && array[1] instanceof Integer)) {//Integer??ʾ?ǰ???Year??????????
				Integer from = null, to = null;
				if (array[0] != null && array[0] instanceof Integer) {
					from = (Integer) array[0];
				}
				if (array[1] != null && array[1] instanceof Integer) {
					to = (Integer) array[1];
				}
				String s;
				if (from != null && to != null) {
					s = yearFieldName + ">=" + from;
					s += "&&" + yearFieldName + "<=" + to;
				} else if (from != null) {
					s = yearFieldName + ">=" + from;
				} else {
					s = yearFieldName + "<=" + to;
				}
				nodes = new Node[2];
				nodes[1] = new Expression(s).getHome();
				return nodes;
			}
			//?????ǰ?????????????Ϊ3???????ҵ?ȥԭ????ȡ?????м??????????????????
			Integer from = array[0] == null ? null : DateFactory.get().year( (Date) array[0]);
			Integer to = array[1] == null ? null : DateFactory.get().year( (Date) array[1]);
			if (from != null && to != null && (to - from < 2)) {
				return null;//?ò?????????
			}
			
			nodes = new Node[2];
			Node n1, n2;
			Node and1 = null;//????T????????
			Node and2 = null;
			if (array[0] != null) {
				if (!isEQ[0]) {
					n1 = new Greater();
					n1.setLeft(new UnknownSymbol(field));
					n1.setRight(new Constant(array[0]));
				} else {
					n1 = new NotSmaller();
					n1.setLeft(new UnknownSymbol(field));
					n1.setRight(new Constant(array[0]));
				}
				Node small = new Smaller();
				small.setLeft(n1.getLeft());
				try {
					small.setRight(new Constant(DateFactory.parseDate(""+(from + 1) + "-1-1")));
				} catch (ParseException e) {
					throw new RQException("cgroups:" + e.getMessage(), e);
				}
				and1 = new And();
				and1.setLeft(n1);
				and1.setRight(small);
			}
			
			if (array[1] != null) {
				if (!isEQ[1]) {
					n2 = new Smaller();
					n2.setLeft(new UnknownSymbol(field));
					n2.setRight(new Constant(array[1]));
				} else {
					n2 = new NotGreater();
					n2.setLeft(new UnknownSymbol(field));
					n2.setRight(new Constant(array[1]));
				}
				Node great = new NotSmaller();
				great.setLeft(n2.getLeft());
				try {
					great.setRight(new Constant(DateFactory.parseDate("" + to + "-1-1")));
				} catch (ParseException e) {
					throw new RQException("cgroups:" + e.getMessage(), e);
				}
				and2 = new And();
				and2.setLeft(n2);
				and2.setRight(great);
			}
			
			if (and1 != null && and2 != null) {
				nodes[0] = new Or();
				nodes[0].setLeft(and1);
				nodes[0].setRight(and2);
			} else if (and1 != null) {
				nodes[0] = and1;
			} else if (and2 != null) {
				nodes[0] = and2;
			}
			
			//??֯???????????node
			String s;
			if (from != null && to != null) {
				int begin = from + 1;
				s = yearFieldName + ">=" + begin;
				int end = to - 1;
				s += "&&" + yearFieldName + "<=" + end;
			} else if (from != null) {
				int begin = from + 1;
				s = yearFieldName + ">=" + begin;
			} else {
				int end = to - 1;
				s = yearFieldName + "<=" + end;
			}
			nodes[1] = new Expression(s).getHome();
		} else if (timeUnit == 2) {
			if ((array[0] != null && array[0] instanceof Integer) ||
					(array[1] != null && array[1] instanceof Integer)) {//Integer??ʾ?ǰ???Year??????????
				Integer from = null, to = null;
				if (array[0] != null && array[0] instanceof Integer) {
					from = (Integer) array[0];
				}
				if (array[1] != null && array[1] instanceof Integer) {
					to = (Integer) array[1];
				}
				String s;
				if (from != null && to != null) {
					s = monthFieldName + ">=" + from;
					s += "&&" + monthFieldName + "<=" + to;
				} else if (from != null) {
					s = monthFieldName + ">=" + from;
				} else {
					s = monthFieldName + "<=" + to;
				}
				nodes = new Node[2];
				nodes[1] = new Expression(s).getHome();
				return nodes;
			}
			
			//month
			boolean hasInterval1 = array[0] != null;
			boolean hasInterval2 = array[1] != null;
			int fromYear = 0, toYear = 0, fromMonth = 0, toMonth = 0;
			if (hasInterval1) {
				fromYear = DateFactory.get().year( (Date) array[0]);
				fromMonth = DateFactory.get().month( (Date) array[0]);
			}
			if (hasInterval2) {
				toYear = DateFactory.get().year( (Date) array[1]);
				toMonth = DateFactory.get().month( (Date) array[1]);
			}
			int toYearBak = toYear;
			int toMonthBak = toMonth;
			if (hasInterval1) {
				fromMonth ++;
				if (fromMonth == 13) {
					fromMonth = 1;
					fromYear++;
				}
			}
			if (hasInterval2) {
				toMonth --;
				if (toMonth == 0) {
					toMonth = 12;
					toYear--;
				}
			}
			
			if (hasInterval1 && hasInterval2) {
				if (fromYear > toYear) {
					return null;
				}
				else if (fromYear == toYear) {
					if (fromMonth > toMonth) {
						return null;
					}
				}
			}
			
			nodes = new Node[2];
			Node n1, n2;//????T??
			Node and1 = null, and2 = null;
			if (hasInterval1) {
				if (!isEQ[0]) {
					n1 = new Greater();
					n1.setLeft(new UnknownSymbol(field));
					n1.setRight(new Constant(array[0]));
				} else {
					n1 = new NotSmaller();
					n1.setLeft(new UnknownSymbol(field));
					n1.setRight(new Constant(array[0]));
				}
				Node small = new Smaller();
				small.setLeft(n1.getLeft());
				try {
					small.setRight(new Constant(DateFactory.parseDate("" + fromYear + "-" + fromMonth + "-1")));
				} catch (ParseException e) {
					throw new RQException("cgroups:" + e.getMessage(), e);
				}
				and1 = new And();
				and1.setLeft(n1);
				and1.setRight(small);
			}
			
			if (hasInterval2) {
				if (!isEQ[1]) {
					n2 = new Smaller();
					n2.setLeft(new UnknownSymbol(field));
					n2.setRight(new Constant(array[1]));
				} else {
					n2 = new NotGreater();
					n2.setLeft(new UnknownSymbol(field));
					n2.setRight(new Constant(array[1]));
				}
				Node great = new NotSmaller();
				great.setLeft(n2.getLeft());
				try {
					great.setRight(new Constant(DateFactory.parseDate("" + toYearBak + "-" + toMonthBak + "-1")));
				} catch (ParseException e) {
					throw new RQException("cgroups:" + e.getMessage(), e);
				}
				and2 = new And();
				and2.setLeft(n2);
				and2.setRight(great);
			}
			
			if (hasInterval1 && hasInterval2) {
				nodes[0] = new Or();
				nodes[0].setLeft(and1);
				nodes[0].setRight(and2);
			} else if (hasInterval1) {
				nodes[0] = and1;
			} else {
				nodes[0] = and2;
			}
			
			//??֯???????????node
			String yearAndMonth = monthFieldName;
			if (hasInterval1 && hasInterval2) {
				String s = yearAndMonth +">=" + (fromYear*100 + fromMonth);
				s += "&& " + yearAndMonth + "<=" + (toYear*100 + toMonth);
				nodes[1] = new Expression(s).getHome();
			} else if (hasInterval1) {
				String s = yearAndMonth + ">=" + (fromYear*100 + fromMonth);
				nodes[1] = new Expression(s).getHome();
			} else {
				String s = yearAndMonth + "<=" + (toYear*100 + toMonth);
				nodes[1] = new Expression(s).getHome();
			}
		} else if (timeUnit == 3) {
			//date
			boolean hasInterval1 = array[0] != null;
			boolean hasInterval2 = array[1] != null;
			Date from = null, to = null;
			Date toBak = null; 
			if (hasInterval1) {
				from = (Date) array[0];
				Calendar calendar = Calendar.getInstance();
				calendar.setTime(from);
				calendar.set(Calendar.HOUR_OF_DAY, 0);
				calendar.set(Calendar.MINUTE, 0);
				calendar.set(Calendar.SECOND, 0);
				calendar.set(Calendar.MILLISECOND, 0);
				calendar.add(Calendar.DATE, 1);
				from = (Date) from.clone();
				from.setTime(calendar.getTimeInMillis());
			}
			if (hasInterval2) {
				to = (Date) array[1];
				toBak = (Date) to.clone();
				Calendar calendar = Calendar.getInstance();
				calendar.setTime(to);
				calendar.set(Calendar.HOUR_OF_DAY, 0);
				calendar.set(Calendar.MINUTE, 0);
				calendar.set(Calendar.SECOND, 0);
				calendar.set(Calendar.MILLISECOND, 0);
				toBak.setTime(calendar.getTimeInMillis());
				calendar.add(Calendar.DATE, -1);
				to = (Date) to.clone();
				to.setTime(calendar.getTimeInMillis());
			}
			if (hasInterval1 && hasInterval2) {
				if (Variant.compare(from, to) > 0) {
					return null;
				}
			}
			
			nodes = new Node[2];
			Node n1, n2;//????T??
			Node and1 = null, and2 = null;
			if (hasInterval1) {
				if (!isEQ[0]) {
					n1 = new Greater();
					n1.setLeft(new UnknownSymbol(field));
					n1.setRight(new Constant(array[0]));
				} else {
					n1 = new NotSmaller();
					n1.setLeft(new UnknownSymbol(field));
					n1.setRight(new Constant(array[0]));
				}
				Node small = new Smaller();
				small.setLeft(n1.getLeft());
				small.setRight(new Constant(from));
				and1 = new And();
				and1.setLeft(n1);
				and1.setRight(small);
			}
			
			if (hasInterval2) {
				if (!isEQ[1]) {
					n2 = new Smaller();
					n2.setLeft(new UnknownSymbol(field));
					n2.setRight(new Constant(array[1]));
				} else {
					n2 = new NotGreater();
					n2.setLeft(new UnknownSymbol(field));
					n2.setRight(new Constant(array[1]));
				}
				Node great = new NotSmaller();
				great.setLeft(n2.getLeft());
				great.setRight(new Constant(toBak));
				and2 = new And();
				and2.setLeft(n2);
				and2.setRight(great);
			}
			
			if (hasInterval1 && hasInterval2) {
				nodes[0] = new Or();
				nodes[0].setLeft(and1);
				nodes[0].setRight(and2);
			} else if (hasInterval1) {
				nodes[0] = and1;
			} else {
				nodes[0] = and2;
			}
			
			//??֯???????????node
			String fieldExp = dateFieldName;
			if (hasInterval1 && hasInterval2) {
				n1 = new NotSmaller();
				n1.setLeft(new UnknownSymbol(fieldExp));
				n1.setRight(new Constant(from));
				n2 = new NotGreater();
				n2.setLeft(new UnknownSymbol(fieldExp));
				n2.setRight(new Constant(to));
				nodes[1] = new And();
				nodes[1].setLeft(n1);
				nodes[1].setRight(n2);
			} else if (hasInterval1) {
				n1 = new NotSmaller();
				n1.setLeft(new UnknownSymbol(fieldExp));
				n1.setRight(new Constant(from));
				nodes[1] = n1;
			} else {
				n2 = new NotGreater();
				n2.setLeft(new UnknownSymbol(fieldExp));
				n2.setRight(new Constant(to));
				nodes[1] = n2;
			}
		}
		return nodes;
	}
	
	/**
	 * ??ȡnode????ʽ????ֶ??? ??????year?ڵ?ʱ??????fields??Ļᱻ???˵???
	 * @param fields ??֪???ֶ???
	 * @param node
	 * @param list
	 */
	private static void parseFilter(ArrayList fields, Node node, ArrayList list) {
		if (node == null) return;
		if (node instanceof UnknownSymbol) {
			list.add(((UnknownSymbol) node).getName());
			return;
		}
		else if (node instanceof Year) {
			IParam param = ((Year) node).getParam();
			if (param.isLeaf()) {
				Node home = param.getLeafExpression().getHome();
				if (home instanceof UnknownSymbol) {
					String name = ((UnknownSymbol) home).getName();
					if (fields.contains(name)) {
						list.add(name);
						return;
					}  
					name = "year(" + name + ")";
					if (fields.contains(name)) {
						list.add(name);
						return;
					} 
				}
			}
			return;
		}
		else if (node instanceof Month 
				&& ((Month) node).getOption() != null
				&& ((Month) node).getOption().indexOf("y") != -1) {
			IParam param = ((Month) node).getParam();
			if (param.isLeaf()) {
				Node home = param.getLeafExpression().getHome();
				if (home instanceof UnknownSymbol) {
					String name = ((UnknownSymbol) home).getName();
					if (fields.contains(name)) {
						list.add(name);
						return;
					}  
					name = "month@y(" + name + ")";
					if (fields.contains(name)) {
						list.add(name);
						return;
					} 
				}
			}
			return;
		}
		
		parseFilter(fields, node.getLeft(), list);
		parseFilter(fields, node.getRight(), list);
	}
	
	public void append(ICursor cursor) throws IOException {
		baseTable.append(cursor);
	}
	
	/**
	 * ԭ?????º????»???
	 * @param srcTable
	 * @throws IOException
	 */
	public void update(PhyTable srcTable) throws IOException {
		//1 ??ԭ????δ???ܵIJ??ֽ??л???
		
		//?õ?Ҫ???ܵ?????
		long count = srcTable.getActualRecordCount();
		count = count - getSrcCount();
		if (count <= 0) return;
		//???㻺??????
		int fcount = exps == null ? 0 : exps.length;
		if (newExps != null) fcount += newExps.length;
		int capacity = EnvUtil.getCapacity(srcTable.getAllColNames().length + fcount);
		//?õ?ԭ??Ҫ???ܵļ?¼???α?
		ICursor cursor = srcTable.cursor();
		cursor.skip(getSrcCount());//?????Ѿ????ܵ?
		//??֯???ܵ??õIJ???
		int expsLength = exps.length;
		int newExpsLength = newExps.length;
		Expression expressions[] = new Expression[expsLength];//?????ֶα???ʽ
		for (int i = 0; i < expsLength; i++) {
			expressions[i] = new Expression(exps[i]);
		}
		Expression newExpressions[] = new Expression[newExpsLength];//?????ֶα???ʽ
		for (int i = 0; i < newExpsLength; i++) {
			newExpressions[i] = new Expression(newExps[i]);
		}
		String names[] = new String[expsLength];//?µķ????ֶ???
		String newNames[] = new String[newExpsLength];//?µĻ????ֶ???
		String cols[] = baseTable.colNames;
		System.arraycopy(cols, 0, names, 0, names.length);
		System.arraycopy(cols, names.length, newNames, 0, newNames.length);
		
		//?õ???׷?????ݵķ?????ܽ???α?
		ICursor cs = cursor.groupx(expressions, names, newExpressions,
				newNames, null, ctx, capacity / 2);
		
		//2??cs?鲢??Ԥ??????????(baseTable)??
		//?õ??ܿ????????水?鴦??
		CuboidTable baseTable = this.baseTable;
		int totalBlockCount = baseTable.getDataBlockCount();
		if (totalBlockCount == 0) {
			baseTable.append(cs);
			baseTable.appendCache();
			setSrcCount(srcTable.getActualRecordCount());
			writeHeader();
			flush();
			return;
		}
		
		BlockLinkReader rowReader = baseTable.getRowReader(true);//????reader
		ObjectReader segmentReader = baseTable.getSegmentObjectReader();//?ֶ???Ϣreader
		Object []maxValues = new Object[expsLength];//ÿһ?ε????ֵ
		Sequence dataToInsert = new Sequence();//???Ҫ?????????
		
		//ȡһ?????ݳ???
		Sequence data = cs.fetch(ICursor.FETCHCOUNT);
		if (data == null || data.length() == 0) {
			return;
		}
		int idx = 1;
		int len = data.length();
		BaseRecord record = (BaseRecord) data.get(idx);
		Object vals[] = record.getFieldValues();
		
		//?????д?????ֶε?reader writer
		byte[] bytes = new byte[FIXED_OBJ_LEN];
		RowBufferReader bufferReader = new RowBufferReader(null, bytes);
		RowBufferWriter bufferWriter = new RowBufferWriter(null, bytes);
		
		RandomAccessFile rafile = raf;//new RandomAccessFile(file, "rw");
		ObjectReader reader = new ObjectReader(rowReader, blockSize - ComTable.POS_SIZE);;
		//????ÿһ????д???
		NEXT:
		for(int i = 0; i < totalBlockCount; i++) {
			int curRecordCount = 0;
			int curRecordSum = segmentReader.readInt32();//??ǰ???????
			segmentReader.readLong40();//??ǰ??ĵ?ַ		
			for (int k = 0; k < expsLength; ++k) {
				segmentReader.skipObject();//??????Сֵ
				maxValues[k] = segmentReader.readObject();//?õ????ֵ
			}
			Object curObjs[] = null;
			int blockSize = reader.readInt32();//???ǿ??С??ƫ?ƹ?ȥ
			long position = reader.position();
			//?ӱ??????һ???????Ա?
			curObjs = new Object[fcount];
			reader.skipObject();//????α??
			for (int k = 0; k < expsLength; ++k) {
				curObjs[k] = reader.readObject();
			}
			curRecordCount = 1;
			
			while(true) {
				//?ж?record?Ƿ??????????
				int cmp = Variant.compare(vals[0], maxValues[0]);
				if (cmp <= 0) {
					//?????ڱ?????
					cmp = Variant.compareArrays(vals, curObjs, expsLength);
					if (cmp < 0) {
						//??һ???Dz??룬????һ??record
						dataToInsert.add(record);
						
						//???dataȡ???ˣ?????ȡ????һЩ
						idx++;
						if (idx > len) {
							data = cs.fetch(ICursor.FETCHCOUNT);
							if (data == null || data.length() == 0) {
								break NEXT;//???û????һ??record??
							}
							idx = 1;
							len = data.length();
						}
						record = (BaseRecord) data.get(idx);
						vals = record.getFieldValues();
					} else if (cmp == 0) {
						//?ҵ??ˣ????£?????һ??record??????????һ??
						
						//?????ٻ???
						for (int k = 0; k < newExpsLength; ++k) {
							long pos = calcPosition(rowReader, reader);//Ҫ???µĵ?ַ
							reader.readFully(bytes);//??ȡ????9?ֽ?
							bufferReader.reset();//buffer reader ??λ
							Object obj = bufferReader.readObject();//??ȡһ???????Ļ????ֶ?
							obj = regroup(obj, vals[expsLength + k], newNames[k]);//?ٻ???
							bufferWriter.reset();
							bufferWriter.writeFixedLengthObject(obj);
							rewrite(rafile, pos, bytes);//????
						}
						
						//????һ??record,???dataȡ???ˣ?????ȡ????һЩ
						idx++;
						if (idx > len) {
							data = cs.fetch(ICursor.FETCHCOUNT);
							if (data == null || data.length() == 0) {
								break NEXT;//???û????һ??record??
							}
							idx = 1;
							len = data.length();
						}
						record = (BaseRecord) data.get(idx);
						vals = record.getFieldValues();
						
						//????????һ??
						curRecordCount++;
						if (curRecordCount > curRecordSum) {
							break;//?жϱ????Ƿ??????
						}
						
						//????α??
						reader.skipObject();
						//??????(ά)?????ֶ?
						for (int k = 0; k < expsLength; ++k) {
							curObjs[k] = reader.readObject();
						}
					} else {
						//????????һ??
						
						//????û???????Ļ????ֶ?
						for (int k = 0; k < newExpsLength; ++k) {
							reader.skipBytes(FIXED_OBJ_LEN);
						}
						
						//?жϱ????Ƿ??????
						curRecordCount++;
						if (curRecordCount > curRecordSum) {
							break;
						}
						
						//????α??
						reader.skipObject();
						//??????(ά)?????ֶ?
						for (int k = 0; k < expsLength; ++k) {
							curObjs[k] = reader.readObject();
						}
					}
				} else {
					//??һ??
					reader.skip(blockSize - (reader.position() - position));
					break;
				}
			
			}
		}
		
		segmentReader.close();
		rowReader.close();
		
		//???data?ﻹ??????
		if (data != null && data.length() != 0 && idx <= len) {
			Sequence seq = data.split(idx);
			baseTable.append(new MemoryCursor(seq));
		}
		//cs????ܻ???????
		baseTable.append(cs);
		
		//?в???????????й鲢
		if (dataToInsert.length() != 0) {
			baseTable.append(new MemoryCursor(dataToInsert), "m");
		}
		
		setSrcCount(srcTable.getActualRecordCount());
		writeHeader();
		flush();
	}
	
	/**
	 * ????src?½?һ???????壬д????file
	 * @param file
	 * @param srcCount Ŀǰ?̶?Ϊ0?????ܻ????????ô?
	 * @param src
	 * @throws IOException
	 */
	public Cuboid(File file, int srcCount, Cuboid src) throws IOException {
		this.file = file;
		this.raf = new RandomAccessFile(file, "rw");
		this.ctx = src.ctx;
		if (ctx != null) {
			ctx.addResource(this);
		}
		
		System.arraycopy(src.reserve, 0, reserve, 0, reserve.length);
		blockSize = src.blockSize;
		enlargeSize = src.enlargeSize;
		
		headerBlockLink = new BlockLink(this);
		headerBlockLink.setFirstBlockPos(applyNewBlock());
		
		baseTable = new CuboidTable(this, null, (RowPhyTable)src.baseTable);
		super.baseTable = baseTable;
		
		exps = src.exps;
		newExps = src.newExps;
		this.srcCount = srcCount;
		
		structManager = new StructManager();
		
		writePswHash = src.writePswHash;
		readPswHash = src.readPswHash;
		distribute = src.distribute;
		
		save();
	}
	
	private Object regroup(Object obj1, Object obj2, String op) {
		if (obj1 == null) {
			return obj2;
		}
		if (obj2 == null) {
			return obj1;
		}
		if (op.indexOf("sum(") == 0) {
			return Variant.add(obj1, obj2);
		} else if (op.indexOf("count(") == 0) {
			return Variant.add(obj1, obj2);
		} else if (op.indexOf("max(") == 0) {
			if (Variant.compare(obj1, obj2, true) > 0) {
				return obj1;
			} else {
				return obj2;
			}
		} else if (op.indexOf("min(") == 0) {
			if (Variant.compare(obj1, obj2, true) < 0) {
				return obj1;
			} else {
				return obj2;
			}
		}
		MessageManager mm = EngineMessage.get();
		throw new RQException(op + ":" + mm.getMessage("engine.unknownGroupsMethod"));
	}
	
	private long calcPosition(BlockLinkReader rowReader, ObjectReader rowDataReader) throws IOException {
		rowDataReader.hasNext();
		return rowReader.position() + (rowDataReader.position() % (blockSize - POS_SIZE));
	}
	
	private void rewrite(RandomAccessFile raf, long pos, byte[] bytes) throws IOException {
		raf.seek(pos);
		int offset = (int) (pos % this.blockSize);
		int rest = blockSize - POS_SIZE - offset;
		if (rest < FIXED_OBJ_LEN) {
			raf.write(bytes, 0, rest);
			byte[] nextPos = new byte[POS_SIZE];
			raf.readFully(nextPos);
			pos = (((long)(nextPos[0] & 0xff) << 32) +
					((long)(nextPos[1] & 0xff) << 24) +
					((nextPos[2] & 0xff) << 16) +
					((nextPos[3] & 0xff) <<  8) +
					(nextPos[4] & 0xff));
			raf.seek(pos);
			raf.write(bytes, rest, FIXED_OBJ_LEN - rest);
		} else {
			raf.write(bytes);
		}
	}

	public long getSrcCount() {
		return srcCount;
	}

	public void setSrcCount(long srcCount) {
		this.srcCount = srcCount;
	}
	
	/**
	 * ???Ƶ?ǰ?????嵽file ???????ƽṹ?????????ݣ?
	 * @param file
	 * @return
	 */
	public Cuboid dup(File file) {
		try {
			String []srcColNames = baseTable.getColNames();
			int len = srcColNames.length;
			String []colNames = new String[len];
			boolean[] isDim = ((RowPhyTable)baseTable).getDimIndex();
			for (int i = 0; i < len; i++) {
				if (isDim[i]) {
					colNames[i] = "#" + srcColNames[i];
				} else {
					colNames[i] = srcColNames[i];
				}
			}
			
			Cuboid cuboid = new Cuboid(file, colNames, baseTable.serialBytesLen, ctx,
					"cuboid", "cuboid", exps, newExps);
			cuboid.save();
			return cuboid;
		} catch (Exception e) {
			throw new RQException(e.getMessage(), e);
		}
	}
	
	/**
	 * ?õ??????????????????
	 * @param srcTable
	 * @return
	 */
    public static List getCuboids(ClusterPhyTable srcTable) {
    	String folderPath = Env.getMainPath();
    	String queryStr = srcTable.getClusterFile().getFileName() + Cuboid.CUBE_PREFIX;
        List fileNameList = new ArrayList();//?ļ????б?
        File f = new File(folderPath);
        if (!f.exists()) { //·????????
            return null;
        }else{
                File fa[] = f.listFiles();
                queryStr = queryStr==null ? "" : queryStr;//??queryStr????Ϊnull,???滻Ϊ?գ?indexOfƥ??ֵ????Ϊnull??
                for (int i = 0; i < fa.length; i++) {
                    File fs = fa[i];
                    if(fs.getName().indexOf(queryStr)!=-1){
                         if (fs.isFile()) {
                             fileNameList.add(fs.getName());
                         }
                     }
                }
        }
        return fileNameList;
    }
    
    /**
     * ???¼?Ⱥ??????????
     * ????Ⱥ????д???¼?¼ʱʹ??
     * @param srcTable
     * @param ctx
     */
    public static void update(ClusterPhyTable srcTable, Context ctx) {
    	 List cuboids =  getCuboids(srcTable);
    	 if (cuboids != null) {
    		 for (String C : cuboids) {
    				FileObject fo = new FileObject(C);
    				if (!fo.isExists()) {
    					continue;
    				}
    				File file = fo.getLocalFile().file();
    				RowComTable table = null;
    				try {
    					table = new Cuboid(file, null);
    					table.checkPassword("cuboid");
    					PhyTable baseTable = table.getBaseTable();
    					String fields[] = baseTable.getAllColNames();
    					String expNames[] = baseTable.getSortedColNames();
    					
    					int  expLength = expNames.length;
    					Expression exps[] = new Expression[expLength];
    					for (int i = 0; i < expLength; i++) {
    						exps[i] = new Expression(expNames[i]);
    					}
    					
    					int newExpsLength = fields.length - expLength;
    					Expression newExps[] = new Expression[newExpsLength];
    					for (int i = 0; i < newExpsLength; i++) {
    						newExps[i] = new Expression(fields[i + expLength]);
    					}
    					table.close();
    					
    					String names[] = new String[expLength];
    					String newNames[] = new String[newExpsLength];
    					ClusterCursor cc = srcTable.cursor(null, null, null, null, null, null, 0, null, ctx);
    					ICursor cursor = new MemoryCursor((Sequence) cc.groups(exps, names, newExps, newNames, null, ctx));

    					if (fo.isExists())
    					{
    						fo.delete();
    					}
    					
    					//????
    					file = fo.getLocalFile().file();
    					String colNames[] = new String[fields.length];
    					int sbytes[] = new int[fields.length];
    					int i = 0;
    					System.arraycopy(fields, 0, colNames, 0,  fields.length);
    					System.arraycopy(fields, expLength, newNames, 0,  newExpsLength);
    					for(String n : expNames) {
    						colNames[i++] = "#" + n;
    					}
    					
    					Cuboid ctable = null;
    					try {
    						ctable = new Cuboid(file, colNames, sbytes, ctx, "cuboid", "cuboid",
    								expNames, newNames);
    						ctable.save();
    						ctable.close();
    						ctable = new Cuboid(file, ctx);//???´?
    						ctable.checkPassword("cuboid");
    						ctable.append(cursor);
    						ctable.writeHeader();
    						ctable.close();
    					} catch (Exception e) {
    						if (ctable != null) ctable.close();
    						file.delete();
    						throw new RQException(e.getMessage(), e);
    					}
    				} catch (Exception e) {
    					if (table != null) table.close();
    					throw new RQException(e.getMessage(), e);
    				}
    			
    		 }
    	 }
    }
    
	public String[] getExps() {
		return exps;
	}

	public String[] getNewExps() {
		return newExps;
	}

}

/**
 * ???????????
 * Ԥ???ܵ?????ʵ?ʴ?????????
 * @author runqian
 *
 */
class CuboidTable extends RowPhyTable {
	public CuboidTable(ComTable groupTable, RowPhyTable parent) {
		super(groupTable, parent);
	}
	
	public CuboidTable(ComTable groupTable, RowPhyTable parent, RowPhyTable src) throws IOException {
		super(groupTable, parent, src);
	}
	
	protected void init() {
		super.init();
	}
	
	/**
	 * д????????
	 */
	public void append(ICursor cursor, String opt) throws IOException {
		if (opt == null || opt.indexOf('m') == -1 || !isSorted) {
			append(cursor);
			return;
		}
		
		// ??֧?ִ???????????鲢׷??
		if (!isSingleTable()) {
			throw new RQException("'append@m' is unimplemented in annex table!");
		}
		
		int []serialBytesLen = this.serialBytesLen;
		
		// ?鲢?????????ȱ??浽??ʱ?ļ?
		Cuboid groupTable = (Cuboid)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];
				}
			}
			Cuboid tmpGroupTable = new Cuboid(tmpFile, colNames, serialBytesLen, ctx,
					"cuboid", "cuboid", groupTable.exps, groupTable.newExps);
			tmpGroupTable.readHeader();
			tmpGroupTable.checkPassword("cuboid");
			
			CuboidTable baseTable = (CuboidTable) tmpGroupTable.baseTable;
			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();
		}
	}
	
	protected void appendDataBlock(Sequence data, int start, int end) throws IOException {
		BaseRecord 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 = (BaseRecord) data.get(i);
			Object[] vals = r.getFieldValues();
			//??һ??????????д??buffer
			bufferWriter.writeObject(++recNum);//?д?Ҫ??дһ??α??
			for (int j = 0; j < count; j++) {
				Object obj = vals[j];
				if (isDim[j]) {
					bufferWriter.writeObject(obj);
					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;
				} else {
					bufferWriter.writeObject(obj);//bufferWriter.writeFixedLengthObject(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]);
				}
			}
		}
	}
}