/*
 * Copyright (C) 2005 NTT DATA Corporation
 * 
 */
package org.postgresforest.vm;

import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.postgresforest.Driver;
import org.postgresforest.jdbc3.AbstractJdbc3Connection;
import org.postgresforest.jdbc3.Jdbc3Connection;
import org.postgresforest.util.GT;
import org.postgresforest.util.PSQLException;
import org.postgresforest.vm.err.ForestSQLState;
import org.postgresforest.vm.gsc.GscData;

/**
 *　クエリリライタ.
 *  SQL文をParserクラスと、GSCdataクラスを用いてSQL構文解析とクエリのリライト行い、
 *  結果をQueryInfoクラスにセットする
 * 
 *
 */

public class ReWriter {
	/** 不明 */
	public static final int TYPE_NONE = -1;
	/** パーティション */
	public static final int TYPE_PARTITION = 0;
	/** 分散（多重化・VIEWへの問い合わせ） */
	public static final int TYPE_DISPERSION = 1;
	/** 並列（INSERT・UPDATE・DELETE） */
	public static final int TYPE_PARALLEL = 2;

	/** 変換元SQL文 */
	protected String m_srcSql;
	/** GSCクラスインスタンス */
	protected GscData m_gsc;
	/** リライトクエリ情報のリスト */
	protected ArrayList m_info;
	/** クエリタイプ */
	protected int m_queyType = TYPE_NONE;

	/** パーティション分割テーブルのSUFFIXフォーマット */
	protected static DecimalFormat m_dcFmt = new DecimalFormat("00");

	/** 構文解析クラス (ローカルからフィールドへ。結果集約で外部から使用するため)@since 2.0 */
	protected Parser m_paser;

	/** ログ出力 @since 2.1 */
	protected LogUtil m_logUtil;

	/**
	 * コンストラクタ 
	 * @param gsc　グローバルシステムカタログクラス
	 */

	public ReWriter(GscData gsc) {

		m_gsc = gsc;
		m_logUtil = m_gsc.getLogUtil();//Ver2.1

	}

	/**
	 * リライトクエリ情報の取得
	 * @return
	 */

	public ArrayList getQueryInfo() {

		return m_info;

	}

	/**
	 * SQL文の設定.
	 * SQL文の構文解析を実行し、SQLのリライトおよび接続先のを取得し、QueryInfoクラス（リスト）に格納
	 * 
	 * @param sql 対象SQL文
	 * @throws SQLException
	 */
	public void rewrite(Parser parser) throws SQLException{

		clearSrcSql();

		m_paser = parser;
		m_srcSql = m_paser.getSrcSql();
		

		//QueryInfo作成
		switch (m_paser.getType()) {
			
			case Parser.SELECT :

				makeSelectQuery(m_paser);
				break;

			case Parser.DELETE :

				makeDeleteQuery(m_paser);
				break;

			case Parser.INSERT :

				makeInsertQuery(m_paser);
				break;

			case Parser.UPDATE :

				makeUpdateQuery(m_paser);
				break;

			default :
				//その他の場合すべてのサーバにSQLを投げる
				makeOtherQuery();
				break;
		}
		
		//Ver2.0 ログ追加
		if (Driver.logInfo){
			//抽出更新カラム名\
			QueryInfo qi;
			for( int i = 0; i < m_info.size(); i++){
				qi = (QueryInfo)m_info.get(i);
				
				AbstractJdbc3Connection con = (AbstractJdbc3Connection)qi.getConnection();

				m_logUtil.info("ReWriter Result: Connection URL = " + con.getURL());
				//パーティションNo.
				m_logUtil.info("ReWriter Result: Partition No = " + qi.getPartNo());
				//リライトしたSQL
				m_logUtil.info("ReWriter Result: The rewritten SQL = " + qi.getSql());
				//抽出テーブル名
				String[] tables = qi.getTables();
				String allname = "";
				try {
					for (int j = 0; j < tables.length; j++){
						allname = allname + tables[j] + ",";
					}
				} catch (Exception e) {
				}
				m_logUtil.info("ReWriter Result: Extraction Table Name = " + allname);
			}
		}
	}

