Merge pull request #53 from PostgreSQL-For-Wordpress/mb-fix-install

Migrate install rewriting to new rewriters
This commit is contained in:
Matthew Bucci
2023-11-07 15:32:53 -08:00
committed by GitHub
14 changed files with 569 additions and 388 deletions

View File

@ -10,6 +10,7 @@
*/
// This is required by class-wpdb so we must load it first
require_once ABSPATH . '/wp-includes/version.php';
require_once ABSPATH . '/wp-includes/cache.php';
require_once ABSPATH . '/wp-includes/l10n.php';
@ -20,14 +21,6 @@ if (!function_exists('wpsql_is_resource')) {
}
}
// Logs are put in the pg4wp directory
define( 'PG4WP_LOG', PG4WP_ROOT.'/logs/');
// Check if the logs directory is needed and exists or create it if possible
if( (PG4WP_DEBUG || PG4WP_LOG_ERRORS) &&
!file_exists( PG4WP_LOG) &&
is_writable(dirname( PG4WP_LOG)))
mkdir( PG4WP_LOG);
// Load the driver defined in 'db.php'
require_once(PG4WP_ROOT . '/driver_' . DB_DRIVER . '.php');

View File

@ -36,16 +36,25 @@ if(!defined('PG4WP_ROOT')) {
// This defines the directory where PG4WP files are loaded from
// 3 places checked : wp-content, wp-content/plugins and the base directory
if(file_exists(ABSPATH . '/wp-content/pg4wp')) {
define('PG4WP_ROOT', ABSPATH . '/wp-content/pg4wp');
} elseif(file_exists(ABSPATH . '/wp-content/plugins/pg4wp')) {
define('PG4WP_ROOT', ABSPATH . '/wp-content/plugins/pg4wp');
} elseif(file_exists(ABSPATH . '/pg4wp')) {
define('PG4WP_ROOT', ABSPATH . '/pg4wp');
if(file_exists(ABSPATH . 'wp-content/pg4wp')) {
define('PG4WP_ROOT', ABSPATH . 'wp-content/pg4wp');
} elseif(file_exists(ABSPATH . 'wp-content/plugins/pg4wp')) {
define('PG4WP_ROOT', ABSPATH . 'wp-content/plugins/pg4wp');
} elseif(file_exists(ABSPATH . 'pg4wp')) {
define('PG4WP_ROOT', ABSPATH . 'pg4wp');
} else {
die('PG4WP file directory not found');
}
// Logs are put in the pg4wp directory
if (!defined('PG4WP_LOG')) {
define('PG4WP_LOG', PG4WP_ROOT . '/logs/');
}
// Check if the logs directory is needed and exists or create it if possible
if((PG4WP_DEBUG || PG4WP_LOG_ERRORS) && !file_exists(PG4WP_LOG) && is_writable(dirname(PG4WP_LOG))) {
mkdir(PG4WP_LOG);
}
// Here happens all the magic
require_once(PG4WP_ROOT . '/core.php');
} // Protection against multiple loading

View File

@ -10,42 +10,82 @@
* This file remaps all wpsql_* calls to mysql_* original name
*/
function wpsql_num_rows($result)
{ return mysql_num_rows($result); }
{
return mysql_num_rows($result);
}
function wpsql_numrows($result)
{ return mysql_num_rows($result); }
{
return mysql_num_rows($result);
}
function wpsql_num_fields($result)
{ return mysql_num_fields($result); }
{
return mysql_num_fields($result);
}
function wpsql_fetch_field($result)
{ return mysql_fetch_field($result); }
{
return mysql_fetch_field($result);
}
function wpsql_fetch_object($result)
{ return mysql_fetch_object($result); }
{
return mysql_fetch_object($result);
}
function wpsql_free_result($result)
{ return mysql_free_result($result); }
{
return mysql_free_result($result);
}
function wpsql_affected_rows()
{ return mysql_affected_rows(); }
{
return mysql_affected_rows();
}
function wpsql_fetch_row($result)
{ return mysql_fetch_row($result); }
{
return mysql_fetch_row($result);
}
function wpsql_data_seek($result, $offset)
{ return mysql_data_seek( $result, $offset ); }
{
return mysql_data_seek($result, $offset);
}
function wpsql_error()
{ return mysql_error();}
{
return mysql_error();
}
function wpsql_fetch_assoc($result)
{ return mysql_fetch_assoc($result); }
{
return mysql_fetch_assoc($result);
}
function wpsql_escape_string($s)
{ return mysql_real_escape_string($s); }
function wpsql_real_escape_string($s,$c=NULL)
{ return mysql_real_escape_string($s,$c); }
{
return mysql_real_escape_string($s);
}
function wpsql_real_escape_string($s, $c = null)
{
return mysql_real_escape_string($s, $c);
}
function wpsql_get_server_info()
{ return mysql_get_server_info(); }
{
return mysql_get_server_info();
}
function wpsql_result($result, $i, $fieldname)
{ return mysql_result($result, $i, $fieldname); }
{
return mysql_result($result, $i, $fieldname);
}
function wpsql_connect($dbserver, $dbuser, $dbpass)
{ return mysql_connect($dbserver, $dbuser, $dbpass); }
{
return mysql_connect($dbserver, $dbuser, $dbpass);
}
function wpsql_fetch_array($result)
{ return mysql_fetch_array($result); }
{
return mysql_fetch_array($result);
}
function wpsql_select_db($dbname, $connection_id)
{ return mysql_select_db($dbname, $connection_id); }
{
return mysql_select_db($dbname, $connection_id);
}
function wpsql_query($sql)
{ return mysql_query($sql); }
{
return mysql_query($sql);
}
function wpsql_insert_id($table)
{ return mysql_insert_id($table); }
{
return mysql_insert_id($table);
}

View File

@ -174,7 +174,7 @@ function pg4wp_init()
$result = pg_query($connection, $sql);
if ((PG4WP_DEBUG || PG4WP_LOG_ERRORS) && $result === false) {
$error = pg_last_error();
$error = pg_last_error($connection);
error_log("[" . microtime(true) . "] Error creating MySQL-compatible field function: $error\n", 3, PG4WP_LOG . 'pg4wp_errors.log');
}
}

