<?php
/**
 * @package SQLite Integration
 * @author Kojima Toshiyasu, Justin Adie
 *
 */

/**
 * This class does the real work
 * accepts a request from wpdb class, initialize PDO instance,
 * execute SQL statement, and returns the results to wpdb class.
 */
class PDOEngine extends PDO {
  public  $is_error = false;
  public  $found_rows_result;
  private $rewritten_query;
  private $query_type;
  private $results = null;
  private $_results = null;
  private $pdo;
  private $prepared_query;
  private $extracted_variables = array();
  private $error_messages = array();
  private $errors;
  public  $queries = array();
  private $last_insert_id;
  private $affected_rows;
  private $column_names;
  private $num_rows;
  private $return_value;
  private $can_insert_multiple_rows = false;
  private $param_num;
  protected $has_active_transaction = false;

  /**
   * Constructor
   * @param array $DatabaseParams
   */
  function __construct() {
    $this->init();
  }
  function __destruct() {
    $this->pdo = null;
    return true;
  }
  
  /**
   * Function to initialize database
   * checks if there's a database directory and database file, creates the tables,
   * and binds the user defined function to the pdo object
   * @return boolean
   */
  private function init() {
    $dsn = 'sqlite:' . FQDB;
    $result = $this->prepare_directory();
    if (!$result) return false;
    if (is_file(FQDB)) {
      $locked = false;
    	do {
      	try {
      		if ($locked) $locked = false;
          $this->pdo = new PDO(
              $dsn,  // data source name
              null,  // user name
              null,  // user password
              array( // PDO options
                  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
                  ));
          $statement = $this->pdo->query("SELECT COUNT(*) FROM sqlite_master WHERE type='table'");
          $number_of_tables = $statement->fetchColumn(0);
          $statement = null;
          if ($number_of_tables == 0) {
            $this->make_sqlite_tables();
          }
        } catch (PDOException $err) {
          $status = $err->getCode();
          // code 5 => The database file is locked
          // code 6 => A table in the database is locked
          if ($status == 5 || $status == 6) {
            $locked = true;
          } else {
            $message = __("Database connection error!<br />", 'sqlite-integration');
            $message .= sprintf(__("Error message is: %s", 'sqlite-integration'), $err->getMessage());
            $this->set_error(__LINE__, __FUNCTION__, $message);
            return false;
          }
        }
      } while ($locked);
      require_once UDF_FILE;
      new PDOSQLiteUDFS($this->pdo);
      if (version_compare($this->get_sqlite_version(), '3.7.11', '>=')) {
        $this->can_insert_multiple_rows = true;
      }
    } else { // database file is not found, so we make it and create tables...
      try {
        $this->pdo = new PDO($dsn, null, null, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
      } catch (PDOException $err) {
        $message = __("Database connection error!<br />", 'sqlite-integration');
        $message .= sprintf(__("Error message is: %s", 'sqlite-integration'), $err->getMessage());
        $this->set_error(__LINE__, __FUNCTION__, $message);
        return false;
      }
      $this->make_sqlite_tables();
    }
  }

  /**
   * Make database direcotry and .htaccess file
   * executed once while installation process
   */
  private function prepare_directory() {
    global $wpdb;
    $u = umask(0000);
    if (!is_dir(FQDBDIR)) {
      if (!@mkdir(FQDBDIR, 0777, true)) {
        umask($u);
        $message = __('Unable to create the required directory! Please check your server settings.', 'sqlite-integration');
        echo $message;
        return false;
      }
    }
    if (!is_writable(FQDBDIR)) {
      umask($u);
      $message = __('Unable to create a file in the directory! Please check your server settings.', 'sqlite-integration');
      echo $message;
      return false;
    }
    if (!is_file(FQDBDIR . '.htaccess')) {
      $fh = fopen(FQDBDIR . '.htaccess', "w");
      if (!$fh) {
        umask($u);
        $message = __('Unable to create a file in the directory! Please check your server settings.', 'sqlite-integration');
        echo $message;
        return false;
      }
      fwrite($fh, "DENY FROM ALL");
      fclose($fh);
    }
    umask($u);
    return true;
  }
  /**
   * Make database file itself and WordPress tables
   * executed once while installation process
   */
  private function make_sqlite_tables() {
    require_once PDODIR . "install.php";
  }
  
  public function query($query) {
    $this->flush();
    
    $this->queries[] = "Raw query:\t$query";
    $res = $this->determine_query_type($query);
    if (!$res) {
      $bailoutString = sprintf(__("<h1>Unknown query type</h1><p>Sorry, we cannot determine the type of query that is requested.</p><p>The query is %s</p>", 'sqlite-integration'), $query);
      $this->set_error(__LINE__, __FUNCTION__, $bailoutString);
    }
    switch (strtolower($this->query_type)) {
      case 'foundrows':
        $this->results = $this->found_rows_result;
        $this->num_rows = count($this->results);
        $this->found_rows_result = null;
        break;
      case 'insert':
        if ($this->can_insert_multiple_rows) {
          $this->execute_insert_query_new($query);
        } else {
          $this->execute_insert_query($query);
        }
        break;
      case 'create':
        $result = $this->execute_create_query($query);
        $this->return_value = $result;
        break;
      case 'alter':
        $result = $this->execute_alter_query($query);
        $this->return_value = $result;
        break;
      case 'show_variables':
        $result = $this->show_variables_workaround($query);
        break;
      case 'drop_index':
      	$pattern = '/^\\s*(DROP\\s*INDEX\\s*.*?)\\s*ON\\s*(.*)/im';
      	if (preg_match($pattern, $query, $match)) {
      		$drop_query = 'ALTER TABLE ' . trim($match[2]) . ' ' . trim($match[1]);
      		$this->query_type = 'alter';
      		$result = $this->execute_alter_query($drop_query);
      		$this->return_value = $result;
      	} else {
      		$this->return_value = false;
      	}
      	break;
      default:
        $engine = $this->prepare_engine($this->query_type);
        $this->rewritten_query = $engine->rewrite_query($query, $this->query_type);
        $this->queries[] = "Rewritten: $this->rewritten_query";
        $this->extract_variables();
        $statement = $this->prepare_query();
        $this->execute_query($statement);
        if (!$this->is_error) {
          $this->process_results($engine);
        } else {// Error
          ;
        }
        break;
    }
    if (defined('PDO_DEBUG') && PDO_DEBUG === true) {
      file_put_contents(FQDBDIR . 'debug.txt', $this->get_debug_info(), FLIE_APPEND);
    }
    return $this->return_value;
  }

  public function get_insert_id() {
    return $this->last_insert_id;
  }
  public function get_affected_rows() {
    return $this->affected_rows;
  }
  public function get_columns() {
    return $this->column_names;
  }
  public function get_query_results() {
    return $this->results;
  }
  public function get_num_rows() {
    return $this->num_rows;
  }
  public function get_return_value() {
    return $this->return_value;
  }

	public function get_error_message(){
		if (count($this->error_messages) === 0){
			$this->is_error = false;
			$this->error_messages = array();
			return '';
		}
		$output = '<div style="clear:both">&nbsp;</div>';
		if ($this->is_error === false){
// 			return $output;
      return '';
		}
		$output .= "<div class=\"queries\" style=\"clear:both; margin_bottom:2px; border: red dotted thin;\">Queries made or created this session were<br/>\r\n\t<ol>\r\n";
		foreach ($this->queries as $q){
			$output .= "\t\t<li>".$q."</li>\r\n";
		}
		$output .= "\t</ol>\r\n</div>";
		foreach ($this->error_messages as $num=>$m){
			$output .= "<div style=\"clear:both; margin_bottom:2px; border: red dotted thin;\" class=\"error_message\" style=\"border-bottom:dotted blue thin;\">Error occurred at line {$this->errors[$num]['line']} in Function {$this->errors[$num]['function']}. <br/> Error message was: $m </div>";
		}
		
		ob_start();
		debug_print_backtrace();
		$output .= "<pre>" . ob_get_contents() . "</pre>";
		ob_end_clean();
		return $output;
	
	}
	
	private function get_debug_info(){
	  $output = '';
	  foreach ($this->queries as $q){
	    $output .= $q ."\r\n";
	  }
	  return $output;
	}
	
	private function flush(){
	  $this->rewritten_query     = '';
	  $this->query_type          = '';
	  $this->results             = null;
	  $this->_results            = null;
	  $this->last_insert_id      = null;
	  $this->affected_rows       = null;
	  $this->column_names        = array();
	  $this->num_rows            = null;
	  $this->return_value        = null;
	  $this->extracted_variables = array();
	  $this->error_messages      = array();
	  $this->is_error            = false;
	  $this->queries             = array();
	  $this->param_num           = 0;
	}
	
	private function prepare_engine($query_type = null) {
	  if (stripos($query_type, 'create') !== false) {
	    require_once PDODIR . 'query_create.class.php';
	    $engine = new CreateQuery();
	  } elseif (stripos($query_type, 'alter') !== false) {
	    require_once PDODIR . 'query_alter.class.php';
	    $engine = new AlterQuery();
	  } else {
	    require_once PDODIR . 'query.class.php';
	    $engine = new PDOSQLiteDriver();
	  }
	  return $engine;
	}
	
  private function prepare_query(){
		$this->queries[] = "Prepare:\t". $this->prepared_query;
		$reason = 0;
		$message = '';
		$statement = null;
		do {
		  try {
  			$statement = $this->pdo->prepare($this->prepared_query);
		  } catch (PDOException $err) {
		    $reason = $err->getCode();
		    $message = $err->getMessage();
		  }
		} while (5 == $reason || 6 == $reason);
		
		if ($reason > 0){
			$err_message = sprintf(__("Problem preparing the PDO SQL Statement.  Error was: %s", 'sqlite-integration'), $message);
	    $this->set_error(__LINE__, __FUNCTION__, $err_message);
		}
		return $statement;
	}

	private function execute_query($statement) {
		$reason = 0;
		$message = '';
		if (!is_object($statement))
		  return;
		if (count($this->extracted_variables) > 0) {
			$this->queries[] = "Executing: ". var_export($this->extracted_variables, true);
			do {
				if ($this->query_type == 'update' || $this->query_type == 'replace') {
					try {
						$this->beginTransaction();
						$statement->execute($this->extracted_variables);
						$this->commit();
					} catch (PDOException $err) {
						$reason = $err->getCode();
						$message = $err->getMessage();
						$this->rollBack();
					}
				} else {
				  try {
	  				$statement->execute($this->extracted_variables);
				  } catch (PDOException $err) {
				    $reason = $err->getCode();
				    $message = $err->getMessage();
				  }
				}
			} while (5 == $reason || 6 == $reason);
		} else {
			$this->queries[] = "Executing: (no parameters)\t ";
			do{
			  if ($this->query_type == 'update' || $this->query_type == 'replace') {
			  	try {
			  		$this->beginTransaction();
			  		$statement->execute();
			  		$this->commit();
			  	} catch (PDOException $err) {
			  		$reason = $err->getCode();
			  		$message = $err->getMessage();
			  		$this->rollBack();
			  	}
			  } else {
					try {
		  				$statement->execute();
				  } catch (PDOException $err) {
				    $reason = $err->getCode();
				    $message = $err->getMessage();
				  }
			  }
			} while (5 == $reason || 6 == $reason);
		}
		if ($reason > 0) {
			$err_message = sprintf(__("Error while executing query! Error message was: %s", 'sqlite-integration'), $message);
			$this->set_error(__LINE__, __FUNCTION__, $err_message);
			return false;
		} else {
			$this->_results = $statement->fetchAll(PDO::FETCH_OBJ);
		}
		//generate the results that $wpdb will want to see
		switch ($this->query_type) {
			case "insert":
			case "update":
			case "replace":
				$this->last_insert_id = $this->pdo->lastInsertId();
				$this->affected_rows = $statement->rowCount();
				$this->return_value = $this->affected_rows;
			  break;
			case "select":
			case "show":
			case "showcolumns":
			case "showindex":
			case "describe":
      case "desc":
//  			case "foundrows":
				$this->num_rows = count($this->_results);
				$this->return_value = $this->num_rows;
			  break;
			case "delete":
				$this->affected_rows = $statement->rowCount();
				$this->return_value = $this->affected_rows;
			  break;
			case "alter":
			case "drop":
			case "create":
			case "optimize":
			case "truncate":
				if ($this->is_error) {
					$this->return_value = false;
				} else {
					$this->return_value = true;
				}
			  break;
		}
	}
	
	private function extract_variables() {
		if ($this->query_type == 'create') {
			$this->prepared_query = $this->rewritten_query;
			return;
		}
		
		//long queries can really kill this
		$pattern = '/(?<!\\\\)([\'"])(.*?)(?<!\\\\)\\1/imsx';
		$_limit = $limit = ini_get('pcre.backtrack_limit');
		do {
			if ($limit > 10000000) {
				$message = __("The query is too big to parse properly", 'sqlite-integration');
				$this->set_error(__LINE__, __FUNCTION__, $message);
				break; //no point in continuing execution, would get into a loop
			} else {
				ini_set('pcre.backtrack_limit', $limit);
				$query = preg_replace_callback($pattern, array($this,'replace_variables_with_placeholders'), $this->rewritten_query);	
			}
			$limit = $limit * 10;
		} while (empty($query));
		
		//reset the pcre.backtrack_limist
		ini_set('pcre.backtrack_limit', $_limit);
		$this->queries[]= "With Placeholders: $query ";
		$this->prepared_query = $query;
	}
	
	private function replace_variables_with_placeholders($matches) {
		//remove the wordpress escaping mechanism
		$param = stripslashes($matches[0]);
		
		//remove trailing spaces
		$param = trim($param); 
		
		//remove the quotes at the end and the beginning
		if (in_array($param{strlen($param)-1}, array("'",'"'))) {
			$param = substr($param,0,-1) ;//end
		}
		if (in_array($param{0}, array("'",'"'))) {
			$param = substr($param, 1); //start
		}
		//$this->extracted_variables[] = $param;
		$key = ':param_'.$this->param_num++;
		$this->extracted_variables[] = $param;
		//return the placeholder
		//return ' ? ';
		return ' '.$key.' ';
	}

	/**
   * takes the query string ,determines the type and returns the type string
   * if the query is the type PDO for Wordpress can't executes, returns false
   * @param string $query
   * @return boolean|string
   */
  private function determine_query_type($query) {
    $result = preg_match('/^\\s*(EXPLAIN|PRAGMA|SELECT\\s*FOUND_ROWS|SELECT|INSERT|UPDATE|REPLACE|DELETE|ALTER|CREATE|DROP\\s*INDEX|DROP|SHOW\\s*\\w+\\s*\\w+\\s*|DESCRIBE|DESC|TRUNCATE|OPTIMIZE)/i', $query, $match);
    
    if (!$result) {
      return false;
    }
    $this->query_type = strtolower($match[1]);
    if (stripos($this->query_type, 'found') !== false) {
      $this->query_type = 'foundrows';
    }
    if (stripos($this->query_type, 'show') !== false) {
      if (stripos($this->query_type, 'show tables') !== false) {
        $this->query_type = 'show';
      } elseif (stripos($this->query_type, 'show columns') !== false || stripos($this->query_type, 'show fields') !== false) {
        $this->query_type = 'showcolumns';
      } elseif (stripos($this->query_type, 'show index') !== false || stripos($this->query_type, 'show indexes') !== false || stripos($this->query_type, 'show keys') !== false) {
        $this->query_type = 'showindex';
      } elseif (stripos($this->query_type, 'show variables') !== false || stripos($this->query_type, 'show global variables') !== false || stripos($this->query_type, 'show session variables') !== false) {
        $this->query_type = 'show_variables';
      } else {
        return false;
      }
    }
    if (stripos($this->query_type, 'drop index') !== false) {
    	$this->query_type = 'drop_index';
    }
    return true;
  }

  /**
   * SQLite version 3.7.11 began support multiple rows insert with values
   * clause. This is for that version or later.
   * @param string $query
   */
  private function execute_insert_query_new($query) {
  	$engine = $this->prepare_engine($this->query_type);
    $this->rewritten_query = $engine->rewrite_query($query, $this->query_type);
    $this->queries[] = "Rewritten: $this->rewritten_query";
    $this->extract_variables();
    $statement = $this->prepare_query();
    $this->execute_query($statement);
  }
  /**
   * executes the INSERT query for SQLite version 3.7.10 or lesser
   * @param string $query
   */
  private function execute_insert_query($query) {
    global $wpdb;
    $multi_insert = false;
    $statement = null;
    $engine = $this->prepare_engine($this->query_type);
    if (preg_match('/(INSERT.*?VALUES\\s*)(\(.*\))/imsx', $query, $matched)) {
      $query_prefix = $matched[1];
      $values_data = $matched[2];
      if (stripos($values_data, 'ON DUPLICATE KEY') !== false) {
        $exploded_parts = $values_data;
      } elseif (stripos($query_prefix, "INSERT INTO $wpdb->comments") !== false) {
        $exploded_parts = $values_data;
      } else {
        $exploded_parts = $this->parse_multiple_inserts($values_data);
      }
      $count = count($exploded_parts);
      if ($count > 1) {
        $multi_insert = true;
      }
    }
    if ($multi_insert) {
      $first = true;
      foreach ($exploded_parts as $value) {
        if (substr($value, -1, 1) === ')') {
          $suffix = '';
        } else {
          $suffix = ')';
        }
        $query_string = $query_prefix . ' ' . $value . $suffix;
        $this->rewritten_query = $engine->rewrite_query($query_string, $this->query_type);
        $this->queries[] = "Rewritten: $this->rewritten_query";
        $this->extracted_variables = array();
        $this->extract_variables();
        if ($first) {
          $statement = $this->prepare_query();
          $this->execute_query($statement);
          $first = false;
        } else {
          $this->execute_query($statement);
        }
      }
    } else {
      $this->rewritten_query = $engine->rewrite_query($query, $this->query_type);
      $this->queries[] = "Rewritten: $this->rewritten_query";
      $this->extract_variables();
      $statement = $this->prepare_query();
      $this->execute_query($statement);
    }
  }

  /**
   * helper function for execute_insert_query()
   * @param string $values
   * @return array
   */
  private function parse_multiple_inserts($values) {
    $tokens = preg_split("/(''|(?<!\\\\)'|(?<!\()\),(?=\s*\())/s", $values, -1, PREG_SPLIT_DELIM_CAPTURE);
    $exploded_parts = array();
    $part = '';
    $literal = false;
    foreach ($tokens as $token) {
      switch ($token) {
        case "),":
          if (!$literal) {
            $exploded_parts[] = $part;
            $part = '';
          } else {
            $part .= $token;
          }
          break;
        case "'":
          if ($literal) {
            $literal = false;
          } else {
            $literal = true;
          }
          $part .= $token;
          break;
        default:
          $part .= $token;
          break;
      }
    }
    if (!empty($part)) {
      $exploded_parts[] = $part;
    }
    return $exploded_parts;
  }
  
  /**
   * function to execute CREATE query
   * @param string
   * @return boolean
   */
  private function execute_create_query($query) {
    $engine = $this->prepare_engine($this->query_type);
    $rewritten_query = $engine->rewrite_query($query);
    $reason = 0;
    $message = '';
//     $queries = explode(";", $this->rewritten_query);
    try {
      $this->beginTransaction();
      foreach ($rewritten_query as $single_query) {
        $this->queries[] = "Executing:\t" . $single_query;
        $single_query = trim($single_query);
        if (empty($single_query)) continue;
          $this->pdo->exec($single_query);
      }
      $this->commit();
    } catch (PDOException $err) {
      $reason = $err->getCode();
      $message = $err->getMessage();
      if (5 == $reason || 6 == $reason) {
        $this->commit();
      } else {
        $this->rollBack();
      }
    }
    if ($reason > 0) {
      $err_message = sprintf(__("Problem in creating table or index. Error was: %s", 'sqlite-integration'), $message);
      $this->set_error(__LINE__, __FUNCTION__, $err_message);
      return false;
    }
    return true;
  }

  /**
   * function to execute ALTER TABLE query
   * @param string
   * @return boolean
   */
  private function execute_alter_query($query) {
    $engine = $this->prepare_engine($this->query_type);
    $reason = 0;
    $message = '';
    $rewritten_query = $engine->rewrite_query($query, $this->query_type);
    try {
      $this->beginTransaction();
      if (is_array($rewritten_query)) {
        foreach ($rewritten_query as $single_query) {
          $this->queries[] = "Executing:\t" . $single_query;
          $single_query = trim($single_query);
          if (empty($single_query)) continue;
          $this->pdo->exec($single_query);
        }
      } else {
        $this->queries[] = "Executing:\t" . $rewritten_query;
        $rewritten_query = trim($rewritten_query);
        $this->pdo->exec($rewritten_query);
      }
      $this->commit();
    } catch (PDOException $err) {
      $reason = $err->getCode();
      $message = $err->getMessage();
      if (5 == $reason || 6 == $reason) {
        $this->commit();
        usleep(10000);
      } else {
        $this->rollBack();
      }
    }
    if ($reason > 0) {
      $err_message = sprintf(__("Problem in executing alter query. Error was: %s", 'sqlite-integration'), $message);
      $this->set_error(__LINE__, __FUNCTION__, $err_message);
      return false;
    }
    return true;
  }
 
   /**
   * function to execute SHOW VARIABLES query
   * 
   * This query is meaningless for SQLite. This function returns null data and
   * avoid the error message.
   * 
   * @param string
   * @return ObjectArray
   */
  private function show_variables_workaround($query) {
    $dummy_data = array('Variable_name' => '', 'Value' => null);
    $pattern = '/SHOW\\s*VARIABLES\\s*LIKE\\s*(.*)?$/im';
    if (preg_match($pattern, $query, $match)) {
      $value = str_replace("'", '', $match[1]);
      $dummy_data['Variable_name'] = trim($value);
      // this is set for Wordfence Security Plugin
      if ($value == 'max_allowed_packet') $dummy_data['Value'] = 1047552;
    }
    $_results[] = new ObjectArray($dummy_data);
    $this->results = $_results;
    $this->num_rows = count($this->results);
    $this->return_value = $this->num_rows;
    return true;
  }
  
  private function process_results($engine) {
    if (in_array($this->query_type, array('describe', 'desc', 'showcolumns'))) {
      $this->convert_to_columns_object();
    } elseif ('showindex' === $this->query_type){
      $this->convert_to_index_object();
    } else {
      $this->results = $this->_results;
    }
  }

  private function set_error ($line, $function, $message){
    global $wpdb;
    $this->errors[] = array("line"=>$line, "function"=>$function);
    $this->error_messages[] = $message;
    $this->is_error = true;
    if ($wpdb->suppress_errors) return false;
    if (!$wpdb->show_errors) return false;
    file_put_contents (FQDBDIR .'debug.txt', "Line $line, Function: $function, Message: $message \n", FILE_APPEND);
  }
  
  /**
   *	method that takes the associative array of query results and creates a numeric array of anonymous objects
   */
  private function convert_to_object(){
    $_results = array();
    if (count ($this->results) === 0){
      echo $this->get_error_message();
    } else {
      foreach($this->results as $row){
        $_results[] = new ObjectArray($row);
      }
    }
    $this->results = $_results;
  }
  
  /**
   * method to rewrite pragma results to mysql compatible array
   * when query_type is describe, we use sqlite pragma function.
   * see pdo_sqlite_driver.php
   */
  private function convert_to_columns_object() {
    $_results = array();
    $_columns = array( //Field names MySQL SHOW COLUMNS returns
        'Field'   => "",
        'Type'    => "",
        'Null'    => "",
        'Key'     => "",
        'Default' => "",
        'Extra'   => ""
    );
    if (count($this->_results) === 0) {
      echo $this->get_error_message();
    } else {
      foreach ($this->_results as $row) {
        $_columns['Field']   = $row->name;
        $_columns['Type']    = $row->type;
        $_columns['Null']    = $row->notnull ? "NO" : "YES";
        $_columns['Key']     = $row->pk ? "PRI" : "";
        $_columns['Default'] = $row->dflt_value;
        $_results[] = new ObjectArray($_columns);
      }
    }
    $this->results = $_results;
  }
  /**
   * rewrites the result of SHOW INDEX to the Object compatible with MySQL
   */
  private function convert_to_index_object() {
    $_results = array();
    $_columns = array(
        'Table'        => "",
        'Non_unique'   => "",// unique -> 0, not unique -> 1
        'Key_name'     => "",// the name of the index
        'Seq_in_index' => "",// column sequence number in the index. begins at 1
        'Column_name'  => "",
        'Collation'    => "",//A(scend) or NULL
        'Cardinality'  => "",
        'Sub_part'     => "",// set to NULL
        'Packed'       => "",// How to pack key or else NULL
        'Null'         => "",// If column contains null, YES. If not, NO.
        'Index_type'   => "",// BTREE, FULLTEXT, HASH, RTREE
        'Comment'      => ""
    );
    if (count($this->_results) == 0) {
      echo $this->get_error_message();
    } else {
      foreach ($this->_results as $row) {
        if ($row->type == 'table' && !stripos($row->sql, 'primary'))
          continue;
        if ($row->type == 'index' && stripos($row->name, 'sqlite_autoindex') !== false)
          continue;
        switch ($row->type) {
          case 'table':
            $pattern1 = '/^\\s*PRIMARY.*\((.*)\)/im';
            $pattern2 = '/^\\s*(\\w+)?\\s*.*PRIMARY.*(?!\()/im';
            if (preg_match($pattern1, $row->sql, $match)) {
              $col_name = trim($match[1]);
              $_columns['Key_name']    = 'PRIMARY';
              $_columns['Non_unique']  = 0;
              $_columns['Column_name'] = $col_name;
            } elseif (preg_match($pattern2, $row->sql, $match)) {
              $col_name = trim($match[1]);
              $_columns['Key_name']    = 'PRIMARY';
              $_columns['Non_unique']  = 0;
              $_columns['Column_name'] = $col_name;
            }
            break;
          case 'index':
            if (stripos($row->sql, 'unique') !== false) {
              $_columns['Non_unique'] = 0;
            } else {
              $_columns['Non_unique'] = 1;
            }
            if (preg_match('/^.*\((.*)\)/i', $row->sql, $match)) {
              $col_name = str_replace("'", '', $match[1]);
              $_columns['Column_name'] = trim($col_name);
            }
            $_columns['Key_name'] = $row->name;
            break;
          default:
            break;
        }
        $_columns['Table']       = $row->tbl_name;
        $_columns['Collation']   = NULL;
        $_columns['Cardinality'] = 0;
        $_columns['Sub_part']    = NULL;
        $_columns['Packed']      = NULL;
        $_columns['Null']        = 'NO';
        $_columns['Index_type']  = 'BTREE';
        $_columns['Comment']     = '';
        $_results[] = new ObjectArray($_columns);
      }
    }
    $this->results = $_results;
  }

  /**
   * function to get SQLite library version
   * this is used for checking if SQLite can execute multiple rows insert
   * @return version number string or 0
   */
  private function get_sqlite_version() {
    try {
      $statement = $this->pdo->prepare('SELECT sqlite_version()');
      $statement->execute();
      $result = $statement->fetch(PDO::FETCH_NUM);
      return $result[0];
    } catch (PDOException $err) {
      return '0';
    }
  }
  /**
   * function call to PDO::beginTransaction()
   * @see PDO::beginTransaction()
   */
  public function beginTransaction() {
  	if ($this->has_active_transaction) {
  		return false;
  	} else {
  		$this->has_active_transaction = $this->pdo->beginTransaction();
  		return $this->has_active_transaction;
  	}
  }
  /**
   * function call to PDO::commit()
   * @see PDO::commit()
   */
  public function commit() {
  	$this->pdo->commit();
  	$this->has_active_transaction = false;
  }
  /**
   * function call to PDO::rollBack()
   * @see PDO::rollBack()
   */
  public function rollBack() {
  	$this->pdo->rollBack();
  	$this->has_active_transaction = false;
  }
}

class ObjectArray {
  function __construct($data = null,&$node= null) {
    foreach ($data as $key => $value) {
      if ( is_array($value) ) {
        if (!$node) {
          $node =& $this;
        }
        $node->$key = new stdClass();
        self::__construct($value,$node->$key);
      } else {
        if (!$node) {
          $node =& $this;
        }
        $node->$key = $value;
      }
    }
  }
}
?>