	/**
	 * SELECTのQueryInfo作成
	 * 
	 * @param ps 構文解析クラス
	 * @throws SQLException
	 */
	protected void makeSelectQuery(Parser ps) throws SQLException{
		//┌───────────┬───────────┐
		//│・サブクエリを含む->×│パーティション属性指定│
		//│・リライト対象はひとつ├───┬───────┤
		//│  目のパーティションテ│ 無し │     有り     │
		//│  ブルのみ            │      │実テーブル数  │
		//│                      │      ├───┬───┤
		//│                      │      │  １  │１以上│
		//├───────┬───┼───┼───┼───┤
		//│Order By      │ 無し │ Ｂ   │ Ａ   │  Ｂ  │
		//│Group By      ├───┼───┼───┼───┤
		//│句がある      │ 有り │ ×   │ Ａ   │  ×  │
		//└───────┴───┴───┴───┴───┘
		
		//Ａ：１つサーバに対してQuery発行の実テーブルにリライトして。
		//Ｂ：対象となるサーバに対してQuery発行
		//    COUNT(*)を除く集約関数がSELECTに存在しない。->COUNT(*)の場合も他の集計関数と同様にViewへのQueryとする
		//×：ViewへのQuery発行
		
		HashMap qiMap = new HashMap();
		int partTableIdx = -1;
	
		String[] tables = ps.getTables();
		for (int i = 0; i < tables.length; i++) {
			String tbl = tables[i];
	
			if (m_gsc.isPartition(tbl)) {
	
				partTableIdx = i;
	
				HashMap cols = ps.getWhereColumns(tbl);
	
				ArrayList colNames = m_gsc.getPartCol(tbl);
				if(colNames.size() == 1){
					//パーティション属性が１つ
				    PartitionColumnInfo colinfo = (PartitionColumnInfo) colNames.get(0);
					String colName = colinfo.getName();
				    
					if(cols.containsKey(colName)){
	
						HashMap partCol = new HashMap();
						ArrayList values = (ArrayList)cols.get(colName);
						//パーティションの候補
						for (int j = 0; j < values.size(); j++) {
							partCol.put(colName, values.get(j));						
				
							int partNum = m_gsc.getPartNo(tbl, partCol, GscData.KEY_TYPE_NAME);
							Integer numKey = new Integer(partNum);
							if(!qiMap.containsKey(numKey)){
								QueryInfo qi = new QueryInfo();
								qi.setTable(tables);
								qi.setPartNo(partNum);
								qiMap.put(numKey,qi);
							}
	
						}
			   
					}else{
						//パーティション属性の指定がないので、
						//パーティション数分リライトの必要有り
						int num = m_gsc.getPartCount(tbl);
						for (int j = 0; j < num; j++) {
							QueryInfo qi = new QueryInfo();
							qi.setTable(tables);
							qi.setPartNo(j);
							qiMap.put(new Integer(j),qi);
						}
					}
	
				}else{
					//パーティション属性が１以上
					HashMap partCol = new HashMap();

					for (int j = 0; j < colNames.size(); j++) {
					    PartitionColumnInfo colinfo = (PartitionColumnInfo) colNames.get(j);
						String colName = colinfo.getName();
					    
						if( cols.containsKey(colName) ){

							ArrayList values = (ArrayList)cols.get(colName);
							if(values.size() == 1 ){
								
								partCol.put(colName, values.get(0));						

							}else{
								//パーティション属性が複数の場合は、条件値が複数設定されている場合
								//パーティションの特定はしない（UPDATEを参照）
								partCol.clear();
								 break;
							}
	
						}else{
							//条件にパーティションがないのでパーティションの特定はできない
							partCol.clear();
							break;
						}
	
					}
					if( !partCol.isEmpty() ){
						int partNum = m_gsc.getPartNo(tbl, partCol, GscData.KEY_TYPE_NAME);
						QueryInfo qi = new QueryInfo();
						qi.setTable(tables);
						qi.setPartNo(partNum);
						qiMap.put(new Integer(partNum), qi);
					}
					//040120ADD 複数パーティションの時のパーティション文リライト処理を追加
					else{
						//パーティション属性の指定がないので、
						//パーティション数分リライトの必要有り
						int num = m_gsc.getPartCount(tbl);
						for (int j = 0; j < num; j++) {
							QueryInfo qi = new QueryInfo();
							qi.setTable(tables);
							qi.setPartNo(j);
							qiMap.put(new Integer(j),qi);
						}
					}
	
				}
				
				//パーティションテーブルのりライトは
				//1つ目のパーティションテーブルに対してのみ
				//なのでbreak;	
				break;
			}
	
		}
	
		//Ver1.1 FOR UPDATE でパーティション化テーブルの場合、パーティションがひとつに特定できないときはエラー
		if(qiMap.size() > 1 && ps.isForUpdate()){
			//複数パーティションにFORUPDATEが設定されている
			throw new PSQLException(GT.tr("FOR UPDATE is set as two or more partitions."), ForestSQLState.INTERNAL_ERROR );

		}
		
		//Ver3.0 パーティション化2テーブルのJOINはエラーとする
		if(partTableIdx != -1){
			for (int i = partTableIdx + 1; i < tables.length; i++) {
				if (m_gsc.isPartition2(tables[i]))
					//パーティション化2テーブルでは処理できません。
					throw new PSQLException(GT.tr("It cannot be processed in Partition table 2."), ForestSQLState.INTERNAL_ERROR );
			}
		}

		//Ｂ（複数テーブルへのアクセス）の場合
		//GROUP BY ORDER BY COUNT(*)を除く集約関数が指定されていたら、×（ViewへのQuery発行）
		//->COUNT(*)の場合も他の集計関数と同様にViewへのQueryとする
		//Ver1.1 DISTINCT の指定がされていた場合もViewへのQueryとする
		//       集合演算子（UNION,INTERSECT,EXCEPT）が指定されていた場合もViewへのQueryとする
		if( 1 < qiMap.size() && 
		   (/*ps.hasGroupBy()   || Ver2.0*/
			( ps.hasGroupBy() && ps.getGroupList().size() == 0 ) || 
			( ps.hasGroupBy() && ps.hasExecFunction() != true ) || 
			/*ps.hasOrderBy()   || Ver2.0*/ 
			( ps.hasOrderBy() && ps.getOrderList().size() == 0 ) || 
			( ps.hasGroupBy() && ps.hasOrderBy() &&  ps.hasLimit() ) || //GROUP BY,ORDER BY,LIMITが指定されたらVIEW 
			ps.hasOffset()    || //Ver2.0 
		    ps.hasFunction()  || 
		    ps.hasJoinTable() || 
			ps.hasDistinct()  ||
			ps.hasUnion()     ||
			ps.hasHaving()    ) ){
			qiMap.clear();
		}
		
		
		//QueryInfo リスト生成				
		if ( qiMap.isEmpty() ) {
			
			//×の処理
		    
			//Ver3.0 パーティション化2でVIEWへの処理はエラーとする
			if (partTableIdx != -1 && m_gsc.isPartition2(tables[partTableIdx]))
				//パーティション化2テーブルでは処理できません。
				throw new PSQLException(GT.tr("It cannot be processed in Partition table 2."), ForestSQLState.INTERNAL_ERROR );
			    
			//Ver1.1 FOR UPDATEの追加			
			if(ps.isForUpdate()){

				if(Driver.forestTestLog)
					System.out.println( "The query type was specified in parallel.");

				if (Driver.logInfo)
					m_logUtil.info("The query type was specified in parallel.");
			
				//クエリタイプのセット
				m_queyType = TYPE_PARALLEL;
	
				//テーブルがパーティションではないか、VIEWで処理
				ArrayList conlist = null;
				if(tables.length <= 0){
					//テーブルが指定されていない場合は、すべてのサーバの中から接続を決定
					conlist = m_gsc.getDistServerList();
				}else{
					//テーブルが指定されているときは、処理可能サーバから接続を決定
					conlist = m_gsc.getDistServerList(tables[0],tables);
				}
	

				//QueryInfoの作成
				for (int i = 0; i < conlist.size(); i++) {
					QueryInfo qf = new QueryInfo();
					qf.setConnection((Connection)conlist.get(i));
					qf.setSql(m_srcSql);
					qf.setTable(tables);

					m_info.add(qf);
				}


			}else{

				if(Driver.forestTestLog)
					System.out.println( "The query type was specified to be dispersion.");

				if (Driver.logInfo)
					m_logUtil.info("The query type was specified to be dispersion.");
			
				//クエリタイプのセット
				m_queyType = TYPE_DISPERSION;
	
				//テーブルがパーティションではないか、VIEWで処理
				Connection con = null;
				if(tables.length <= 0){
					//テーブルが指定されていない場合は、すべてのサーバの中から接続を決定
					con = m_gsc.getDistServer();
				}else{
					//テーブルが指定されているときは、処理可能サーバから接続を決定
					con = m_gsc.getDistServer(tables);
				}
	
				//QueryInfoの作成
				QueryInfo qf = new QueryInfo();
				qf.setConnection(con);
				qf.setSql(m_srcSql);
				qf.setTable(tables);
	
				m_info.add(qf);

			}
			
	
		} else {
	
			//Ａ、Ｂの場合

			//Ver1.1 FOR UPDATEの追加			
			if(ps.isForUpdate()){


				if(Driver.forestTestLog)
					System.out.println( "The query type was specified in parallel.");

				if (Driver.logInfo)
					m_logUtil.info("The query type was specified in parallel.");
		
				//クエリタイプのセット
				m_queyType = TYPE_PARALLEL;

				//QueryInfoの作成
				QueryInfo qiSrc = (QueryInfo)qiMap.values().iterator().next();

				int partNum = qiSrc.getPartNo();
				String table = tables[partTableIdx];
				//Ver3.0
				//String distSql = rewriteTable(m_srcSql,table,partNum);
				Parser.TableInfo tableInfo = ps.getTableInfoByName(table);
				String distSql = rewriteTable(m_srcSql,table,tableInfo.getTableAlias(),partNum);
				
				//@@クエリインフォの複製
				
				ArrayList conlist = null;
				if(tables.length <= 0){
					//テーブルが指定されていない場合は、すべてのサーバの中から接続を決定
					conlist = m_gsc.getDistServerList();
				}else{
					//テーブルが指定されているときは、処理可能サーバから接続を決定
				    //Ver3.0 パーティション化2に対応
				    //conlist = m_gsc.getDistServerList(tbls[0],tbls);
				    conlist = m_gsc.getDistServerList(table,partNum);
				}
	
				for (int i = 0; i < conlist.size(); i++) {
					
					QueryInfo qf = new QueryInfo();
	
					qf.setTable(qiSrc.getTables());
					qf.setPartNo(qiSrc.getPartNo());
	
					qf.setSql(distSql);
					qf.setConnection((Connection)conlist.get(i));

					m_info.add(qf);
				}



			} else {


				if(Driver.forestTestLog)
					System.out.println( "The query type was specified to be a partition.");

				if (Driver.logInfo)
					m_logUtil.info("The query type was specified to be a partition.");
		
				//クエリタイプのセット
				m_queyType = TYPE_PARTITION;



				
				for (Iterator iter = qiMap.values().iterator(); iter.hasNext();) {
					QueryInfo queryInfo = (QueryInfo) iter.next();
		
					//テーブル名と、パーティションNo.取得
					int partNum = queryInfo.getPartNo();
					String table = tables[partTableIdx];
					
					//コネクションとリライトSQLを取得
					//Ver3.0
					//String distSql = rewriteTable(m_srcSql,table,partNum);
					Parser.TableInfo tableInfo = ps.getTableInfoByName(table);
					String distSql = rewriteTable(m_srcSql,table,tableInfo.getTableAlias(),partNum);

					Connection con = m_gsc.getDistServer(table,partNum);
		
					//コネクションとリライトSQLを設定
					queryInfo.setSql(distSql);
					queryInfo.setConnection(con);
		
					//QueryInfoリストに設定
					m_info.add(queryInfo);
					
				}
			}
		
		}
	}


