<?php
/*
 * $Id: database.inc.php,v 1.3 2004/08/31 03:20:57 youka Exp $
 */


/**
 * DB管理クラス。
 * 
 * WikiIDごとにシングルトン。
 * また、sqlite関数のラッパー。失敗したときに例外を投げる。
 */
class DataBase
{
	protected $link;	//DBへのリンク
	protected $transaction = 0;	//トランザクションのネスト数
	
	
	/**
	 * インスタンスを取得する。IDごとにシングルトン。
	 */
	static function getinstance($id = WIKIID)
	{
		static $ins = array();
		
		if(!isset($ins[$id])){
			$ins[$id] = new DataBase($id);
		}
		return $ins[$id];
	}
	
	
	/**
	 * コンストラクタ。
	 */
	protected function __construct($id)
	{
		$file = DATA_DIR . $id . '.db';
		$this->link = sqlite_open($file, 0666, $error);
		if($this->link == false){
			throw new MyException("DBファイル(${id}.db)を開けませんでした。\n$error");
		}
	}
	
	
	/**
	 * デストラクタ。
	 */
	function __destruct()
	{
		if($this->transaction > 0){
			$this->query("ROLLBACK");
		}
		if($this->link != false){
			sqlite_close($this->link);
		}
	}
	
	
	/**
	 * クエリを実行する。
	 * 
	 * @param	string	$query	SQL文。
	 * @return	resource	sqlite_query()の返値。失敗した場合は例外を投げる。
	 */
	function query($query)
	{
		$result = sqlite_unbuffered_query($this->link, $query);
		if($result == false){
			if(mb_strlen($query) > 83){
				$str = mb_substr($query, 0, 80) . '...';
			}
			else{
				$str = $query;
			}
			throw new DBException("クエリを実行できませんでした（${str}）。", $this->link);
		}
		return $result;
	}
	
	
	/**
	 * 結果セットからレコードを取得する。
	 * 
	 * @param	resource	$result	query()の返値。
	 * @return	mixed	レコードデータを含む連想配列を返す。レコードが無い場合はfalseを返す。
	 */
	function fetch($result)
	{
		$ret = sqlite_fetch_array($result);
		if(get_magic_quotes_runtime()){
			return array_map('stripslashes', $ret);
		}
		return $ret;
	}
	
	
	/**
	 * クエリパラメータ用に文字列をエスケープする。
	 * 
	 * @param	string	$str	エスケープしたい文字列。
	 * @return	string	エスケープした文字列。
	 */
	function escape($str)
	{
		//空文字列をsqlite_escape_string()に渡すと謎の3バイトが帰ってくる。
		return $str == '' ? '' : sqlite_escape_string($str);
	}
	
	
	/**
	 * 直前のクエリにより変更されたレコード数を返す。
	 * 
	 * @return	int
	 */
	function changes()
	{
		return sqlite_changes($this->link);
	}
	
	
	/**
	 * "BEGIN TRANSACTION"を発行する。
	 */
	function begin()
	{
		if($this->transaction == 0){
			$this->query("BEGIN TRANSACTION");
		}
		$this->transaction++;
	}
	
	
	/**
	 * "COMMIT"を発行する。
	 */
	function commit()
	{
		$this->transaction--;
		if($this->transaction == 0){
			$this->query("COMMIT");
		}
	}
	
	
	/**
	 * そのテーブルが存在するかを確認する。
	 * 
	 * @param	string	$table	テーブル名
	 */
	function istable($table)
	{
		$_table = $this->escape($table);
		$query = "SELECT name FROM (SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table') WHERE name = '$_table'";
		return $this->fetch($this->query($query)) != false;
	}
	
	
	/**
	 * ユーザ関数を登録する（sqlite_create_function()ラッパー）。
	 */
	function create_function($function_name, $callback, $num_args = null)
	{
		if($num_args === null){
			return sqlite_create_function($this->link, $function_name, $callback);
		}
		else{
			return sqlite_create_function($this->link, $function_name, $callback, $num_args);
		}
	}
	
	
	/**
	 * 集約UDFを登録する（sqlite_create_aggregate()ラッパー）。
	 */
	function create_aggregate($function_name, $step_func, $finalize_func, $num_args = null)
	{
		if($num_args === null){
			return sqlite_create_aggregate($this->link, $function_name, $step_func, $finalize_func);
		}
		else{
			return sqlite_create_aggregate($this->link, $function_name, $step_func, $finalize_func, $num_args);
		}
	}
}


?>