<?php
/*
 * database2/Connection.class.php
 * 
 * CopyRight(C) 2010 Shopformer Development Team. All Right Reserved
 * URL: http://sourceforge.jp/projects/shopformer/
 * 
 * 
 * Mail: m_nakashima@users.sourceforge.jp
 * Auther: Masanori Nakashima
 * Last Update: 2010-06-18
 */
require_once(dirname(__FILE__)
.DIRECTORY_SEPARATOR.'AbstractData.class.php');
/**
 * AbstractConnectionの実装クラスオブジェクトのラッパークラスです。
 */
class database2_Connection {
	
	/** spider_HttpRequestオブジェクトへの参照	*/
	var $request			= null;
	
	/** データベースホスト配列: 0番目の要素が更新可能接続ホストで固定とする	*/
	var $databaseHostArray	= array();
	/** データベースポート配列	0番目の要素が更新可能接続ポートで固定とする	*/
	var $databasePortArray	= array();
	
	/** データベースタイプ	マスタ・スレーブ共通	*/
	var $databaseType		= null;
	/** データベース名称	マスタ・スレーブ共通	*/
	var $databaseName		= null;
	/** 接続ユーザ名		マスタ・スレーブ共通	*/
	var $databaseUser		= null;
	/** 接続パスワード		マスタ・スレーブ共通	*/
	var $databasePass		= null;
	
	/** AbstractConnection実装オブジェクト：マスタ接続インスタンス	*/
	var $connectionWritable	= null;
	/** AbstractConnection実装オブジェクト：スレーブ接続インスタンス	*/
	var $connectionReadOnly	= null;
	
	/** トランザクション中フラグ	*/
	var $inTransaction		= false;

