<?php
/**
 * AbstractDatabaseManager
 * 番組のクラスはこれをオーバーライドして利用します。一般DB用。
 * $Id: AbstractDatabaseManager.inc,v 1.1 2015/10/08 11:18:56 wanggb Exp $
 * @author iimuro
 * @access public
 * @package jp.aimslib2.database
 */

class AbstractDatabaseManager{
	
	/**
	 * 使用するデータベースの種類。
	 * init時にセットする。
	 * 指定がない場合はPostgres?
	 */
	public $dbtype = null;
	
	/**
	 * 接続する先
	 */
	public $adaptor;
	
	/**
	 * SQL文生成
	 */
	public $builder;
	
	/**
	 * 型変換等データベースの差異があるもの
	 */
	public $util;
	
	
	/**
	 * 接続に必要な情報でクラスを初期化します。
	 * @param String データベース名
	 * @param String データベースユーザー名
	 * @param String 接続パスワード
	 * @param String データベースホスト名。自サーバー内ならlocalhost。
	 * @param String ポート番号
	 * @param String データベース種類。デフォルトは"PostgreSQL"
	 */
	public function init($db_name, $user, $pass, $host, $port, $dbtype = "PostgreSQL"){
		$this->dbtype = $dbtype;
		if (strtolower($dbtype) == "postgresql") {
			// 接続情報をアダプターに設定。以降、接続要求はこれらの値を利用。
			$this->adaptor = new PostgresDatabaseAdaptor();
			$this->adaptor->init($host, $port, $db_name, $user, $pass);
			// SQL文はPostgres用
			$this->builder = new PostgresSQLBuilder();
			// 型、関数もPostgres用→executeQueryを使うときの文章生成に利用。
			$this->util = new PostgresUtil();
			
		} elseif (strtolower($dbtype) == "mysql") {
			// 接続情報をアダプターに設定。以降、接続要求はこれらの値を利用。
			$this->adaptor = new MySQLDatabaseAdaptor();
			$this->adaptor->init($host, $port, $db_name, $user, $pass);
			// SQL文はMySQL用
			$this->builder = new MySQLSQLBuilder();
			// 型、関数もMySQL用→executeQueryを使うときの文章生成に利用。
			//$this->util = new MySQLUtil();
		} else {
			ErrorLogger::doOutput("Unsupported Databse Type!");
		}
		
	}
	
	/**
	 * @abstract
	 */
	public function getConnection(){
		return $this->adaptor->con;
	}
	
	/**
	 * SQL文を実行します。
	 */
	public function executeQuery($sql){
//echo "<br>".$sql;
		return $this->adaptor->db_exec($sql);
	}
	
	
	/**
	 * 
	 */
	public function db_copy_from($table, $data, $delimiter = "\t"){
		return $this->adaptor->db_copy_from($table, $data, $delimiter);
	}
	/**
	 * 
	 */
	public function doDelect($table, $w_param){
		
		$option = "";
		if($w_param != null){
			$option = " WHERE " . $this->builder->createOptionCondition($w_param);
		}
				
		$sql = "DELETE FROM " . $table . $option . ";";

		$this->executeQuery($sql);
	}
	/**
	 * 
	 */
	public function doSelect($table, $w_param, $orderkey = null, $direction = "ASC", $offset = null, $limit = null, $selectkey = "*"){
		
		$option = "";
		if($w_param != null){
			$option = " WHERE " . $this->builder->createOptionCondition($w_param);
		}
		$order = "";
		if($orderkey != null){
			$order .= " ORDER BY " . $orderkey . " " . $direction;
		}
		$subquery = $this->builder->createSubQuery($offset, $limit);
		
		
		$sql = "SELECT ".$selectkey." FROM " . $table . $option . $order . $subquery . ";";
		
		$rowset = $this->executeQuery($sql);
		
		return $rowset;
	}
	
	
	/**
	 *
	 */
	public function doInsert($table, $v_param){
		$sql1 = "INSERT INTO " . $table . " ";
		$sql1 .= $this->builder->createInsertValueOption($v_param);
		
		if (strtolower($this->dbtype) == "postgresql") {
			$sql1 .= " returning id";
		}
		//return $this->executeQuery($sql1);
		
		$tmp = $this->executeQuery($sql1);
		
		
		if (isset($tmp[0]["id"])) {
			return $tmp[0]["id"];
		}
		return $tmp;
	}
	
	
	/**
	 *
	 */
	public function doInsertAndReturn($table, $v_param){
		
		$result = null;
		
		if (strtolower($this->dbtype) == "postgresql") {
			
			$sql1 = "INSERT INTO " . $table . " ";
			$sql1 .= $this->builder->createInsertValueOption($v_param);
			
			$sql1 .= " returning *";
			
			$result = $this->executeQuery($sql1);
			
		} elseif (strtolower($this->dbtype) == "mysql") {
			
			$sql1 = "INSERT INTO " . $table . " ";
			$sql1 .= $this->builder->createInsertValueOption($v_param);
						
			$this->executeQuery($sql1);
			
			$sql2 = "select last_insert_id() as id";
			$tmp = $this->executeQuery($sql2);
			
			if (isset($tmp[0]["id"])) {
				error_log("error: new id is " . $tmp[0]["id"], 0);
				
				$w_param = array();
				$w_param["id"] = $tmp[0]["id"];
				
				$result = $this->doSelect($table, $w_param, null, null, 0, 1);
				 
			} else {
				// エラー？
				error_log("error: id not found at select last_insert_id()", 0);
				return null;
			}
		
		} else {
			ErrorLogger::doOutput("Unsupported Databse Type!");
		}
		
		return $result;
	}
	