	/**
	 * INSERTのQueryInfo作成
	 * @param ps 構文解析クラス
	 * @throws PSQLException
	 */
	protected void makeInsertQuery(Parser ps) throws SQLException {

		//クエリタイプのセット
		m_queyType = TYPE_PARALLEL;

		String insertSql = m_srcSql;

		String[] tables = ps.getTables();

		String tableName = tables[0];
	
		ArrayList conlist = null;	//ADD Ver3.0
		
		//パーティション化テーブルの場合	
		if (m_gsc.isPartition(tableName)) {

			int partNum = -1;
			ArrayList cols = ps.getInsertColumns();

			if (cols == null) {
				//カラム名が指定されていない。		

				//パーティション属性の項目番号リスト取得
				HashMap partCol = new HashMap();

				ArrayList colNums = m_gsc.getPartCol(tableName);
				if(colNums != null){
				
					ArrayList vals = ps.getInsertValues();
	
					for (int i = 0; i < colNums.size(); i++) {
					    PartitionColumnInfo colinfo = (PartitionColumnInfo) colNums.get(i);
						int colNum = colinfo.getNumber();
						int valIdx = colNum - 1; //項目番号は、1から始まるため-1
						if( valIdx < vals.size()){
							partCol.put(new Integer(colNum), vals.get(valIdx));
						}else{
							partCol.put(new Integer(colNum), null);
						}
					}
				}
				//パーティションNo特定
				partNum =
					m_gsc.getPartNo(tableName, partCol, GscData.KEY_TYPE_NO);

			} else {

				HashMap partCol = new HashMap();
				ArrayList colNames = m_gsc.getPartCol(tableName);
				if(colNames != null){
					for (int i = 0; i < colNames.size(); i++) {
					    PartitionColumnInfo colinfo = (PartitionColumnInfo) colNames.get(i);
						String colName = colinfo.getName();
						ParamValue value = ps.getInsertValue(colName);
						partCol.put(colName, value);
	
					}
				}
				//パーティションNo特定
				partNum =
					m_gsc.getPartNo(tableName, partCol, GscData.KEY_TYPE_NAME);
			}

			//SQLの書き換え
			insertSql = rewriteTable(insertSql, tableName, partNum);
			//ADD Ver3.0 パーティションが存在するテーブルのみを接続先に
			conlist = m_gsc.getDistServerList(tableName, partNum);
		}

		//QueryInfo リスト生成				
		//MOD Ver3.0
		//Connection[] conlist = m_gsc.getDistServerList(tbls[0]); 
		if(conlist == null ){
			conlist = m_gsc.getDistServerList(tableName); 
		}
		for (int i = 0; i < conlist.size(); i++) {
			QueryInfo qf = new QueryInfo();
			qf.setConnection((Connection)conlist.get(i));
			qf.setSql(insertSql);
			qf.setTable(tables);

			m_info.add(qf);
		}

		//TODO??複数テーブルの指定？？

	}