	/**
	 * コンストラクタ
	 */
	function database2_Connection( & $request, $databaseName, $databaseUser, $databasePass, $databaseType, $hosts, $ports ) {
		$this->request		= $request;
		$this->databaseName	= $databaseName;
		$this->databaseUser	= $databaseUser;
		$this->databasePass	= $databasePass;
		$this->databaseType	= $databaseType;
		if( !is_array($hosts) ) {
			$this->databaseHostArray	= array();
			array_push( $this->databaseHostArray, $hosts );
		} else {
			$this->databaseHostArray	= $hosts;
		}
		if( !is_array($ports) ) {
			$this->databasePortArray	= array();
			array_push( $this->databasePortArray, $ports );
		} else {
			$this->databasePortArray	= $ports;
		}
	}
	/**
	 * 更新可能なマスタ接続を取得します。
	 * @access private
	 */
	function _connectWritable() {
		if( !is_null( $this->connectionWritable ) ) {
			return $this->connectionWritable;
		}
		$host		= $this->databaseHostArray[0];
		$port		= $this->databasePortArray[0];
		$connection	= $this->_connect( $host, $port );
		if( $connection === false ) {
			return false;
		} else {
			return $this->connectionWritable = & $connection;
		}
	}
	/**
	 * 読み取りのみのスレーブ接続を取得します
	 * @access private
	 */
	function _connectReadOnly() {
		if( !is_null( $this->connectionReadOnly ) ) {
			return $this->connectionReadOnly;
		}
		$slaveHosts	= $this->databaseHostArray;
		$slavePorts	= $this->databasePortArray;
		$host		= null;
		$port		= null;
		if( count($slaveHosts) > 1 ) {
			array_shift($slaveHosts);
			array_shift($slavePorts);
			$key		= array_rand($slaveHosts);
			$host		= $slaveHosts[$key];
			if( isset( $slavePorts[$key]) ) {
				$port	= $slavePorts[$key];
			}
		} else if( count($slaveHosts) == 1 ) {
			$host		= $slaveHosts[0];
			if( isset( $slavePorts[0]) ) {
				$port	= $slavePorts[0];
			}
		} else {
			return false;
		}
		$connection	= $this->_connect( $host, $port );
		if( $connection === false ) {
			return false;
		} else {
			return $this->connectionReadOnly = & $connection;
		}
	}
	/**
	 * ホストとポートを指定してデータベース接続を取得します
	 * @access private
	 */
	function _connect( $host, $port ) {
		// 利用するクラスの決定
		$className			= 'database2_connection_'.ucwords($this->databaseType);
		if( !class_exists($className) ) {
			$filePathClassFile	= dirname(__FILE__).DIRECTORY_SEPARATOR
				.'connection'.DIRECTORY_SEPARATOR.ucwords($this->databaseType).'.class.php';
			if( !file_exists($filePathClassFile) ) {
				$this->request->addLocaledError('database2.error.noclass',SPIDER_LOG_LEVEL_FATAL,array($filePathClassFile));
				return false;
			} else {
				require_once( $filePathClassFile );
				if( !class_exists($className) ) {
					$this->request->addLocaledError('database2.error.noclass',SPIDER_LOG_LEVEL_FATAL,array($filePathClassFile));
					return false;
				}
			}
		}
		$connection		= new $className;
		$connection->_setHttpRequest( $this->request );
		if( !is_a($connection,'database2_AbstractConnection') ) {
			$this->request->addLocaledError('database2.error.noclass',SPIDER_LOG_LEVEL_FATAL,array($filePathClassFile));
			return false;
		}
		if( $connection->_connect( $this->databaseName, $this->databaseUser, $this->databasePass, $host, $port ) ) {
			return $connection;
		}
		return false;
	}
	/**
	 * データベースから切断します
	 */
	function disconnect() {
		if( !is_null( $this->connectionReadOnly ) ) {
			$this->connectionReadOnly->_disconnect();
		}
		if( !is_null( $this->connectionWritable ) ) {
			$this->connectionWritable->_disconnect();
		}
	}
	/**
	 * トランザクション状態に応じて適切なデータベース接続を取得します
	 */
	function _getProperConnection() {
		$connection	= false;
		if( $this->inTransaction ) {
			$connection	= $this->_connectWritable();
		} else {
			$connection	= $this->_connectReadOnly();
		}
		return $connection;
	}
	/**
	 * 文字列をquoteします
	 * @param string $value 文字列
	 * @param string $type カラム型
	 * @param boolean $quote quoteする場合はtrue、しない場合はfalse
	 * @param boolean $escape_wildcards ワイルドカードをquoteする場合はtrue、しない場合はfalse
	 * @return string quoteした文字列
	 * @access public
	 */
	function quote( $value, $type = null, $quote = true, $escape_wildcards = false ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_quote( $value, $type, $quote, $escape_wildcards );
		}
	}
	/**
	 * 文字列をエスケープします
	 * @param string $text
	 * @param boolean $escape_wildcards
	 * @return 
	 * @access public
	 */
	function escape( $text, $escape_wildcards = false ){
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_escape( $text, $escape_wildcards );
		}
	}
	/**
	 * Transactionを開始します
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function beginTransaction( $savepoint = null ) {
		$this->inTransaction	= true;
		$connection	= $this->_connectWritable();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_beginTransaction( $savepoint );
		}
	}
	/**
	 * トランザクションをコミットします
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function commit( $savepoint = null ) {
		$this->inTransaction	= false;
		$connection	= $this->_connectWritable();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_commit( $savepoint );
		}
	}
	/**
	 * トランザクションをロールバックします
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function rollback( $savepoint = null ) {
		$this->inTransaction	= false;
		$connection	= $this->_connectWritable();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_rollback( $savepoint );
		}
	}
	/**
	 * 渡されたSQLクエリ文を実行します
	 * @param string $query SQLクエリ
	 * @param array $types 指定した場合は、結果セットのカラムの型が取得したものに設定されます。
	 * @param boolean $result_class
	 * @param boolean $result_wrap_class 
	 * @return 
	 * @access public
	 */
	function query( $query, $types = null, $result_class = true, $result_wrap_class = false ) {
		$query	= trim( $query );
		$connection	= false;
		if( preg_match('/^[sE][eE][lL][eE][cC][tT]/',$query) > 0 || preg_match('/^[sS][hH][oO][wW]/',$query) > 0  ) {
			// 問い合わせ系ＳＱＬ文の場合にはトランザクション状態に応じた適切なコネクションを取得
			$connection	= $this->_getProperConnection();
		} else {
			// 更新系ＳＱＬ文の場合にはマスタコネクションを取得
			$connection	= $this->_connectWritable();
		}
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_query( $query );
		}
	}
	/**
	 * 渡されたSQLクエリ文を実行して全ての結果を取得します
	 * @param string $query SQLクエリ
	 * @param array $types 指定した場合は、結果セットのカラムの型が取得したものに設定されます。
	 * @param integer $fetchmode 使用する取得モード。
	 * @param boolean $rekey trueを指定すると、配列は以下のように変更されます。
	 * 			結果セットのカラム数が二つより多い場合は、値はカラム2からカラムnまでの値の配列となります。
	 * 			結果セットのカラムが二つだけの場合は、戻り値は二番目のカラムの値を表すスカラー値になります。
	 * 			($force_arrayパラメータで配列を強制している場合は除きます)
	 * @param boolean $force_array クエリの返すカラム数が二つである場合にのみ使用します。
	 * 			trueを設定すると、戻り値の配列の値がスカラー値ではなく一要素の配列となります。
	 * @param  boolean $group trueを設定すると、戻り値の配列の値が別の配列でラップされます。
	 * 			同一のキー(最初のカラムの値)が複数表れた場合に、それが既存の値を上書きせずに配列に追加されるようになります。
	 * @return mixed 入れ子状の配列、あるいは失敗した場合にMDB_Errorを返します。
	 * @access public
	 */
	function queryAll( $query, $fetchtype=null, $loadRelation=false, $setOrg=true ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			$result = $connection->_queryAll( $query, $fetchtype, $setOrg );
			$this->releaseLimit();
			if( $result !== false ) {
				if( $loadRelation ) {
					foreach( $result as $key => $row ) {
						if( is_object( $row ) ) {
							// オプション情報読み込みメソッドがあるならコール
							if( $loadRelation === true ) {
								if( method_exists($result[$key],'loadRelation') ) {
									$result[$key]->loadRelation($this);
								} else if( method_exists($result[$key],'_load_optional_data') ) {
									$result[$key]->_load_optional_data($this);
								} else if( method_exists($result[$key],'_load_optional_relation') ) {
									$result[$key]->_load_optional_relation($this);
								}
							} else if( strlen(trim($loadRelation)) > 0 ) {
								// オプション読み込みメソッドが指定されているならコール
								if( strpos($loadRelation,'(') != false && strpos($loadRelation,')') != false ) {
									// メソッド呼び出しプログラム記述
									eval($loadRelation);
								} else if( method_exists($result[$key],$loadRelation) ) {
									$result[$key]->$loadRelation($this);
								}
							} 
						}
					}
				}
				return $result;
			} else {
				return false;
			}
		}
	}
	/**
	 * 渡されたSQLクエリ文を実行して1レコードのみ結果を取得します
	 * @param string $query SQLクエリ
	 * @param array $types 指定した場合は、結果セットのカラムの型が取得したものに設定されます。
	 * @param integer $fetchmode 使用する取得モード。
	 * @return 
	 * @access public
	 */
	function queryRow( $query, $fetchtype=null, $loadRelation=false, $setOrg=true ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			$result = $connection->_queryRow( $query, $fetchtype, $setOrg );
			if( $result !== false ) {
				if( $loadRelation && is_object( $result ) ) {
					// オプション情報読み込みメソッドがあるならコール
					if( $loadRelation === true ) {
						if( method_exists($result,'loadRelation') ) {
							$result->loadRelation($this);
						} else if( method_exists($result,'_load_optional_data') ) {
							$result->_load_optional_data($this);
						} else if( method_exists($result,'_load_optional_relation') ) {
							$result->_load_optional_relation($this);
						}
					} else if( strlen(trim($loadRelation)) > 0 ) {
						// オプション読み込みメソッドが指定されているならコール
						if( strpos($loadRelation,'(') != false && strpos($loadRelation,')') != false ) {
							// メソッド呼び出しプログラム記述
							eval($loadRelation);
						} else if( method_exists($result,$loadRelation) ) {
							$result->$loadRelation($this);
						}
					} 
				}
				return $result;
			} else {
				return false;
			}
		}
	}
	/**
	 * 渡されたSQLクエリ文を実行して1カラム分のみ結果を取得します
	 * @param string $query SQLクエリ
	 * @param string $type 指定した場合は、結果セットのカラムの型が取得したものに設定されます。
	 * @param integer $column
	 * @return 
	 * @access public
	 */
	function queryOne( $query ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_queryOne( $query );
		}
	}
	/**
	 * 問い合わせ結果レコードの行数とオフセットを設定します
	 * @param string $limit
	 * @param string $offset
	 * @return 
	 * @access public
	 */
	function setLimit( $limit, $offset = null ) {
		if( $this->connectionWritable ) {
			$this->connectionWritable->_setLimit( $limit, $offset );
		}
		if( $this->connectionReadOnly ) {
			$this->connectionReadOnly->_setLimit( $limit, $offset );
		}
	}
	/**
	 * 問い合わせ結果レコードの行数とオフセットを全て解除します
	 * @return 
	 * @access public
	 */
	function releaseLimit() {
		if( $this->connectionWritable ) {
			$this->connectionWritable->limit	= null;
			$this->connectionWritable->offset	= null;
		}
		if( $this->connectionReadOnly ) {
			$this->connectionReadOnly->limit	= null;
			$this->connectionReadOnly->offset	= null;
		}
	}
	/**
	 * 問い合わせ結果レコードの行数とオフセットを設定します
	 * @param string $limit
	 * @param string $offset
	 * @return 
	 * @access public
	 */
	function setRowToFields( $row, & $daoBase, $setOrg=true, $setDivided=true ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_setRowToFields( $row, $daoBase, $setOrg, $setDivided );
		}
	}
	/**
	 * ユニークキーから一意のデータをオブジェクトに設定します。
	 * @param object &$daoBase database_AbstractDataオブジェクト
	 * @param mixed $uniqueId スカラー値またはハッシュ
	 * @return boolean 渡されたユニークIDでデータを一意に絞り込んで取得出来たらtrue
	 */
	function loadById( & $daoBase, $uniqueId, $loadRelation=true, $ignoreFieldNames=array(), $setOrg=true, $setDivided=true ) {
		$tableInformationHash	= $this->getTableInformationHash( $daoBase );
		if( false !== $tableInformationHash ) {
			// フィールド一覧を取得できたらユニークキーフィールドを探す
			$this->_setUniqueFieldInfo( $daoBase );
			if( count($daoBase->baseUniqueKeyFiledNames) == 0 ) {
				// ユニークフィールドがない場合はロードできない
				$this->warn('[warning][loadById] '.$daoBase->getTableName().' don\'t have unique fields!');
				return false;
			} else if( is_object($uniqueId) ) {
				// ユニークフィールドのオブジェクト渡しは非対応
				$this->warn('[warning][loadById] '.$daoBase->getTableName().'. uniqueId must be hash or scaler!');
				return false;
			} else {
				$conditionArray	= array();
				if( is_array($uniqueId) ) {
					// ハッシュで渡されていることとする
					foreach( $uniqueId as $key => $value ) {
						if( in_array( $key, $daoBase->baseUniqueKeyFiledNames ) ) {
							$condition	= $key.'='.$this->quote($value);
							array_push($conditionArray,$condition);
						}
					}
				} else {
					// スカラー値なら最初の要素の値として設定
					$condition	= $daoBase->baseUniqueKeyFiledNames[0].'='.$this->quote($uniqueId);
					array_push($conditionArray,$condition);
				}
				// SELECT 対象のフィールドを確認
				$selectTargetStrings	= '*';
				if( is_array($ignoreFieldNames) && count($ignoreFieldNames) > 0 ) {
					$targetFields	= array();
					foreach( $tableInformationHash as $fieldName => $fieldInfo ) {
						if( !in_array($fieldName,$ignoreFieldNames) ) {
							// 無視するフィールド以外なら追加
							array_push($targetFields,$fieldName);
						}
					}
					if( count($targetFields) > 0 ) {
						$selectTargetStrings	= implode(',',$targetFields);
					} else {
						$this->request->addLocaledError('database2.error.load.allignored',SPIDER_LOG_LEVEL_FATAL,array());
						return false;
					}
				}
				$sql	= 'SELECT '.$selectTargetStrings.' FROM '.$daoBase->getTableName().' WHERE '.implode(' AND ',$conditionArray);
				$result	= $this->queryAll( $sql, null, 'hash' );
				if ( $result === false ) {
					$this->request->addLocaledError('database2.error.common',SPIDER_LOG_LEVEL_FATAL,array($this->error_message.':'.$sql));
					return false;
				} else if(count($result)==0){
					$this->warn('[warning][loadById] '.$daoBase->getTableName().' has no data!:'.$sql);
					return false;
				} else if(count($result)>1){
					$this->warn('[warning][loadById] '.$daoBase->getTableName().' has '.count($result).' datas!:'.$sql);
					return false;
				} else {
					$result	= $this->setRowToFields( $result[0], $daoBase, $setOrg, $setDivided );
					if( $result && $loadRelation ) {
						// オプション情報読み込みメソッドがあるならコール
						if( $loadRelation === true ) {
							if( method_exists($daoBase,'loadRelation') ) {
								$daoBase->loadRelation($this);
							} else if( method_exists($daoBase,'_load_optional_data') ) {
								$daoBase->_load_optional_data($this);
							} else if( method_exists($daoBase,'_load_optional_relation') ) {
								$daoBase->_load_optional_relation($this);
							}
						} else if( strlen(trim($loadRelation)) > 0 ) {
							// オプション読み込みメソッドが指定されているならコール
							if( strpos($loadRelation,'(') != false && strpos($loadRelation,')') != false ) {
								// メソッド呼び出しプログラム記述
								eval($loadRelation);
							} else if( method_exists($daoBase,$loadRelation) ) {
								$daoBase->$loadRelation($this);
							}
						} 
					}
					return $result;
				}
			}
		}
		return false;
	}
	/**
	 * プライマリキーから一意のデータをオブジェクトに設定します。
	 * @param object &$daoBase database_AbstractDataオブジェクト
	 * @param number $number スカラー値
	 * @return boolean 渡されたプライマリ番号でデータを一意に絞り込んで取得出来たらtrue
	 */
	function loadByNumber( & $daoBase, $number, $loadRelation=true, $ignoreFieldNames=array(), $setOrg=true, $setDivided=true ) {
		$tableInformationHash	= $this->getTableInformationHash( $daoBase );
		if( false !== $tableInformationHash ) {
			// フィールド一覧を取得できたら自動番号フィールドを探す
			$this->_setUniqueFieldInfo( $daoBase );
			if( is_null($daoBase->baseSerialFieldName) ) {
				// 自動番号フィールドがない場合はロードできない
				$this->warn('[warning][loadByNumber] '.$daoBase->getTableName().' don\'t have serial fields!');
				return false;
			} else if( is_object($number) ) {
				// 自動番号フィールドのオブジェクト渡しは非対応
				$this->warn('[warning][loadByNumber] '.$daoBase->getTableName().'. $number needs to numeric strings!');
				return false;
			} else if( is_array($number) ) {
				// 自動番号フィールドの配列渡しは非対応
				$this->warn('[warning][loadByNumber] '.$daoBase->getTableName().'. $number needs to numeric strings!');
				return false;
			} else if( !is_numeric($number) ) {
				// 自動番号フィールドが数字でない場合は非対応
				$this->warn('[warning][loadByNumber] '.$daoBase->getTableName().'. $number needs to numeric strings!');
				return false;
			} else {
				// SELECT 対象のフィールドを確認
				$selectTargetStrings	= '*';
				if( is_array($ignoreFieldNames) && count($ignoreFieldNames) > 0 ) {
					$targetFields	= array();
					foreach( $tableInformationHash as $fieldName => $fieldInfo ) {
						if( !in_array($fieldName,$ignoreFieldNames) ) {
							// 無視するフィールド以外なら追加
							array_push($targetFields,$fieldName);
						}
					}
					if( count($targetFields) > 0 ) {
						$selectTargetStrings	= implode(',',$targetFields);
					} else {
						$this->request->addLocaledError('database2.error.load.allignored',SPIDER_LOG_LEVEL_FATAL,array());
						return false;
					}
				} 
				$sql	= 'SELECT '.$selectTargetStrings.' FROM '.$daoBase->getTableName().' WHERE '.$daoBase->baseSerialFieldName.'='.$number;
				$result	= $this->queryAll( $sql, null, 'hash' );
				if ( $result === false ) {
					$this->request->addLocaledError('database2.error.common',SPIDER_LOG_LEVEL_FATAL,array($this->error_message.':'.$sql));
					return false;
				} else if(count($result)==0){
					$this->warn('[warning][loadById] '.$daoBase->getTableName().' has no data!:'.$sql);
					return false;
				} else if(count($result)>1){
					$this->warn('[warning][loadById] '.$daoBase->getTableName().' has '.count($result).' datas!:'.$sql);
					return false;
				} else {
					$result	= $this->setRowToFields( $result[0], $daoBase, $setOrg, $setDivided );
					if( $result && $loadRelation ) {
						// オプション情報読み込みメソッドがあるならコール
						if( $loadRelation === true ) {
							if( method_exists($daoBase,'loadRelation') ) {
								$daoBase->loadRelation($this);
							} else if( method_exists($daoBase,'_load_optional_data') ) {
								$daoBase->_load_optional_data($this);
							} else if( method_exists($daoBase,'_load_optional_relation') ) {
								$daoBase->_load_optional_relation($this);
							}
						} else if( strlen(trim($loadRelation)) > 0 ) {
							// オプション読み込みメソッドが指定されているならコール
							if( strpos($loadRelation,'(') != false && strpos($loadRelation,')') != false ) {
								// メソッド呼び出しプログラム記述
								eval($loadRelation);
							} else if( method_exists($daoBase,$loadRelation) ) {
								$daoBase->$loadRelation($this);
							}
						} 
					}
					return $result;
				}
			}
		}
		return false;
	}
	/**
	 * 渡されたデータ抽象オブジェクトのデータをレコードとしてデータベースに追加します
	 * 本メソッドは拡張クラスにて必ずオーバーライドして実装する抽象メソッドとします。
	 * @param object &$daoBase データ抽象オブジェクト
	 * @return boolean 成功したらtrue,失敗したらfalseを返します
	 * @abstract
	 * @access public
	 */
	// 2010-06-01 参照渡し宣言のデフォルト値記述でエラーになる問題を回避：根本的な問題＋現在第2引数に利用がないのでdatabaseパッケージを見直すまで参照渡し記述をやめる