	/**
	 * 
	 */
	public function doUpdate($table, $w_param, $v_param){
		$set_param = $this->builder->createSetValueOption($v_param);
		if(count($w_param) > 0){
			$where_option = $this->builder->createOptionCondition($w_param);
			$sql = "UPDATE " . $table . " SET $set_param WHERE $where_option";
		}else{
			$sql = "UPDATE " . $table . " SET $set_param";
		}

		$this->executeQuery($sql);
	}
	
	
	/**
	 * 明示的にトランザクションを管理したいときに利用します。
	 * トランザクション開始。
	 */
	public function doBegin(){
		
		$sql = "BEGIN";
		
		$this->executeQuery($sql);
	}
	
	
	/**
	 * 明示的にトランザクションを管理したいときに利用します。
	 * トランザクション終了。
	 */
	public function doEnd(){
		
		$sql = "COMMIT";
		
		$this->executeQuery($sql);
	}
	
	/**
	 * 明示的にトランザクションを管理したいときに利用します。
	 * トランザクション破棄して終了。
	 */
	public function doRollback(){
		
		$sql = "ROLLBACK";
		
		$this->executeQuery($sql);
	}
	
	
	
	/**
	 * resultset を一括挿入
	 */
	public function db_copy_resultset($table, $column_name, $resultset, $delimiter = "\t"){
		
		$data = array();
		foreach($resultset as $row){
			$line = "";
			
			for($i=0; $i < count($column_name); $i++) {
				
				if(isset($row[$column_name[$i]])){
					$line .= $row[$column_name[$i]];
				}else{
					$line .= "\N";
				}
				if($i < count($column_name) - 1){
					$line .= $delimiter;
				}
			}
			
			$line .= "\n";
			
			array_push($data, $line);
			
		}
		array_push($data, "\\.\n");
		
		return $this->db_copy_from($table, $data, $delimiter);
	}
	
	/**
	 * ラージオブジェクトを保存します。OIDが指定されない場合は新規に作成します。
	 * この処理を行う前にはかならずトランザク ションブロックの中に括る必要があります。
	 * @access public
	 * @param string $data ラージオブジェクトバイナリデータ
	 * @param int $oid ラージオブジェクトOID
	 * @return int ラージオブジェクトOID。失敗時はnull。
	 */
	public function writeLargeObject($data, $oid = null) {
		return $this->adaptor->writeLargeObject($data, $oid);
	}
	
	/**
	 * ラージオブジェクトを読み出します。
	 * この処理を行う前にはかならずトランザク ションブロックの中に括る必要があります。
	 * @access public
	 * @param int $oid ラージオブジェクトOID
	 * @return int ラージオブジェクトバイナリデータ。失敗時はnull。
	 */
	public function readLargeObject($oid) {
		return $this->adaptor->readLargeObject($oid);
	}

	/**
	 * ラージオブジェクトを削除します。
	 * この処理を行う前にはかならずトランザク ションブロックの中に括る必要があります。
	 * @access public
	 * @param int $oid ラージオブジェクトOID
	 * @return boolean 成功時true
	 */
	public function deleteLargeObject($oid = null) {
		return $this->adaptor->deleteLargeObject($oid);
	}

	
	/**
	 * データベースのデータの変換や定型文の生成などのUtilクラスを取得します。
	 */
	public function getUtility() {
		return $this->util;
	}
}
?>