2013-07-07 06:49:38 +00:00
< ? php
/**
2014-01-29 09:35:06 +00:00
* This file defines PDOEngine class .
*
2013-07-07 06:49:38 +00:00
* @ package SQLite Integration
2014-01-29 09:35:06 +00:00
* @ author Kojima Toshiyasu
2013-07-07 06:49:38 +00:00
*
*/
/**
2014-01-29 09:35:06 +00:00
* This class extends PDO class and does the real work .
*
* It accepts a request from wpdb class , initialize PDO instance ,
* execute SQL statement , and returns the results to WordPress .
2013-07-07 06:49:38 +00:00
*/
class PDOEngine extends PDO {
2014-01-29 09:35:06 +00:00
/**
* Class variable to check if there is an error .
*
* @ var boolean
*/
2013-07-07 06:49:38 +00:00
public $is_error = false ;
2014-01-29 09:35:06 +00:00
/**
* Class variable which is used for CALC_FOUND_ROW query .
*
* @ var unsigned integer
*/
public $found_rows_result = null ;
/**
* Class variable to store the rewritten queries .
*
* @ var array
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $rewritten_query ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to have what kind of query to execute .
*
* @ var string
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $query_type ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the result of the query .
*
* @ var reference to the PHP object
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $results = null ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the results of the query .
*
* This is for the backward compatibility .
*
* @ var reference to the PHP object
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $_results = null ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to reference to the PDO instance .
*
* @ var PDO object
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $pdo ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the query string prepared to execute .
*
* @ var string | array
*/
2013-07-07 06:49:38 +00:00
private $prepared_query ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the values in the query string .
*
* @ var array
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $extracted_variables = array ();
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the error messages .
*
* @ var array
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $error_messages = array ();
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the file name and function to cause error .
*
* @ var array
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $errors ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the query strings .
*
* @ var array
*/
2013-07-07 06:49:38 +00:00
public $queries = array ();
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the affected row id .
*
* @ var unsigned integer
* @ access private
*/
2013-07-07 06:49:38 +00:00
private $last_insert_id ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the number of rows affected .
*
* @ var unsigned integer
*/
2013-07-07 06:49:38 +00:00
private $affected_rows ;
2014-01-29 09:35:06 +00:00
/**
* Class variable to store the queried column names .
*
* @ var array
*/
2013-07-07 06:49:38 +00:00
private $column_names ;
private $num_rows ;
private $return_value ;
private $can_insert_multiple_rows = false ;
private $param_num ;
2014-01-29 09:35:06 +00:00
/**
* Varible to check if there is an active transaction .
* @ var boolean
*/
2013-07-07 06:49:38 +00:00
protected $has_active_transaction = false ;
/**
* Constructor
2014-01-29 09:35:06 +00:00
*
* @ param none
2013-07-07 06:49:38 +00:00
*/
function __construct () {
$this -> init ();
}
2014-01-29 09:35:06 +00:00
/**
* Destructor
*
* @ return boolean
*/
2013-07-07 06:49:38 +00:00
function __destruct () {
$this -> pdo = null ;
return true ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to initialize database .
*
* It checks if there ' s a database directory and database file , creates the tables ,
* and binds the user defined function to the pdo object .
*
2013-07-07 06:49:38 +00:00
* @ 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 {
2013-09-25 07:40:16 +00:00
$message = 'Database connection error!<br />' ;
$message .= sprintf ( " Error message is: %s " , $err -> getMessage ());
2013-07-07 06:49:38 +00:00
$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 ) {
2013-09-25 07:40:16 +00:00
$message = 'Database initialization error!<br />' ;
$message .= sprintf ( " Error message is: %s " , $err -> getMessage ());
2013-07-07 06:49:38 +00:00
$this -> set_error ( __LINE__ , __FUNCTION__ , $message );
return false ;
}
$this -> make_sqlite_tables ();
}
}
/**
2014-01-29 09:35:06 +00:00
* This function makes database direcotry and . htaccess file .
*
* It is executed only once when the installation begins .
2013-07-07 06:49:38 +00:00
*/
private function prepare_directory () {
global $wpdb ;
$u = umask ( 0000 );
if ( ! is_dir ( FQDBDIR )) {
2013-09-15 21:04:53 +00:00
if ( !@ mkdir ( FQDBDIR , 0707 , true )) {
2013-07-07 06:49:38 +00:00
umask ( $u );
2013-08-13 00:34:40 +00:00
$message = 'Unable to create the required directory! Please check your server settings.' ;
2013-07-07 06:49:38 +00:00
echo $message ;
return false ;
}
}
if ( ! is_writable ( FQDBDIR )) {
umask ( $u );
2013-08-13 00:34:40 +00:00
$message = 'Unable to create a file in the directory! Please check your server settings.' ;
2013-07-07 06:49:38 +00:00
echo $message ;
return false ;
}
if ( ! is_file ( FQDBDIR . '.htaccess' )) {
$fh = fopen ( FQDBDIR . '.htaccess' , " w " );
if ( ! $fh ) {
umask ( $u );
2013-08-13 00:34:40 +00:00
$message = 'Unable to create a file in the directory! Please check your server settings.' ;
2013-07-07 06:49:38 +00:00
echo $message ;
return false ;
}
2013-09-25 07:40:16 +00:00
fwrite ( $fh , 'DENY FROM ALL' );
2013-07-07 06:49:38 +00:00
fclose ( $fh );
}
umask ( $u );
return true ;
}
/**
2014-01-29 09:35:06 +00:00
* function to call install () function which overrides WordPress install () .
*
* This function is executed only once during the installation process .
2013-07-07 06:49:38 +00:00
*/
private function make_sqlite_tables () {
2013-09-25 07:40:16 +00:00
require_once PDODIR . 'install.php' ;
2013-07-07 06:49:38 +00:00
}
2014-01-29 09:35:06 +00:00
/**
* Function to execute query () .
*
* @ param $query full SQL statement string
* @ return mixed according to the query type
* @ see PDO :: query ()
*/
2013-07-07 06:49:38 +00:00
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' :
2014-01-29 09:35:06 +00:00
$_column = array ( 'FOUND_ROWS()' => '' );
$column = array ();
if ( ! is_null ( $this -> found_rows_result )) {
$this -> num_rows = count ( $this -> found_rows_result );
foreach ( $this -> found_rows_result [ 0 ] as $key => $value ) {
$_column [ 'FOUND_ROWS()' ] = $value ;
}
$column [] = new ObjectArray ( $_column );
$this -> results = $column ;
$this -> found_rows_result = null ;
}
2013-07-07 06:49:38 +00:00
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 ;
2013-07-24 02:31:01 +00:00
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 ;
2013-07-07 06:49:38 +00:00
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 ) {
2014-01-29 09:35:06 +00:00
file_put_contents ( FQDBDIR . 'debug.txt' , $this -> get_debug_info (), FILE_APPEND );
2013-07-07 06:49:38 +00:00
}
return $this -> return_value ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return inserted row id .
*
* @ return unsigned integer
*/
2013-07-07 06:49:38 +00:00
public function get_insert_id () {
return $this -> last_insert_id ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return the number of rows affected .
*
* @ return unsigned integer
*/
2013-07-07 06:49:38 +00:00
public function get_affected_rows () {
return $this -> affected_rows ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return the queried column names .
*
* @ return string
*/
2013-07-07 06:49:38 +00:00
public function get_columns () {
return $this -> column_names ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return the queried result data .
*
* @ return mixed
*/
2013-07-07 06:49:38 +00:00
public function get_query_results () {
return $this -> results ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return the number of rows from the queried result .
*
* @ return unsigned integer
*/
2013-07-07 06:49:38 +00:00
public function get_num_rows () {
return $this -> num_rows ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return the queried results according to the query types .
*
* @ return mixed
*/
2013-07-07 06:49:38 +00:00
public function get_return_value () {
return $this -> return_value ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return error messages .
*
* @ return string
*/
2013-07-07 06:49:38 +00:00
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"> </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 ();
2013-09-25 07:40:16 +00:00
$output .= '<pre>' . ob_get_contents () . '</pre>' ;
2013-07-07 06:49:38 +00:00
ob_end_clean ();
return $output ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to return information about query string for debugging .
*
* @ return string
*/
2013-07-07 06:49:38 +00:00
private function get_debug_info (){
$output = '' ;
foreach ( $this -> queries as $q ){
2014-01-29 09:35:06 +00:00
$output .= $q . " \n " ;
2013-07-07 06:49:38 +00:00
}
return $output ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to clear previous data .
*/
2013-07-07 06:49:38 +00:00
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 ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to include the apropreate class files .
*
* It is not a good habit to change the include files programatically .
* Needs to be fixed some other way .
*
* @ param string $query_type
* @ return object reference to apropreate driver
*/
2013-07-07 06:49:38 +00:00
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 ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to create a PDO statement object from the query string .
*
* @ return PDOStatement
*/
2013-07-07 06:49:38 +00:00
private function prepare_query (){
2013-09-25 07:40:16 +00:00
$this -> queries [] = 'Prepare: ' . $this -> prepared_query ;
2013-07-07 06:49:38 +00:00
$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 ){
2013-09-25 07:40:16 +00:00
$err_message = sprintf ( " Problem preparing the PDO SQL Statement. Error was: %s " , $message );
2013-07-07 06:49:38 +00:00
$this -> set_error ( __LINE__ , __FUNCTION__ , $err_message );
}
return $statement ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to execute PDO statement object .
*
* This function executes query and sets the variables to give back to WordPress .
* The variables are class fields . So if success , no return value . If failure , it
* returns void and stops .
*
* @ param object $statement of PDO statement
* @ return boolean
*/
2013-07-07 06:49:38 +00:00
private function execute_query ( $statement ) {
$reason = 0 ;
$message = '' ;
if ( ! is_object ( $statement ))
2014-01-29 09:35:06 +00:00
return false ;
2013-07-07 06:49:38 +00:00
if ( count ( $this -> extracted_variables ) > 0 ) {
2013-09-25 07:40:16 +00:00
$this -> queries [] = 'Executing: ' . var_export ( $this -> extracted_variables , true );
2013-07-07 06:49:38 +00:00
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 {
2013-09-25 07:40:16 +00:00
$this -> queries [] = 'Executing: (no parameters)' ;
2013-07-07 06:49:38 +00:00
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 ) {
2013-09-25 07:40:16 +00:00
$err_message = sprintf ( " Error while executing query! Error message was: %s " , $message );
2013-07-07 06:49:38 +00:00
$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 ) {
2013-09-25 07:40:16 +00:00
case 'insert' :
case 'update' :
case 'replace' :
2013-07-07 06:49:38 +00:00
$this -> last_insert_id = $this -> pdo -> lastInsertId ();
$this -> affected_rows = $statement -> rowCount ();
$this -> return_value = $this -> affected_rows ;
break ;
2013-09-25 07:40:16 +00:00
case 'select' :
case 'show' :
case 'showcolumns' :
case 'showindex' :
case 'describe' :
case 'desc' :
2013-11-06 19:23:32 +00:00
case 'check' :
case 'analyze' :
2013-07-07 06:49:38 +00:00
// case "foundrows":
$this -> num_rows = count ( $this -> _results );
$this -> return_value = $this -> num_rows ;
break ;
2013-09-25 07:40:16 +00:00
case 'delete' :
2013-07-07 06:49:38 +00:00
$this -> affected_rows = $statement -> rowCount ();
$this -> return_value = $this -> affected_rows ;
break ;
2013-09-25 07:40:16 +00:00
case 'alter' :
case 'drop' :
case 'create' :
case 'optimize' :
case 'truncate' :
2013-07-07 06:49:38 +00:00
if ( $this -> is_error ) {
$this -> return_value = false ;
} else {
$this -> return_value = true ;
}
break ;
}
}
2014-01-29 09:35:06 +00:00
/**
* Function to extract field data to an array and prepare the query statement .
*
* If original SQL statement is CREATE query , this function do nothing and return
* true . This returned value is not used .
*
* @ return boolean
*/
2013-07-07 06:49:38 +00:00
private function extract_variables () {
if ( $this -> query_type == 'create' ) {
$this -> prepared_query = $this -> rewritten_query ;
2014-01-29 09:35:06 +00:00
return true ;
2013-07-07 06:49:38 +00:00
}
//long queries can really kill this
$pattern = '/(?<!\\\\)([\'"])(.*?)(?<!\\\\)\\1/imsx' ;
$_limit = $limit = ini_get ( 'pcre.backtrack_limit' );
do {
if ( $limit > 10000000 ) {
2013-09-25 07:40:16 +00:00
$message = 'The query is too big to parse properly' ;
2013-07-07 06:49:38 +00:00
$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 );
2013-09-25 07:40:16 +00:00
$this -> queries [] = 'With Placeholders: ' . $query ;
2013-07-07 06:49:38 +00:00
$this -> prepared_query = $query ;
}
2014-01-29 09:35:06 +00:00
/**
* Call back function to replace field data with PDO parameter .
*
* @ param string $matches
* @ return string
*/
2013-07-07 06:49:38 +00:00
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 . ' ' ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to determine which query type the argument is .
*
* It takes the query string , determines the type and returns the type string .
* If the query is the type that SQLite Integration can ' t executes , returns false .
*
2013-07-07 06:49:38 +00:00
* @ param string $query
* @ return boolean | string
*/
private function determine_query_type ( $query ) {
2013-11-06 19:23:32 +00:00
$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|CHECK|ANALYZE)/i' , $query , $match );
2013-07-07 06:49:38 +00:00
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 ;
}
}
2013-07-24 02:31:01 +00:00
if ( stripos ( $this -> query_type , 'drop index' ) !== false ) {
$this -> query_type = 'drop_index' ;
}
2013-07-07 06:49:38 +00:00
return true ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to execute INSERT query for SQLite version 3.7 . 11 or later .
*
* SQLite version 3.7 . 11 began to support multiple rows insert with values
2013-07-07 06:49:38 +00:00
* clause . This is for that version or later .
2014-01-29 09:35:06 +00:00
*
2013-07-07 06:49:38 +00:00
* @ 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 );
2013-09-25 07:40:16 +00:00
$this -> queries [] = 'Rewritten: ' . $this -> rewritten_query ;
2013-07-07 06:49:38 +00:00
$this -> extract_variables ();
$statement = $this -> prepare_query ();
$this -> execute_query ( $statement );
}
/**
2014-01-29 09:35:06 +00:00
* Function to execute INSERT query for SQLite version 3.7 . 10 or lesser .
*
* It executes the INSERT query for SQLite version 3.7 . 10 or lesser . It is
* necessary to rewrite multiple row values .
*
2013-07-07 06:49:38 +00:00
* @ 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 );
2013-09-25 07:40:16 +00:00
$this -> queries [] = 'Rewritten: ' . $this -> rewritten_query ;
2013-07-07 06:49:38 +00:00
$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 );
2013-09-25 07:40:16 +00:00
$this -> queries [] = 'Rewritten: ' . $this -> rewritten_query ;
2013-07-07 06:49:38 +00:00
$this -> extract_variables ();
$statement = $this -> prepare_query ();
$this -> execute_query ( $statement );
}
}
/**
2014-01-29 09:35:06 +00:00
* Function to help rewriting multiple row values insert query .
*
* It splits the values clause into an array to execute separately .
*
2013-07-07 06:49:38 +00:00
* @ 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 ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to execute CREATE query .
*
2013-07-07 06:49:38 +00:00
* @ 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 ) {
2013-09-25 07:40:16 +00:00
$err_message = sprintf ( " Problem in creating table or index. Error was: %s " , $message );
2013-07-07 06:49:38 +00:00
$this -> set_error ( __LINE__ , __FUNCTION__ , $err_message );
return false ;
}
return true ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to execute ALTER TABLE query .
*
2013-07-07 06:49:38 +00:00
* @ param string
* @ return boolean
*/
private function execute_alter_query ( $query ) {
$engine = $this -> prepare_engine ( $this -> query_type );
$reason = 0 ;
$message = '' ;
2013-09-11 19:39:01 +00:00
$re_query = '' ;
2013-07-07 06:49:38 +00:00
$rewritten_query = $engine -> rewrite_query ( $query , $this -> query_type );
2013-09-11 19:39:01 +00:00
if ( is_array ( $rewritten_query ) && array_key_exists ( 'recursion' , $rewritten_query )) {
$re_query = $rewritten_query [ 'recursion' ];
unset ( $rewritten_query [ 'recursion' ]);
}
2013-07-07 06:49:38 +00:00
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 ();
}
}
2013-09-11 19:39:01 +00:00
if ( $re_query != '' ) {
$this -> query ( $re_query );
}
2013-07-07 06:49:38 +00:00
if ( $reason > 0 ) {
2013-09-25 07:40:16 +00:00
$err_message = sprintf ( " Problem in executing alter query. Error was: %s " , $message );
2013-07-07 06:49:38 +00:00
$this -> set_error ( __LINE__ , __FUNCTION__ , $err_message );
return false ;
}
return true ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to execute SHOW VARIABLES query
2013-07-07 06:49:38 +00:00
*
2014-01-29 09:35:06 +00:00
* This query is meaningless for SQLite . This function returns null data with some
* exceptions and only avoids the error message .
2013-07-07 06:49:38 +00:00
*
* @ 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
2013-09-15 21:04:53 +00:00
if ( $value == 'max_allowed_packet' ) {
$dummy_data [ 'Value' ] = 1047552 ;
} else {
$dummy_data [ 'Value' ] = '' ;
}
2013-07-07 06:49:38 +00:00
}
$_results [] = new ObjectArray ( $dummy_data );
$this -> results = $_results ;
$this -> num_rows = count ( $this -> results );
$this -> return_value = $this -> num_rows ;
return true ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to format the queried data to that of MySQL .
*
* @ param string $engine
*/
2013-07-07 06:49:38 +00:00
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 ();
2013-11-06 19:23:32 +00:00
} elseif ( in_array ( $this -> query_type , array ( 'check' , 'analyze' ))) {
$this -> convert_result_check_or_analyze ();
2013-07-07 06:49:38 +00:00
} else {
$this -> results = $this -> _results ;
}
}
2014-01-29 09:35:06 +00:00
/**
* Function to format the error messages and put out to the file .
*
* When $wpdb :: suppress_errors is set to true or $wpdb :: show_errors is set to false ,
* the error messages are ignored .
*
* @ param string $line where the error occurred .
* @ param string $function to indicate the function name where the error occurred .
* @ param string $message
* @ return boolean
*/
2013-07-07 06:49:38 +00:00
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 );
}
/**
2014-01-29 09:35:06 +00:00
* Function to change the queried data to PHP object format .
*
* It takes the associative array of query results and creates a numeric
* array of anonymous objects
*
* @ access private
2013-07-07 06:49:38 +00:00
*/
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 ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to convert the SHOW COLUMNS query data to an object .
*
* It rewrites pragma results to mysql compatible array
2013-07-07 06:49:38 +00:00
* when query_type is describe , we use sqlite pragma function .
2014-01-29 09:35:06 +00:00
*
* @ access private
2013-07-07 06:49:38 +00:00
*/
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 ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to convert SHOW INDEX query data to PHP object .
*
* It rewrites the result of SHOW INDEX to the Object compatible with MySQL
2013-09-09 01:39:25 +00:00
* added the WHERE clause manipulation ( ver 1.3 . 1 )
2014-01-29 09:35:06 +00:00
*
* @ access private
2013-07-07 06:49:38 +00:00
*/
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 );
}
2013-09-09 01:39:25 +00:00
if ( stripos ( $this -> queries [ 0 ], 'WHERE' ) !== false ) {
preg_match ( '/WHERE\\s*(.*)$/im' , $this -> queries [ 0 ], $match );
list ( $key , $value ) = explode ( '=' , $match [ 1 ]);
$key = trim ( $key );
$value = preg_replace ( " /[ \ ';]/ " , '' , $value );
$value = trim ( $value );
foreach ( $_results as $result ) {
if ( stripos ( $value , $result -> $key ) !== false ) {
unset ( $_results );
$_results [] = $result ;
break ;
}
}
}
2013-07-07 06:49:38 +00:00
}
$this -> results = $_results ;
}
2014-01-29 09:35:06 +00:00
/**
* Function to the CHECK query data to an object .
*
* @ access private
*/
2013-11-06 19:23:32 +00:00
private function convert_result_check_or_analyze () {
$results = array ();
if ( $this -> query_type == 'check' ) {
$_columns = array (
'Table' => '' ,
'Op' => 'check' ,
'Msg_type' => 'status' ,
'Msg_text' => 'OK'
);
} else {
$_columns = array (
'Table' => '' ,
'Op' => 'analyze' ,
'Msg_type' => 'status' ,
'Msg_text' => 'Table is already up to date'
);
}
$_results [] = new ObjectArray ( $_columns );
$this -> results = $_results ;
}
2013-07-07 06:49:38 +00:00
/**
2014-01-29 09:35:06 +00:00
* Function to check SQLite library version .
*
* This is used for checking if SQLite can execute multiple rows insert .
*
2013-07-07 06:49:38 +00:00
* @ return version number string or 0
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
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' ;
}
}
/**
2014-01-29 09:35:06 +00:00
* Function to call PDO :: beginTransaction () .
*
2013-07-07 06:49:38 +00:00
* @ see PDO :: beginTransaction ()
2014-01-29 09:35:06 +00:00
* @ return boolean
2013-07-07 06:49:38 +00:00
*/
public function beginTransaction () {
if ( $this -> has_active_transaction ) {
return false ;
} else {
$this -> has_active_transaction = $this -> pdo -> beginTransaction ();
return $this -> has_active_transaction ;
}
}
/**
2014-01-29 09:35:06 +00:00
* Function to call PDO :: commit () .
*
2013-07-07 06:49:38 +00:00
* @ see PDO :: commit ()
*/
public function commit () {
$this -> pdo -> commit ();
$this -> has_active_transaction = false ;
}
/**
2014-01-29 09:35:06 +00:00
* Function to call PDO :: rollBack () .
*
2013-07-07 06:49:38 +00:00
* @ see PDO :: rollBack ()
*/
public function rollBack () {
$this -> pdo -> rollBack ();
$this -> has_active_transaction = false ;
}
}
2014-01-29 09:35:06 +00:00
/**
* Class to change queried data to PHP object .
*
* @ author kjm
*/
2013-07-07 06:49:38 +00:00
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 ;
}
}
}
}
?>