	/**
	 * UPDATEのQueryInfo作成
	 * @param ps 構文解析クラス
	 * @throws PSQLException
	 */
	protected void makeUpdateQuery(Parser ps) throws SQLException{
		//クエリタイプのセット

		m_queyType = TYPE_PARALLEL;

		String[] tables = ps.getTables();

		String tableName = tables[0];

		ArrayList conlist = null;	//ADD Ver3.0

		String updateSql = m_srcSql;

		//変更するカラムにパーティション属性が指定されているかチェック
		ArrayList setCols = ps.getSetColumns();
		ArrayList partCols = m_gsc.getPartCol(tableName);
		if(partCols != null){
			for (int i = 0; i < partCols.size(); i++) {
			    PartitionColumnInfo colinfo = (PartitionColumnInfo) partCols.get(i);
				String partCol = colinfo.getName();
				if (setCols.contains(partCol)) {
					//変更するカラムにパーティション属性が指定されている
					throw new PSQLException(GT.tr("The partition attribute ({0}) is specified to be the column to change.",partCol) , ForestSQLState.INTERNAL_ERROR );
				}
			}
		}
		//┌───────┬───┬───────┐
		//│              │      │カラム設定値  │
		//├───────┼───┼───┬───┤
		//│              │      │ １   │1以上 │
		//├───────┼───┼───┼───┤
		//│パーティション│  １  │ ○   │  △  │
		//│　　　        ├───┼───┼───┤
		//│属性数        │ 1以上│ ○   │  ×  │
		//└───────┴───┴───┴───┘

		//○：パーティション属性特定可能
		//△：パーティション属性場合により特定可能
		//×：パーティション属性特定は行わない。


		//パーティションテーブルの場合
		if (m_gsc.isPartition(tableName)) {

			boolean canRewrite = true;

			int partNum = -1;

			HashMap cols = ps.getWhereColumns(tableName);

			ArrayList colNames = m_gsc.getPartCol(tableName);
			if(colNames != null){
			
				if(colNames.size() == 1){
				    PartitionColumnInfo colinfo = (PartitionColumnInfo) partCols.get(0);
				    String colName = colinfo.getName();
					//パーティション属性が１つ
					if(cols.containsKey(colName)){
	
						HashMap partCol = new HashMap();
						ArrayList values = (ArrayList)cols.get(colName);
						for (int j = 0; j < values.size(); j++) {
							partCol.put(colName, values.get(j));						
							
							int partNum_Wk	= m_gsc.getPartNo(tableName, partCol, GscData.KEY_TYPE_NAME);
							if( partNum == -1){
								partNum	= partNum_Wk;
							}else if(partNum != partNum_Wk ){
								//パーティションがひとつに特定できない
								canRewrite = false;
								break;
							}
	
						}
						   
	
					}else{
						//条件にパーティションがないのでパーティションの特定はできない
						canRewrite = false;
					}
	
	
				}else{
					//パーティション属性が１以上
	
					HashMap partCol = new HashMap();
					for (int i = 0; i < colNames.size(); i++) {
					    PartitionColumnInfo colinfo = (PartitionColumnInfo) partCols.get(i);
					    String colName = colinfo.getName();
						if(cols.containsKey(colName)){
	
							ArrayList values = (ArrayList)cols.get(colName);
							if(values.size() == 1 ){
								partCol.put(colName, values.get(0));						
							}else{
								//パーティションの特定はしない
								canRewrite = false;
								break;
							}
	
						}else{
							//条件にパーティションがないのでパーティションの特定はできない
							canRewrite = false;
							break;
						}
	
					}
	
					//040128MOD パーティションの特定ができないのにパーティション番号を求めエラーとなるを修正 
					//if( !partCol.isEmpty() ){
					if( canRewrite && !partCol.isEmpty() ){
						partNum = m_gsc.getPartNo(tableName, partCol, GscData.KEY_TYPE_NAME);
					}
	
				}
			}
			//パーティションが単一テーブルに特定できる場合
			if(canRewrite){

				//パーティションNo特定
				//SQLの書き換え
				updateSql = rewriteTable(updateSql,tableName,partNum);
		
				// Ver 3.0
				conlist = m_gsc.getDistServerList(tableName, partNum);

			}
			else{
			    //Ver3.0 
			    //パーティション化2では、パーティションが特定できない場合、エラーとする
			    if(m_gsc.isPartition2(tableName))
			    	//パーティション化2テーブルでは処理できません。
			        throw new PSQLException(GT.tr("It cannot be processed in Partition table 2."), ForestSQLState.INTERNAL_ERROR );
			}
		}


		//QueryInfo リスト生成				
		//MOD Ver3.0
		//Connection[] conlist = m_gsc.getDistServerList(tbls[0]); 
		if(conlist == null ){
			conlist = m_gsc.getDistServerList(tableName); 
		}
		for (int i = 0; i < conlist.size(); i++) {
			QueryInfo qf = new QueryInfo();
			qf.setConnection((Connection)conlist.get(i));
			qf.setSql(updateSql);
			qf.setTable(tables);

			m_info.add(qf);
		}

		//TODO??複数テーブルの指定？？

	}


