CodeIgniterの学習 26 - sqlite3 (PDO経由) を CodeIgniterで半ば強引に使ってみる(実験)

今日は CodeIgniterで、PDOを使ってsqlite3を使ってみることにした。

他のかたが挑戦しているのも前に見かけた気がしたけど、取りあえず俺の作業ログも書いておく。



(2009/11/19追記)
CodeIgniterのフォーラムで、SQLite3 + CI 1.7.2の別の実装を見つけた。
スレッド: http://codeigniter.com/forums/viewthread/134706/

このスレッド内に書かれてるけど、
http://blog.trevorbramble.com/past/2009/9/20/codeigniter_sqlite3/
で投稿主さんのソースがダウンロード出来る。
俺はちょっと眺めただけだけど、こちらの投稿の方も参考にするといいかも。
(2009/11/19追記終わり)

目的

メインのデータベースとして使う気はさらさら無いのだが、

  • デバグ用ログをsqlite3のファイルDBで保存したりとか、
  • csv形式のデータエクスポートみたいに、sqlite形式でのデータエクスポートをしたりとか、(で他のバッチとかプログラムに噛ませたりとか)、
  • 認証済みユーザー毎に専用のテンポラリDB(仮想DB)として使ったりとか、
  • 別プログラムのsqlite3形式のDBをアップロードしてデータを読み込んで何か処理させたりとか

そういった付随用途で使うと面白いかもと思ったので、取りあえず使えるようにしてみる事にした。


元ネタを魔改造してみる

元ネタは、http://codeigniter.com/wiki/PDO_SQLite3/ です。

この元ネタは、system/database/drivers/sqlite/sqlite_driver.phpとかをベースにPDO化したソースなのだけれど、

ちょっとwiki上の記述が散漫になっているのと、
貼られた時期のソースと、現在の最新版のsqlite_driver.phpのソースとの構成に差違が結構あったので、
例によって整理+一部俺俺改造してみた。



なお、いつもの事ながら無保証です。
余興程度に思ってください。(そもそもあまりPDOもsqlite3もサワリ位しか知らないし。)

メインのDBで使ったり、複雑な使い方をするのは、全然オススメ出来ません。

(インスタンスをガバっとシリアライズしようとしても、PDOは「cannot serialize or unserialize PDO instances」とかエラーが出てシリアライズ出来ないし。クエリ結果のfield_dataがとれないので強引にエラーにならないようにしているし。)

まあ俺の目的程度での使い道ならあるかも。


使い方、設置方法

基本は、元ネタ http://codeigniter.com/wiki/PDO_SQLite3/ を参照のこと。

1:新規作成ファイル

  • ./system/database/drivers/pdo/pdo_driver.php
  • ./system/database/drivers/pdo/pdo_result.php

を作る。(作成先はapplication以下ではない、pdoディレクトリは新設。)

改造ソースの中身は下に貼っておく。

(元ネタ+整理+version1.6.3の sqlite_driver.php sqlite_result.php とマージ + 微妙に改造です。中身の解説は面倒なのでdiffでも取ってソースを見てね。)


2:新規作成ディレクト

DBの保存先用に、./application/data/sqlite3/ というディレクトリを作った。
この中に temp.db とか md5("ユーザーid+SALT").db とか作って保存するつもり。

ウチのディレクトリ構成は、http://d.hatena.ne.jp/dix3/20080919/1221815250 みたいにしているんで、

http経由で直接覗かれることはないけど、そうじゃない場合は.htaccess等で

RewriteEngine On
RewriteBase /
RewriteRule \.db$ - [F]
#RewriteRule \.db$ - [R=404]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php|images|img|css|js|xajax_js|tmp|robots\.txt|.+\.gif$|.+\.jpg$|.+\.png$|.+\.js$|.+\.css$)
RewriteRule ^(.*)$ /index.php/$1 [L]

とでもして、拡張子で禁止しておけばいいと思う。


3:DBとテーブルを準備する

本来の目的は上記のような事なのだが、
動作確認用にとりあえずデフォルトDBとして使ってみることにした。

ちょうどdb_session用のテーブルと、タスクリストの適当テーブルが有るので、
これを作ってみる。

シェル上で

cd ./application/data/sqlite3/
sqlite3 temp.db

と起動して、

CREATE TABLE IF NOT EXISTS  `ci_sessions` (
session_id TEXT DEFAULT 0 NOT NULL PRIMARY KEY ,
ip_address TEXT DEFAULT 0 NOT NULL,
user_agent TEXT NOT NULL,
last_activity INTEGER  DEFAULT 0 NOT NULL,
session_data text default '' NOT NULL
);