View File

@ -1,297 +0,0 @@
<?php
/**
* @package PostgreSQL_For_Wordpress
* @version $Id$
* @author Hawk__, www.hawkix.net
*/
/**
* This file registers functions used only when installing or upgrading WordPress
*/
// List of types translations (the key is the mysql one, the value is the text to use instead)
$GLOBALS['pg4wp_ttr'] = array(
'bigint(20)' => 'bigint',
'bigint(10)' => 'int',
'int(11)' => 'int',
'tinytext' => 'text',
'mediumtext' => 'text',
'longtext' => 'text',
'unsigned' => '',
'gmt datetime NOT NULL default \'0000-00-00 00:00:00\'' => 'gmt timestamp NOT NULL DEFAULT timezone(\'gmt\'::text, now())',
'default \'0000-00-00 00:00:00\'' => 'DEFAULT now()',
'\'0000-00-00 00:00:00\'' => 'now()',
'datetime' => 'timestamp',
'DEFAULT CHARACTER SET utf8' => '',
// WP 2.7.1 compatibility
'int(4)' => 'smallint',
// For WPMU (starting with WP 3.2)
'tinyint(2)' => 'smallint',
'tinyint(1)' => 'smallint',
"enum('0','1')" => 'smallint',
'COLLATE utf8_general_ci' => '',
// For flash-album-gallery plugin
'tinyint' => 'smallint',
);
function pg4wp_installing( $sql, &$logto)
{
global $wpdb;
// Emulate SHOW commands
if( 0 === strpos( $sql, 'SHOW') || 0 === strpos( $sql, 'show'))
{
// SHOW VARIABLES LIKE emulation for sql_mode
// Used by nextgen-gallery plugin
if( 0 === strpos( $sql, "SHOW VARIABLES LIKE 'sql_mode'"))
{
// Act like MySQL default configuration, where sql_mode is ""
$sql = 'SELECT \'sql_mode\' AS "Variable_name", \'\' AS "Value";';
}
// SHOW COLUMNS emulation
elseif( preg_match('/SHOW\s+(FULL\s+)?COLUMNS\s+(?:FROM\s+|IN\s+)`?(\w+)`?(?:\s+LIKE\s+(.+)|\s+WHERE\s+(.+))?/i', $sql, $matches))
{
$logto = 'SHOWCOLUMN';
$full = $matches[1];
$table = $matches[2];
$like = isset($matches[3]) ? $matches[3] : FALSE;
$where = isset($matches[4]) ? $matches[4] : FALSE;
// Wrap as sub-query to emulate WHERE behavior
$sql = ($where ? 'SELECT * FROM (' : '').
'SELECT column_name as "Field",
data_type as "Type",'.($full ? '
NULL as "Collation",' : '').'
is_nullable as "Null",
\'\' as "Key",
column_default as "Default",
\'\' as "Extra"'.($full ? ',
\'select,insert,update,references\' as "Privileges",
\'\' as "Comment"' : '').'
FROM information_schema.columns
WHERE table_name = \''.$table.'\''.($like ? '
AND column_name LIKE '.$like : '').($where ? ') AS columns
WHERE '.$where : '').';';
}
// SHOW INDEX emulation
elseif( 0 === strpos( $sql, 'SHOW INDEX'))
{
$logto = 'SHOWINDEX';
$pattern = '/SHOW INDEX FROM\s+(\w+)/';
preg_match( $pattern, $sql, $matches);
$table = $matches[1];
// Note: Row order must be in column index position order
$sql = 'SELECT bc.relname AS "Table",
CASE WHEN i.indisunique THEN \'0\' ELSE \'1\' END AS "Non_unique",
CASE WHEN i.indisprimary THEN \'PRIMARY\' WHEN bc.relname LIKE \'%usermeta\' AND ic.relname = \'umeta_key\'
THEN \'meta_key\' ELSE REPLACE( ic.relname, \''.$table.'_\', \'\') END AS "Key_name",
a.attname AS "Column_name",
NULL AS "Sub_part"
FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a
WHERE bc.oid = i.indrelid
AND ic.oid = i.indexrelid
AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum)
AND a.attrelid = bc.oid
AND bc.relname = \''.$table.'\'
ORDER BY "Key_name", CASE a.attnum
WHEN i.indkey[0] THEN 0
WHEN i.indkey[1] THEN 1
WHEN i.indkey[2] THEN 2
WHEN i.indkey[3] THEN 3
WHEN i.indkey[4] THEN 4
WHEN i.indkey[5] THEN 5
WHEN i.indkey[6] THEN 6
WHEN i.indkey[7] THEN 7
END;';
}
// SHOW TABLES emulation
elseif( preg_match('/SHOW\s+(FULL\s+)?TABLES\s+(?:LIKE\s+(.+)|WHERE\s+(.+))?/i', $sql, $matches))
{
$logto = 'SHOWTABLES';
$full = $matches[1];
$like = $matches[2];
$where = $matches[3];
// Wrap as sub-query to emulate WHERE behavior
$sql = ($where ? 'SELECT * FROM (' : '').
'SELECT table_name as "Tables_in_'.$wpdb->dbname.'"'.($full ? ',
table_type AS "Table_type"' : '').'
FROM information_schema.tables'.($like ? '
WHERE table_name LIKE '.$like : '').($where ? ') AS tables
WHERE '.$where : '').';';
}
}
// Table alteration
elseif( 0 === strpos( $sql, 'ALTER TABLE'))
{
$logto = 'ALTER';
$pattern = '/ALTER TABLE\s+(\w+)\s+CHANGE COLUMN\s+([^\s]+)\s+([^\s]+)\s+([^ ]+)( unsigned|)\s*(NOT NULL|)\s*(default (.+)|)/';
if( 1 === preg_match( $pattern, $sql, $matches))
{
$table = $matches[1];
$col = $matches[2];
$newname = $matches[3];
$type = strtolower($matches[4]);
if( isset($GLOBALS['pg4wp_ttr'][$type]))
$type = $GLOBALS['pg4wp_ttr'][$type];
$unsigned = $matches[5];
$notnull = $matches[6];
$default = $matches[7];
$defval = $matches[8];
if( isset($GLOBALS['pg4wp_ttr'][$defval]))
$defval = $GLOBALS['pg4wp_ttr'][$defval];
$newq = "ALTER TABLE $table ALTER COLUMN $col TYPE $type";
if( !empty($notnull))
$newq .= ", ALTER COLUMN $col SET NOT NULL";
if( !empty($default))
$newq .= ", ALTER COLUMN $col SET DEFAULT $defval";
if( $col != $newname)
$newq .= ";ALTER TABLE $table RENAME COLUMN $col TO $newcol;";
$sql = $newq;
}
$pattern = '/ALTER TABLE\s+(\w+)\s+ALTER COLUMN\s+/';
if( 1 === preg_match( $pattern, $sql))
{
// Translate default values
$sql = str_replace(
array_keys($GLOBALS['pg4wp_ttr']), array_values($GLOBALS['pg4wp_ttr']), $sql);
}
$pattern = '/ALTER TABLE\s+(\w+)\s+ADD COLUMN\s+([^\s]+)\s+([^ ]+)( unsigned|)\s+(NOT NULL|)\s*(default (.+)|)/';
if( 1 === preg_match( $pattern, $sql, $matches))
{
$table = $matches[1];
$col = $matches[2];
$type = strtolower($matches[3]);
if( isset($GLOBALS['pg4wp_ttr'][$type]))
$type = $GLOBALS['pg4wp_ttr'][$type];
$unsigned = $matches[4];
$notnull = $matches[5];
$default = $matches[6];
$defval = $matches[7];
if( isset($GLOBALS['pg4wp_ttr'][$defval]))
$defval = $GLOBALS['pg4wp_ttr'][$defval];
$newq = "ALTER TABLE $table ADD COLUMN $col $type";
if( !empty($default))
$newq .= " DEFAULT $defval";
if( !empty($notnull))
$newq .= " NOT NULL";
$sql = $newq;
}
$pattern = '/ALTER TABLE\s+(\w+)\s+ADD (UNIQUE |)KEY\s+([^\s]+)\s+\(((?:[^\(\)]+|\([^\(\)]+\))+)\)/';
if( 1 === preg_match( $pattern, $sql, $matches))
{
$table = $matches[1];
$unique = $matches[2];
$index = $matches[3];
$columns = $matches[4];
// Remove prefix indexing
// Rarely used and apparently unnecessary for current uses
$columns = preg_replace( '/\([^\)]*\)/', '', $columns);
// Workaround for index name duplicate
$index = $table.'_'.$index;
$sql = "CREATE {$unique}INDEX $index ON $table ($columns)";
}
$pattern = '/ALTER TABLE\s+(\w+)\s+DROP INDEX\s+([^\s]+)/';
if( 1 === preg_match( $pattern, $sql, $matches))
{
$table = $matches[1];
$index = $matches[2];
$sql = "DROP INDEX ${table}_${index}";
}
$pattern = '/ALTER TABLE\s+(\w+)\s+DROP PRIMARY KEY/';
if( 1 === preg_match( $pattern, $sql, $matches))
{
$table = $matches[1];
$sql = "ALTER TABLE ${table} DROP CONSTRAINT ${table}_pkey";
}
}
// Table description
elseif( 0 === strpos( $sql, 'DESCRIBE'))
{
$logto = 'DESCRIBE';
preg_match( '/DESCRIBE\s+(\w+)/', $sql, $matches);
$table_name = $matches[1];
$sql = "SELECT pg_attribute.attname AS \"Field\",
CASE pg_type.typname
WHEN 'int2' THEN 'int(4)'
WHEN 'int4' THEN 'int(11)'
WHEN 'int8' THEN 'bigint(20) unsigned'
WHEN 'varchar' THEN 'varchar(' || pg_attribute.atttypmod-4 || ')'
WHEN 'timestamp' THEN 'datetime'
WHEN 'text' THEN 'longtext'
ELSE pg_type.typname
END AS \"Type\",
CASE WHEN pg_attribute.attnotnull THEN ''
ELSE 'YES'
END AS \"Null\",
CASE pg_type.typname
WHEN 'varchar' THEN substring(pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) FROM '^''(.*)''.*$')
WHEN 'timestamp' THEN CASE WHEN pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) LIKE '%now()%' THEN '0000-00-00 00:00:00' ELSE pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) END
ELSE pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid)
END AS \"Default\"
FROM pg_class
INNER JOIN pg_attribute
ON (pg_class.oid=pg_attribute.attrelid)
INNER JOIN pg_type
ON (pg_attribute.atttypid=pg_type.oid)
LEFT JOIN pg_attrdef
ON (pg_class.oid=pg_attrdef.adrelid AND pg_attribute.attnum=pg_attrdef.adnum)
WHERE pg_class.relname='$table_name' AND pg_attribute.attnum>=1 AND NOT pg_attribute.attisdropped;";
} // DESCRIBE
// Fix table creations
elseif( 0 === strpos($sql, 'CREATE TABLE'))
{
$logto = 'CREATE';
$sql = str_replace( 'CREATE TABLE IF NOT EXISTS ', 'CREATE TABLE ', $sql);
$pattern = '/CREATE TABLE [`]?(\w+)[`]?/';
preg_match($pattern, $sql, $matches);
$table = $matches[1];
// Remove trailing spaces
$sql = trim( $sql).';';
// Translate types and some other replacements
$sql = str_replace(
array_keys($GLOBALS['pg4wp_ttr']), array_values($GLOBALS['pg4wp_ttr']), $sql);
// Fix auto_increment by adding a sequence
$pattern = '/int[ ]+NOT NULL auto_increment/';
preg_match($pattern, $sql, $matches);
if($matches)
{
$seq = $table . '_seq';
$sql = str_replace( 'NOT NULL auto_increment', "NOT NULL DEFAULT nextval('$seq'::text)", $sql);
$sql .= "\nCREATE SEQUENCE $seq;";
}
// Support for INDEX creation
$pattern = '/,\s+(UNIQUE |)KEY\s+([^\s]+)\s+\(((?:[\w]+(?:\([\d]+\))?[,]?)*)\)/';
if( preg_match_all( $pattern, $sql, $matches, PREG_SET_ORDER))
foreach( $matches as $match)
{
$unique = $match[1];
$index = $match[2];
$columns = $match[3];
$columns = preg_replace( '/\(\d+\)/', '', $columns);
// Workaround for index name duplicate
$index = $table.'_'.$index;
$sql .= "\nCREATE {$unique}INDEX $index ON $table ($columns);";
}
// Now remove handled indexes
$sql = preg_replace( $pattern, '', $sql);
}// CREATE TABLE
elseif( 0 === strpos($sql, 'DROP TABLE'))
{
$logto = 'DROPTABLE';
$pattern = '/DROP TABLE.+ [`]?(\w+)[`]?$/';
preg_match($pattern, $sql, $matches);
$table = $matches[1];
$seq = $table . '_seq';
$sql .= ";\nDROP SEQUENCE IF EXISTS $seq;";
}// DROP TABLE
return $sql;
}

