2009-07-15 14:07:44 +00:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* @package PostgreSQL_For_Wordpress
|
|
|
|
|
* @version $Id$
|
|
|
|
|
* @author Hawk__, www.hawkix.net
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Provides a driver for PostgreSQL
|
|
|
|
|
*
|
|
|
|
|
* This file maps original mysql_* functions with PostgreSQL equivalents
|
|
|
|
|
*
|
|
|
|
|
* This was originally based on usleepless's original 'mysql2pgsql.php' file, many thanks to him
|
|
|
|
|
*/
|
2011-08-01 23:07:34 +00:00
|
|
|
// Check pgsql extension is loaded
|
|
|
|
|
if ( !extension_loaded('pgsql') )
|
|
|
|
|
wp_die( 'Your PHP installation appears to be missing the PostgreSQL extension which is required by WordPress with PG4WP.' );
|
2009-07-15 14:07:44 +00:00
|
|
|
|
|
|
|
|
// Initializing some variables
|
2011-08-05 22:43:58 +00:00
|
|
|
$GLOBALS['pg4wp_version'] = '7.0';
|
2009-07-15 14:07:44 +00:00
|
|
|
$GLOBALS['pg4wp_result'] = 0;
|
2011-09-10 22:11:30 +00:00
|
|
|
$GLOBALS['pg4wp_numrows_query'] = '';
|
2009-07-15 14:07:44 +00:00
|
|
|
$GLOBALS['pg4wp_ins_table'] = '';
|
|
|
|
|
$GLOBALS['pg4wp_ins_field'] = '';
|
2012-05-21 18:06:44 +00:00
|
|
|
$GLOBALS['pg4wp_last_insert'] = '';
|
2011-08-05 21:55:31 +00:00
|
|
|
$GLOBALS['pg4wp_connstr'] = '';
|
|
|
|
|
$GLOBALS['pg4wp_conn'] = false;
|
2016-08-17 22:35:24 +02:00
|
|
|
|
2016-08-22 15:45:34 -06:00
|
|
|
function wpsql_ping($conn)
|
|
|
|
|
{ return pg_ping($conn); }
|
2009-07-15 14:07:44 +00:00
|
|
|
function wpsql_num_rows($result)
|
|
|
|
|
{ return pg_num_rows($result); }
|
|
|
|
|
function wpsql_numrows($result)
|
|
|
|
|
{ return pg_num_rows($result); }
|
|
|
|
|
function wpsql_num_fields($result)
|
2010-02-04 21:36:46 +00:00
|
|
|
{ return pg_num_fields($result); }
|
2009-07-15 14:07:44 +00:00
|
|
|
function wpsql_fetch_field($result)
|
|
|
|
|
{ return 'tablename'; }
|
|
|
|
|
function wpsql_fetch_object($result)
|
|
|
|
|
{ return pg_fetch_object($result); }
|
|
|
|
|
function wpsql_free_result($result)
|
|
|
|
|
{ return pg_free_result($result); }
|
|
|
|
|
function wpsql_affected_rows()
|
|
|
|
|
{
|
|
|
|
|
if( $GLOBALS['pg4wp_result'] === false)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return pg_affected_rows($GLOBALS['pg4wp_result']);
|
|
|
|
|
}
|
|
|
|
|
function wpsql_fetch_row($result)
|
|
|
|
|
{ return pg_fetch_row($result); }
|
|
|
|
|
function wpsql_data_seek($result, $offset)
|
|
|
|
|
{ return pg_result_seek ( $result, $offset ); }
|
|
|
|
|
function wpsql_error()
|
2011-08-05 21:55:31 +00:00
|
|
|
{ if( $GLOBALS['pg4wp_conn']) return pg_last_error(); else return ''; }
|
2009-07-15 14:07:44 +00:00
|
|
|
function wpsql_fetch_assoc($result) { return pg_fetch_assoc($result); }
|
|
|
|
|
function wpsql_escape_string($s) { return pg_escape_string($s); }
|
2012-05-20 16:58:04 +00:00
|
|
|
function wpsql_real_escape_string($s,$c=NULL) { return pg_escape_string($s); }
|
2011-08-04 22:24:02 +00:00
|
|
|
function wpsql_get_server_info() { return '5.0.30'; } // Just want to fool wordpress ...
|
2014-08-21 16:50:36 +00:00
|
|
|
|
|
|
|
|
/**** Modified version of wpsql_result() is at the bottom of this file
|
2009-07-15 14:07:44 +00:00
|
|
|
function wpsql_result($result, $i, $fieldname)
|
|
|
|
|
{ return pg_fetch_result($result, $i, $fieldname); }
|
2014-08-21 16:50:36 +00:00
|
|
|
****/
|
2009-07-15 14:07:44 +00:00
|
|
|
|
|
|
|
|
function wpsql_connect($dbserver, $dbuser, $dbpass)
|
|
|
|
|
{
|
2011-08-05 21:55:31 +00:00
|
|
|
$GLOBALS['pg4wp_connstr'] = '';
|
2012-05-20 22:17:54 +00:00
|
|
|
$hostport = explode(':', $dbserver);
|
|
|
|
|
if( !empty( $hostport[0]))
|
|
|
|
|
$GLOBALS['pg4wp_connstr'] .= ' host='.$hostport[0];
|
|
|
|
|
if( !empty( $hostport[1]))
|
|
|
|
|
$GLOBALS['pg4wp_connstr'] .= ' port='.$hostport[1];
|
2011-08-05 21:55:31 +00:00
|
|
|
if( !empty( $dbuser))
|
|
|
|
|
$GLOBALS['pg4wp_connstr'] .= ' user='.$dbuser;
|
|
|
|
|
if( !empty( $dbpass))
|
|
|
|
|
$GLOBALS['pg4wp_connstr'] .= ' password='.$dbpass;
|
|
|
|
|
elseif( !PG4WP_INSECURE)
|
|
|
|
|
wp_die( 'Connecting to your PostgreSQL database without a password is considered insecure.
|
|
|
|
|
<br />If you want to do it anyway, please set "PG4WP_INSECURE" to true in your "db.php" file.' );
|
2016-08-17 22:35:24 +02:00
|
|
|
|
2016-08-22 17:25:22 -06:00
|
|
|
// PostgreSQL must connect to a specific database (unlike MySQL)
|
|
|
|
|
// Guess at one here and reconnect as required in wpsql_select_db
|
|
|
|
|
$dbname = defined('DB_NAME') && DB_NAME ? DB_NAME : 'template1';
|
2016-08-22 18:21:10 -06:00
|
|
|
return pg_connect( $GLOBALS['pg4wp_connstr'].' dbname='.$dbname);
|
2009-07-15 14:07:44 +00:00
|
|
|
}
|
2011-08-05 21:55:31 +00:00
|
|
|
|
|
|
|
|
// The effective connection happens here
|
2009-07-15 14:07:44 +00:00
|
|
|
function wpsql_select_db($dbname, $connection_id = 0)
|
|
|
|
|
{
|
2011-08-05 21:55:31 +00:00
|
|
|
$pg_connstr = $GLOBALS['pg4wp_connstr'].' dbname='.$dbname;
|
|
|
|
|
|
2016-08-22 17:25:22 -06:00
|
|
|
// Note: pg_connect returns existing connection for same connstr
|
2017-04-08 13:33:13 -06:00
|
|
|
$GLOBALS['pg4wp_conn'] = $conn = pg_connect($pg_connstr);
|
|
|
|
|
|
|
|
|
|
if( !$conn)
|
|
|
|
|
return $conn;
|
|
|
|
|
|
|
|
|
|
$ver = pg_version($conn);
|
|
|
|
|
if( isset($ver['server']))
|
|
|
|
|
$GLOBALS['pg4wp_version'] = $ver['server'];
|
|
|
|
|
|
2011-08-05 22:43:58 +00:00
|
|
|
// Now we should be connected, we "forget" about the connection parameters (if this is not a "test" connection)
|
2011-08-05 21:55:31 +00:00
|
|
|
if( !defined('WP_INSTALLING') || !WP_INSTALLING)
|
|
|
|
|
$GLOBALS['pg4wp_connstr'] = '';
|
|
|
|
|
|
2011-08-01 21:43:06 +00:00
|
|
|
// Execute early transmitted commands if needed
|
2017-04-08 16:20:39 -06:00
|
|
|
if( !empty($GLOBALS['pg4wp_pre_sql']))
|
2011-08-01 21:43:06 +00:00
|
|
|
foreach( $GLOBALS['pg4wp_pre_sql'] as $sql2run)
|
|
|
|
|
wpsql_query( $sql2run);
|
2011-08-05 21:55:31 +00:00
|
|
|
|
2017-04-08 16:20:58 -06:00
|
|
|
pg4wp_init($conn);
|
|
|
|
|
|
2017-04-08 13:33:13 -06:00
|
|
|
return $conn;
|
2009-07-15 14:07:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function wpsql_fetch_array($result)
|
|
|
|
|
{
|
|
|
|
|
$res = pg_fetch_array($result);
|
|
|
|
|
|
|
|
|
|
if( is_array($res) )
|
|
|
|
|
foreach($res as $v => $k )
|
|
|
|
|
$res[$v] = trim($k);
|
|
|
|
|
return $res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function wpsql_query($sql)
|
|
|
|
|
{
|
2011-08-05 21:55:31 +00:00
|
|
|
if( !$GLOBALS['pg4wp_conn'])
|
2011-08-01 21:43:06 +00:00
|
|
|
{
|
|
|
|
|
// Catch SQL to be executed as soon as connected
|
|
|
|
|
$GLOBALS['pg4wp_pre_sql'][] = $sql;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-09-10 23:38:01 +00:00
|
|
|
|
2011-10-31 21:50:04 +00:00
|
|
|
$initial = $sql;
|
2011-09-10 23:38:01 +00:00
|
|
|
$sql = pg4wp_rewrite( $sql);
|
|
|
|
|
|
|
|
|
|
$GLOBALS['pg4wp_result'] = pg_query($sql);
|
|
|
|
|
if( (PG4WP_DEBUG || PG4WP_LOG_ERRORS) && $GLOBALS['pg4wp_result'] === false && $err = pg_last_error())
|
2011-10-31 21:50:04 +00:00
|
|
|
{
|
|
|
|
|
$ignore = false;
|
|
|
|
|
if( defined('WP_INSTALLING') && WP_INSTALLING)
|
|
|
|
|
{
|
|
|
|
|
global $table_prefix;
|
2012-05-15 19:19:51 +00:00
|
|
|
$ignore = strpos($err, 'relation "'.$table_prefix);
|
2011-10-31 21:50:04 +00:00
|
|
|
}
|
|
|
|
|
if( ! $ignore )
|
|
|
|
|
error_log('['.microtime(true)."] Error running :\n$initial\n---- converted to ----\n$sql\n----> $err\n---------------------\n", 3, PG4WP_LOG.'pg4wp_errors.log');
|
|
|
|
|
}
|
2011-09-10 23:38:01 +00:00
|
|
|
return $GLOBALS['pg4wp_result'];
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-20 21:57:33 +00:00
|
|
|
function wpsql_insert_id($lnk = NULL)
|
2011-09-10 23:38:01 +00:00
|
|
|
{
|
|
|
|
|
global $wpdb;
|
|
|
|
|
$ins_field = $GLOBALS['pg4wp_ins_field'];
|
2012-05-20 21:57:33 +00:00
|
|
|
$table = $GLOBALS['pg4wp_ins_table'];
|
2012-05-21 18:06:44 +00:00
|
|
|
$lastq = $GLOBALS['pg4wp_last_insert'];
|
2011-09-10 23:38:01 +00:00
|
|
|
|
2012-05-20 21:57:33 +00:00
|
|
|
$seq = $table . '_seq';
|
2011-09-10 23:38:01 +00:00
|
|
|
|
2012-05-20 21:57:33 +00:00
|
|
|
// Table 'term_relationships' doesn't have a sequence
|
2012-05-21 18:06:44 +00:00
|
|
|
if( $table == $wpdb->term_relationships)
|
2012-05-20 21:57:33 +00:00
|
|
|
{
|
|
|
|
|
$sql = 'NO QUERY';
|
|
|
|
|
$data = 0;
|
|
|
|
|
}
|
2012-05-21 18:06:44 +00:00
|
|
|
// When using WP_Import plugin, ID is defined in the query
|
|
|
|
|
elseif('post_author' == $ins_field && false !== strpos($lastq,'ID'))
|
|
|
|
|
{
|
|
|
|
|
$sql = 'ID was in query ';
|
|
|
|
|
$pattern = '/.+\'(\d+).+$/';
|
|
|
|
|
preg_match($pattern, $lastq, $matches);
|
|
|
|
|
$data = $matches[1];
|
|
|
|
|
// We should update the sequence on the next non-INSERT query
|
|
|
|
|
$GLOBALS['pg4wp_queued_query'] = "SELECT SETVAL('$seq',(SELECT MAX(\"ID\") FROM $table)+1);";
|
|
|
|
|
}
|
2011-09-10 23:38:01 +00:00
|
|
|
else
|
2012-05-20 21:57:33 +00:00
|
|
|
{
|
|
|
|
|
$sql = "SELECT CURRVAL('$seq')";
|
|
|
|
|
|
|
|
|
|
$res = pg_query($sql);
|
|
|
|
|
if( false !== $res)
|
|
|
|
|
$data = pg_fetch_result($res, 0, 0);
|
|
|
|
|
elseif( PG4WP_DEBUG || PG4WP_ERROR_LOG)
|
|
|
|
|
{
|
|
|
|
|
$log = '['.microtime(true)."] wpsql_insert_id() was called with '$table' and '$ins_field'".
|
2015-09-12 09:53:23 -07:00
|
|
|
" and returned the error:\n".pg_last_error().
|
|
|
|
|
"\nFor the query:\n".$sql.
|
|
|
|
|
"\nThe latest INSERT query was :\n'$lastq'\n";
|
2012-05-20 21:57:33 +00:00
|
|
|
error_log( $log, 3, PG4WP_LOG.'pg4wp_errors.log');
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-10 23:38:01 +00:00
|
|
|
if( PG4WP_DEBUG && $sql)
|
2012-05-20 21:57:33 +00:00
|
|
|
error_log( '['.microtime(true)."] Getting inserted ID for '$table' ('$ins_field') : $sql => $data\n", 3, PG4WP_LOG.'pg4wp_insertid.log');
|
|
|
|
|
|
2011-09-10 23:38:01 +00:00
|
|
|
return $data;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-24 15:41:21 -06:00
|
|
|
// Convert MySQL FIELD function to CASE statement
|
|
|
|
|
// https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_field
|
|
|
|
|
// Other implementations: https://stackoverflow.com/q/1309624
|
|
|
|
|
function pg4wp_rewrite_field($matches)
|
|
|
|
|
{
|
|
|
|
|
$case = 'CASE ' . trim($matches[1]);
|
|
|
|
|
$comparands = explode(',', $matches[2]);
|
|
|
|
|
foreach($comparands as $i => $comparand) {
|
|
|
|
|
$case .= ' WHEN ' . trim($comparand) . ' THEN ' . ($i + 1);
|
|
|
|
|
}
|
|
|
|
|
$case .= ' ELSE 0 END';
|
|
|
|
|
return $case;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-10 23:38:01 +00:00
|
|
|
function pg4wp_rewrite( $sql)
|
|
|
|
|
{
|
2016-06-30 15:38:14 -07:00
|
|
|
// Note: Can be called from constructor before $wpdb is set
|
2011-08-04 22:11:49 +00:00
|
|
|
global $wpdb;
|
2011-08-26 23:12:33 +00:00
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
$logto = 'queries';
|
2010-04-09 00:04:58 +00:00
|
|
|
// The end of the query may be protected against changes
|
|
|
|
|
$end = '';
|
2009-07-15 14:07:44 +00:00
|
|
|
|
|
|
|
|
// Remove unusefull spaces
|
|
|
|
|
$initial = $sql = trim($sql);
|
|
|
|
|
|
|
|
|
|
if( 0 === strpos($sql, 'SELECT'))
|
|
|
|
|
{
|
|
|
|
|
$logto = 'SELECT';
|
|
|
|
|
// SQL_CALC_FOUND_ROWS doesn't exist in PostgreSQL but it's needed for correct paging
|
|
|
|
|
if( false !== strpos($sql, 'SQL_CALC_FOUND_ROWS'))
|
|
|
|
|
{
|
2011-08-01 22:34:09 +00:00
|
|
|
$sql = str_replace('SQL_CALC_FOUND_ROWS', '', $sql);
|
2011-09-11 00:00:04 +00:00
|
|
|
$GLOBALS['pg4wp_numrows_query'] = $sql;
|
|
|
|
|
if( PG4WP_DEBUG)
|
2011-10-31 21:50:04 +00:00
|
|
|
error_log( '['.microtime(true)."] Number of rows required for :\n$sql\n---------------------\n", 3, PG4WP_LOG.'pg4wp_NUMROWS.log');
|
2009-07-15 14:07:44 +00:00
|
|
|
}
|
|
|
|
|
elseif( false !== strpos($sql, 'FOUND_ROWS()'))
|
2011-09-10 22:11:30 +00:00
|
|
|
{
|
2011-09-11 00:00:04 +00:00
|
|
|
// Here we convert the latest query into a COUNT query
|
2011-09-10 22:11:30 +00:00
|
|
|
$sql = $GLOBALS['pg4wp_numrows_query'];
|
|
|
|
|
// Remove any LIMIT ... clause (this is the blocking part)
|
|
|
|
|
$pattern = '/\s+LIMIT.+/';
|
|
|
|
|
$sql = preg_replace( $pattern, '', $sql);
|
|
|
|
|
// Now add the COUNT() statement
|
|
|
|
|
$pattern = '/SELECT\s+([^\s]+)\s+(FROM.+)/';
|
|
|
|
|
$sql = preg_replace( $pattern, 'SELECT COUNT($1) $2', $sql);
|
|
|
|
|
}
|
[nextgen-gallery] Support for CONVERT() in place of CAST()
MySQL provides a CONVERT function for doing data type conversion.
This currently results in errors such as:
Error running :
SELECT image_slug, SUBSTR(image_slug, 10) AS 'i' FROM wp_ngg_pictures WHERE (image_slug LIKE 'img_0601-%' AND CONVERT(SUBSTR(image_slug, 10), SIGNED) BETWEEN 1 AND 2147483647) OR image_slug = 'img_0601' ORDER BY i DESC LIMIT 1
---- converted to ----
SELECT image_slug, SUBSTR(image_slug, 10) AS "i" FROM wp_ngg_pictures WHERE (image_slug ILIKE 'img_0601-%' AND CONVERT(SUBSTR(image_slug, 10), SIGNED) BETWEEN 1 AND 2147483647) OR image_slug = 'img_0601' ORDER BY i DESC LIMIT 1
----> ERROR: column "signed" does not exist
LINE 1: ... 'img_0601-%' AND CONVERT(SUBSTR(image_slug, 10), SIGNED) BE...
Recognize this function and replace it with CAST().
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:27:32 -06:00
|
|
|
|
[nextgen-gallery] Check ORDER BY in SELECT DISTINCT
MySQL supports ordering by columns which do not appear in the field list
for a SELECT DISTINCT statement while PostgreSQL does not. This results
in errors such as:
Error running :
SELECT DISTINCT pid , GROUP_CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS `extras` FROM `wp_ngg_pictures` LEFT OUTER JOIN `wp_postmeta` ON `wp_postmeta`.`post_id` = `extras_post_id` WHERE (`exclude` = 0) AND (`galleryid` IN (2)) GROUP BY wp_ngg_pictures.pid ORDER BY `sortorder` ASC
---- converted to ----
SELECT DISTINCT pid , string_agg(CONCAT_WS('@@', meta_key, meta_value), ',') AS extras FROM wp_ngg_pictures LEFT OUTER JOIN wp_postmeta ON wp_postmeta.post_id = extras_post_id WHERE (exclude = 0) AND (galleryid IN (2)) GROUP BY wp_ngg_pictures.pid ORDER BY sortorder ASC
----> ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ...yid IN (2)) GROUP BY wp_ngg_pictures.pid ORDER BY sortorder ...
^
To avoid this error, ensure that the field listed in the ORDER BY
statement also appears in the SELECT statement. To support GROUP BY
statements, ensure that it is aggregated using MIN() to mimic the MySQL
behavior.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:33:15 -06:00
|
|
|
// Ensure that ORDER BY column appears in SELECT DISTINCT fields
|
|
|
|
|
$pattern = '/^SELECT DISTINCT.*ORDER BY\s+(\S+)/';
|
|
|
|
|
if( preg_match( $pattern, $sql, $matches) &&
|
|
|
|
|
strpos( $sql, $matches[1]) > strpos( $sql, 'ORDER BY') &&
|
|
|
|
|
false === strpos( $sql, '*'))
|
|
|
|
|
{
|
|
|
|
|
if( false !== strpos( $sql, 'GROUP BY'))
|
|
|
|
|
{
|
|
|
|
|
$pattern = '/ FROM /';
|
|
|
|
|
$sql = preg_replace( $pattern, ', MIN('.$matches[1].') AS '.$matches[1].' FROM ', $sql, 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$pattern = '/ FROM /';
|
|
|
|
|
$sql = preg_replace( $pattern, ', '.$matches[1].' FROM ', $sql, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
[nextgen-gallery] Support for CONVERT() in place of CAST()
MySQL provides a CONVERT function for doing data type conversion.
This currently results in errors such as:
Error running :
SELECT image_slug, SUBSTR(image_slug, 10) AS 'i' FROM wp_ngg_pictures WHERE (image_slug LIKE 'img_0601-%' AND CONVERT(SUBSTR(image_slug, 10), SIGNED) BETWEEN 1 AND 2147483647) OR image_slug = 'img_0601' ORDER BY i DESC LIMIT 1
---- converted to ----
SELECT image_slug, SUBSTR(image_slug, 10) AS "i" FROM wp_ngg_pictures WHERE (image_slug ILIKE 'img_0601-%' AND CONVERT(SUBSTR(image_slug, 10), SIGNED) BETWEEN 1 AND 2147483647) OR image_slug = 'img_0601' ORDER BY i DESC LIMIT 1
----> ERROR: column "signed" does not exist
LINE 1: ... 'img_0601-%' AND CONVERT(SUBSTR(image_slug, 10), SIGNED) BE...
Recognize this function and replace it with CAST().
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:27:32 -06:00
|
|
|
// Convert CONVERT to CAST
|
|
|
|
|
$pattern = '/CONVERT\(([^()]*(\(((?>[^()]+)|(?-2))*\))?[^()]*),\s*([^\s]+)\)/x';
|
|
|
|
|
$sql = preg_replace( $pattern, 'CAST($1 AS $4)', $sql);
|
2011-08-04 23:01:39 +00:00
|
|
|
|
2012-05-09 20:59:27 +00:00
|
|
|
// Handle CAST( ... AS CHAR)
|
|
|
|
|
$sql = preg_replace( '/CAST\((.+) AS CHAR\)/', 'CAST($1 AS TEXT)', $sql);
|
2015-06-06 20:29:58 -06:00
|
|
|
|
|
|
|
|
// Handle CAST( ... AS SIGNED)
|
|
|
|
|
$sql = preg_replace( '/CAST\((.+) AS SIGNED\)/', 'CAST($1 AS INTEGER)', $sql);
|
2012-05-09 20:59:27 +00:00
|
|
|
|
2010-04-04 09:45:30 +00:00
|
|
|
// Handle COUNT(*)...ORDER BY...
|
2012-05-09 21:33:33 +00:00
|
|
|
$sql = preg_replace( '/COUNT(.+)ORDER BY.+/s', 'COUNT$1', $sql);
|
2009-07-15 14:07:44 +00:00
|
|
|
|
2011-08-24 20:57:55 +00:00
|
|
|
// In order for users counting to work...
|
|
|
|
|
$matches = array();
|
|
|
|
|
if( preg_match_all( '/COUNT[^C]+\),/',$sql, $matches))
|
|
|
|
|
{
|
|
|
|
|
foreach( $matches[0] as $num => $one)
|
|
|
|
|
{
|
|
|
|
|
$sub = substr( $one, 0, -1);
|
|
|
|
|
$sql = str_replace( $sub, $sub.' AS count'.$num, $sql);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
$pattern = '/LIMIT[ ]+(\d+),[ ]*(\d+)/';
|
|
|
|
|
$sql = preg_replace($pattern, 'LIMIT $2 OFFSET $1', $sql);
|
|
|
|
|
|
|
|
|
|
$pattern = '/DATE_ADD[ ]*\(([^,]+),([^\)]+)\)/';
|
|
|
|
|
$sql = preg_replace( $pattern, '($1 + $2)', $sql);
|
2015-06-06 20:35:11 -06:00
|
|
|
|
2017-09-24 14:07:40 -06:00
|
|
|
$pattern = '/FIELD[ ]*\(([^\),]+),([^\)]+)\)/';
|
|
|
|
|
$sql = preg_replace_callback( $pattern, 'pg4wp_rewrite_field', $sql);
|
|
|
|
|
|
2015-06-06 20:35:11 -06:00
|
|
|
$pattern = '/GROUP_CONCAT\(([^()]*(\(((?>[^()]+)|(?-2))*\))?[^()]*)\)/x';
|
|
|
|
|
$sql = preg_replace( $pattern, "string_agg($1, ',')", $sql);
|
2017-09-24 14:17:31 -06:00
|
|
|
|
|
|
|
|
// Convert MySQL RAND function to PostgreSQL RANDOM function
|
|
|
|
|
$pattern = '/RAND[ ]*\([ ]*\)/';
|
|
|
|
|
$sql = preg_replace( $pattern, 'RANDOM()', $sql);
|
2009-07-15 14:07:44 +00:00
|
|
|
|
2010-01-19 20:05:57 +00:00
|
|
|
// UNIX_TIMESTAMP in MYSQL returns an integer
|
2011-08-16 16:04:10 +00:00
|
|
|
$pattern = '/UNIX_TIMESTAMP\(([^\)]+)\)/';
|
2010-01-19 20:05:57 +00:00
|
|
|
$sql = preg_replace( $pattern, 'ROUND(DATE_PART(\'epoch\',$1))', $sql);
|
|
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
$date_funcs = array(
|
2010-02-01 21:34:28 +00:00
|
|
|
'DAYOFMONTH(' => 'EXTRACT(DAY FROM ',
|
2010-01-19 19:50:28 +00:00
|
|
|
'YEAR(' => 'EXTRACT(YEAR FROM ',
|
|
|
|
|
'MONTH(' => 'EXTRACT(MONTH FROM ',
|
|
|
|
|
'DAY(' => 'EXTRACT(DAY FROM ',
|
2009-07-15 14:07:44 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$sql = str_replace( 'ORDER BY post_date DESC', 'ORDER BY YEAR(post_date) DESC, MONTH(post_date) DESC', $sql);
|
2010-03-07 22:52:38 +00:00
|
|
|
$sql = str_replace( 'ORDER BY post_date ASC', 'ORDER BY YEAR(post_date) ASC, MONTH(post_date) ASC', $sql);
|
2009-07-15 14:07:44 +00:00
|
|
|
$sql = str_replace( array_keys($date_funcs), array_values($date_funcs), $sql);
|
2010-03-07 22:52:38 +00:00
|
|
|
$curryear = date( 'Y');
|
|
|
|
|
$sql = str_replace( 'FROM \''.$curryear, 'FROM TIMESTAMP \''.$curryear, $sql);
|
2009-07-15 14:07:44 +00:00
|
|
|
|
2011-08-01 22:42:10 +00:00
|
|
|
// MySQL 'IF' conversion - Note : NULLIF doesn't need to be corrected
|
|
|
|
|
$pattern = '/ (?<!NULL)IF[ ]*\(([^,]+),([^,]+),([^\)]+)\)/';
|
2010-04-07 23:41:32 +00:00
|
|
|
$sql = preg_replace( $pattern, ' CASE WHEN $1 THEN $2 ELSE $3 END', $sql);
|
2015-06-03 01:12:12 -06:00
|
|
|
|
|
|
|
|
// Act like MySQL default configuration, where sql_mode is ""
|
|
|
|
|
$pattern = '/@@SESSION.sql_mode/';
|
|
|
|
|
$sql = preg_replace( $pattern, "''", $sql);
|
2009-07-15 14:07:44 +00:00
|
|
|
|
2016-06-30 15:38:14 -07:00
|
|
|
if( isset($wpdb))
|
|
|
|
|
{
|
|
|
|
|
$sql = str_replace('GROUP BY '.$wpdb->prefix.'posts.ID', '' , $sql);
|
|
|
|
|
}
|
2009-07-15 14:07:44 +00:00
|
|
|
$sql = str_replace("!= ''", '<> 0', $sql);
|
|
|
|
|
|
|
|
|
|
// MySQL 'LIKE' is case insensitive by default, whereas PostgreSQL 'LIKE' is
|
|
|
|
|
$sql = str_replace( ' LIKE ', ' ILIKE ', $sql);
|
|
|
|
|
|
|
|
|
|
// INDEXES are not yet supported
|
|
|
|
|
if( false !== strpos( $sql, 'USE INDEX (comment_date_gmt)'))
|
|
|
|
|
$sql = str_replace( 'USE INDEX (comment_date_gmt)', '', $sql);
|
|
|
|
|
|
2010-02-01 21:45:55 +00:00
|
|
|
// HB : timestamp fix for permalinks
|
2010-02-02 13:16:16 +00:00
|
|
|
$sql = str_replace( 'post_date_gmt > 1970', 'post_date_gmt > to_timestamp (\'1970\')', $sql);
|
2010-02-01 21:45:55 +00:00
|
|
|
|
2011-08-26 23:12:33 +00:00
|
|
|
// Akismet sometimes doesn't write 'comment_ID' with 'ID' in capitals where needed ...
|
2016-06-30 15:38:14 -07:00
|
|
|
if( isset($wpdb) && false !== strpos( $sql, $wpdb->comments))
|
2011-08-26 23:12:33 +00:00
|
|
|
$sql = str_replace(' comment_id ', ' comment_ID ', $sql);
|
2015-10-02 14:02:31 -07:00
|
|
|
|
2017-04-14 12:54:14 -06:00
|
|
|
// MySQL treats a HAVING clause without GROUP BY like WHERE
|
|
|
|
|
if( false !== strpos($sql, 'HAVING') && false === strpos($sql, 'GROUP BY'))
|
|
|
|
|
{
|
|
|
|
|
if( false === strpos($sql, 'WHERE'))
|
|
|
|
|
$sql = str_replace('HAVING', 'WHERE', $sql);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$pattern = '/WHERE\s+(.*?)\s+HAVING\s+(.*?)(\s*(?:ORDER|LIMIT|PROCEDURE|INTO|FOR|LOCK|$))/';
|
|
|
|
|
$sql = preg_replace( $pattern, 'WHERE ($1) AND ($2) $3', $sql);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-02 14:02:31 -07:00
|
|
|
// MySQL allows integers to be used as boolean expressions
|
|
|
|
|
// where 0 is false and all other values are true.
|
|
|
|
|
//
|
|
|
|
|
// Although this could occur anywhere with any number, so far it
|
|
|
|
|
// has only been observed as top-level expressions in the WHERE
|
|
|
|
|
// clause and only with 0. For performance, limit current
|
|
|
|
|
// replacements to that.
|
|
|
|
|
$pattern_after_where = '(?:\s*$|\s+(GROUP|HAVING|ORDER|LIMIT|PROCEDURE|INTO|FOR|LOCK))';
|
|
|
|
|
$pattern = '/(WHERE\s+)0(\s+AND|\s+OR|' . $pattern_after_where . ')/';
|
|
|
|
|
$sql = preg_replace( $pattern, '$1false$2', $sql);
|
|
|
|
|
|
|
|
|
|
$pattern = '/(AND\s+|OR\s+)0(' . $pattern_after_where . ')/';
|
|
|
|
|
$sql = preg_replace( $pattern, '$1false$2', $sql);
|
|
|
|
|
|
[nextgen-gallery] Fix identifiers quoted as strings
MySQL allows quoting identifiers, such as column names, as strings using
single-quotes in addition to quoting as identifiers using grave accents.
PostgreSQL does not, resulting in errors such as:
Error running :
SELECT DISTINCT wp_ngg_pictures.* , GROUP_CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS 'extras' FROM `wp_ngg_pictures` LEFT OUTER JOIN `wp_postmeta` ON `wp_postmeta`.`post_id` = `extras_post_id` GROUP BY wp_ngg_pictures.pid LIMIT 1
---- converted to ----
SELECT DISTINCT wp_ngg_pictures.* , GROUP_CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS 'extras' FROM wp_ngg_pictures LEFT OUTER JOIN wp_postmeta ON wp_postmeta.post_id = extras_post_id GROUP BY wp_ngg_pictures.pid LIMIT 1
----> ERROR: syntax error at or near "'extras'"
LINE 1: ..._CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS 'extras' F...
^
Fix this by replacing single quotes with grave accents when they occur
after ") AS ". This strategy obviously has both false-positive and
false-negative issues, but suits the current needs and should be
relatively safe from false-positives. Proper replacement would require
parsing the SQL.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:44:12 -06:00
|
|
|
// MySQL supports strings as names, PostgreSQL needs identifiers.
|
|
|
|
|
// Limit to after closing parenthesis to reduce false-positives
|
|
|
|
|
// Currently only an issue for nextgen-gallery plugin
|
2015-09-12 09:51:11 -07:00
|
|
|
$pattern = '/\) AS \'([^\']+)\'/';
|
[nextgen-gallery] Fix identifiers quoted as strings
MySQL allows quoting identifiers, such as column names, as strings using
single-quotes in addition to quoting as identifiers using grave accents.
PostgreSQL does not, resulting in errors such as:
Error running :
SELECT DISTINCT wp_ngg_pictures.* , GROUP_CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS 'extras' FROM `wp_ngg_pictures` LEFT OUTER JOIN `wp_postmeta` ON `wp_postmeta`.`post_id` = `extras_post_id` GROUP BY wp_ngg_pictures.pid LIMIT 1
---- converted to ----
SELECT DISTINCT wp_ngg_pictures.* , GROUP_CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS 'extras' FROM wp_ngg_pictures LEFT OUTER JOIN wp_postmeta ON wp_postmeta.post_id = extras_post_id GROUP BY wp_ngg_pictures.pid LIMIT 1
----> ERROR: syntax error at or near "'extras'"
LINE 1: ..._CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS 'extras' F...
^
Fix this by replacing single quotes with grave accents when they occur
after ") AS ". This strategy obviously has both false-positive and
false-negative issues, but suits the current needs and should be
relatively safe from false-positives. Proper replacement would require
parsing the SQL.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:44:12 -06:00
|
|
|
$sql = preg_replace( $pattern, ') AS "$1"', $sql);
|
2009-07-15 14:07:44 +00:00
|
|
|
} // SELECT
|
|
|
|
|
elseif( 0 === strpos($sql, 'UPDATE'))
|
|
|
|
|
{
|
|
|
|
|
$logto = 'UPDATE';
|
|
|
|
|
$pattern = '/LIMIT[ ]+\d+/';
|
|
|
|
|
$sql = preg_replace($pattern, '', $sql);
|
|
|
|
|
|
2010-04-09 00:45:33 +00:00
|
|
|
// For correct bactick removal
|
2010-04-09 00:58:19 +00:00
|
|
|
$pattern = '/[ ]*`([^` ]+)`[ ]*=/';
|
2010-04-09 00:45:33 +00:00
|
|
|
$sql = preg_replace( $pattern, ' $1 =', $sql);
|
|
|
|
|
|
Replace '0000-00-00 00:00:00' with NOW() in UPDATE
As in INSERT statements, '0000-00-00 00:00:00' needs to be replaced with
a value that can be stored in PostgreSQL to avoid errors such as:
Error running :
UPDATE `wp_posts` SET `post_author` = 2, `post_date` = '2015-06-03 03:13:45', `post_date_gmt` = '0000-00-00 00:00:00', `post_content` = 'eyJpZF9maWVsZCI6IklEIiwiX19kZWZhdWx0c19zZXQiOnRydWV9', `post_content_filtered` = 'eyJpZF9maWVsZCI6IklEIiwiX19kZWZhdWx0c19zZXQiOnRydWV9', `post_title` = 'Untitled ngg_pictures', `post_excerpt` = '', `post_status` = 'draft', `post_type` = 'ngg_pictures', `comment_status` = 'closed', `ping_status` = 'closed', `post_password` = '', `post_name` = 'mixin_nextgen_table_extras', `to_ping` = '', `pinged` = '', `post_modified` = '2015-06-03 03:13:45', `post_modified_gmt` = '2015-06-03 09:13:45', `post_parent` = 0, `menu_order` = 0, `post_mime_type` = '', `guid` = 'https://example.com/?post_type=ngg_pictures&p=237' WHERE `ID` = 237
---- converted to ----
UPDATE wp_posts SET post_author = 2, post_date = '2015-06-03 03:13:45', post_date_gmt = '0000-00-00 00:00:00', post_content = 'eyJpZF9maWVsZCI6IklEIiwiX19kZWZhdWx0c19zZXQiOnRydWV9', post_content_filtered = 'eyJpZF9maWVsZCI6IklEIiwiX19kZWZhdWx0c19zZXQiOnRydWV9', post_title = 'Untitled ngg_pictures', post_excerpt = '', post_status = 'draft', post_type = 'ngg_pictures', comment_status = 'closed', ping_status = 'closed', post_password = '', post_name = 'mixin_nextgen_table_extras', to_ping = '', pinged = '', post_modified = '2015-06-03 03:13:45', post_modified_gmt = '2015-06-03 09:13:45', post_parent = 0, menu_order = 0, post_mime_type = '', guid = 'https://example.com/?post_type=ngg_pictures&p=237' WHERE "ID" = 237
----> ERROR: date/time field value out of range: "0000-00-00 00:00:00"
LINE 1: ...ost_date = '2015-06-03 03:13:45', post_date_gmt = '0000-00-0...
Match the INSERT behavior and replace it with NOW() in the GMT timezone.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:51:26 -06:00
|
|
|
// Those are used when we need to set the date to now() in gmt time
|
|
|
|
|
$sql = str_replace( "'0000-00-00 00:00:00'", 'now() AT TIME ZONE \'gmt\'', $sql);
|
|
|
|
|
|
2012-06-14 09:15:09 +00:00
|
|
|
// For correct ID quoting
|
Tighten quoting of ID identifiers
The quoting of identifiers which contain "ID" can match things other
than identifiers, such as strings which contain "ID" and subsequently
"=". An example of this occurring in practice is:
Error running :
UPDATE `wp_ngg_pictures` SET `pid` = '42', `post_id` = 0, `galleryid` = '3', `filename` = 'img_0683.jpg', `description` = '', `alttext` = 'img_0683', `imagedate` = '2011-02-05 11:52:31', `exclude` = '0', `sortorder` = '0', `meta_data` = 'eyIwIjpmYWxzZSwiYXBlcnR1cmUiOiJGIDUuNiIsImNyZWRpdCI6ZmFsc2UsImNhbWVyYSI6IkNhbm9uIEVPUyBSRUJFTCBUMWkiLCJjYXB0aW9uIjpmYWxzZSwiY3JlYXRlZF90aW1lc3RhbXAiOiJGZWJydWFyeSA1LCAyMDExIDExOjUyIGFtIiwiY29weXJpZ2h0IjpmYWxzZSwiZm9jYWxfbGVuZ3RoIjoiNTUgbW0iLCJpc28iOjQwMCwic2h1dHRlcl9zcGVlZCI6IjFcLzYwIHNlYyIsImZsYXNoIjoiRmlyZWQiLCJ0aXRsZSI6ZmFsc2UsImtleXdvcmRzIjpmYWxzZSwid2lkdGgiOjE2MDAsImhlaWdodCI6MTA2Nywic2F2ZWQiOnRydWUsInRodW1ibmFpbCI6eyJ3aWR0aCI6MTAwLCJoZWlnaHQiOjc1fSwibmdnMGR5bi0weDI1MC0wMGYwdzAxMWMwMTByMTEwZjExMHIwMTB0MDEwIjp7IndpZHRoIjozNzUsImhlaWdodCI6MjUwLCJmaWxlbmFtZSI6ImltZ18wNjgzLmpwZy1uZ2dpZDAyNDItbmdnMGR5bi0weDI1MC0wMGYwdzAxMWMwMTByMTEwZjExMHIwMTB0MDEwLmpwZyIsImdlbmVyYXRlZCI6IjAuNjczMzIxMDAgMTQzMzYyOTQxMiJ9fQ==', `image_slug` = 'img_0683', `extras_post_id` = '310', `updated_at` = '1433629412' WHERE `pid` = '42'
---- converted to ----
UPDATE wp_ngg_pictures SET pid = '42', post_id = 0, galleryid = '3', filename = 'img_0683.jpg', description = '', alttext = 'img_0683', imagedate = '2011-02-05 11:52:31', exclude = '0', sortorder = '0', meta_data = "'eyIwIjpmYWxzZSwiYXBlcnR1cmUiOiJGIDUuNiIsImNyZWRpdCI6ZmFsc2UsImNhbWVyYSI6IkNhbm9uIEVPUyBSRUJFTCBUMWkiLCJjYXB0aW9uIjpmYWxzZSwiY3JlYXRlZF90aW1lc3RhbXAiOiJGZWJydWFyeSA1LCAyMDExIDExOjUyIGFtIiwiY29weXJpZ2h0IjpmYWxzZSwiZm9jYWxfbGVuZ3RoIjoiNTUgbW0iLCJpc28iOjQwMCwic2h1dHRlcl9zcGVlZCI6IjFcLzYwIHNlYyIsImZsYXNoIjoiRmlyZWQiLCJ0aXRsZSI6ZmFsc2UsImtleXdvcmRzIjpmYWxzZSwid2lkdGgiOjE2MDAsImhlaWdodCI6MTA2Nywic2F2ZWQiOnRydWUsInRodW1ibmFpbCI6eyJ3aWR0aCI6MTAwLCJoZWlnaHQiOjc1fSwibmdnMGR5bi0weDI1MC0wMGYwdzAxMWMwMTByMTEwZjExMHIwMTB0MDEwIjp7IndpZHRoIjozNzUsImhlaWdodCI6MjUwLCJmaWxlbmFtZSI6ImltZ18wNjgzLmpwZy1uZ2dpZDAyNDItbmdnMGR5bi0weDI1MC0wMGYwdzAxMWMwMTByMTEwZjExMHIwMTB0MDEwLmpwZyIsImdlbmVyYXRlZCI6IjAuNjczMzIxMDAgMTQzMzYyOTQxMiJ9fQ=" =', image_slug = 'img_0683', extras_post_id = '310', updated_at = '1433629412' WHERE pid = '42'
----> ERROR: syntax error at or near "img_0683"
LINE 1: ...jczMzIxMDAgMTQzMzYyOTQxMiJ9fQ=" =', image_slug = 'img_0683',...
To avoid this, ensure that the matched substring is not part of a SQL
string literal.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:55:51 -06:00
|
|
|
$pattern = '/(,|\s)[ ]*([^ \']*ID[^ \']*)[ ]*=/';
|
|
|
|
|
$sql = preg_replace( $pattern, '$1 "$2" =', $sql);
|
2012-06-14 09:15:09 +00:00
|
|
|
|
2010-04-09 00:45:33 +00:00
|
|
|
// This will avoid modifications to anything following ' SET '
|
|
|
|
|
list($sql,$end) = explode( ' SET ', $sql, 2);
|
|
|
|
|
$end = ' SET '.$end;
|
2009-07-15 14:07:44 +00:00
|
|
|
} // UPDATE
|
|
|
|
|
elseif( 0 === strpos($sql, 'INSERT'))
|
|
|
|
|
{
|
|
|
|
|
$logto = 'INSERT';
|
|
|
|
|
$sql = str_replace('(0,',"('0',", $sql);
|
|
|
|
|
$sql = str_replace('(1,',"('1',", $sql);
|
|
|
|
|
|
|
|
|
|
// Fix inserts into wp_categories
|
2011-08-26 23:12:33 +00:00
|
|
|
if( false !== strpos($sql, 'INSERT INTO '.$wpdb->categories))
|
2009-07-15 14:07:44 +00:00
|
|
|
{
|
|
|
|
|
$sql = str_replace('"cat_ID",', '', $sql);
|
|
|
|
|
$sql = str_replace("VALUES ('0',", "VALUES(", $sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Those are used when we need to set the date to now() in gmt time
|
|
|
|
|
$sql = str_replace( "'0000-00-00 00:00:00'", 'now() AT TIME ZONE \'gmt\'', $sql);
|
|
|
|
|
|
|
|
|
|
// Multiple values group when calling INSERT INTO don't always work
|
2011-08-26 23:12:33 +00:00
|
|
|
if( false !== strpos( $sql, $wpdb->options) && false !== strpos( $sql, '), ('))
|
2009-07-15 14:07:44 +00:00
|
|
|
{
|
|
|
|
|
$pattern = '/INSERT INTO.+VALUES/';
|
|
|
|
|
preg_match($pattern, $sql, $matches);
|
|
|
|
|
$insert = $matches[0];
|
|
|
|
|
$sql = str_replace( '), (', ');'.$insert.'(', $sql);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-16 00:11:44 +00:00
|
|
|
// Support for "INSERT ... ON DUPLICATE KEY UPDATE ..." is a dirty hack
|
|
|
|
|
// consisting in deleting the row before inserting it
|
|
|
|
|
if( false !== $pos = strpos( $sql, 'ON DUPLICATE KEY'))
|
|
|
|
|
{
|
2011-09-10 23:38:01 +00:00
|
|
|
// Get the elements we need (table name, first field, corresponding value)
|
|
|
|
|
$pattern = '/INSERT INTO\s+([^\(]+)\(([^,]+)[^\(]+VALUES\s*\(([^,]+)/';
|
2010-01-16 00:11:44 +00:00
|
|
|
preg_match($pattern, $sql, $matches);
|
2012-05-20 19:24:06 +00:00
|
|
|
$table = trim( $matches[1], ' `');
|
|
|
|
|
if( !in_array(trim($matches[1],'` '), array($wpdb->posts,$wpdb->comments)))
|
|
|
|
|
{
|
|
|
|
|
// Remove 'ON DUPLICATE KEY UPDATE...' and following
|
|
|
|
|
$sql = substr( $sql, 0, $pos);
|
|
|
|
|
// Add a delete query to handle the maybe existing data
|
|
|
|
|
$sql = 'DELETE FROM '.$table.' WHERE '.$matches[2].' = '.$matches[3].';'.$sql;
|
|
|
|
|
}
|
2010-01-16 00:11:44 +00:00
|
|
|
}
|
Support INSERT IGNORE
MySQL supports the IGNORE modifier on INSERT statements, which ignores
uniqueness errors resulting from the INSERT. This causes syntax errors
in PostgreSQL such as the following:
Error running :
INSERT IGNORE INTO `wp_options` ( `option_name`, `option_value`, `autoload` ) VALUES ('auto_updater.lock', '1433306517', 'no') /* LOCK */
---- converted to ----
INSERT IGNORE INTO wp_options ( option_name, option_value, autoload ) VALUES ('auto_updater.lock', '1433306517', 'no') /* LOCK */
----> ERROR: syntax error at or near "IGNORE"
LINE 1: INSERT IGNORE INTO wp_options ( option_name, option_value, a...
^
Provide support for INSERT IGNORE using a PostgreSQL DO statement with
an exception handler for uniqueness errors.
This has the drawback that it requires PostgreSQL 9.0 or later, support
for plpgsql, and USAGE privileges for plpgsql for the current user. But
these are all common, and it allows us to support INSERT IGNORE
statements generically. If this is later found to be too much of a
problem, it is possible to rewrite the query on a query-specific basis
to an INSERT SELECT statement without a FROM clause.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-06 20:58:03 -06:00
|
|
|
elseif( 0 === strpos($sql, 'INSERT IGNORE'))
|
|
|
|
|
{
|
|
|
|
|
// Note: Requires PostgreSQL 9.0 and USAGE privilege.
|
|
|
|
|
// Could do query-specific rewrite using SELECT without FROM
|
|
|
|
|
// as in http://stackoverflow.com/a/13342031
|
|
|
|
|
$sql = 'DO $$BEGIN INSERT'.substr($sql, 13).'; EXCEPTION WHEN unique_violation THEN END;$$;';
|
|
|
|
|
}
|
2010-01-16 00:11:44 +00:00
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
// To avoid Encoding errors when inserting data coming from outside
|
|
|
|
|
if( preg_match('/^.{1}/us',$sql,$ar) != 1)
|
|
|
|
|
$sql = utf8_encode($sql);
|
|
|
|
|
|
2010-04-09 00:45:33 +00:00
|
|
|
// This will avoid modifications to anything following ' VALUES'
|
2010-04-09 00:09:06 +00:00
|
|
|
list($sql,$end) = explode( ' VALUES', $sql, 2);
|
|
|
|
|
$end = ' VALUES'.$end;
|
2012-05-20 12:30:56 +00:00
|
|
|
|
|
|
|
|
// When installing, the sequence for table terms has to be updated
|
|
|
|
|
if( defined('WP_INSTALLING') && WP_INSTALLING && false !== strpos($sql, 'INSERT INTO `'.$wpdb->terms.'`'))
|
|
|
|
|
$end .= ';SELECT setval(\''.$wpdb->terms.'_seq\', (SELECT MAX(term_id) FROM '.$wpdb->terms.')+1);';
|
|
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
} // INSERT
|
|
|
|
|
elseif( 0 === strpos( $sql, 'DELETE' ))
|
|
|
|
|
{
|
|
|
|
|
$logto = 'DELETE';
|
2015-06-06 21:03:09 -06:00
|
|
|
|
|
|
|
|
// ORDER BY is not supported in DELETE queries, and not required
|
|
|
|
|
// when LIMIT is not present
|
|
|
|
|
if( false !== strpos( $sql, 'ORDER BY') && false === strpos( $sql, 'LIMIT'))
|
|
|
|
|
{
|
|
|
|
|
$pattern = '/ORDER BY \S+ (ASC|DESC)?/';
|
|
|
|
|
$sql = preg_replace( $pattern, '', $sql);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
// LIMIT is not allowed in DELETE queries
|
|
|
|
|
$sql = str_replace( 'LIMIT 1', '', $sql);
|
2010-01-16 00:11:44 +00:00
|
|
|
$sql = str_replace( ' REGEXP ', ' ~ ', $sql);
|
2010-01-16 22:15:14 +00:00
|
|
|
|
|
|
|
|
// This handles removal of duplicate entries in table options
|
|
|
|
|
if( false !== strpos( $sql, 'DELETE o1 FROM '))
|
2011-08-26 23:12:33 +00:00
|
|
|
$sql = "DELETE FROM $wpdb->options WHERE option_id IN " .
|
|
|
|
|
"(SELECT o1.option_id FROM $wpdb->options AS o1, $wpdb->options AS o2 " .
|
2010-01-16 22:15:14 +00:00
|
|
|
"WHERE o1.option_name = o2.option_name " .
|
|
|
|
|
"AND o1.option_id < o2.option_id)";
|
Rewrite multi-table delete of wp_options
During schema upgrade, populate_options() deletes all expired transients
and their corresponding transient timeouts. The queries produce the
following errors:
Error running :
DELETE a, b FROM wp_options a, wp_options b
WHERE a.option_name LIKE '\_transient\_%'
AND a.option_name NOT LIKE '\_transient\_timeout\_%'
AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
AND b.option_value < 1433110465
---- converted to ----
DELETE a, b FROM wp_options a, wp_options b
WHERE a.option_name LIKE '\_transient\_%'
AND a.option_name NOT LIKE '\_transient\_timeout\_%'
AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
AND b.option_value < 1433110465
----> ERROR: syntax error at or near "a"
LINE 1: DELETE a, b FROM wp_options a, wp_options b
^
---------------------
Error running :
DELETE a, b FROM wp_options a, wp_options b
WHERE a.option_name LIKE '\_site\_transient\_%'
AND a.option_name NOT LIKE '\_site\_transient\_timeout\_%'
AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) )
AND b.option_value < 1433110465
---- converted to ----
DELETE a, b FROM wp_options a, wp_options b
WHERE a.option_name LIKE '\_site\_transient\_%'
AND a.option_name NOT LIKE '\_site\_transient\_timeout\_%'
AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) )
AND b.option_value < 1433110465
----> ERROR: syntax error at or near "a"
LINE 1: DELETE a, b FROM wp_options a, wp_options b
^
---------------------
Since PostgreSQL does not support multi-table DELETE statements,
significant rewriting must be done. Since I could not think of a good
generic way to perform this rewriting, recognize this statement
specifically and provide an alternative version.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2015-06-03 00:42:27 -06:00
|
|
|
// Rewrite _transient_timeout multi-table delete query
|
|
|
|
|
elseif( 0 === strpos( $sql, 'DELETE a, b FROM wp_options a, wp_options b'))
|
|
|
|
|
{
|
|
|
|
|
$where = substr( $sql, strpos($sql, 'WHERE ') + 6);
|
|
|
|
|
$where = rtrim( $where, " \t\n\r;");
|
|
|
|
|
// Fix string/number comparison by adding check and cast
|
|
|
|
|
$where = str_replace( 'AND b.option_value', 'AND b.option_value ~ \'^[0-9]+$\' AND CAST(b.option_value AS BIGINT)', $where);
|
|
|
|
|
// Mirror WHERE clause to delete both sides of self-join.
|
|
|
|
|
$where2 = strtr( $where, array('a.' => 'b.', 'b.' => 'a.'));
|
|
|
|
|
$sql = 'DELETE FROM wp_options a USING wp_options b WHERE '.
|
|
|
|
|
'('.$where.') OR ('.$where2.');';
|
|
|
|
|
}
|
2011-08-26 23:12:33 +00:00
|
|
|
|
|
|
|
|
// Akismet sometimes doesn't write 'comment_ID' with 'ID' in capitals where needed ...
|
|
|
|
|
if( false !== strpos( $sql, $wpdb->comments))
|
|
|
|
|
$sql = str_replace(' comment_id ', ' comment_ID ', $sql);
|
2009-07-15 14:07:44 +00:00
|
|
|
}
|
|
|
|
|
// Fix tables listing
|
|
|
|
|
elseif( 0 === strpos($sql, 'SHOW TABLES'))
|
|
|
|
|
{
|
|
|
|
|
$logto = 'SHOWTABLES';
|
|
|
|
|
$sql = 'SELECT tablename FROM pg_tables WHERE schemaname = \'public\';';
|
|
|
|
|
}
|
2011-08-02 00:00:07 +00:00
|
|
|
// Rewriting optimize table
|
|
|
|
|
elseif( 0 === strpos($sql, 'OPTIMIZE TABLE'))
|
|
|
|
|
{
|
|
|
|
|
$logto = 'OPTIMIZE';
|
|
|
|
|
$sql = str_replace( 'OPTIMIZE TABLE', 'VACUUM', $sql);
|
|
|
|
|
}
|
2011-08-26 21:57:44 +00:00
|
|
|
// Handle 'SET NAMES ... COLLATE ...'
|
2012-05-15 19:01:56 +00:00
|
|
|
elseif( 0 === strpos($sql, 'SET NAMES') && false !== strpos($sql, 'COLLATE'))
|
2011-08-26 21:57:44 +00:00
|
|
|
{
|
|
|
|
|
$logto = 'SETNAMES';
|
|
|
|
|
$sql = "SET NAMES 'utf8'";
|
|
|
|
|
}
|
2011-08-03 21:30:21 +00:00
|
|
|
// Load up upgrade and install functions as required
|
2015-06-03 01:03:54 -06:00
|
|
|
$begin = strtoupper( substr( $sql, 0, 3));
|
2012-05-20 12:57:04 +00:00
|
|
|
$search = array( 'SHO', 'ALT', 'DES', 'CRE', 'DRO');
|
2011-08-27 00:32:14 +00:00
|
|
|
if( in_array($begin, $search))
|
2011-08-03 21:30:21 +00:00
|
|
|
{
|
|
|
|
|
require_once( PG4WP_ROOT.'/driver_pgsql_install.php');
|
2010-01-19 22:56:00 +00:00
|
|
|
$sql = pg4wp_installing( $sql, $logto);
|
2011-08-03 21:30:21 +00:00
|
|
|
}
|
2009-07-15 14:07:44 +00:00
|
|
|
|
2010-02-01 21:40:16 +00:00
|
|
|
// WP 2.9.1 uses a comparison where text data is not quoted
|
|
|
|
|
$pattern = '/AND meta_value = (-?\d+)/';
|
|
|
|
|
$sql = preg_replace( $pattern, 'AND meta_value = \'$1\'', $sql);
|
2019-07-23 17:16:03 +08:00
|
|
|
|
|
|
|
|
// Add type cast for meta_value field when it's compared to number
|
|
|
|
|
$pattern = '/AND meta_value < (\d+)/';
|
|
|
|
|
$sql = preg_replace($pattern, 'AND meta_value::bigint < $1', $sql);
|
2011-08-24 20:57:55 +00:00
|
|
|
|
|
|
|
|
// Generic "INTERVAL xx YEAR|MONTH|DAY|HOUR|MINUTE|SECOND" handler
|
|
|
|
|
$pattern = '/INTERVAL[ ]+(\d+)[ ]+(YEAR|MONTH|DAY|HOUR|MINUTE|SECOND)/';
|
2010-04-09 00:04:58 +00:00
|
|
|
$sql = preg_replace( $pattern, "'\$1 \$2'::interval", $sql);
|
2010-01-20 22:58:55 +00:00
|
|
|
$pattern = '/DATE_SUB[ ]*\(([^,]+),([^\)]+)\)/';
|
|
|
|
|
$sql = preg_replace( $pattern, '($1::timestamp - $2)', $sql);
|
2010-01-20 07:32:26 +00:00
|
|
|
|
2010-04-09 00:04:58 +00:00
|
|
|
// Remove illegal characters
|
|
|
|
|
$sql = str_replace('`', '', $sql);
|
|
|
|
|
|
2010-01-16 00:11:44 +00:00
|
|
|
// Field names with CAPITALS need special handling
|
|
|
|
|
if( false !== strpos($sql, 'ID'))
|
|
|
|
|
{
|
|
|
|
|
$pattern = '/ID([^ ])/';
|
|
|
|
|
$sql = preg_replace($pattern, 'ID $1', $sql);
|
|
|
|
|
$pattern = '/ID$/';
|
|
|
|
|
$sql = preg_replace($pattern, 'ID ', $sql);
|
|
|
|
|
$pattern = '/\(ID/';
|
|
|
|
|
$sql = preg_replace($pattern, '( ID', $sql);
|
|
|
|
|
$pattern = '/,ID/';
|
|
|
|
|
$sql = preg_replace($pattern, ', ID', $sql);
|
2012-05-15 19:01:56 +00:00
|
|
|
$pattern = '/[0-9a-zA-Z_]+ID/';
|
2010-01-16 00:11:44 +00:00
|
|
|
$sql = preg_replace($pattern, '"$0"', $sql);
|
|
|
|
|
$pattern = '/\.ID/';
|
|
|
|
|
$sql = preg_replace($pattern, '."ID"', $sql);
|
|
|
|
|
$pattern = '/[\s]ID /';
|
|
|
|
|
$sql = preg_replace($pattern, ' "ID" ', $sql);
|
|
|
|
|
$pattern = '/"ID "/';
|
|
|
|
|
$sql = preg_replace($pattern, ' "ID" ', $sql);
|
|
|
|
|
} // CAPITALS
|
|
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
// Empty "IN" statements are erroneous
|
|
|
|
|
$sql = str_replace( 'IN (\'\')', 'IN (NULL)', $sql);
|
|
|
|
|
$sql = str_replace( 'IN ( \'\' )', 'IN (NULL)', $sql);
|
|
|
|
|
$sql = str_replace( 'IN ()', 'IN (NULL)', $sql);
|
|
|
|
|
|
2012-05-20 21:57:33 +00:00
|
|
|
// Put back the end of the query if it was separated
|
|
|
|
|
$sql .= $end;
|
|
|
|
|
|
2011-08-01 20:48:51 +00:00
|
|
|
// For insert ID catching
|
|
|
|
|
if( $logto == 'INSERT')
|
|
|
|
|
{
|
|
|
|
|
$pattern = '/INSERT INTO (\w+)\s+\([ a-zA-Z_"]+/';
|
|
|
|
|
preg_match($pattern, $sql, $matches);
|
|
|
|
|
$GLOBALS['pg4wp_ins_table'] = $matches[1];
|
2015-09-22 08:40:02 -07:00
|
|
|
$match_list = explode(' ', $matches[0]);
|
2011-08-01 20:48:51 +00:00
|
|
|
if( $GLOBALS['pg4wp_ins_table'])
|
|
|
|
|
{
|
|
|
|
|
$GLOBALS['pg4wp_ins_field'] = trim($match_list[3],' () ');
|
|
|
|
|
if(! $GLOBALS['pg4wp_ins_field'])
|
|
|
|
|
$GLOBALS['pg4wp_ins_field'] = trim($match_list[4],' () ');
|
|
|
|
|
}
|
2012-05-21 18:06:44 +00:00
|
|
|
$GLOBALS['pg4wp_last_insert'] = $sql;
|
|
|
|
|
}
|
|
|
|
|
elseif( isset($GLOBALS['pg4wp_queued_query']))
|
|
|
|
|
{
|
|
|
|
|
pg_query($GLOBALS['pg4wp_queued_query']);
|
|
|
|
|
unset($GLOBALS['pg4wp_queued_query']);
|
2011-08-01 20:48:51 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-20 10:24:18 +00:00
|
|
|
// Correct quoting for PostgreSQL 9.1+ compatibility
|
|
|
|
|
$sql = str_replace( "\\'", "''", $sql);
|
|
|
|
|
$sql = str_replace( '\"', '"', $sql);
|
|
|
|
|
|
2009-07-15 14:07:44 +00:00
|
|
|
if( PG4WP_DEBUG)
|
|
|
|
|
{
|
|
|
|
|
if( $initial != $sql)
|
2011-10-31 21:50:04 +00:00
|
|
|
error_log( '['.microtime(true)."] Converting :\n$initial\n---- to ----\n$sql\n---------------------\n", 3, PG4WP_LOG.'pg4wp_'.$logto.'.log');
|
2009-07-15 14:07:44 +00:00
|
|
|
else
|
2011-10-31 21:50:04 +00:00
|
|
|
error_log( '['.microtime(true)."] $sql\n---------------------\n", 3, PG4WP_LOG.'pg4wp_unmodified.log');
|
2009-07-15 14:07:44 +00:00
|
|
|
}
|
2011-09-10 23:38:01 +00:00
|
|
|
return $sql;
|
2009-07-15 14:07:44 +00:00
|
|
|
}
|
2014-08-21 16:50:36 +00:00
|
|
|
|
2017-04-08 16:20:58 -06:00
|
|
|
// Database initialization
|
|
|
|
|
function pg4wp_init()
|
|
|
|
|
{
|
|
|
|
|
// Provide (mostly) MySQL-compatible field function
|
|
|
|
|
// Note: MySQL accepts heterogeneous argument types. No easy fix.
|
|
|
|
|
// Can define version with typed first arg to cover some cases.
|
|
|
|
|
// Note: ROW_NUMBER+unnest doesn't guarantee order, but is simple/fast.
|
|
|
|
|
// If it breaks, try https://stackoverflow.com/a/8767450
|
|
|
|
|
$result = pg_query(<<<SQL
|
|
|
|
|
CREATE OR REPLACE FUNCTION field(anyelement, VARIADIC anyarray)
|
|
|
|
|
RETURNS BIGINT AS
|
|
|
|
|
$$
|
|
|
|
|
SELECT rownum
|
|
|
|
|
FROM (SELECT ROW_NUMBER() OVER () AS rownum, elem
|
|
|
|
|
FROM unnest($2) elem) numbered
|
|
|
|
|
WHERE numbered.elem = $1
|
|
|
|
|
UNION ALL
|
|
|
|
|
SELECT 0
|
|
|
|
|
$$
|
|
|
|
|
LANGUAGE SQL IMMUTABLE;
|
|
|
|
|
SQL
|
|
|
|
|
);
|
|
|
|
|
if( (PG4WP_DEBUG || PG4WP_LOG_ERRORS) && $result === false )
|
|
|
|
|
{
|
|
|
|
|
$err = pg_last_error();
|
|
|
|
|
error_log('['.microtime(true)."] Error creating MySQL-compatible field function: $err\n", 3, PG4WP_LOG.'pg4wp_errors.log');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-21 16:50:36 +00:00
|
|
|
/*
|
|
|
|
|
Quick fix for wpsql_result() error and missing wpsql_errno() function
|
|
|
|
|
Source : http://vitoriodelage.wordpress.com/2014/06/06/add-missing-wpsql_errno-in-pg4wp-plugin/
|
|
|
|
|
*/
|
|
|
|
|
function wpsql_result($result, $i, $fieldname = null) {
|
|
|
|
|
if (is_resource($result)) {
|
|
|
|
|
if ($fieldname) {
|
|
|
|
|
return pg_fetch_result($result, $i, $fieldname);
|
|
|
|
|
} else {
|
|
|
|
|
return pg_fetch_result($result, $i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function wpsql_errno( $connection) {
|
|
|
|
|
$result = pg_get_result($connection);
|
|
|
|
|
$result_status = pg_result_status($result);
|
|
|
|
|
return pg_result_error_field($result_status, PGSQL_DIAG_SQLSTATE);
|
|
|
|
|
}
|