Merge pull request #93 from PostgreSQL-For-Wordpress/rewrite-fixes
Improve string replacements
This commit is contained in:
@ -28,31 +28,67 @@ class AlterTableSQLRewriter extends AbstractSQLRewriter
|
||||
public function rewrite(): string
|
||||
{
|
||||
$sql = $this->original();
|
||||
|
||||
|
||||
$sql = $this->rewrite_numeric_type($sql);
|
||||
$sql = $this->rewrite_columns_with_protected_names($sql);
|
||||
|
||||
if (str_contains($sql, 'ADD INDEX') || str_contains($sql, 'ADD UNIQUE INDEX')) {
|
||||
$sql = $this->rewriteAddIndex($sql);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
if (str_contains($sql, 'CHANGE COLUMN')) {
|
||||
$sql = $this->rewriteChangeColumn($sql);
|
||||
return $sql;
|
||||
}
|
||||
if (str_contains($sql, 'ALTER COLUMN')) {
|
||||
$sql = $this->rewriteAlterColumn($sql);
|
||||
return $sql;
|
||||
}
|
||||
if (str_contains($sql, 'ADD COLUMN')) {
|
||||
$sql = $this->rewriteAddColumn($sql);
|
||||
return $sql;
|
||||
}
|
||||
if (str_contains($sql, 'ADD KEY') || str_contains($sql, 'ADD UNIQUE KEY')) {
|
||||
$sql = $this->rewriteAddKey($sql);
|
||||
return $sql;
|
||||
}
|
||||
if (str_contains($sql, 'DROP INDEX')) {
|
||||
$sql = $this->rewriteDropIndex($sql);
|
||||
return $sql;
|
||||
}
|
||||
if (str_contains($sql, 'DROP PRIMARY KEY')) {
|
||||
$sql = $this->rewriteDropPrimaryKey($sql);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
private function rewriteAddIndex(string $sql): string
|
||||
{
|
||||
$pattern = '/ALTER TABLE\s+(\w+)\s+ADD (UNIQUE |)INDEX\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;
|
||||
|
||||
// Add backticks around index name and column name, and include IF NOT EXISTS clause
|
||||
$sql = "CREATE {$unique}INDEX IF NOT EXISTS `{$index}` ON `{$table}` (`{$columns}`)";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
private function rewriteChangeColumn(string $sql): string
|
||||
{
|
||||
$pattern = '/ALTER TABLE\s+(\w+)\s+CHANGE COLUMN\s+([^\s]+)\s+([^\s]+)\s+([^ ]+)( unsigned|)\s*(NOT NULL|)\s*(default (.+)|)/';
|
||||
@ -164,7 +200,7 @@ class AlterTableSQLRewriter extends AbstractSQLRewriter
|
||||
if(1 === preg_match($pattern, $sql, $matches)) {
|
||||
$table = $matches[1];
|
||||
$index = $matches[2];
|
||||
$sql = "DROP INDEX ${table}_${index}";
|
||||
$sql = "DROP INDEX {$table}_{$index}";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
@ -176,7 +212,7 @@ class AlterTableSQLRewriter extends AbstractSQLRewriter
|
||||
|
||||
if(1 === preg_match($pattern, $sql, $matches)) {
|
||||
$table = $matches[1];
|
||||
$sql = "ALTER TABLE ${table} DROP CONSTRAINT ${table}_pkey";
|
||||
$sql = "ALTER TABLE {$table} DROP CONSTRAINT {$table}_pkey";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
@ -227,4 +263,28 @@ class AlterTableSQLRewriter extends AbstractSQLRewriter
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
private function rewrite_columns_with_protected_names($sql)
|
||||
{
|
||||
// Splitting the SQL statement into parts before "(", inside "(", and after ")"
|
||||
if (preg_match('/^(CREATE TABLE IF NOT EXISTS|CREATE TABLE|ALTER TABLE)\s+([^\s]+)\s*\((.*)\)(.*)$/is', $sql, $matches)) {
|
||||
$prefix = $matches[1] . ' ' . $matches[2] . ' (';
|
||||
$columnsAndKeys = $matches[3];
|
||||
$suffix = ')' . $matches[4];
|
||||
|
||||
$regex = '/(?:^|\s*,\s*)(\b(?:timestamp|date|time|default)\b)\s*(?=\s+\w+)/i';
|
||||
|
||||
// Callback function to add quotes around protected column names
|
||||
$callback = function($matches) {
|
||||
$whitespace = str_replace($matches[1], "", $matches[0]);
|
||||
return $whitespace . '"' . $matches[1] . '"';
|
||||
};
|
||||
|
||||
// Replace protected column names with quoted versions within columns and keys part
|
||||
$columnsAndKeys = preg_replace_callback($regex, $callback, $columnsAndKeys);
|
||||
return $prefix . $columnsAndKeys . $suffix;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ class CreateTableSQLRewriter extends AbstractSQLRewriter
|
||||
);
|
||||
|
||||
$sql = $this->rewrite_numeric_type($sql);
|
||||
$sql = $this->rewrite_columns_with_protected_names($sql);
|
||||
|
||||
// Support for UNIQUE INDEX creation
|
||||
$pattern = '/,\s*(UNIQUE |)KEY\s+(`[^`]+`|\w+)\s+\(((?:[^()]|\([^)]*\))*)\)/';
|
||||
@ -133,4 +134,28 @@ class CreateTableSQLRewriter extends AbstractSQLRewriter
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
private function rewrite_columns_with_protected_names($sql)
|
||||
{
|
||||
// Splitting the SQL statement into parts before "(", inside "(", and after ")"
|
||||
if (preg_match('/^(CREATE TABLE IF NOT EXISTS|CREATE TABLE|ALTER TABLE)\s+([^\s]+)\s*\((.*)\)(.*)$/is', $sql, $matches)) {
|
||||
$prefix = $matches[1] . ' ' . $matches[2] . ' (';
|
||||
$columnsAndKeys = $matches[3];
|
||||
$suffix = ')' . $matches[4];
|
||||
|
||||
$regex = '/(?:^|\s*,\s*)(\b(?:timestamp|date|time|default)\b)\s*(?=\s+\w+)/i';
|
||||
|
||||
// Callback function to add quotes around protected column names
|
||||
$callback = function($matches) {
|
||||
$whitespace = str_replace($matches[1], "", $matches[0]);
|
||||
return $whitespace . '"' . $matches[1] . '"';
|
||||
};
|
||||
|
||||
// Replace protected column names with quoted versions within columns and keys part
|
||||
$columnsAndKeys = preg_replace_callback($regex, $callback, $columnsAndKeys);
|
||||
return $prefix . $columnsAndKeys . $suffix;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,6 @@ class SelectSQLRewriter extends AbstractSQLRewriter
|
||||
if(isset($wpdb)) {
|
||||
$sql = str_replace('GROUP BY ' . $wpdb->prefix . 'posts.ID', '', $sql);
|
||||
}
|
||||
$sql = str_replace("!= ''", '<> 0', $sql);
|
||||
|
||||
// MySQL 'LIKE' is case insensitive by default, whereas PostgreSQL 'LIKE' is
|
||||
$sql = str_replace(' LIKE ', ' ILIKE ', $sql);
|
||||
|
@ -1,9 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerErrors="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuites>
|
||||
<testsuite name="tests">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
|
@ -114,7 +114,7 @@ final class rewriteTest extends TestCase
|
||||
CREATE TABLE IF NOT EXISTS wp_itsec_dashboard_lockouts (
|
||||
id serial,
|
||||
ip varchar(40),
|
||||
time timestamp NOT NULL,
|
||||
"time" timestamp NOT NULL,
|
||||
count int NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
@ -184,7 +184,7 @@ final class rewriteTest extends TestCase
|
||||
CREATE TABLE IF NOT EXISTS wp_itsec_dashboard_lockouts (
|
||||
id serial,
|
||||
ip varchar(40),
|
||||
time timestamp NOT NULL,
|
||||
"time" timestamp NOT NULL,
|
||||
count int NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
@ -222,8 +222,8 @@ final class rewriteTest extends TestCase
|
||||
"ID" bigserial,
|
||||
ip varchar(60) NOT NULL,
|
||||
created int,
|
||||
timestamp int NOT NULL,
|
||||
date timestamp NOT NULL,
|
||||
"timestamp" int NOT NULL,
|
||||
"date" timestamp NOT NULL,
|
||||
referred text NOT NULL,
|
||||
agent varchar(255) NOT NULL,
|
||||
platform varchar(255),
|
||||
@ -264,7 +264,7 @@ final class rewriteTest extends TestCase
|
||||
page_id bigserial,
|
||||
uri varchar(190) NOT NULL,
|
||||
type varchar(180) NOT NULL,
|
||||
date date NOT NULL,
|
||||
"date" date NOT NULL,
|
||||
count int NOT NULL,
|
||||
id int NOT NULL,
|
||||
PRIMARY KEY (page_id)
|
||||
@ -479,8 +479,136 @@ final class rewriteTest extends TestCase
|
||||
$this->assertSame(trim($expected), trim($postgresql));
|
||||
}
|
||||
|
||||
public function test_it_doesnt_rewrite_when_it_doesnt_need_to()
|
||||
{
|
||||
$sql = <<<SQL
|
||||
SELECT p.ID FROM wp_posts p
|
||||
WHERE post_type='scheduled-action'
|
||||
AND p.post_status IN ('pending')
|
||||
AND p.post_modified_gmt <= '2023-11-27 14:23:34'
|
||||
AND p.post_password != '' ORDER BY p.post_date_gmt ASC LIMIT 0, 20
|
||||
SQL;
|
||||
|
||||
$expected = <<<SQL
|
||||
SELECT p."ID" , p.post_date_gmt FROM wp_posts p
|
||||
WHERE post_type='scheduled-action'
|
||||
AND p.post_status IN ('pending')
|
||||
AND p.post_modified_gmt <= '2023-11-27 14:23:34'
|
||||
AND p.post_password != '' ORDER BY p.post_date_gmt ASC LIMIT 20 OFFSET 0
|
||||
SQL;
|
||||
|
||||
$postgresql = pg4wp_rewrite($sql);
|
||||
$this->assertSame(trim($expected), trim($postgresql));
|
||||
}
|
||||
|
||||
public function test_it_handles_alter_tables_with_indexes()
|
||||
{
|
||||
$sql = <<<SQL
|
||||
ALTER TABLE wp_e_events ADD INDEX `created_at_index` (`created_at`)
|
||||
SQL;
|
||||
|
||||
$expected = <<<SQL
|
||||
CREATE INDEX IF NOT EXISTS wp_e_events_created_at_index ON wp_e_events (created_at)
|
||||
SQL;
|
||||
|
||||
$postgresql = pg4wp_rewrite($sql);
|
||||
$this->assertSame(trim($expected), trim($postgresql));
|
||||
|
||||
}
|
||||
|
||||
public function test_it_handles_alter_tables_with_unique_indexes()
|
||||
{
|
||||
$sql = <<<SQL
|
||||
ALTER TABLE wp_e_events ADD UNIQUE INDEX `created_at_index` (`created_at`)
|
||||
SQL;
|
||||
|
||||
$expected = <<<SQL
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS wp_e_events_created_at_index ON wp_e_events (created_at)
|
||||
SQL;
|
||||
|
||||
$postgresql = pg4wp_rewrite($sql);
|
||||
$this->assertSame(trim($expected), trim($postgresql));
|
||||
}
|
||||
|
||||
public function test_it_rewrites_protected_column_names()
|
||||
{
|
||||
$sql = <<<SQL
|
||||
CREATE TABLE wp_cmplz_cookiebanners (
|
||||
"ID" int NOT NULL DEFAULT nextval('wp_cmplz_cookiebanners_seq'::text),
|
||||
banner_version int NOT NULL,
|
||||
default int NOT NULL
|
||||
);
|
||||
SQL;
|
||||
|
||||
$expected = <<<SQL
|
||||
CREATE TABLE IF NOT EXISTS wp_cmplz_cookiebanners (
|
||||
"ID" int NOT NULL DEFAULT nextval('wp_cmplz_cookiebanners_seq'::text),
|
||||
banner_version int NOT NULL,
|
||||
"default" int NOT NULL
|
||||
);
|
||||
SQL;
|
||||
|
||||
$postgresql = pg4wp_rewrite($sql);
|
||||
$this->assertSame(trim($expected), trim($postgresql));
|
||||
}
|
||||
|
||||
public function test_it_rewrites_advanced_protected_column_names()
|
||||
{
|
||||
$sql = <<<SQL
|
||||
CREATE TABLE wp_statistics_pages (
|
||||
page_id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
uri varchar(190) NOT NULL,
|
||||
type varchar(180) NOT NULL,
|
||||
date date NOT NULL,
|
||||
count int(11) NOT NULL,
|
||||
id int(11) NOT NULL,
|
||||
UNIQUE KEY date_2 (date,uri),
|
||||
KEY url (uri),
|
||||
KEY date (date),
|
||||
KEY id (id),
|
||||
KEY `uri` (`uri`,`count`,`id`),
|
||||
PRIMARY KEY (`page_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci
|
||||
SQL;
|
||||
|
||||
$expected = <<<SQL
|
||||
CREATE TABLE IF NOT EXISTS wp_statistics_pages (
|
||||
page_id bigserial,
|
||||
uri varchar(190) NOT NULL,
|
||||
type varchar(180) NOT NULL,
|
||||
"date" date NOT NULL,
|
||||
count int NOT NULL,
|
||||
id int NOT NULL,
|
||||
PRIMARY KEY (page_id)
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS wp_statistics_pages_date_2 ON wp_statistics_pages (date,uri);
|
||||
CREATE INDEX IF NOT EXISTS wp_statistics_pages_url ON wp_statistics_pages (uri);
|
||||
CREATE INDEX IF NOT EXISTS wp_statistics_pages_date ON wp_statistics_pages (date);
|
||||
CREATE INDEX IF NOT EXISTS wp_statistics_pages_id ON wp_statistics_pages (id);
|
||||
CREATE INDEX IF NOT EXISTS wp_statistics_pages_uri ON wp_statistics_pages (uri,count,id);
|
||||
SQL;
|
||||
|
||||
$postgresql = pg4wp_rewrite($sql);
|
||||
$this->assertSame(trim($expected), trim($postgresql));
|
||||
}
|
||||
|
||||
|
||||
public function test_it_doesnt_remove_single_quotes()
|
||||
{
|
||||
$sql = <<<SQL
|
||||
SELECT COUNT(*) FROM wp_comments WHERE user_id = 5 AND comment_approved = '1'
|
||||
SQL;
|
||||
|
||||
$expected = <<<SQL
|
||||
SELECT COUNT(*) FROM wp_comments WHERE user_id = 5 AND comment_approved = '1'
|
||||
SQL;
|
||||
|
||||
$postgresql = pg4wp_rewrite($sql);
|
||||
$this->assertSame(trim($expected), trim($postgresql));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
|
Reference in New Issue
Block a user