	/**
	 * DELETEのQueryInfo作成
	 * @param ps  構文解析クラス
	 * @throws PSQLException
	 */
	protected void makeDeleteQuery(Parser ps) throws SQLException{
		
		//クエリータイプ設定
		m_queyType = TYPE_PARALLEL;

		String deleteSql = m_srcSql;

		String[] tables = ps.getTables();

		String tableName = tables[0];

		ArrayList conlist = null;	//ADD Ver3.0


		//┌───────┬───┬───────┐
		//│              │      │カラム設定値  │
		//├───────┼───┼───┬───┤
		//│              │      │ １   │1以上 │
		//├───────┼───┼───┼───┤
		//│パーティション│  １  │ ○   │  △  │
		//│　　　        ├───┼───┼───┤
		//│属性数        │ 1以上│ ○   │  ×  │
		//└───────┴───┴───┴───┘

		//○：パーティション属性特定可能
		//△：パーティション属性場合により特定可能
		//×：パーティション属性特定は行わない。

		//パーティションテーブルの場合
		if (m_gsc.isPartition(tableName)) {

			boolean canRewrite = true;

			int partNum = -1;

			HashMap cols = ps.getWhereColumns(tableName);

			ArrayList colNames = m_gsc.getPartCol(tableName);
			if(colNames != null){
				if(colNames.size() == 1){
				    PartitionColumnInfo colinfo = (PartitionColumnInfo) colNames.get(0);
				    String colName = colinfo.getName();
					//パーティション属性が１つ
					if(cols.containsKey(colName)){
	
						HashMap partCol = new HashMap();
						ArrayList values = (ArrayList)cols.get(colName);
						for (int j = 0; j < values.size(); j++) {
							partCol.put(colName, values.get(j));						
							
							int partNum_Wk	= m_gsc.getPartNo(tableName, partCol, GscData.KEY_TYPE_NAME);
							if( partNum == -1){
								partNum	= partNum_Wk;
							}else if(partNum != partNum_Wk ){
								//パーティションがひとつに特定できない
								canRewrite = false;
								break;
							}
	
						}
						   
	
					}else{
						//条件にパーティションがないのでパーティションの特定はできない
						canRewrite = false;
					}
	
				}else{
					//パーティション属性が１以上
	
					HashMap partCol = new HashMap();
					for (int i = 0; i < colNames.size(); i++) {
					    PartitionColumnInfo colinfo = (PartitionColumnInfo) colNames.get(i);
					    String colName = colinfo.getName();
						if(cols.containsKey(colName)){
	
							ArrayList values = (ArrayList)cols.get(colName);
							if(values.size() == 1 ){
								partCol.put(colName, values.get(0));						
							}else{
								//パーティションの特定はしない
								 canRewrite = false;
								 break;
							}
	
						}else{
							//条件にパーティションがないのでパーティションの特定はできない
							canRewrite = false;
							break;
						}
					}
					
					//040128MOD パーティションの特定ができないのにパーティション番号を求めエラーとなるを修正 
					//if( !partCol.isEmpty() ){
					if( canRewrite && !partCol.isEmpty() ){
						partNum = m_gsc.getPartNo(tableName, partCol, GscData.KEY_TYPE_NAME);
					}
					
				}
			}
			//パーティションが単一テーブルに特定できる場合
			if(canRewrite){
				//パーティションNo特定
				//SQLの書き換え
				deleteSql = rewriteTable(deleteSql,tableName,partNum);
				
				// Ver 3.0
				conlist = m_gsc.getDistServerList(tableName, partNum);
				
				
			}
			else{
			    //Ver3.0 
			    //パーティション化2では、パーティションが特定できない場合、エラーとする
			    if(m_gsc.isPartition2(tableName))
			    	//パーティション化2テーブルでは処理できません。
			        throw new PSQLException(GT.tr("It cannot be processed in Partition table 2."), ForestSQLState.INTERNAL_ERROR );
			}
		}

		//QueryInfo リスト生成				
		//MOD Ver3.0
		//Connection[] conlist = m_gsc.getDistServerList(tbls[0]); 
		if(conlist == null ){
			conlist = m_gsc.getDistServerList(tableName); 
		}
		for (int i = 0; i < conlist.size(); i++) {
			QueryInfo qf = new QueryInfo();
			qf.setConnection((Connection)conlist.get(i));
			qf.setSql(deleteSql);
			qf.setTable(tables);

			m_info.add(qf);
		}

		//TODO??複数テーブルの指定？？

	}