View File

@ -12,7 +12,7 @@ spl_autoload_register(function ($className) {
function createSQLRewriter(string $sql): AbstractSQLRewriter
{
$sql = trim($sql);
if (preg_match('/^(SELECT|INSERT|UPDATE|DELETE|SHOW TABLES|OPTIMIZE TABLE|SET NAMES|SHOW FULL COLUMNS)\b/i', $sql, $matches)) {
if (preg_match('/^(SELECT|INSERT|UPDATE|DELETE|DESCRIBE|ALTER TABLE|CREATE TABLE|DROP TABLE|SHOW INDEX|SHOW VARIABLES|SHOW TABLES|OPTIMIZE TABLE|SET NAMES|SHOW FULL COLUMNS)\b/i', $sql, $matches)) {
// Convert to a format suitable for class names (e.g., "SHOW TABLES" becomes "ShowTables")
$type = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($matches[1]))));
$className = $type . 'SQLRewriter';
@ -61,7 +61,6 @@ function pg4wp_rewrite($sql)
default:
}
$sql = loadInstallFunctions($sql, $logto);
$sql = correctMetaValue($sql);
$sql = handleInterval($sql);
$sql = cleanAndCapitalize($sql);
@ -107,24 +106,6 @@ function pg4wp_rewrite($sql)
return $sql;
}
/**
* Load upgrade and install functions as required.
*
* @param string $sql SQL query string
* @param string $logto Logging type
* @return string Modified SQL query string
*/
function loadInstallFunctions($sql, &$logto)
{
$begin = strtoupper(substr($sql, 0, 3));
$search = array('SHO', 'ALT', 'DES', 'CRE', 'DRO');
if (in_array($begin, $search)) {
require_once(PG4WP_ROOT . '/driver_pgsql_install.php');
$sql = pg4wp_installing($sql, $logto);
}
return $sql;
}
/**
* Correct the meta_value field for WP 2.9.1 and add type cast.
*

View File

@ -0,0 +1,124 @@
<?php
class AlterTableSQLRewriter extends AbstractSQLRewriter
{
public function rewrite(): string
{
// List of types translations (the key is the mysql one, the value is the text to use instead)
$typeTranslations = array(
'bigint(20)' => 'bigint',
'bigint(10)' => 'int',
'int(11)' => 'int',
'tinytext' => 'text',
'mediumtext' => 'text',
'longtext' => 'text',
'unsigned' => '',
'gmt datetime NOT NULL default \'0000-00-00 00:00:00\'' => 'gmt timestamp NOT NULL DEFAULT timezone(\'gmt\'::text, now())',
'default \'0000-00-00 00:00:00\'' => 'DEFAULT now()',
'\'0000-00-00 00:00:00\'' => 'now()',
'datetime' => 'timestamp',
'DEFAULT CHARACTER SET utf8' => '',
// WP 2.7.1 compatibility
'int(4)' => 'smallint',
// For WPMU (starting with WP 3.2)
'tinyint(2)' => 'smallint',
'tinyint(1)' => 'smallint',
"enum('0','1')" => 'smallint',
'COLLATE utf8_general_ci' => '',
// For flash-album-gallery plugin
'tinyint' => 'smallint',
);
$pattern = '/ALTER TABLE\s+(\w+)\s+CHANGE COLUMN\s+([^\s]+)\s+([^\s]+)\s+([^ ]+)( unsigned|)\s*(NOT NULL|)\s*(default (.+)|)/';
if(1 === preg_match($pattern, $sql, $matches)) {
$table = $matches[1];
$col = $matches[2];
$newname = $matches[3];
$type = strtolower($matches[4]);
if(isset($typeTranslations[$type])) {
$type = $typeTranslations[$type];
}
$unsigned = $matches[5];
$notnull = $matches[6];
$default = $matches[7];
$defval = $matches[8];
if(isset($typeTranslations[$defval])) {
$defval = $typeTranslations[$defval];
}
$newq = "ALTER TABLE $table ALTER COLUMN $col TYPE $type";
if(!empty($notnull)) {
$newq .= ", ALTER COLUMN $col SET NOT NULL";
}
if(!empty($default)) {
$newq .= ", ALTER COLUMN $col SET DEFAULT $defval";
}
if($col != $newname) {
$newq .= ";ALTER TABLE $table RENAME COLUMN $col TO $newcol;";
}
$sql = $newq;
}
$pattern = '/ALTER TABLE\s+(\w+)\s+ALTER COLUMN\s+/';
if(1 === preg_match($pattern, $sql)) {
// Translate default values
$sql = str_replace(
array_keys($typeTranslations),
array_values($typeTranslations),
$sql
);
}
$pattern = '/ALTER TABLE\s+(\w+)\s+ADD COLUMN\s+([^\s]+)\s+([^ ]+)( unsigned|)\s+(NOT NULL|)\s*(default (.+)|)/';
if(1 === preg_match($pattern, $sql, $matches)) {
$table = $matches[1];
$col = $matches[2];
$type = strtolower($matches[3]);
if(isset($typeTranslations[$type])) {
$type = $typeTranslations[$type];
}
$unsigned = $matches[4];
$notnull = $matches[5];
$default = $matches[6];
$defval = $matches[7];
if(isset($typeTranslations[$defval])) {
$defval = $typeTranslations[$defval];
}
$newq = "ALTER TABLE $table ADD COLUMN $col $type";
if(!empty($default)) {
$newq .= " DEFAULT $defval";
}
if(!empty($notnull)) {
$newq .= " NOT NULL";
}
$sql = $newq;
}
$pattern = '/ALTER TABLE\s+(\w+)\s+ADD (UNIQUE |)KEY\s+([^\s]+)\s+\(((?:[^\(\)]+|\([^\(\)]+\))+)\)/';
if(1 === preg_match($pattern, $sql, $matches)) {
$table = $matches[1];
$unique = $matches[2];
$index = $matches[3];
$columns = $matches[4];
// Remove prefix indexing
// Rarely used and apparently unnecessary for current uses
$columns = preg_replace('/\([^\)]*\)/', '', $columns);
// Workaround for index name duplicate
$index = $table . '_' . $index;
$sql = "CREATE {$unique}INDEX $index ON $table ($columns)";
}
$pattern = '/ALTER TABLE\s+(\w+)\s+DROP INDEX\s+([^\s]+)/';
if(1 === preg_match($pattern, $sql, $matches)) {
$table = $matches[1];
$index = $matches[2];
$sql = "DROP INDEX ${table}_${index}";
}
$pattern = '/ALTER TABLE\s+(\w+)\s+DROP PRIMARY KEY/';
if(1 === preg_match($pattern, $sql, $matches)) {
$table = $matches[1];
$sql = "ALTER TABLE ${table} DROP CONSTRAINT ${table}_pkey";
}
return $sql;
}
}

View File

@ -0,0 +1,80 @@
<?php
class CreateTableSQLRewriter extends AbstractSQLRewriter
{
public function rewrite(): string
{
// List of types translations (the key is the mysql one, the value is the text to use instead)
$typeTranslations = array(
'bigint(20)' => 'bigint',
'bigint(10)' => 'int',
'int(11)' => 'int',
'tinytext' => 'text',
'mediumtext' => 'text',
'longtext' => 'text',
'unsigned' => '',
'gmt datetime NOT NULL default \'0000-00-00 00:00:00\'' => 'gmt timestamp NOT NULL DEFAULT timezone(\'gmt\'::text, now())',
'default \'0000-00-00 00:00:00\'' => 'DEFAULT now()',
'\'0000-00-00 00:00:00\'' => 'now()',
'datetime' => 'timestamp',
'DEFAULT CHARACTER SET utf8mb4' => '',
'DEFAULT CHARACTER SET utf8' => '',
// WP 2.7.1 compatibility
'int(4)' => 'smallint',
// For WPMU (starting with WP 3.2)
'tinyint(2)' => 'smallint',
'tinyint(1)' => 'smallint',
"enum('0','1')" => 'smallint',
'COLLATE utf8mb4_unicode_520_ci' => '',
'COLLATE utf8_general_ci' => '',
// For flash-album-gallery plugin
'tinyint' => 'smallint',
);
$sql = $this->original();
$sql = str_replace('CREATE TABLE IF NOT EXISTS ', 'CREATE TABLE ', $sql);
$pattern = '/CREATE TABLE [`]?(\w+)[`]?/';
preg_match($pattern, $sql, $matches);
$table = $matches[1];
// Remove trailing spaces
$sql = trim($sql) . ';';
// Translate types and some other replacements
$sql = str_replace(
array_keys($typeTranslations),
array_values($typeTranslations),
$sql
);
// Fix auto_increment by adding a sequence
$pattern = '/int[ ]+NOT NULL auto_increment/';
preg_match($pattern, $sql, $matches);
if($matches) {
$seq = $table . '_seq';
$sql = str_replace('NOT NULL auto_increment', "NOT NULL DEFAULT nextval('$seq'::text)", $sql);
$sql .= "\nCREATE SEQUENCE $seq;";
}
// Support for INDEX creation
$pattern = '/,\s+(UNIQUE |)KEY\s+([^\s]+)\s+\(((?:[\w]+(?:\([\d]+\))?[,]?)*)\)/';
if(preg_match_all($pattern, $sql, $matches, PREG_SET_ORDER)) {
foreach($matches as $match) {
$unique = $match[1];
$index = $match[2];
$columns = $match[3];
$columns = preg_replace('/\(\d+\)/', '', $columns);
// Workaround for index name duplicate
$index = $table . '_' . $index;
$sql .= "\nCREATE {$unique}INDEX $index ON $table ($columns);";
}
}
// Now remove handled indexes
$sql = preg_replace($pattern, '', $sql);
return $sql;
}
}

View File

@ -0,0 +1,80 @@
<?php
class DescribeSQLRewriter extends AbstractSQLRewriter
{
public function rewrite(): string
{
$sql = $this->original();
$table = $this->extractTableName($sql);
return $this->generatePostgresDescribeTable($table);
}
/**
* Extracts table name from a "DESCRIBE" SQL statement.
*
* @param string $sql The SQL statement
* @return string|null The table name if found, or null otherwise
*/
protected function extractTableName($sql)
{
$pattern = "/DESCRIBE ['\"`]?([^'\"`]+)['\"`]?/i";
if (preg_match($pattern, $sql, $matches)) {
return $matches[1];
}
return null;
}
/**
* Generates a PostgreSQL-compatible SQL query to mimic MySQL's "DESCRIBE".
*
* @param string $tableName The table name
* @return string The generated SQL query
*/
public function generatePostgresDescribeTable($tableName)
{
$sql = <<<SQL
SELECT
f.attnum AS number,
f.attname AS name,
f.attnum,
f.attnotnull AS notnull,
pg_catalog.format_type(f.atttypid,f.atttypmod) AS type,
CASE
WHEN p.contype = 'p' THEN 't'
ELSE 'f'
END AS primarykey,
CASE
WHEN p.contype = 'u' THEN 't'
ELSE 'f'
END AS uniquekey,
CASE
WHEN p.contype = 'f' THEN g.relname
END AS foreignkey,
CASE
WHEN p.contype = 'f' THEN p.confkey
END AS foreignkey_fieldnum,
CASE
WHEN p.contype = 'f' THEN g.relname
END AS foreignkey,
CASE
WHEN p.contype = 'f' THEN p.conkey
END AS foreignkey_connnum,
CASE
WHEN f.atthasdef = 't' THEN pg_get_expr(d.adbin, d.adrelid)
END AS default
FROM pg_attribute f
JOIN pg_class c ON c.oid = f.attrelid
JOIN pg_type t ON t.oid = f.atttypid
LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
LEFT JOIN pg_class AS g ON p.confrelid = g.oid
WHERE c.relkind = 'r'::char
AND n.nspname = 'public'
AND c.relname = '$tableName'
AND f.attnum > 0 ORDER BY number
SQL;
return $sql;
}
}