CREATE TABLE IF NOT EXISTS tasklist (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
user_name VARCHAR(128) NULL,
title VARCHAR(255) NULL,
note TEXT NULL,
target_date DATETIME NULL,
limit_date DATETIME NULL,
del_flg INTEGER  NOT NULL DEFAULT 0,
created DATETIME NULL ,
modified DATETIME NULL
);

を貼り付けてテーブルを作ってみる。

sqliteで準備されている型以外は勝手にTEXT型になってしまうが、気にしない。


作られたテーブルの中身はこんな感じ

sqlite> pragma table_info(ci_sessions);
0|session_id|TEXT|99|0|1
1|ip_address|TEXT|99|0|0
2|user_agent|TEXT|99||0
3|last_activity|INTEGER|99|0|0
4|session_data|text|99|''|0

sqlite> pragma table_info(tasklist);
0|id|INTEGER|99||1
1|user_name|VARCHAR(128)|0||0
2|title|VARCHAR(255)|0||0
3|note|TEXT|0||0
4|target_date|DATETIME|0||0
5|limit_date|DATETIME|0||0
6|del_flg|INTEGER|99|0|0
7|created|DATETIME|0||0
8|modified|DATETIME|0||0

sqlite>.quit

最後にパーミッションを変えておく

chown apache:apache temp.db


4)application/config/database.phpを変更し、デフォルトDBを切り替えておく

application/config/database.php に以下を変更、追記

<?php
//上略
//$active_group = "default"
$active_group = "temp";//defaultをtempに変更
$active_record = TRUE;

//中略
$db['default']['dbcollat'] = "utf8_general_ci";

//tempという名前で呼べるようにした。
$db['temp']['hostname'] = "";
$db['temp']['username'] = "";
$db['temp']['password'] = "";
$db['temp']['database'] = "sqlite:" .APPPATH ."data/sqlite3/temp.db";
$db['temp']['dbdriver'] = "pdo";
$db['temp']['dbprefix'] = "";
$db['temp']['pconnect'] = FALSE;
$db['temp']['db_debug'] = TRUE;
$db['temp']['cache_on'] = FALSE;
$db['temp']['cachedir'] = "";
$db['temp']['char_set'] = "utf8";
$db['temp']['dbcollat'] = "utf8_general_ci";

//下略
 ?>


ちなみにデフォルトDBを変えなくても、ソース内(コントローラとか)で

<?php
$this->db2hoge = $this->load->database('temp', TRUE); 
$query = $this->db2hoge -> get( 'tasklist' );

var_dump($query->result());
exit;
 ?>

みたいに $a = $this->load->database('temp', TRUE);
ってやるとどこでも別DBを呼べる。

var_dump($query->result());


array
0 =>
object(stdClass)[27]
public 'id' => string '1' (length=1)

public 'user_name' => string '' (length=0)
public 'title' => string 'sqlite3を強引に使えるようにしてみた' (length=49)

public 'note' => string '何となく動くみたいだね。' (length=36)
public 'target_date' => string '' (length=0)

public 'limit_date' => string 'でも型はゆるゆる' (length=24)
public 'del_flg' => string '0' (length=1)

public 'created' => string '' (length=0)
public 'modified' => string '' (length=0)

1 =>
object(stdClass)[28]
public 'id' => string '2' (length=1)

public 'user_name' => string '' (length=0)
public 'title' => string 'ああああああ' (length=18)

public 'note' => string 'たのしいな' (length=15)
public 'target_date' => string 'ふがふが' (length=12)

public 'limit_date' => string '' (length=0)
public 'del_flg' => string '0' (length=1)

public 'created' => string '' (length=0)
public 'modified' => string '' (length=0)



ソース

sqlite3 PDO対応改変ソース2つはこんな感じ、長いけど貼っておく。

1)./system/database/drivers/pdo/pdo_driver.php