	/**
	 * SELECT,INSERT,UPDATE,DELETE以外ののQueryInfo作成
	 * 
	 * @throws SQLException
	 */
	protected void makeOtherQuery() throws SQLException{

		//クエリータイプ設定
		m_queyType = TYPE_PARALLEL;

		//QueryInfo リスト生成				
		ArrayList conlist = m_gsc.getDistServerList();
		for (int i = 0; i < conlist.size(); i++) {
			QueryInfo qf = new QueryInfo();
			qf.setConnection((Connection)conlist.get(i));
			qf.setSql(m_srcSql);

			m_info.add(qf);
		}


	}

	/**
	 * SQL文のクリア
	 */
	protected void clearSrcSql() {

		m_srcSql = null;

		if(m_info == null){
			m_info = new ArrayList();
		}else{
		    m_info.clear();
		}


		m_queyType = TYPE_NONE;

	}

	/**
	 * クエリの処理振り分け、結果集約タイプ取得
	 * @return　結果集約タイプ
	 */

	public int getQueryType() {

		return m_queyType;

	}

	/**
	 * リトライ用コネクションの取得.
	 * アクセス失敗によりサーバを切り離した場合の代替コネクションを取得する。
	 * このメソッドが呼び出される前に、切り離し（GSC.setServerBroken）が呼ばれていること。
	 * 
	 * @param info 接続先／リライトSQL QueryInfo
	 * @return　新しい接続先Connection
	 * @throws SQLException
	 */
	public Connection getRetryConnection(QueryInfo info) throws SQLException{

		Connection con = null;

		if (m_queyType == TYPE_PARTITION) {

			int partNo = info.getPartNo();

			String[] table = info.getTables();
			for (int i = 0; i < table.length; i++) {
				if(m_gsc.isPartition(table[i])){
					con = m_gsc.getDistServer(table[i], partNo);
					break;
				}
			}

		} else if (m_queyType == TYPE_DISPERSION) {
			
			String[] tables = info.getTables();
			if(tables == null || tables.length <= 0){
				//テーブルが指定されていない場合は、すべてのサーバの中から接続を決定
				con = m_gsc.getDistServer();
			}else{
				con =  m_gsc.getDistServer(info.getTables());
			}

		}

		return con;

	}