View File

@ -0,0 +1,17 @@
<?php
class DropTableSQLRewriter extends AbstractSQLRewriter
{
public function rewrite(): string
{
$sql = $this->original();
$pattern = '/DROP TABLE.+ [`]?(\w+)[`]?$/';
preg_match($pattern, $sql, $matches);
$table = $matches[1];
$seq = $table . '_seq';
$sql .= ";\nDROP SEQUENCE IF EXISTS $seq;";
return $sql;
}
}

View File

@ -15,7 +15,8 @@ class ShowFullColumnsSQLRewriter extends AbstractSQLRewriter
* @param string $sql The SQL statement
* @return string|null The table name if found, or null otherwise
*/
protected function extractTableNameFromShowColumns($sql) {
protected function extractTableNameFromShowColumns($sql)
{
$pattern = "/SHOW FULL COLUMNS FROM ['\"`]?([^'\"`]+)['\"`]?/i";
if (preg_match($pattern, $sql, $matches)) {
return $matches[1];
@ -29,7 +30,8 @@ class ShowFullColumnsSQLRewriter extends AbstractSQLRewriter
* @param string $tableName The table name
* @return string The generated SQL query
*/
function generatePostgresShowColumns($tableName) {
public function generatePostgresShowColumns($tableName)
{
$sql = <<<SQL
SELECT
a.attname AS "Field",

View File

@ -0,0 +1,62 @@
<?php
class ShowIndexSQLRewriter extends AbstractSQLRewriter
{
public function rewrite(): string
{
$sql = $this->original();
$table = $this->extractTableNameFromShowIndex($sql);
return $this->generatePostgresShowIndexFrom($table);
}
/**
* Extracts table name from a "SHOW FULL COLUMNS" SQL statement.
*
* @param string $sql The SQL statement
* @return string|null The table name if found, or null otherwise
*/
protected function extractVariableName($sql)
{
$pattern = "/SHOW INDEX FROM ['\"`]?([^'\"`]+)['\"`]?/i";
if (preg_match($pattern, $sql, $matches)) {
return $matches[1];
}
return null;
}
/**
* Generates a PostgreSQL-compatible SQL query to mimic MySQL's "SHOW INDEX FROM".
*
* @param string $tableName The table name
* @return string The generated SQL query
*/
public function generatePostgresShowIndexFrom($tableName)
{
$sql = <<<SQL
SELECT bc.relname AS "Table",
CASE WHEN i.indisunique THEN '0' ELSE '1' END AS "Non_unique",
CASE WHEN i.indisprimary THEN 'PRIMARY' WHEN bc.relname LIKE '%usermeta' AND ic.relname = 'umeta_key'
THEN 'meta_key' ELSE REPLACE( ic.relname, '' . $table . '_', '') END AS "Key_name",
a.attname AS "Column_name",
NULL AS "Sub_part"
FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a
WHERE bc.oid = i.indrelid
AND ic.oid = i.indexrelid
AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum)
AND a.attrelid = bc.oid
AND bc.relname = '' . $tableName . ''
ORDER BY "Key_name", CASE a.attnum
WHEN i.indkey[0] THEN 0
WHEN i.indkey[1] THEN 1
WHEN i.indkey[2] THEN 2
WHEN i.indkey[3] THEN 3
WHEN i.indkey[4] THEN 4
WHEN i.indkey[5] THEN 5
WHEN i.indkey[6] THEN 6
WHEN i.indkey[7] THEN 7
END
SQL;
return $sql;
}
}

View File

@ -0,0 +1,43 @@
<?php
class ShowVariablesSQLRewriter extends AbstractSQLRewriter
{
public function rewrite(): string
{
$sql = $this->original();
$table = $this->extractVariableName($sql);
return $this->generatePostgres($sql, $variableName);
}
/**
* Extracts table name from a "SHOW FULL COLUMNS" SQL statement.
*
* @param string $sql The SQL statement
* @return string|null The table name if found, or null otherwise
*/
protected function extractVariableName($sql)
{
$pattern = "/SHOW VARIABLES LIKE ['\"`]?([^'\"`]+)['\"`]?/i";
if (preg_match($pattern, $sql, $matches)) {
return $matches[1];
}
return null;
}
/**
* Generates a PostgreSQL-compatible SQL query to mimic MySQL's "SHOW VARIABLES".
*
* @param string $tableName The table name
* @return string The generated SQL query
*/
public function generatePostgres($sql, $variableName)
{
if ($variableName == "sql_mode") {
// Act like MySQL default configuration, where sql_mode is ""
return "SELECT '$variableName' AS \"Variable_name\", '' AS \"Value\";";
}
// return untransformed sql
return $sql;
}
}

47
wp-includes/version.php Normal file
View File

@ -0,0 +1,47 @@
<?php
/**
* WordPress Version
*
* Contains version information for the current WordPress release.
*
* @package WordPress
* @since 1.2.0
*/
/**
* The WordPress version string.
*
* Holds the current version number for WordPress core. Used to bust caches
* and to enable development mode for scripts when running from the /src directory.
*
* @global string $wp_version
*/
$wp_version = '6.3.2';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
*
* @global int $wp_db_version
*/
$wp_db_version = 55853;
/**
* Holds the TinyMCE version.
*
* @global string $tinymce_version
*/
$tinymce_version = '49110-20201110';
/**
* Holds the required PHP version.
*
* @global string $required_php_version
*/
$required_php_version = '7.0.0';
/**
* Holds the required MySQL version.
*
* @global string $required_mysql_version
*/
$required_mysql_version = '5.0';