//	function insert( & $daoBase, & $request = null ) {
	function insert( & $daoBase, $request = null ) {
		$tableInformationHash	= $this->getTableInformationHash( $daoBase );
		$insertFieldHash	= array();
		if( false !== $tableInformationHash ) {
			// フィールド一覧を取得できたら
			if( !is_null($request)
				&& 'spider_httprequest' == strtolower(get_class($request))
				&& method_exists($daoBase,'preInsert') ) {
				// Requestオブジェクトが渡されていてpreInsertメソッドがあるなら実行
				if( !$daoBase->preInsert($request) ) {
					return false;
				}
			} else if( method_exists($daoBase,'pre_insert') ) {
				$daoBase->pre_insert($this);
			} 
			foreach ( $tableInformationHash as $fieldName => $fieldInformation ) {
				if( isset($fieldInformation['is_serial']) && $fieldInformation['is_serial'] === true ) {
					// オートナンバーのフィールドはインサートしない
				} else {
					// それ以外のフィールドはインサート対象
					$insertFieldHash[$fieldName]	= $this->quote( $daoBase->$fieldName );
				}
			}
			// INSERT文作成
			if ( count( $insertFieldHash ) > 0 ) {
				$sql	= 'INSERT INTO '.$daoBase->getTableName()
					.'('.implode(',', array_keys($insertFieldHash) ).')'
					.' VALUES ('
					. implode(',', $insertFieldHash).')';
				// 実行
				$result = $this->query( $sql );
				if ( $result === false ) {
					$this->error($this->error_message.':'.$sql);
					return false;
				}
				$this->debug('[EXECUTE INSERT]'.$sql);
				if( !is_null($request)
					&& 'spider_httprequest' == strtolower(get_class($request))
					&& method_exists($daoBase,'postInsert') ) {
					// Requestオブジェクトが渡されていてpostInsertメソッドがあるなら実行
					$daoBase->postInsert($request);
				} else if( method_exists($daoBase,'post_insert') ) {
					return $daoBase->post_insert($this,$result);
				} 
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}
	/**
	 * 渡されたデータ抽象オブジェクトのデータに該当するレコードを更新します
	 * 本メソッドは拡張クラスにて必ずオーバーライドして実装する抽象メソッドとします。
	 * @param object &$daoBase データ抽象オブジェクト
	 * @param array $historyLimitDays 0の場合は履歴登録しない, マイナス値の場合は履歴を登録して消さない, +値は履歴保存日数 
	 * @return boolean 成功したらtrue,失敗したらfalseを返します
	 * @access public
	 */
	// 2010-06-01 参照渡し宣言のデフォルト値記述でエラーになる問題を回避：根本的な問題＋現在第2引数に利用がないのでdatabaseパッケージを見直すまで参照渡し記述をやめる
//	function update( & $daoBase, $historyLimitDays=false, & $request = null  ) {
	function update( & $daoBase, $historyLimitDays=false, $request = null  ) {
		if( true === $historyLimitDays ) {
			$historyLimitDays	= 180;
		}
		$tableInformationHash	= $this->getTableInformationHash( $daoBase );
		$updatedFieldHash		= array();
		$updatedConditionHash	= array();
		if( false !== $tableInformationHash ) {
			// フィールド一覧を取得できたら
			if( !is_null($request)
				&& 'spider_httprequest' == strtolower(get_class($request))
				&& method_exists($daoBase,'preUpdate') ) {
				// Requestオブジェクトが渡されていてpreUpdateメソッドがあるなら実行
				if( !$daoBase->preUpdate($request) ){
					return false;
				}
			} else if( method_exists($daoBase,'pre_update') ) {
				$daoBase->pre_update($this);
			} 
			foreach ( $tableInformationHash as $fieldName => $fieldInformation ) {
				if( isset($fieldInformation['is_serial']) && $fieldInformation['is_serial'] === true ) {
					// オートナンバーのフィールドは更新条件
					$updatedConditionHash[$fieldName]	= $this->quote( $daoBase->$fieldName );
				} else {
					// それ以外のフィールドは値が異なるなら更新対象
					$fieldNameOrg	= $fieldName.'_org';
					if( $daoBase->$fieldNameOrg != $daoBase->$fieldName ) {
						$updatedFieldHash[$fieldName]	= $this->quote( $daoBase->$fieldName );
					}
				}
			}
			// UPDATE文作成
			if ( count($updatedFieldHash) > 0 && count($updatedConditionHash) > 0 ) {
				// 差分があるなら更新処理を行う
				if( false !== $historyLimitDays && is_numeric($historyLimitDays) && $historyLimitDays!=0 ) {
					// 最初に履歴登録が必要なら登録
					$this->history( $daoBase, true, date('Y-m-d H:i:s'), $historyLimitDays );
				}
				// データベース更新実行
				$sql		= 'UPDATE '.$daoBase->getTableName().' SET';
				$counter	= 0;
				foreach( $updatedFieldHash as $fieldName => $value ) {
					if( $counter == 0 ) {
						$sql	.= ' '. $fieldName.'='.$value;
					} else {
						$sql	.= ', '. $fieldName.'='.$value;
					}
					$counter++;
				}
				$sql	.= ' WHERE ';
				$counter	= 0;
				foreach( $updatedConditionHash as $fieldName => $value ) {
					if( $counter == 0 ) {
						$sql	.= ' '. $fieldName.'='.$value;
					} else {
						$sql	.= ' AND '. $fieldName.'='.$value;
					}
					$counter++;
				}
				// 実行
				$result = $this->query( $sql );
				if ( $result === false ) {
					$this->error('[ERROR][update]:'.$sql);
					return false;
				}
				$this->debug('[EXECUTE UPDATE]'.$sql);
				if( !is_null($request)
					&& 'spider_httprequest' == strtolower(get_class($request))
					&& method_exists($daoBase,'postUpdate') ) {
					// Requestオブジェクトが渡されていてpostUpdateメソッドがあるなら実行
					$daoBase->postUpdate($request);
				} else if( method_exists($daoBase,'post_update') ) {
					return $daoBase->post_update($this,$result);
				} 
				return true;
			} else {
				return true;
			}
		}
		return false;
	}
	/**
	 * 渡されたデータ抽象オブジェクトのデータに該当するレコードを削除します
	 * 本メソッドは拡張クラスにて必ずオーバーライドして実装する抽象メソッドとします。
	 * @param object &$daoBase データ抽象オブジェクト
	 * @param array $historyLimitDays 0の場合は履歴登録しない, マイナス値の場合は履歴を登録して消さない, +値は履歴保存日数 
	 * @return boolean 成功したらtrue,失敗したらfalseを返します
	 * @access public
	 */
	// 2010-06-01 参照渡し宣言のデフォルト値記述でエラーになる問題を回避：根本的な問題＋現在第2引数に利用がないのでdatabaseパッケージを見直すまで参照渡し記述をやめる
//	function delete( & $daoBase, $historyLimitDays=false, & $request = null  ) {
	function delete( & $daoBase, $historyLimitDays=false, $request = null  ) {
		if( true === $historyLimitDays ) {
			$historyLimitDays	= 180;
		}
		$tableInformationHash	= $this->getTableInformationHash( $daoBase );
		$deleteConditionHash	= array();
		if( false !== $tableInformationHash ) {
			// フィールド一覧を取得できたら
			if( !is_null($request)
				&& 'spider_httprequest' == strtolower(get_class($request))
				&& method_exists($daoBase,'preDelete') ) {
				// Requestオブジェクトが渡されていてpreDeleteメソッドがあるなら実行
				if( !$daoBase->preDelete($request,$this) ) {
					return false;
				}
			} else if( method_exists($daoBase,'pre_delete') ) {
				$daoBase->pre_delete($this);
			} 
			foreach ( $tableInformationHash as $fieldName => $fieldInformation ) {
				if( isset($fieldInformation['is_serial']) && $fieldInformation['is_serial'] === true ) {
					// オートナンバーのフィールドが削除条件
					$deleteConditionHash[$fieldName]	= $this->quote( $daoBase->$fieldName );
				}
			}
			// DELETE文作成
			if ( count($deleteConditionHash) > 0 ) {
				// 差分があるなら更新処理を行う
				if( false !== $historyLimitDays && is_numeric($historyLimitDays) && $historyLimitDays!=0 ) {
					// 最初に履歴登録が必要なら登録
					$this->history( $daoBase, true, date('Y-m-d H:i:s'), $historyLimitDays );
				}
				// データベース削除実行
				$sql	= 'DELETE FROM '.$daoBase->getTableName();
				$sql	.= ' WHERE ';
				$counter	= 0;
				foreach( $deleteConditionHash as $fieldName => $value ) {
					if( $counter == 0 ) {
						$sql	.= ' '. $fieldName.'='.$value;
					} else {
						$sql	.= ' AND '. $fieldName.'='.$value;
					}
					$counter++;
				}
				// 実行
				$result = $this->query( $sql );
				if ( $result === false ) {
					$this->error($this->error_message.':'.$sql);
					return false;
				}
				$this->debug('[EXECUTE DELETE]'.$sql);
				if( !is_null($request)
					&& 'spider_httprequest' == strtolower(get_class($request))
					&& method_exists($daoBase,'postDelete') ) {
					// Requestオブジェクトが渡されていてpostDeleteメソッドがあるなら実行
					$daoBase->postDelete($request);
				} else if( method_exists($daoBase,'post_delete') ) {
					return $daoBase->post_delete($this,$result);
				} 
				return true;
			} else {
				return false;
			}
		}
		return false;
	}
	/**
	 * 渡されたAbstractDataオブジェクトの情報をデータ履歴テーブルに登録します。
	 * @param $daoBase Daoオブジェクト
	 * @param $historyId 履歴ID,trueにすると自動発行,falseだとIDなしでインサート
	 * @param $historyDatetime 履歴登録日,trueなら現在日時,falseなら日時なしでインサート
	 * @param $historyLimitDays 履歴保存日数。falseなら消さない
	 * @param $fieldSuffix インサートするフィールド名に対するメンバ変数名のフィールドサフィックス。デフォルトは'_org'
	 */
	function history( & $daoBase, $historyId=true, $historyDatetime=true, $historyLimitDays=false, $fieldSuffix='_org' ) {
		$historyTableName		= $daoBase->getTableName().'_history';
		$tableInformationHash	= $this->getTableInformationHash( $daoBase );
		$insertFieldHash	= array();
		if( false !== $tableInformationHash ) {
			if( $this->existsTable( $historyTableName ) ) {
				// フィールド一覧を取得できたら
				foreach ( $tableInformationHash as $fieldName => $fieldInformation ) {
					// 全てのフィールドがインサート対象
					$orgFieldName	= $fieldName.$fieldSuffix;
					if( !is_null($daoBase->$orgFieldName) && strlen($daoBase->$orgFieldName) > 0 ) {
						$insertFieldHash[$fieldName]	= $this->quote( $daoBase->$orgFieldName );
					}
				}
				// 履歴IDの生成
				if( true === $historyId ) {
					// trueなら自動生成
					$historyId	= $this->createUniqueKey( $historyTableName, 'history_id', 'history_number', date('YmdHis').'{num:4}' );
					$insertFieldHash['history_id']	= $this->quote( $historyId );
				} else if( strlen($historyId) > 0 ) {
					// 入力があるなら指定IDで登録
					$insertFieldHash['history_id']	= $this->quote( $historyId );
				} else {
					// それ以外は登録しない
				}
				// 履歴IDをAbstractDataオブジェクトに格納する
				$daoBase->history_id	= $historyId;
				// 履歴登録日
				if( true === $historyDatetime ) {
					// trueの場合は自動で現在日時
					$insertFieldHash['history_date']	= $this->quote( date('Y-m-d H:i:s') );
				} else if( strlen($historyDatetime) > 0
					&& preg_match('/^[0-9]{4}\\-[0-9]{1,2}\\-[0-9]{1,2}\\s[0-9]{1,2}\\:[0-9]{1,2}\\:[0-9]{1,2}$/',$historyDatetime) > 0 ) {
					// Y-m-d H:i:s形式の場合はそのまま登録
					$insertFieldHash['history_date']	= $this->quote( $historyDatetime );
				} else {
					// 日時フォーマットにマッチしない場合は日時を登録しない
				}
				// INSERT文作成
				if ( count( $insertFieldHash ) > 0 ) {
					$sql	= 'INSERT INTO '.$historyTableName
						.'('.implode(',', array_keys($insertFieldHash) ).')'
						.' VALUES ('
						. implode(',', $insertFieldHash).')';
					// 実行
					$result = $this->query( $sql );
					if ( $result === false ) {
						$this->error('[history]['.$daoBase->getTableName().'] fail to regist history! : '.$sql);
						return false;
					}
					$this->debug('[EXECUTE INSERT]'.$sql);
					// 履歴期限日カラムがあるテーブルの場合期限を過ぎた履歴を消去
					if( preg_match('/^[0-9]{1,4}$/',$historyLimitDays) > 0 && $historyLimitDays > 0 ) {
						$sql	= 'DELETE FROM '.$historyTableName
							.' WHERE history_date<'.$this->quote(date('Y-m-d H:i:s',strtotime('-'.$historyLimitDays.' day')));
						$result = $this->query( $sql );
						if ( $result === false ) {
							$this->error('[history]['.$daoBase->getTableName().'] fail to delete old history! : '.$sql);
							return false;
						}
					}
					return true;
				} else {
					return false;
				}
			}
		} else {
			return false;
		}
	}
	/**
	 * 指定のDaoObjectを履歴IDの状態に復元します
	 */
	function recover( & $daoBase, $historyId, $historyPrev=false, $deleteUseHistory=false ) {
		$historyTableName		= $daoBase->getTableName().'_history';
		$tableInformationHash	= $this->getTableInformationHash( $daoBase );
		$insertFieldHash	= array();
		if( false !== $tableInformationHash ) {
			if( $this->existsTable( $historyTableName ) ) {
				// 履歴テーブルが存在するなら履歴を探す
				$keyConditionArray	= array();
				foreach ( $tableInformationHash as $fieldName => $fieldInformation ) {
					if( isset($fieldInformation['is_serial']) && $fieldInformation['is_serial'] === true ) {
						// オートナンバーのフィールドは追加条件
						$str	= $fieldName.'='.$this->quote( $daoBase->$fieldName );
						array_push( $keyConditionArray, $str );
						break;
					}
				}
				$sql	= 'SELECT * FROM '.$historyTableName
					.' WHERE history_id='.$this->quote($historyId)
					.' AND '.implode(' AND ', $keyConditionArray )
				;
				$result	= $this->queryAll($sql,'hash');
				if( false === $result ) {
					return false;
				} else if( count( $result ) != 1 ){
					return false;
				} else {
					foreach ( $tableInformationHash as $fieldName => $fieldInformation ) {
						$daoBase->$fieldName	= $result[0][$fieldName];
					}
					if( $this->update( $daoBase, $historyPrev ) ) {
						if( $deleteUseHistory ) {
							$sql	= 'DELETE FROM '.$historyTableName.' WHERE history_id='.$this->quote($historyId);
							$dbo->query( $sql );
						}
						return true;
					} else {
						return false;
					}
				}
			}
		}
	}
	/**
	 * 指定DAOオブジェクトのユニーク識別子フィールドに次のデータのIDを発行して設定します。
	 * @param $daoBase DAOオブジェクトへの参照
	 * @param $format 連続識別子のフォーマット文字列。連番箇所は{num:桁数}で指定できる。
	 */
	function setNextId( & $daoBase, $format='ID{num:6}' ) {
		if( $this->_setUniqueFieldInfo( $daoBase ) === false ) {
			$this->error('[setNextId]['.$daoBase->getTableName().'] fail to get table information!');
			return false;
		} else {
			// フィールド一覧を取得できたら
			if( is_null($daoBase->baseSerialFieldName) ) {
				$this->error('[setNextId]['.$daoBase->getTableName().'] target table has no serial!');
				return false;
			} else if( count($daoBase->baseUniqueKeyFiledNames) == 0 ) {
				$this->error('[setNextId]['.$daoBase->getTableName().'] target table has no unique key!');
				return false;
			}
			// ユニークフィールドごとに設定されていなければ発行
			foreach( $daoBase->baseUniqueKeyFiledNames as $uniqueName ) {
				if( strlen(trim($daoBase->$uniqueName)) == 0 ) {
					$nextId	= $this->createUniqueKey( $daoBase->getTableName(), $uniqueName, $daoBase->baseSerialFieldName, $format );
					if( false === $nextId ) {
						return false;
					} else {
						$daoBase->$uniqueName	= $nextId;
					}
				}
			}
			return true;
		}
	}
	/**
	 * 指定テーブル指定フィールドの連続識別子の次の値の文字列を指定フォーマットで取得します。
	 * @param $tableName 対象テーブル名
	 * @param $targetFieldName 対象フィールド名
	 * @param $sortFieldName 最後のインサートを抽出する為のDESCソートフィールド名。通常はシリアルフィールド名を渡す
	 * @param $format 連続識別子のフォーマット文字列。連番箇所は{num:桁数}で指定できる。
	 * @return 次の連続識別子文字列
	 */
	function createUniqueKey( $tableName, $targetFieldName, $sortFieldName, $format='IDAA{num:4}' ) {
		// フォーマットのSQL問い合わせ用文字列を発行
		$regx	= $format;
		if( preg_match_all( '/\\{num\\:([0-9]+)\\}/', $regx, $matches, PREG_PATTERN_ORDER ) > 0 ) {
			foreach( $matches[0] as $key => $matchString ) {
				$numLen	= $matches[1][$key];
				$repStr	= '';
				for( $i=0; $i<$numLen;$i++ ) {
					$repStr	.= '_';
				}
				$regx	= str_replace($matchString,$repStr,$regx);
			}
		}
		$sql	= 'SELECT '.$targetFieldName.' FROM '.$tableName
			.' WHERE '.$targetFieldName.' LIKE '.$this->quote($regx)
			.' ORDER BY '.$targetFieldName.' DESC';
		$this->setLimit( 1, 0 );
		$result	= $this->queryAll( $sql );
		if ( $result === false ) {
			// 問い合わせに失敗した場合
			$this->error('[createUniqueKey]['.$tableName.']['.$targetFieldName.']'.$sql);
			return false;
		} else {
			// 最後の行を取得出来たら
			if( count($result) > 0 ) {
				// 行が挿入されている場合は挿入済みの行に合わせる
				$lastId		= trim($result[0][$targetFieldName]);
				$lastIdElms	= util_CharUtility::explodeAN($lastId,1);
				$nextElms	= $lastIdElms;
				for( $i=count($nextElms)-1; $i>=0; $i-- ) {
					$elm	= $nextElms[$i];
					if( preg_match('/[0-9]+/',$elm) > 0 ) {
						// 数字要素ならインクリメント
						$elm++;
						if( strlen($elm) > strlen($nextElms[$i]) ) {
							// 桁数がオーバーフローする場合は1に戻してひとつ前の要素処理
							$elm	= 1;
							$nextElms[$i]	= sprintf('%0'.strlen($nextElms[$i]).'d',$elm);
						} else {
							// 桁がオーバーフローしていない場合は数値をインクリメントしてbreak
							$nextElms[$i]	= sprintf('%0'.strlen($nextElms[$i]).'d',$elm);
							break;
						}
					} else if( preg_match('/[A-Z]/',$elm) > 0 ) {
						// アルファベット大文字の場合
						$charNumArray	= unpack('C*',$elm);
						$charNum		= $charNumArray[0];
						$charNum++;
						if( $charNum > 90 ) {
							// 桁数オーバーフロー
							$nextElms[$i]	= 'A';
						} else {
							// オーバーフローなし
							$nextElms[$i]	= pack('C*',$charNum);
							break;
						}
					}
				}
				return implode('',$nextElms);
			} else {
				// 行がまだ挿入されていない場合は連番初期値で発行
				$nextId	= $format;
				if( preg_match_all( '/\\{num\\:([0-9]+)\\}/', $nextId, $matches, PREG_PATTERN_ORDER ) > 0 ) {
					foreach( $matches[0] as $key => $matchString ) {
						$numLen	= $matches[1][$key];
						$numStr	= sprintf('%0'.$numLen.'d',1);
						$nextId	= str_replace($matchString,$numStr,$nextId);
					}
				}
				return $nextId;
			}
		}
	}
	/**
	 * データアクセスオブジェクトにフィールドキー情報のリファレンスを設定します
	 */
	function _setUniqueFieldInfo( & $daoBase ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_setUniqueFieldInfo( $daoBase );
		}
	}
	/**
	 * 渡されたデータ抽象オブジェクトに該当するテーブル情報ハッシュを取得します。
	 * 【テーブル情報ハッシュの構造】
	 * array( [フィールド名] => [フィールド詳細情報ハッシュ] )
	 * 
	 * 【フィールド詳細情報ハッシュの構造】
	 * array(
	 * 		'type'			=> 'int/char/varchar/text...',
	 * 		'size'			=> 数値
	 * 		'not_null'		=> true/false
	 * 		'key'			=> 'primary/unique'
	 * 		'is_serial'		=> true/false
	 * 		'has_default'	=> true/false
	 * )
	 */
	function getTableInformationHash( $daoBase ) {
		return $this->getTableInformationHashByName( $daoBase->getTableName() );
	}
	/**
	 * テーブル名称を指定して該当するテーブル情報ハッシュを取得します。
	 * 【テーブル情報ハッシュの構造】
	 * array( [フィールド名] => [フィールド詳細情報ハッシュ] )
	 * 
	 * 【フィールド詳細情報ハッシュの構造】
	 * array(
	 * 		'type'			=> 'int/char/varchar/text...',
	 * 		'size'			=> 数値
	 * 		'not_null'		=> true/false
	 * 		'key'			=> 'primary/unique'
	 * 		'is_serial'		=> true/false
	 * 		'has_default'	=> true/false
	 * )
	 */
	function getTableInformationHashByName( $tableName ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->getTableInformationHashByName( $tableName );
		}
	}
	/**
	 * 指定の名称のテーブルが存在するか確認します
	 */
	function existsTable( $tableName ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_existsTable( $tableName );
		}
	}
	/**
	 * 指定の名称のデータベースが存在するか確認します
	 */
	function existsDatabase( $databaseName ) {
		$connection	= $this->_getProperConnection();
		if( $connection === false ) {
			return false;
		} else {
			return $connection->_existsDatabase( $databaseName );
		}
	}
	/**
	 * SQLファイルからSQL文を配列で取得します
	 */
	function getQueryArrayFromFile( $file_path ) {
		if( $str = database2_Connection::getQuerysStringsFromFile( $file_path ) ) {
			return database2_Connection::explodeQuerysStrings( $str );
		}
		return false;
	}
	/**
	 * SQL記述ファイルの内容からコメントを除去して文字列として返します
	 */
	function getQuerysStringsFromFile( $file_path ) {
		if( file_exists($file_path) ) {
			$strings	= file_get_contents( $file_path );
			if( 'UTF-8' != mb_detect_encoding($strings) ) {
				$strings	= mb_convert_encoding( $strings, 'UTF-8', 'auto' );
			}
			$strings	= str_replace("\r\n","\n", $strings );
			$strings	= str_replace("\r","\n", $strings );
			$lines		= explode("\n",$strings);
			$new_lines	= array();
			foreach( $lines as $line ) {
				if( strlen(trim($line)) > 0 ) {
					if( strpos( $line, '--') === false ) {
						array_push($new_lines, $line );
					} else {
						$line	= substr( $line, 0, strpos( $line, '--') );
						if( strlen($line) > 0 ) {
							$line	.= "\n";
							array_push($new_lines, $line );
						}
					}
				}
			}
			return implode( "", $new_lines );
		}
		return false;
	}
	/**
	 * 複数のSQL文がまとまった文字列を個々のSQL文に分割して返します
	 */
	function explodeQuerysStrings( $query_string ) {
		$query_array	= explode( ';', $query_string );
		$new_array		= array();
		foreach( $query_array as $key => $value ) {
			$value	= str_replace("\r\n"," ", $value );
			$value	= str_replace("\r"," ", $value );
			$value	= str_replace("\n"," ", $value );
			$value	= str_replace("\t"," ", $value );
			$value	= preg_replace('/[\\s]+/',' ', $value );
			$value	= trim( $value );
			if( strlen( $value ) > 0 ) {
				array_push( $new_array, $value );
			}
		}
		return $new_array;
	}
	/**
	 * 年月日指定区間の検索条件文字列を作成してリクエストに属性登録します
	 */
	function createTermConditionStrings( & $request, $targetFieldName, $paramPrefix, $attributePrefix ){
		$startYear		= $request->getParam($paramPrefix.'sy','get');
		$startMonth		= $request->getParam($paramPrefix.'sm','get');
		$startDay			= $request->getParam($paramPrefix.'sd','get');
		$limitYear		= $request->getParam($paramPrefix.'ey','get');
		$limitMonth		= $request->getParam($paramPrefix.'em','get');
		$limitDay			= $request->getParam($paramPrefix.'ed','get');
		// 初期化
		$startDate		= '';
		$limitDate		= '';
		// 条件開始日の調整
		if( preg_match('/^(19[789][0-9]|20[0-2][0-9]|203[0-8])$/',$startYear) > 0 ) {
			// 年が入力されていれば月日入力はなくても1/1で調整
			if( preg_match('/^((|0)[1-9]|1[0-2])$/',$startMonth) == 0 ) {
				$startMonth	= '1';
			}
			if( preg_match('/^((|0)[1-9]|[12][0-9]|3[0-1])$/',$startDay) == 0 ) {
				$startDay	= '1';
			}
			$startDate		= $startYear.'-'.$startMonth.'-'.$startDay.' 00:00:00';
			$request->setAttribute($attributePrefix.'StartYear', $startYear );
			$request->setAttribute($attributePrefix.'StartMonth', $startMonth );
			$request->setAttribute($attributePrefix.'StartDay', $startDay );
		}
		// 条件終了日の調整
		if( preg_match('/^(19[789][0-9]|20[0-2][0-9]|203[0-8])$/',$limitYear) > 0 ) {
			// 年が入力されていれば月日入力はなくても1/1で調整
			if( preg_match('/^((|0)[1-9]|1[0-2])$/',$limitMonth) == 0 ) {
				$limitMonth	= '12';
			}
			if( preg_match('/^((|0)[1-9]|[12][0-9]|3[0-1])$/',$limitDay) == 0 ) {
				$limitDay	= date('t',strtotime($limitYear.'-'.$limitMonth.'-01'));
			}
			$limitDate		= $limitYear.'-'.$limitMonth.'-'.$limitDay.' 23:59:59';
			$request->setAttribute($attributePrefix.'LimitYear', $limitYear );
			$request->setAttribute($attributePrefix.'LimitMonth', $limitMonth );
			$request->setAttribute($attributePrefix.'LimitDay', $limitDay );
		}
		$conditionArray	= array();
		if( strlen($startDate) > 0 ) {
			array_push($conditionArray,$targetFieldName.'>='.$this->quote($startDate));
		}
		if( strlen($limitDate) > 0 ) {
			array_push($conditionArray,$targetFieldName.'<='.$this->quote($limitDate));
		}
		if( count($conditionArray) > 0 ) {
			return '('.implode(' AND ',$conditionArray).')';
		} else {
			return '';
		}
	}
	/**
	 * ログを出力します
	 * @param $message ログメッセージ
	 */
	function writeLog( $message, $log_level=SPIDER_LOG_LEVEL_INFO ) {
		if( !is_null($this->request) ) {
			$message	= '[database_Connection]'.$message;
			$this->request->writeLog($message,$log_level);
		}
	}
	/**
	 * Fatalレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function fatal($message){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_FATAL );
	}
	/**
	 * Errorレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function error($message){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_ERROR );
	}
	/**
	 * Warningレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function warn($message){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_WARNING );
	}
	/**
	 * Noticeレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function notice($message){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_NOTICE );
	}
	/**
	 * Infoレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function info($message){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_INFO );
	}
	/**
	 * Debugレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function debug($message){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_DEBUG );
	}
}
?>