	/**
	 * テーブル名書き換え処理
	 * パーティションテーブルのテーブル名をパーティションNoに基づいて実テーブルに書き換える
	 * @param srcSql	元SQL
	 * @param strTable　書き換え対象テーブル名
	 * @param nPartNo　　パーティションNo
	 * @return　書き換え後のSQL
	 * @throws SQLException
	 * @see rewriteTable(String srcSql, String strTable,	String strAlias, int nPartNo) throws SQLException
	 */
	protected String rewriteTable(String srcSql, String strTable,	int nPartNo) throws SQLException{
	    return rewriteTable(srcSql, strTable, null, nPartNo);
	}
	/**
	 * テーブル名書き換え処理
	 * パーティションテーブルのテーブル名をパーティションNoに基づいて実テーブルに書き換える
	 * Aliasチェック対応
	 * @param srcSql	元SQL
	 * @param strTable　書き換え対象テーブル名
	 * @param nPartNo　　パーティションNo
	 * @return　書き換え後のSQL
	 * @throws SQLException
	 * @since 3.0
	 */
	protected String rewriteTable(String srcSql, String strTable,	String strAlias, int nPartNo) throws SQLException{
		//実テーブル名生成
		String newTable = strTable + "_" + m_dcFmt.format(nPartNo);
		//" テーブル名 "を置換する

		StringBuffer distSql = new StringBuffer();

		//トークナイザー取得
		StringReader fr = new StringReader(srcSql);
		StreamTokenizer tokenizer = new StreamTokenizer(fr);
		tokenizer.resetSyntax();
		tokenizer.wordChars('a', 'z');
		tokenizer.wordChars('A', 'Z');
		tokenizer.wordChars('_', '_');
		tokenizer.wordChars('*', '*');

		tokenizer.wordChars('0', '9');//Ver1.1 テーブル名に数値が入っている場合にエラーとなるのを修正		


		//文字列以外を小文字に変換
		try {

			//Ver1.1R4
			
			//Ver1.1R4 文字列('でくくられているかどうか）の判断
			boolean isQuoteChar = false;

			int token;
			while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
				switch (token) {
					case StreamTokenizer.TT_WORD :
						//Ver1.1R4
						//文字列ならテーブル名の置換は行わない
						//トークナイザーの小文字変換を使わないので、equalsIgnoreCaseで判断
						//if(strTable.compareTo(tokenizer.sval) == 0){
						if(!isQuoteChar && 
						   strTable.equalsIgnoreCase(tokenizer.sval) ){
							
						    if(strAlias != null){
						        //Aliasが定義されていたら、テーブル名の次にくるAliasをチェックして置換
						        distSql.append(rewriteAliasTable(tokenizer,strTable,newTable,strAlias));
						        
						    }else{    
						        //Aliasが定義されていなければパーティションテーブル名に置換
						        distSql.append(newTable);
						    }
						
						}else{
							distSql.append(tokenizer.sval);
						}
						break;
					case '\'' :
						if(isQuoteChar &&			
							distSql.substring(distSql.length()-1).equals("\\") ){
							distSql.append((char) tokenizer.ttype);
							break;
						
						}
						isQuoteChar = !isQuoteChar;//文字列判断フラグをトグル切り替え
	
					default :
						distSql.append((char) tokenizer.ttype);
				}
			}
		} catch (IOException e) {
			//SQL変換エラー
			throw new PSQLException(GT.tr("SQL conversion error"), ForestSQLState.INTERNAL_ERROR, e);
		}

