2013-07-07 06:49:38 +00:00
< ? php
/**
2014-01-29 09:35:06 +00:00
* This file defines PDOSQLiteDriver class .
2014-07-14 08:46:32 +00:00
*
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 is for rewriting various query string except CREATE and ALTER .
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
*/
class PDOSQLiteDriver {
2014-01-29 09:35:06 +00:00
/**
* Variable to indicate the query types .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ var string $query_type
*/
2013-07-07 06:49:38 +00:00
public $query_type = '' ;
2014-01-29 09:35:06 +00:00
/**
* Variable to store query string .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ var string
*/
2013-07-07 06:49:38 +00:00
public $_query = '' ;
2014-04-17 02:45:32 +00:00
/**
* Variable to check if rewriting CALC_FOUND_ROWS is needed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* @ var boolean
*/
private $rewrite_calc_found = false ;
/**
* Variable to check if rewriting ON DUPLICATE KEY UPDATE is needed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* @ var boolean
*/
private $rewrite_duplicate_key = false ;
/**
* Variable to check if rewriting index hints is needed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* @ var boolean
*/
private $rewrite_index_hint = false ;
/**
* Variable to check if rewriting BETWEEN is needed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* @ var boolean
*/
private $rewrite_between = false ;
2014-07-14 08:46:32 +00:00
/**
* Variable to check how many times rewriting BETWEEN is needed .
*
* @ var integer
*/
private $num_of_rewrite_between = 0 ;
2014-07-16 04:35:27 +00:00
/**
* Variable to check order by field () with column data .
*
* @ var boolean
*/
private $orderby_field = false ;
2013-07-07 06:49:38 +00:00
/**
2014-01-29 09:35:06 +00:00
* Method to rewrite a query string for SQLite to execute .
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
* @ param strin $query
* @ param string $query_type
* @ return string
*/
public function rewrite_query ( $query , $query_type ){
$this -> query_type = $query_type ;
2014-07-14 08:46:32 +00:00
$this -> _query = $query ;
$this -> parse_query ();
2013-07-07 06:49:38 +00:00
switch ( $this -> query_type ) {
2014-07-14 08:46:32 +00:00
case 'truncate' :
$this -> handle_truncate_query ();
break ;
case 'alter' :
$this -> handle_alter_query ();
break ;
case 'create' :
$this -> handle_create_query ();
break ;
case 'describe' :
case 'desc' :
$this -> handle_describe_query ();
break ;
case 'show' :
$this -> handle_show_query ();
break ;
case 'showcolumns' :
$this -> handle_show_columns_query ();
break ;
case 'showindex' :
$this -> handle_show_index ();
break ;
case 'select' :
//$this->strip_backticks();
$this -> handle_sql_count ();
$this -> rewrite_date_sub ();
$this -> delete_index_hints ();
$this -> rewrite_regexp ();
//$this->rewrite_boolean();
$this -> fix_date_quoting ();
$this -> rewrite_between ();
2014-07-16 04:35:27 +00:00
$this -> handle_orderby_field ();
2014-07-14 08:46:32 +00:00
break ;
case 'insert' :
//$this->safe_strip_backticks();
$this -> execute_duplicate_key_update ();
$this -> rewrite_insert_ignore ();
$this -> rewrite_regexp ();
$this -> fix_date_quoting ();
break ;
case 'update' :
//$this->safe_strip_backticks();
$this -> rewrite_update_ignore ();
//$this->_rewrite_date_sub();
$this -> rewrite_limit_usage ();
$this -> rewrite_order_by_usage ();
$this -> rewrite_regexp ();
$this -> rewrite_between ();
break ;
case 'delete' :
//$this->strip_backticks();
$this -> rewrite_limit_usage ();
$this -> rewrite_order_by_usage ();
$this -> rewrite_date_sub ();
$this -> rewrite_regexp ();
$this -> delete_workaround ();
break ;
case 'replace' :
//$this->safe_strip_backticks();
$this -> rewrite_date_sub ();
$this -> rewrite_regexp ();
break ;
case 'optimize' :
$this -> rewrite_optimize ();
break ;
case 'pragma' :
break ;
default :
if ( defined ( WP_DEBUG ) && WP_DEBUG ) {
break ;
} else {
$this -> return_true ();
break ;
}
2013-07-07 06:49:38 +00:00
}
return $this -> _query ;
}
2014-04-17 02:45:32 +00:00
/**
* Method to parse query string and determine which operation is needed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* Remove backticks and change true / false values into 1 / 0. And determines
* if rewriting CALC_FOUND_ROWS or ON DUPLICATE KEY UPDATE etc is needed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* @ access private
*/
private function parse_query () {
2014-05-05 03:38:15 +00:00
$tokens = preg_split ( " /( \\ \ '|''|')/s " , $this -> _query , - 1 , PREG_SPLIT_DELIM_CAPTURE );
2014-04-17 02:45:32 +00:00
$literal = false ;
$query_string = '' ;
foreach ( $tokens as $token ) {
if ( $token == " ' " ) {
if ( $literal ) {
$literal = false ;
} else {
$literal = true ;
}
} else {
if ( $literal === false ) {
if ( strpos ( $token , '`' ) !== false ) {
$token = str_replace ( '`' , '' , $token );
}
2014-07-14 08:46:32 +00:00
if ( preg_match ( '/\\bTRUE\\b/i' , $token )) {
$token = str_ireplace ( 'TRUE' , '1' , $token );
}
if ( preg_match ( '/\\bFALSE\\b/i' , $token )) {
$token = str_ireplace ( 'FALSE' , '0' , $token );
}
if ( stripos ( $token , 'SQL_CALC_FOUND_ROWS' ) !== false ) {
2014-04-17 02:45:32 +00:00
$this -> rewrite_calc_found = true ;
}
2014-07-14 08:46:32 +00:00
if ( stripos ( $token , 'ON DUPLICATE KEY UPDATE' ) !== false ) {
2014-04-17 02:45:32 +00:00
$this -> rewrite_duplicate_key = true ;
}
2014-07-14 08:46:32 +00:00
if ( stripos ( $token , 'USE INDEX' ) !== false ) {
2014-04-17 02:45:32 +00:00
$this -> rewrite_index_hint = true ;
}
2014-07-14 08:46:32 +00:00
if ( stripos ( $token , 'IGNORE INDEX' ) !== false ) {
2014-04-17 02:45:32 +00:00
$this -> rewrite_index_hint = true ;
}
2014-07-14 08:46:32 +00:00
if ( stripos ( $token , 'FORCE INDEX' ) !== false ) {
2014-04-17 02:45:32 +00:00
$this -> rewrite_index_hint = true ;
}
2014-07-14 08:46:32 +00:00
if ( stripos ( $token , 'BETWEEN' ) !== false ) {
2014-04-17 02:45:32 +00:00
$this -> rewrite_between = true ;
2014-07-14 08:46:32 +00:00
$this -> num_of_rewrite_between ++ ;
2014-04-17 02:45:32 +00:00
}
2014-07-16 04:35:27 +00:00
if ( stripos ( $token , 'ORDER BY FIELD' ) !== false ) {
$this -> orderby_field = true ;
}
2014-04-17 02:45:32 +00:00
}
}
$query_string .= $token ;
}
$this -> _query = $query_string ;
}
2013-07-07 06:49:38 +00:00
/**
2014-01-29 09:35:06 +00:00
* method to handle SHOW TABLES query .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function handle_show_query (){
2014-07-14 08:46:32 +00:00
$this -> _query = str_ireplace ( ' FULL' , '' , $this -> _query );
$table_name = '' ;
2014-02-05 19:29:23 +00:00
$pattern = '/^\\s*SHOW\\s*TABLES\\s*.*?(LIKE\\s*(.*))$/im' ;
2013-07-07 06:49:38 +00:00
if ( preg_match ( $pattern , $this -> _query , $matches )) {
2014-07-14 08:46:32 +00:00
$table_name = str_replace ( array ( " ' " , ';' ), '' , $matches [ 2 ]);
2013-07-07 06:49:38 +00:00
}
if ( ! empty ( $table_name )) {
2014-07-14 08:46:32 +00:00
$suffix = ' AND name LIKE ' . " ' " . $table_name . " ' " ;
2013-07-07 06:49:38 +00:00
} else {
$suffix = '' ;
}
$this -> _query = " SELECT name FROM sqlite_master WHERE type='table' " . $suffix . ' ORDER BY name DESC' ;
}
/**
2014-01-29 09:35:06 +00:00
* Method to strip all column qualifiers ( backticks ) from a query .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* All the back quotes in the query string are removed automatically .
* So it must not be used when INSERT , UPDATE or REPLACE query is executed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* Obsolite since 1.5 . 1
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function strip_backticks (){
2013-07-07 06:49:38 +00:00
$this -> _query = str_replace ( '`' , '' , $this -> _query );
}
/**
2014-01-29 09:35:06 +00:00
* Method to strip all column qualifiers ( backticks ) from a query except post data .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* All the back quotes execpt user data to be inserted are revomved automatically .
* This method must be used when INSERT , UPDATE or REPLACE query is executed .
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* Obsolite since 1.5 . 1
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
*/
2014-02-05 19:29:23 +00:00
private function safe_strip_backticks () {
2014-01-29 09:35:06 +00:00
$query_string = '' ;
2014-02-05 19:29:23 +00:00
$tokens = preg_split ( " /(''|')/s " , $this -> _query , - 1 , PREG_SPLIT_DELIM_CAPTURE );
$literal = false ;
2014-01-29 09:35:06 +00:00
$query_string = '' ;
foreach ( $tokens as $token ) {
if ( $token == " ' " ) {
if ( $literal ) {
$literal = false ;
} else {
$literal = true ;
}
} else {
if ( $literal === false && strpos ( $token , '`' ) !== false ) {
$token = str_replace ( '`' , '' , $token );
}
}
$query_string .= $token ;
}
$this -> _query = $query_string ;
}
/**
* Method to emulate the SQL_CALC_FOUND_ROWS placeholder for MySQL .
2013-07-07 06:49:38 +00:00
*
2014-01-29 09:35:06 +00:00
* This is a kind of tricky play .
2013-07-07 06:49:38 +00:00
* 1. remove SQL_CALC_FOUND_ROWS option , and give it to the pdo engine
2014-03-18 10:06:36 +00:00
* 2. make another $wpdb instance , and execute the rewritten query
* 3. give the returned value ( integer : number of the rows ) to the original instance variable without LIMIT
2014-07-14 08:46:32 +00:00
*
2014-03-18 10:06:36 +00:00
* We no longer use SELECT COUNT query , because it returns the inexact values when used with WP_Meta_Query () .
2014-07-14 08:46:32 +00:00
*
2014-03-18 10:06:36 +00:00
* This kind of statement is required for WordPress to calculate the paging information .
2013-07-07 06:49:38 +00:00
* see also WP_Query class in wp - includes / query . php
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function handle_sql_count (){
2014-04-17 02:45:32 +00:00
if ( ! $this -> rewrite_calc_found ) return ;
global $wpdb ;
// first strip the code. this is the end of rewriting process
$this -> _query = str_ireplace ( 'SQL_CALC_FOUND_ROWS' , '' , $this -> _query );
// we make the data for next SELECE FOUND_ROWS() statement
$unlimited_query = preg_replace ( '/\\bLIMIT\\s*.*/imsx' , '' , $this -> _query );
2014-07-14 08:46:32 +00:00
//$unlimited_query = preg_replace('/\\bGROUP\\s*BY\\s*.*/imsx', '', $unlimited_query);
// we no longer use SELECT COUNT query
2014-04-17 02:45:32 +00:00
//$unlimited_query = $this->_transform_to_count($unlimited_query);
$_wpdb = new PDODB ();
$result = $_wpdb -> query ( $unlimited_query );
$wpdb -> dbh -> found_rows_result = $result ;
$_wpdb = null ;
2013-07-07 06:49:38 +00:00
}
/**
2014-01-29 09:35:06 +00:00
* Call back method to change SELECT query to SELECT COUNT () .
2014-07-14 08:46:32 +00:00
*
2014-03-18 10:06:36 +00:00
* obsolite since version 1.5 . 1
2013-07-07 06:49:38 +00:00
*
* @ param string $query the query to be transformed
2014-01-29 09:35:06 +00:00
* @ return string the transformed query
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function _transform_to_count ( $query ){
2014-01-29 09:35:06 +00:00
$pattern = '/^\\s*SELECT\\s*(DISTINCT|)?.*?FROM\b/isx' ;
2013-07-07 06:49:38 +00:00
$_query = preg_replace ( $pattern , 'SELECT \\1 COUNT(*) FROM ' , $query );
return $_query ;
}
/**
2014-01-29 09:35:06 +00:00
* Method to rewrite INSERT IGNORE to INSERT OR IGNORE .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_insert_ignore (){
2014-07-14 08:46:32 +00:00
$this -> _query = str_ireplace ( 'INSERT IGNORE' , 'INSERT OR IGNORE ' , $this -> _query );
2013-07-07 06:49:38 +00:00
}
/**
2014-01-29 09:35:06 +00:00
* Method to rewrite UPDATE IGNORE to UPDATE OR IGNORE .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_update_ignore (){
2014-07-14 08:46:32 +00:00
$this -> _query = str_ireplace ( 'UPDATE IGNORE' , 'UPDATE OR IGNORE ' , $this -> _query );
2013-07-07 06:49:38 +00:00
}
/**
2014-01-29 09:35:06 +00:00
* Method to rewrite DATE_ADD () function .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* DATE_ADD has a parameter PHP function can ' t parse , so we quote the list and
* pass it to the user defined function .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_date_add (){
2013-07-07 06:49:38 +00:00
//(date,interval expression unit)
$pattern = '/\\s*date_add\\s*\(([^,]*),([^\)]*)\)/imsx' ;
2014-07-14 08:46:32 +00:00
if ( preg_match ( $pattern , $this -> _query , $matches )) {
$expression = " ' " . trim ( $matches [ 2 ]) . " ' " ;
$this -> _query = preg_replace ( $pattern , " date_add( $matches[1] , $expression ) " , $this -> _query );
}
2013-07-07 06:49:38 +00:00
}
/**
2014-01-29 09:35:06 +00:00
* Method to rewrite DATE_SUB () function .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* DATE_SUB has a parameter PHP function can ' t parse , so we quote the list and
* pass it to the user defined function .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_date_sub (){
2013-07-07 06:49:38 +00:00
//(date,interval expression unit)
$pattern = '/\\s*date_sub\\s*\(([^,]*),([^\)]*)\)/imsx' ;
2014-07-14 08:46:32 +00:00
if ( preg_match ( $pattern , $this -> _query , $matches )) {
$expression = " ' " . trim ( $matches [ 2 ]) . " ' " ;
$this -> _query = preg_replace ( $pattern , " date_sub( $matches[1] , $expression ) " , $this -> _query );
}
2013-07-07 06:49:38 +00:00
}
/**
2014-01-29 09:35:06 +00:00
* Method to handle CREATE query .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* If the query is CREATE query , it will be passed to the query_create . class . php .
* So this method can 't be used. It' s here for safety .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function handle_create_query (){
2014-01-29 09:35:06 +00:00
require_once PDODIR . 'query_create.class.php' ;
2013-07-07 06:49:38 +00:00
$engine = new CreateQuery ();
$this -> _query = $engine -> rewrite_query ( $this -> _query );
$engine = null ;
}
2014-07-14 08:46:32 +00:00
2013-07-07 06:49:38 +00:00
/**
2014-01-29 09:35:06 +00:00
* Method to handle ALTER query .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* If the query is ALTER query , it will be passed ot the query_alter . class . php .
* So this method can ' t be used . It is here for safety .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function handle_alter_query (){
2014-07-14 08:46:32 +00:00
require_once PDODIR . 'query_alter.class.php' ;
$engine = new AlterQuery ();
$this -> _query = $engine -> rewrite_query ( $this -> _query , 'alter' );
$engine = null ;
2013-07-07 06:49:38 +00:00
}
2014-07-14 08:46:32 +00:00
2013-07-07 06:49:38 +00:00
/**
2014-01-29 09:35:06 +00:00
* Method to handle DESCRIBE or DESC query .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* DESCRIBE is required for WordPress installation process . DESC is
* an alias for DESCRIBE , but it is not used in core WordPress .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function handle_describe_query (){
2013-07-07 06:49:38 +00:00
$pattern = '/^\\s*(DESCRIBE|DESC)\\s*(.*)/i' ;
if ( preg_match ( $pattern , $this -> _query , $match )) {
2014-07-14 08:46:32 +00:00
$tablename = preg_replace ( '/[\';]/' , '' , $match [ 2 ]);
$this -> _query = " PRAGMA table_info( $tablename ) " ;
2013-07-07 06:49:38 +00:00
}
}
/**
2014-01-29 09:35:06 +00:00
* Method to remove LIMIT clause from DELETE or UPDATE query .
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
* The author of the original 'PDO for WordPress' says update method of wpdb
* insists on adding LIMIT . But the newest version of WordPress doesn ' t do that .
* Nevertheless some plugins use DELETE with LIMIT , UPDATE with LIMIT .
2014-01-29 09:35:06 +00:00
* We need to exclude sub query ' s LIMIT . And if SQLite is compiled with
* ENABLE_UPDATE_DELETE_LIMIT option , we don ' t remove it .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_limit_usage (){
2013-12-17 02:46:42 +00:00
$_wpdb = new PDODB ();
$options = $_wpdb -> get_results ( 'PRAGMA compile_options' );
foreach ( $options as $opt ) {
if ( stripos ( $opt -> compile_option , 'ENABLE_UPDATE_DELETE_LIMIT' ) !== false ) return ;
}
2014-07-14 08:46:32 +00:00
if ( stripos ( $this -> _query , '(select' ) === false ) {
$this -> _query = preg_replace ( '/\\s*LIMIT\\s*[0-9]$/i' , '' , $this -> _query );
}
2013-07-07 06:49:38 +00:00
}
/**
2014-01-29 09:35:06 +00:00
* Method to remove ORDER BY clause from DELETE or UPDATE query .
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
* SQLite compiled without SQLITE_ENABLE_UPDATE_DELETE_LIMIT option can ' t
* execute UPDATE with ORDER BY , DELETE with GROUP BY .
* We need to exclude sub query ' s GROUP BY .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_order_by_usage () {
2014-01-29 09:35:06 +00:00
$_wpdb = new PDODB ();
$options = $_wpdb -> get_results ( 'PRAGMA compile_options' );
foreach ( $options as $opt ) {
if ( stripos ( $opt -> compile_option , 'ENABLE_UPDATE_DELETE_LIMIT' ) !== false ) return ;
}
2014-07-14 08:46:32 +00:00
if ( stripos ( $this -> _query , '(select' ) === false ) {
$this -> _query = preg_replace ( '/\\s+ORDER\\s+BY\\s*.*$/i' , '' , $this -> _query );
}
2013-07-07 06:49:38 +00:00
}
2014-01-29 09:35:06 +00:00
/**
* Method to handle TRUNCATE query .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
*/
2014-02-05 19:29:23 +00:00
private function handle_truncate_query (){
2013-07-07 06:49:38 +00:00
$pattern = '/TRUNCATE TABLE (.*)/im' ;
$this -> _query = preg_replace ( $pattern , 'DELETE FROM $1' , $this -> _query );
}
/**
2014-01-29 09:35:06 +00:00
* Method to handle OPTIMIZE query .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* Original query has the table names , but they are simply ignored .
* Table names are meaningless in SQLite .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_optimize (){
2013-07-07 06:49:38 +00:00
$this -> _query = " VACUUM " ;
}
/**
2014-01-29 09:35:06 +00:00
* Method to rewrite day .
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
* Jusitn Adie says : some wp UI interfaces ( notably the post interface )
* badly composes the day part of the date leading to problems in sqlite
* sort ordering etc .
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
* I don ' t understand that ...
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
* @ return void
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_badly_formed_dates (){
2013-07-07 06:49:38 +00:00
$pattern = '/([12]\d{3,}-\d{2}-)(\d )/ims' ;
$this -> _query = preg_replace ( $pattern , '${1}0$2' , $this -> _query );
}
/**
2014-01-29 09:35:06 +00:00
* Method to remove INDEX HINT .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ return void
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function delete_index_hints (){
2013-08-06 14:59:31 +00:00
$pattern = '/\\s*(use|ignore|force)\\s+index\\s*\(.*?\)/i' ;
2013-07-07 06:49:38 +00:00
$this -> _query = preg_replace ( $pattern , '' , $this -> _query );
}
/**
2014-01-29 09:35:06 +00:00
* Method to fix the date string and quoting .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* This is required for the calendar widget .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* WHERE month ( fieldname ) = 08 is converted to month ( fieldname ) = '8'
* WHERE month ( fieldname ) = '08' is converted to month ( fieldname ) = '8'
2014-07-14 08:46:32 +00:00
*
2013-08-02 17:40:12 +00:00
* I use preg_replace_callback instead of 'e' option because of security reason .
* cf . PHP manual ( regular expression )
2014-07-14 08:46:32 +00:00
*
2013-07-07 06:49:38 +00:00
* @ return void
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function fix_date_quoting () {
2013-08-02 17:40:12 +00:00
$pattern = '/(month|year|second|day|minute|hour|dayofmonth)\\s*\((.*?)\)\\s*=\\s*["\']?(\d{1,4})[\'"]?\\s*/im' ;
2014-02-05 19:29:23 +00:00
$this -> _query = preg_replace_callback ( $pattern , array ( $this , '_fix_date_quoting' ), $this -> _query );
2013-08-02 17:40:12 +00:00
}
2014-01-29 09:35:06 +00:00
/**
* Call back method to rewrite date string .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ param string $match
* @ return string
* @ access private
*/
2014-02-05 19:29:23 +00:00
private function _fix_date_quoting ( $match ) {
2013-08-02 17:40:12 +00:00
$fixed_val = " { $match [ 1 ] } ( { $match [ 2 ] } )=' " . intval ( $match [ 3 ]) . " ' " ;
return $fixed_val ;
2013-07-07 06:49:38 +00:00
}
2014-01-29 09:35:06 +00:00
/**
* Method to rewrite REGEXP () function .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* This method changes function name to regexpp () and pass it to the user defined
* function .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
*/
2014-02-05 19:29:23 +00:00
private function rewrite_regexp (){
2013-07-07 06:49:38 +00:00
$pattern = '/\s([^\s]*)\s*regexp\s*(\'.*?\')/im' ;
$this -> _query = preg_replace ( $pattern , ' regexpp(\1, \2)' , $this -> _query );
}
/**
2014-01-29 09:35:06 +00:00
* Method to rewrite boolean to number .
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* SQLite doesn ' t support true / false type , so we need to convert them to 1 / 0.
2014-07-14 08:46:32 +00:00
*
2014-04-17 02:45:32 +00:00
* Obsolite since 1.5 . 1
2014-07-14 08:46:32 +00:00
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-02-05 19:29:23 +00:00
private function rewrite_boolean () {
2014-07-14 08:46:32 +00:00
$query = str_ireplace ( 'TRUE' , " 1 " , $this -> _query );
$query = str_ireplace ( 'FALSE' , " 0 " , $query );
$this -> _query = $query ;
2013-07-07 06:49:38 +00:00
}
/**
2014-01-29 09:35:06 +00:00
* Method to handl SHOW COLUMN query .
2014-07-14 08:46:32 +00:00
*
* @ access private
*/
private function handle_show_columns_query () {
$this -> _query = str_ireplace ( ' FULL' , '' , $this -> _query );
$pattern_like = '/^\\s*SHOW\\s*(COLUMNS|FIELDS)\\s*FROM\\s*(.*)?\\s*LIKE\\s*(.*)?/i' ;
$pattern = '/^\\s*SHOW\\s*(COLUMNS|FIELDS)\\s*FROM\\s*(.*)?/i' ;
if ( preg_match ( $pattern_like , $this -> _query , $matches )) {
$table_name = str_replace ( " ' " , " " , trim ( $matches [ 2 ]));
$column_name = str_replace ( " ' " , " " , trim ( $matches [ 3 ]));
$query_string = " SELECT sql FROM sqlite_master WHERE tbl_name=' $table_name ' AND sql LIKE '% $column_name %' " ;
$this -> _query = $query_string ;
} elseif ( preg_match ( $pattern , $this -> _query , $matches )) {
$table_name = $matches [ 2 ];
$query_string = preg_replace ( $pattern , " PRAGMA table_info( $table_name ) " , $this -> _query );
$this -> _query = $query_string ;
}
}
/**
* Method to handle SHOW INDEX query .
*
* Moved the WHERE clause manipulation to pdoengin . class . php ( ver 1.3 . 1 )
*
* @ access private
*/
private function handle_show_index () {
$pattern = '/^\\s*SHOW\\s*(?:INDEX|INDEXES|KEYS)\\s*FROM\\s*(\\w+)?/im' ;
if ( preg_match ( $pattern , $this -> _query , $match )) {
$table_name = preg_replace ( " /[ \ ';]/ " , '' , $match [ 1 ]);
$table_name = trim ( $table_name );
$this -> _query = " SELECT * FROM sqlite_master WHERE tbl_name=' $table_name ' " ;
}
}
/**
* Method to handle ON DUPLICATE KEY UPDATE statement .
*
* First we use SELECT query and check if INSERT is allowed or not .
* Rewriting procedure looks like a detour , but I ' ve got no other ways .
*
* Added the literal check since the version 1.5 . 1.
*
* @ return void
* @ access private
*/
private function execute_duplicate_key_update () {
if ( ! $this -> rewrite_duplicate_key ) return ;
$unique_keys_for_cond = array ();
$unique_keys_for_check = array ();
$pattern = '/^\\s*INSERT\\s*INTO\\s*(\\w+)?\\s*(.*)\\s*ON\\s*DUPLICATE\\s*KEY\\s*UPDATE\\s*(.*)$/ims' ;
if ( preg_match ( $pattern , $this -> _query , $match_0 )) {
$table_name = trim ( $match_0 [ 1 ]);
$insert_data = trim ( $match_0 [ 2 ]);
$update_data = trim ( $match_0 [ 3 ]);
// prepare two unique key data for the table
// 1. array('col1', 'col2, col3', etc) 2. array('col1', 'col2', 'col3', etc)
$_wpdb = new PDODB ();
$indexes = $_wpdb -> get_results ( " SHOW INDEX FROM { $table_name } " );
if ( ! empty ( $indexes )) {
foreach ( $indexes as $index ) {
if ( $index -> Non_unique == 0 ) {
$unique_keys_for_cond [] = $index -> Column_name ;
if ( strpos ( $index -> Column_name , ',' ) !== false ) {
$unique_keys_for_check = array_merge ( $unique_keys_for_check , explode ( ',' , $index -> Column_name ));
} else {
$unique_keys_for_check [] = $index -> Column_name ;
}
}
}
$unique_keys_for_check = array_map ( 'trim' , $unique_keys_for_check );
} else {
// Without unique key or primary key, UPDATE statement will affect all the rows!
$query = 'INSERT INTO ' . $table_name . ' ' . $insert_data ;
$this -> _query = $query ;
$_wpdb = null ;
return ;
}
// data check
if ( preg_match ( '/^\((.*)\)\\s*VALUES\\s*\((.*)\)$/ims' , $insert_data , $match_1 )) {
$col_array = explode ( ',' , $match_1 [ 1 ]);
$ins_data_array = explode ( ',' , $match_1 [ 2 ]);
foreach ( $col_array as $col ) {
$val = trim ( array_shift ( $ins_data_array ));
$ins_data_assoc [ trim ( $col )] = $val ;
}
$condition = '' ;
foreach ( $unique_keys_for_cond as $unique_key ) {
if ( strpos ( $unique_key , ',' ) !== false ) {
$unique_key_array = explode ( ',' , $unique_key );
$counter = count ( $unique_key_array );
for ( $i = 0 ; $i < $counter ; ++ $i ) {
$col = trim ( $unique_key_array [ $i ]);
if ( isset ( $ins_data_assoc [ $col ]) && $i == $counter - 1 ) {
$condition .= $col . '=' . $ins_data_assoc [ $col ] . ' OR ' ;
} elseif ( isset ( $ins_data_assoc [ $col ])) {
$condition .= $col . '=' . $ins_data_assoc [ $col ] . ' AND ' ;
} else {
continue ;
}
}
} else {
$col = trim ( $unique_key );
if ( isset ( $ins_data_assoc [ $col ])) {
$condition .= $col . '=' . $ins_data_assoc [ $col ] . ' OR ' ;
} else {
continue ;
}
}
}
$condition = rtrim ( $condition , ' OR ' );
$test_query = " SELECT * FROM { $table_name } WHERE { $condition } " ;
$results = $_wpdb -> query ( $test_query );
$_wpdb = null ;
if ( $results == 0 ) {
$this -> _query = 'INSERT INTO ' . $table_name . ' ' . $insert_data ;
return ;
} else {
if ( preg_match ( '/^\((.*)\)\\s*VALUES\\s*\((.*)\)$/im' , $insert_data , $match_2 )) {
$col_array = explode ( ',' , $match_2 [ 1 ]);
$ins_array = explode ( ',' , $match_2 [ 2 ]);
$count = count ( $col_array );
for ( $i = 0 ; $i < $count ; $i ++ ) {
$col = trim ( $col_array [ $i ]);
$val = trim ( $ins_array [ $i ]);
$ins_array_assoc [ $col ] = $val ;
}
}
$update_data = rtrim ( $update_data , ';' );
$tmp_array = explode ( ',' , $update_data );
foreach ( $tmp_array as $pair ) {
list ( $col , $value ) = explode ( '=' , $pair );
$col = trim ( $col );
$value = trim ( $value );
$update_array_assoc [ $col ] = $value ;
}
foreach ( $update_array_assoc as $key => & $value ) {
if ( preg_match ( '/^VALUES\\s*\((.*)\)$/im' , $value , $match_3 )) {
$col = trim ( $match_3 [ 1 ]);
$value = $ins_array_assoc [ $col ];
}
}
foreach ( $ins_array_assoc as $key => $val ) {
if ( in_array ( $key , $unique_keys_for_check )) {
$where_array [] = $key . '=' . $val ;
}
}
$update_strings = '' ;
foreach ( $update_array_assoc as $key => $val ) {
if ( in_array ( $key , $unique_keys_for_check )) {
$where_array [] = $key . '=' . $val ;
} else {
$update_strings .= $key . '=' . $val . ',' ;
}
}
$update_strings = rtrim ( $update_strings , ',' );
$unique_where = array_unique ( $where_array , SORT_REGULAR );
$where_string = ' WHERE ' . implode ( ' AND ' , $unique_where );
$update_query = 'UPDATE ' . $table_name . ' SET ' . $update_strings . $where_string ;
$this -> _query = $update_query ;
}
}
}
}
/**
* Method to rewrite BETWEEN A AND B clause .
*
* This clause is the same form as natural language , so we have to check if it is
* in the data or SQL statement .
*
2014-01-29 09:35:06 +00:00
* @ access private
2013-07-07 06:49:38 +00:00
*/
2014-07-14 08:46:32 +00:00
private function rewrite_between () {
if ( ! $this -> rewrite_between ) return ;
$pattern = '/\\s*(CAST\([^\)]+?\)|[^\\s\(]*)?\\s*BETWEEN\\s*([^\\s]*)?\\s*AND\\s*([^\\s\)]*)?\\s*/ims' ;
do {
if ( preg_match ( $pattern , $this -> _query , $match )) {
$column_name = trim ( $match [ 1 ]);
$min_value = trim ( $match [ 2 ]);
$max_value = trim ( $match [ 3 ]);
$max_value = rtrim ( $max_value );
$replacement = " ( $column_name >= $min_value AND $column_name <= $max_value ) " ;
$this -> _query = str_ireplace ( $match [ 0 ], $replacement , $this -> _query );
}
$this -> num_of_rewrite_between -- ;
} while ( $this -> num_of_rewrite_between > 0 );
}
2014-07-16 04:35:27 +00:00
/**
* Method to handle ORDER BY FIELD () clause .
*
* When FIELD () function has column name to compare , we can ' t rewrite it with
* use defined functions . When this function detect column name in the argument ,
* it creates another instance , does the query withuot ORDER BY clause and gives
* the result array sorted to the main instance .
*
* If FIELD () function doesn ' t have column name , it will use the user defined
* function . usort () function closure function to compare the items .
*
* @ access private
*/
private function handle_orderby_field () {
if ( ! $this -> orderby_field ) return ;
global $wpdb ;
$pattern = '/\\s+ORDER\\s+BY\\s+FIELD\\s*\(\\s*([^\)]+?)\\s*\)/i' ;
if ( preg_match ( $pattern , $this -> _query , $match )) {
global $flipped ;
$params = explode ( ',' , $match [ 1 ]);
$params = array_map ( 'trim' , $params );
$tbl_col = array_shift ( $params );
$flipped = array_flip ( $params );
$tbl_name = substr ( $tbl_col , 0 , strpos ( $tbl_col , '.' ));
$tbl_name = str_replace ( $wpdb -> prefix , '' , $tbl_name );
if ( $tbl_name && in_array ( $tbl_name , $wpdb -> tables )) {
$query = str_replace ( $match [ 0 ], '' , $this -> _query );
$_wpdb = new PDODB ();
$results = $_wpdb -> get_results ( $query );
$_wpdb = null ;
$compare = function ( $a , $b ) {
global $flipped ;
return $flipped [ $a -> ID ] - $flipped [ $b -> ID ];
};
usort ( $results , $compare );
}
$wpdb -> dbh -> pre_ordered_results = $results ;
}
}
2014-07-14 08:46:32 +00:00
/**
* Method to avoid DELETE with JOIN statement .
*
* wp - admin / includes / upgrade . php contains 'DELETE ... JOIN' statement .
* This query can ' t be replaced with regular expression or udf , so we
* replace all the statement with another . But this query was used in
* the very old version of WordPress when it was upgraded . So we won ' t
* have no chance that this method should be used .
*
* @ access private
*/
private function delete_workaround () {
global $wpdb ;
2014-02-05 19:29:23 +00:00
$pattern = " DELETE o1 FROM $wpdb->options AS o1 JOIN $wpdb->options AS o2 " ;
2014-07-14 08:46:32 +00:00
$pattern2 = " DELETE a, b FROM $wpdb->sitemeta AS a, $wpdb->sitemeta AS b " ;
$rewritten = " DELETE FROM $wpdb->options WHERE option_id IN (SELECT MIN(option_id) FROM $wpdb->options GROUP BY option_name HAVING COUNT(*) > 1) " ;
if ( stripos ( $this -> _query , $pattern ) !== false ) {
$this -> _query = $rewritten ;
} else if ( stripos ( $this -> _query , $pattern2 ) !== false ) {
$time = time ();
$prep_query = " SELECT a.meta_id AS aid, b.meta_id AS bid FROM $wpdb->sitemeta AS a INNER JOIN $wpdb->sitemeta AS b ON a.meta_key='_site_transient_timeout_'||substr(b.meta_key, 17) WHERE b.meta_key='_site_transient_'||substr(a.meta_key, 25) AND a.meta_value < $time " ;
$_wpdb = new PDODB ();
$ids = $_wpdb -> get_results ( $prep_query );
foreach ( $ids as $id ) {
$ids_to_delete [] = $id -> aid ;
$ids_to_delete [] = $id -> bid ;
}
$rewritten = " DELETE FROM $wpdb->sitemeta WHERE meta_id IN ( " . implode ( ',' , $ids_to_delete ) . " ) " ;
$this -> _query = $rewritten ;
}
}
/**
* Method to suppress errors .
*
* When the query string is the one that this class can ' t manipulate ,
* the query string is replaced with the one that always returns true
* and does nothing .
*
* @ access private
*/
private function return_true () {
$this -> _query = 'SELECT 1=1' ;
}
2013-07-07 06:49:38 +00:00
}
?>