Wiki( http://codeigniter.com/wiki/PDO_SQLite3/ )上の情報を元にソースを集約し、
上記を元にCodeigniter 1.6.3の sqlite_driver.phpの書き方、並びに寄せるようにした。
ついでにsqlite_driver内のみに存在していたメソッド(trans_begin trans_rollback trans_commitとか)を追加。
他にlist_fieldsを取得出来るようにとかしている。

メソッドの出現順もsqlite_driver.phpと見比べられるように合わせている。


(09/08/14暫定追記)
pdo_driver.php内のロールバック部分については、コメント欄のmemoletさんの修正内容を参考にしてください。sqliteってトランザクション掛けた方が確か速いんだったっけ?
(暫定追記終わり)

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Code Igniter
 * 
 * An open source application development framework for PHP 4.3.2 or newer
 * 
 * @package CodeIgniter
 * @author Rick Ellis 
 * @copyright Copyright (c) 2006, pMachine, Inc.
 * @license http://www.codeignitor.com/user_guide/license.html
 * @link http://www.codeigniter.com
 * @since Version 1.0
 * @filesource 
 */
// ------------------------------------------------------------------------
/**
 * PDO Database Adapter Class
 * 
 * Note: _DB is an extender class that the app controller
 * creates dynamically based on whether the active record
 * class is being used or not.
 * 
 * @package CodeIgniter
 * @subpackage Drivers
 * @category Database
 * @author Dready 
 * @link http://dready.jexiste.fr/dotclear/
 */

/**
 * modified : dix3 at 08/10/17
 * sqlite3を使えるようにする実験俺俺改造 なんとなく動くけど詳細未テスト
 * Wiki( http://codeigniter.com/wiki/PDO_SQLite3/ )上の情報を元にソースを集約し、 
 * 上記を元にCodeigniter 1.6.3の sqlite_driver.phpの書き方、並びに寄せるようにした。
 * ついでにsqlite_driver内のみに存在していたメソッド(trans_begin trans_rollback trans_commitとか)を追加。
 * 他にlist_fieldsを取得出来るようにとかしている。
 * @link http://d.hatena.ne.jp/dix3/20081019/1224405470
 */

class CI_DB_pdo_driver extends CI_DB {
	var $class_version = '0.1';

	/**
	 * The syntax to count rows is slightly different across different
	 * database engines, so this string appears in each driver and is
	 * used for the count_all() and count_all_results() functions.
	 */
	var $_count_string = "SELECT COUNT(*) AS ";
	var $_random_keyword = ' Random()'; // database specific random keyword
	/**
	 * Non-persistent database connection
	 * 
	 * @access private called by the base class
	 * @return resource 
	 */
	function db_connect()
	{
		$conn_id = FALSE;


		try {
			$conn_id = new PDO ( $this -> database, $this -> username, $this -> password );

			log_message( 'debug', 'connecting ' . $this -> database );
		}
		catch ( PDOException $e ) {
			log_message( 'debug', 'merde' );
			log_message( 'error', $e -> getMessage() );

			if ( $this -> db_debug ) {
				$this -> display_error( $e -> getMessage(), '', TRUE );
			}
		}

		log_message( 'debug', print_r( $conn_id, TRUE ) );

		if ( $conn_id ) {
			log_message( 'debug', 'connection ok' );
		}

		return $conn_id;
	} 
	// --------------------------------------------------------------------
	/**
	 * Persistent database connection
	 * 
	 * @access private called by the base class
	 * @return resource 
	 */
	function db_pconnect()
	{
		try {
			$conn_id = new PDO ( $this -> database, $this -> username, $this -> password, array( PDO :: ATTR_PERSISTENT => TRUE ) );


		}
		catch ( PDOException $e ) {
			log_message( 'error', $e -> getMessage() );

			if ( $this -> db_debug ) {
				$this -> display_error( $e -> getMessage(), '', TRUE );
			}

			return FALSE;
		}

		return $conn_id;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Select the database
	 * 
	 * @access private called by the base class
	 * @return resource 
	 */
	function db_select()
	{
		return TRUE;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Set client character set
	 * 
	 * @access public 
	 * @param string $ 
	 * @param string $ 
	 * @return resource 
	 */
	function db_set_charset( $charset, $collation )
	{ 
		// TODO - add support if needed
		return TRUE;
	} 
	// --------------------------------------------------------------------
	/**
	 * Version number query string
	 * 
	 * @access public 
	 * @return string 
	 */
	function _version()
	{
		return $this -> conn_id -> getAttribute( constant( "PDO::ATTR_SERVER_VERSION" ) );
	} 
	// --------------------------------------------------------------------
	/**
	 * Execute the query
	 * 
	 * @access private called by the base class
	 * @param string $ an SQL query
	 * @return resource 
	 */
	function _execute( $sql )
	{
		$sql = $this -> _prep_query( $sql );
		log_message( 'debug', 'SQL : ' . $sql );
		return @$this -> conn_id -> query( $sql ); // Do we really need to use error supression here? :(
	} 
	// --------------------------------------------------------------------
	/**
	 * Prep the query
	 * 
	 * If needed, each database adapter can prep the query string
	 * 
	 * @access private called by execute()
	 * @param string $ an SQL query
	 * @return string 
	 */
	function _prep_query( $sql )
	{
		return $sql;
	} 
	// --------------------------------------------------------------------
	/**
	 * Begin Transaction
	 * 
	 * @access public 
	 * @return bool 
	 */
	function trans_begin( $test_mode = FALSE )
	{
		if ( ! $this -> trans_enabled ) {
			return TRUE;
		} 
		// When transactions are nested we only begin/commit/rollback the outermost ones
		if ( $this -> _trans_depth > 0 ) {
			return TRUE;
		} 
		// Reset the transaction failure flag.
		// If the $test_mode flag is set to TRUE transactions will be rolled back
		// even if the queries produce a successful result.
		$this -> _trans_failure = ( $test_mode === TRUE ) ? TRUE : FALSE;

		if($this -> conn_id){
			$this -> conn_id -> beginTransaction();
		}

		return TRUE;
	} 
	// --------------------------------------------------------------------
	/**
	 * Commit Transaction
	 * 
	 * @access public 
	 * @return bool 
	 */
	function trans_commit()
	{
		if ( ! $this -> trans_enabled ) {
			return TRUE;
		} 
		// When transactions are nested we only begin/commit/rollback the outermost ones
		if ( $this -> _trans_depth > 0 ) {
			return TRUE;
		}
		if($this -> conn_id){
			$this -> conn_id -> commit();
		}
		return TRUE;
	} 
	// --------------------------------------------------------------------
	/**
	 * Rollback Transaction
	 * 
	 * @access public 
	 * @return bool 
	 */
	function trans_rollback()
	{
		if ( ! $this -> trans_enabled ) {
			return TRUE;
		} 
		// When transactions are nested we only begin/commit/rollback the outermost ones
		if ( $this -> _trans_depth > 0 ) {
			return TRUE;
		}

		if($this -> conn_id){
			$this -> conn_id -> rollback();
		}
		return TRUE;
	} 
	// --------------------------------------------------------------------
	/**
	 * Escape String
	 * 
	 * @access public 
	 * @param string $ 
	 * @return string 
	 */
	function escape_str( $str )
	{
		if ( get_magic_quotes_gpc() ) {
			$str = stripslashes( $str );
		}

		return $this -> conn_id -> quote( $str );
	} 
	// --------------------------------------------------------------------
	/**
	 * Affected Rows
	 * 
	 * @access public 
	 * @return integer 
	 */
	function affected_rows()
	{ 
		// todo:なんか返す
		return 0;
	} 
	// --------------------------------------------------------------------
	/**
	 * Insert ID
	 * 
	 * @access public 
	 * @return integer 
	 */
	function insert_id()
	{
		return @$this -> conn_id -> lastInsertId();
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * "Count All" query
	 * 
	 * Generates a platform-specific query string that counts all records in
	 * the specified database
	 * 
	 * @access public 
	 * @param string $ 
	 * @return string 
	 */
	function count_all( $table = '' )
	{
		if ( $table == '' )
			return '0';

		$query = $this -> query( $this -> _count_string . $this -> _protect_identifiers( 'numrows' ) . " FROM " . $this -> _protect_identifiers( $this -> dbprefix . $table ) );

		if ( $query -> num_rows() == 0 )
			return '0';

		$row = $query -> row();
		return $row -> numrows;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * List table query
	 * 
	 * Generates a platform-specific query string so that the table names can be fetched
	 * 
	 * @access private 
	 * @param boolean $ 
	 * @return string 
	 */
	function _list_tables( $prefix_limit = FALSE )
	{
		$sql = "SELECT name from sqlite_master WHERE type='table'";

		if ( $prefix_limit !== FALSE AND $this -> dbprefix != '' ) {
			$sql .= " AND 'name' LIKE '" . $this -> dbprefix . "%'";
		}
		return $sql;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Show column query
	 * 
	 * Generates a platform-specific query string so that the column names can be fetched
	 * 
	 * @access public 
	 * @param string $ the table name
	 * @return string 
	 */
	function _list_columns( $table = '' )
	{ 
		// Not supported
		return FALSE;
	} 
	// --------------------------------------------------------------------
	/**
	 * Field data query
	 * 
	 * Generates a platform-specific query so that the column data can be retrieved
	 * 
	 * @access public 
	 * @param string $ the table name
	 * @return object 
	 */
	function _field_data( $table )
	{
		return 'PRAGMA table_info(\'' . $this->_escape_table($table) . '\')';
	} 
	// --------------------------------------------------------------------
	/**
	 * The error message string
	 * 
	 * @access private 
	 * @return string 
	 */
	function _error_message()
	{
		$infos = $this -> conn_id -> errorInfo();

		return $infos[2];
	} 
	// --------------------------------------------------------------------
	/**
	 * The error message number
	 * 
	 * @access private 
	 * @return integer 
	 */
	function _error_number()
	{
		$infos = $this -> conn_id -> errorInfo();

		return $infos[1];
	} 
	// --------------------------------------------------------------------
	/**
	 * Escape Table Name
	 * 
	 * This function adds backticks if the table name has a period
	 * in it. Some DBs will get cranky unless periods are escaped
	 * 
	 * @access private 
	 * @param string $ the table name
	 * @return string 
	 */
	function _escape_table( $table )
	{
		if ( stristr( $table, '.' ) ) {
			$table = preg_replace( "/\./", '`.`', $table );
		}

		return $table;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Protect Identifiers
	 * 
	 * This function adds backticks if appropriate based on db type
	 * 
	 * @access private 
	 * @param mixed $ the item to escape
	 * @param boolean $ only affect the first word
	 * @return mixed the item with backticks
	 */
	function _protect_identifiers( $item, $first_word_only = FALSE )
	{
		if ( is_array( $item ) ) {
			$escaped_array = array();

			foreach( $item as $k => $v ) {
				$escaped_array[$this -> _protect_identifiers( $k )] = $this -> _protect_identifiers( $v, $first_word_only );
			}

			return $escaped_array;
		} 
		// This function may get "item1 item2" as a string, and so
		// we may need "item1 item2" and not "item1 item2"
		if ( ctype_alnum( $item ) === FALSE ) {
			if ( strpos( $item, '.' ) !== FALSE ) {
				$aliased_tables = implode( ".", $this -> ar_aliased_tables ) . '.';
				$table_name = substr( $item, 0, strpos( $item, '.' ) + 1 );
				$item = ( strpos( $aliased_tables, $table_name ) !== FALSE ) ? $item = $item : $this -> dbprefix . $item;
			} 
			// This function may get "field >= 1", and need it to return "field >= 1"
			$lbound = ( $first_word_only === TRUE ) ? '' : '|\s|\(';
			//$item = preg_replace( '/(^' . $lbound . ')([\w\d\-\_]+?)(\s|\)|$)/iS', '$1$2$3', $item ); 
			$item = preg_replace( '/(^' . $lbound . ')([\w\d\-\_]+?)(\s|\)|$)/iuS', '$1$2$3', $item );
		}else {
			return "{$item}";
		}

		$exceptions = array( 'AS', '/', '-', '%', '+', '*', 'OR', 'IS' );

		foreach ( $exceptions as $exception ) {
			if ( stristr( $item, " {$exception} " ) !== FALSE ) {
				// $item = preg_replace( '/ (' . preg_quote( $exception ) . ') /i', ' $1 ', $item );
				$item = preg_replace( '/ (' . preg_quote( $exception ) . ') /iu', ' $1 ', $item );
			}
		}
		return $item;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * From Tables
	 * 
	 * This function implicitly groups FROM tables so there is no confusion
	 * about operator precedence in harmony with SQL standards
	 * 
	 * @access public 
	 * @param type $ 
	 * @return type 
	 */
	function _from_tables( $tables )
	{
		if ( ! is_array( $tables ) ) {
			$tables = array( $tables );
		}

		return '(' . implode( ', ', $tables ) . ')';
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Insert statement
	 * 
	 * Generates a platform-specific insert string from the supplied data
	 * 
	 * @access public 
	 * @param string $ the table name
	 * @param array $ the insert keys
	 * @param array $ the insert values
	 * @return string 
	 */
	function _insert( $table, $keys, $values )
	{
		return "INSERT INTO " . $this -> _escape_table( $table ) . " (" . implode( ', ', $keys ) . ") VALUES (" . implode( ', ', $values ) . ")";
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Update statement
	 * 
	 * Generates a platform-specific update string from the supplied data
	 * 
	 * @access public 
	 * @param string $ the table name
	 * @param array $ the update data
	 * @param array $ the where clause
	 * @param array $ the orderby clause
	 * @param array $ the limit clause
	 * @return string 
	 */
	function _update( $table, $values, $where, $orderby = array(), $limit = FALSE )
	{
		foreach( $values as $key => $val ) {
			$valstr[] = $key . " = " . $val;
		}

		$limit = ( ! $limit ) ? '' : ' LIMIT ' . $limit;

		$orderby = ( count( $orderby ) >= 1 )?' ORDER BY ' . implode( ", ", $orderby ):'';

		$sql = "UPDATE " . $this -> _escape_table( $table ) . " SET " . implode( ', ', $valstr );
		$sql .= ( $where != '' AND count( $where ) >= 1 ) ? " WHERE " . implode( " ", $where ) : '';
		$sql .= $orderby . $limit;

		return $sql;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Truncate statement
	 * 
	 * Generates a platform-specific truncate string from the supplied data
	 * If the database does not support the truncate() command
	 * This function maps to "DELETE FROM table"
	 * 
	 * @access public 
	 * @param string $ the table name
	 * @return string 
	 */
	function _truncate( $table )
	{
		return $this -> _delete( $table );
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Delete statement
	 * 
	 * Generates a platform-specific delete string from the supplied data
	 * 
	 * @access public 
	 * @param string $ the table name
	 * @param array $ the where clause
	 * @param string $ the limit clause
	 * @return string 
	 */
	function _delete( $table, $where = array(), $like = array(), $limit = FALSE )
	{
		$conditions = '';

		if ( count( $where ) > 0 OR count( $like ) > 0 ) {
			$conditions = "\nWHERE ";
			$conditions .= implode( "\n", $this -> ar_where );

			if ( count( $where ) > 0 && count( $like ) > 0 ) {
				$conditions .= " AND ";
			}
			$conditions .= implode( "\n", $like );
		}

		$limit = ( ! $limit ) ? '' : ' LIMIT ' . $limit;

		return "DELETE FROM " . $table . $conditions . $limit;
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Limit string
	 * 
	 * Generates a platform-specific LIMIT clause
	 * 
	 * @access public 
	 * @param string $ the sql query string
	 * @param integer $ the number of rows to limit the query to
	 * @param integer $ the offset value
	 * @return string 
	 */
	function _limit( $sql, $limit, $offset )
	{
		if ( $offset == 0 ) {
			$offset = '';
		}else {
			$offset .= ", ";
		}

		return $sql . "LIMIT " . $offset . $limit;
	} 
	// --------------------------------------------------------------------
	/**
	 * Close DB Connection
	 * 
	 * @access public 
	 * @param resource $ 
	 * @return void 
	 */
	function _close( $conn_id )
	{ 
		// Do nothing since PDO don't have close
	} 
	// --------------------------------------------------------------------
	/**
	 * COPY FROM sqlite_driver.php
	 * Rename a table
	 * 
	 * Generates a platform-specific query so that a table can be renamed
	 * 
	 * @access private 
	 * @param string $ the old table name
	 * @param string $ the new table name
	 * @return string 
	 */
	function _rename_table( $table_name, $new_table_name )
	{
		$sql = 'ALTER TABLE ' . $this -> db -> _protect_identifiers( $table_name ) . " RENAME TO " . $this -> db -> _protect_identifiers( $new_table_name );
		return $sql;
	} 
	// --------------------------------------------------------------------
	/**
	 * "Smart" Escape String
	 * 
	 * Escapes data based on type
	 * Sets boolean and null types
	 * 
	 * @access public 
	 * @param string $ 
	 * @return integer 
	 */
	function escape( $str )
	{
		switch ( gettype( $str ) ) {
			case 'string':
				$str = $this -> escape_str( $str );
				break;
			case 'boolean':
				$str = ( $str === FALSE ) ? 0 : 1;
				break;
			default:
				$str = ( $str === null ) ? 'NULL' : $str;
				break;
		}

		return $str;
	} 
	// --------------------------------------------------------------------
	/**
	 * Show table query
	 * 
	 * Generates a platform-specific query string so that the table names can be fetched
	 * 
	 * @access private 
	 * @return string 
	 */
	function _show_tables()
	{
		return "SELECT name from sqlite_master WHERE type='table'";
	} 
	// --------------------------------------------------------------------
	/**
	 * Show columnn query
	 * 
	 * Generates a platform-specific query string so that the column names can be fetched
	 * 
	 * @access private 
	 * @param string $ the table name
	 * @return string 
	 */
	function _show_columns( $table = '' )
	{ 
		// Not supported
		return FALSE;
	} 
	// --------------------------------------------------------------------
	/**
	 * Close DB Connection
	 * 
	 * @access public 
	 * @param resource $ 
	 * @return void 
	 */
	function destroy( $conn_id )
	{
		$conn_id = null;
	}

	//list_fieldsをクエリを投げて取得
	function list_fields($table)
	{
	
		$result_id = $this->_execute($this->_field_data($table));
		$field_names = array();
		foreach($result_id -> fetchAll( PDO :: FETCH_ASSOC ) as $v){
			$field_names[] = $v['name'];
		}
		return $field_names;

		
	}


} //endofclass
/**
 * End of file pdo_driver.php
 */
/**
 * Location: ./system/database/drivers/pdo/pdo_driver.php
 */
 ?>



2)./system/database/drivers/pdo/pdo_result.php

こちらの変更点も1)のpdo_driver.phpと同様。

あと、
テーブルの field_data ( $db->field_data('sometablename'); ) はとれるが、
クエリ結果の field_data ( $query->field_data(); ) はとれないので強引に決めうちで作るようにした。
これでとりあえずscaffoldingの編集画面は落ちなくなる。

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * PDO Result Class
 * 
 * This class extends the parent result class: CI_DB_result
 * 
 * @category Database
 * @author Dready 
 * @link http://dready.jexiste.fr/dotclear/
 */

/**
 * modified : dix3 at 08/10/17
 * sqlite3を使えるようにする実験俺俺改造 なんとなく動くけど詳細未テスト
 * Wiki( http://codeigniter.com/wiki/PDO_SQLite3/ )上の情報を元にソースを集約し、 
 * 上記を元にCodeigniter 1.6.3の sqlite_result.php の書き方、並びに寄せるようにした。
 * 他にlist_fieldsを取得出来るようにとかしている。
 *
 * テーブルの field_data ( $db->field_data('sometablename'); ) はとれるが、
 * クエリ結果の field_data ( $query->field_data(); ) はとれないので強引に決めうちで作るようにした。
 * だからこの結果をそのまま使うのはよろしくないが、とりあえずscaffoldingの編集画面は落ちなくなる。
 * @link http://d.hatena.ne.jp/dix3/20081019/1224405470
 */

class CI_DB_pdo_result extends CI_DB_result {
	var $pdo_results = '';
	var $pdo_index = 0;

	/**
	 * Number of rows in the result set
	 * 
	 * pfff... that's ugly !!!!!!!
	 * 
	 * PHP manual for PDO tell us about nom_rows :
	 * "For most databases, PDOStatement::rowCount() does not return the number of rows affected by
	 * a SELECT statement. Instead, use PDO::query() to issue a SELECT COUNT(*) statement with the
	 * same predicates as your intended SELECT statement, then use PDOStatement::fetchColumn() to
	 * retrieve the number of rows that will be returned.
	 * 
	 * which means
	 * 1/ select count(*) as c from table where $where
	 * => numrows
	 * 2/ select * from table where $where
	 * => treatment
	 * 
	 * Holy cow !
	 * 
	 * @access public 
	 * @return integer 
	 */
	function num_rows()
	{
		if ( !$this -> pdo_results ) {
			$this -> pdo_results = $this -> result_id -> fetchAll( PDO :: FETCH_ASSOC );
		}

		return sizeof( $this -> pdo_results );
	}
	
	// --------------------------------------------------------------------

	/**
	 * Number of fields in the result set
	 * 
	 * @access public 
	 * @return integer 
	 */
	function num_fields()
	{
		if ( is_array( $this -> pdo_results ) ) {
			return sizeof( $this -> pdo_results[$this -> pdo_index] );
		}else {
			return $this -> result_id -> columnCount();
		}
	}


	// --------------------------------------------------------------------

	/**
	 * Fetch Field Names
	 *
	 * Generates an array of column names
	 *
	 * @access	public
	 * @return	array
	 */
	function list_fields()
	{
		$field_names = array();
		if(isset($this -> pdo_results[0])){
			$table_info = $this -> pdo_results[0];
			$field_names = array_keys($table_info);
		}
		return $field_names;
	}


	//COPY FROM  sqlite_result.php 
	// Deprecated
	function field_names()
	{
		return $this->list_fields();
	}

	
	// --------------------------------------------------------------------
	/**
	 * Field data
	 * 
	 * Generates an array of objects containing field meta-data
	 * 
	 * @access public 
	 * @return array 
	 */
	function field_data()
	{
		//テーブルのfield_dataはとれるが、
		//クエリ結果のfield_dataはとれないので強引に決めうちで作るようにした
		//だからこの結果をそのまま使うのはよろしくないが、とりあえずscaffoldingの編集画面は落ちなくなる

		//とれない情報はダミーを突っ込んでいる
		$retval = array();
		$table_info = $this -> pdo_results;

		if(isset($table_info[0]['name'])){
			//テーブルのfield_dataはとれる
			for ( $i = 0; $i < count( $this -> pdo_results ); $i++ ) {
				$F = new stdClass();
				$F->name =  $table_info[$i]['name'] ;
				$F->type = strtolower($table_info[$i]['type']) ;
				$F->default =  $table_info[$i]['dflt_value']  ;
				$F->maxlength = 0;
				$F->primary_key = $table_info[$i]['pk']  ;
				//$F->notnull= $table_info[$i]['notnull'];
				$retval[] = $F;
			}
		}else{
			//クエリ結果のfield_dataはとれないので強引に決めうちで作るようにした
			//だからこの結果をそのまま使うのはよろしくないが、とりあえずscaffoldingの編集画面は落ちなくなる
			//var_dump($this -> pdo_results);exit;
			foreach($this -> pdo_results as $row){
				foreach($row as $k => $v){
					$F = new stdClass();
					$F->name =  $k ;
					$F->type = 'text' ;//text決め打ち
					$F->default = NULL ;  //NULL決め打ち
					$F->maxlength = 0;//0決め打ち
					$F->primary_key = ("id" == $k ) ? "1" : "0" ; //id列がプライマリキーであること決め打ち
					$retval[] = $F;
				}
			}
		}
		return $retval;
	}

	// --------------------------------------------------------------------
	/**
	 * COPY FROM  sqlite_result.php 
	 * Free the result
	 * 
	 * @return null 
	 */
	function free_result()
	{ 
		// Not implemented in SQLite
	}

	// --------------------------------------------------------------------

	/**
	 * Data Seek
	 *
	 * Moves the internal pointer to the desired offset.  We call
	 * this internally before fetching results to make sure the
	 * result set starts at zero
	 *
	 * @access	private
	 * @return	array
	 */
	function _data_seek($n = 0)
	{
		//todo:どうする
		//return sqlite_seek($this->result_id, $n);
	}

	// --------------------------------------------------------------------
	
	/**
	 * Result - associative array
	 * 
	 * Returns the result set as an array
	 * 
	 * @access private 
	 * @return array 
	 */
	function _fetch_assoc()
	{
		if ( is_array( $this -> pdo_results ) ) {
			$i = $this -> pdo_index;
			$this -> pdo_index++;

			if ( isset( $this -> pdo_results[$i] ) ) {
				return $this -> pdo_results[$i];
			}

			return null;
		}

		return $this -> result_id -> fetch( PDO :: FETCH_ASSOC );
	}

	/**
	 * Result - object
	 * 
	 * Returns the result set as an object
	 * 
	 * @access private 
	 * @return object 
	 */
	function _fetch_object()
	{
		if ( is_array( $this -> pdo_results ) ) {
			$i = $this -> pdo_index;
			$this -> pdo_index++;

			if ( isset( $this -> pdo_results[$i] ) ) {
				$back = '';

				foreach ( $this -> pdo_results[$i] as $key => $val ) {
					$back -> $key = $val;
				}

				return $back;
			}

			return null;
		}

		return $this -> result_id -> fetch( PDO :: FETCH_OBJ );
	}
} //endofclass
/**
 * End of file pdo_result.php
 */
/**
 * Location: ./system/database/drivers/pdo/pdo_result.php
 */
 ?>


動作確認

デフォルトDBを切り替えただけで、他のソースは全く変えていないのでコントローラとかは今日は貼らない。


ただ、pdoを使うと
$this -> db_session -> set_flashdata( "tasklist_edit_form", serialize( $this -> validation ) );
みたいにシリアライズしてごにょごにょすることは出来ない。php組み込みのオブジェクトはだめとのこと。



データは、scaffoldingで追加した。



ふむ、動いているようだ。


sqlite3のtemp.dbの中身も見てみる。

一応OKみたい。



dix3はsqlite3をサブDBとしてつかうほうほうをてにいれた!



(08/11/12 記述内 datas → dataに綴り誤りを修正)