		return distSql.toString();
		
		
	}


	/**
	 * テーブル名書き換え処理
	 * パーティションテーブルのテーブル名をパーティションNoに基づいて実テーブルに書き換える
	 * Alias判定.
	 * 次のWORDを含む変換結果
	 * @param tokenizer 文字解析用
	 * @param strTable　書き換え対象テーブル名
	 * @param newTable　パーティションテーブル名
	 * @param strAlias　テーブルの別名
	 * @return　書き換え後のSQL
	 * @throws SQLException
	 * @since 3.0
	 */
	protected String rewriteAliasTable(StreamTokenizer tokenizer, String strTable, String newTable, String strAlias) throws SQLException {

        StringBuffer distSql = new StringBuffer();

        try {

            int token;
            while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
                if (token == StreamTokenizer.TT_WORD) {
                    //テーブル名の次の単語を取り出し、
                    String word = tokenizer.sval;
                    if (strAlias.equals(word)) {
                        //Aliasと一致したら、テーブル名を書き換え
                        distSql.insert(0, newTable);
                    } else if( word.equalsIgnoreCase("AS") ){
                        //AS はそのまま読み込み
                        distSql.append(word);
                        continue;
                    } else {
	                    //Aliasと不一致のときは、テーブル名そのまま
	                    distSql.insert(0, strTable);
	                }
                    distSql.append(word);

                    break;
                }
                //単語以外はそのまま連結
                distSql.append((char) tokenizer.ttype);
                if (token == ',') {
                    //カンマは終了
                    break;
                }

            }
        } catch (IOException e) {
            //SQL変換エラー
            throw new PSQLException(GT.tr("SQL conversion error"),
                    ForestSQLState.INTERNAL_ERROR, e);
        }

        return distSql.toString();

    }

}
