From 0b09d08fb1991b8f6c36d2ade71f069650a7f8a8 Mon Sep 17 00:00:00 2001 From: Matthew Bucci Date: Tue, 16 Jan 2024 22:17:07 -0800 Subject: [PATCH 1/5] remove unncessary string replacement for != to <> --- pg4wp/rewriters/SelectSQLRewriter.php | 1 - tests/rewriteTest.php | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/pg4wp/rewriters/SelectSQLRewriter.php b/pg4wp/rewriters/SelectSQLRewriter.php index 90b6d37..d90b8f0 100644 --- a/pg4wp/rewriters/SelectSQLRewriter.php +++ b/pg4wp/rewriters/SelectSQLRewriter.php @@ -110,7 +110,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); diff --git a/tests/rewriteTest.php b/tests/rewriteTest.php index 786c3a3..5ea4c30 100644 --- a/tests/rewriteTest.php +++ b/tests/rewriteTest.php @@ -382,6 +382,27 @@ final class rewriteTest extends TestCase $this->assertSame(trim($expected), trim($postgresql)); } + public function test_it_doesnt_rewrite_when_it_doesnt_need_to() + { + $sql = <<assertSame(trim($expected), trim($postgresql)); + } protected function setUp(): void From 0dd4213cfccc8e2cc54834a36fd0ca367d29b42a Mon Sep 17 00:00:00 2001 From: Matthew Bucci Date: Tue, 16 Jan 2024 22:45:28 -0800 Subject: [PATCH 2/5] handle Alter Table Add Index behavior --- pg4wp/rewriters/AlterTableSQLRewriter.php | 38 +++++++++++++++++++++-- phpunit.xml | 5 ++- tests/rewriteTest.php | 29 +++++++++++++++++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/pg4wp/rewriters/AlterTableSQLRewriter.php b/pg4wp/rewriters/AlterTableSQLRewriter.php index 658a499..054c7ce 100644 --- a/pg4wp/rewriters/AlterTableSQLRewriter.php +++ b/pg4wp/rewriters/AlterTableSQLRewriter.php @@ -35,28 +35,62 @@ class AlterTableSQLRewriter extends AbstractSQLRewriter { $sql = $this->original(); + 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 (.+)|)/'; @@ -168,7 +202,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; @@ -180,7 +214,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; diff --git a/phpunit.xml b/phpunit.xml index 02f5990..0078610 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,9 +1,12 @@ - + tests diff --git a/tests/rewriteTest.php b/tests/rewriteTest.php index 5ea4c30..931d14f 100644 --- a/tests/rewriteTest.php +++ b/tests/rewriteTest.php @@ -404,6 +404,35 @@ final class rewriteTest extends TestCase $this->assertSame(trim($expected), trim($postgresql)); } + public function test_it_handles_alter_tables_with_indexes() + { + $sql = <<assertSame(trim($expected), trim($postgresql)); + + } + + public function test_it_handles_alter_tables_with_unique_indexes() + { + $sql = <<assertSame(trim($expected), trim($postgresql)); + } + protected function setUp(): void { From b76a569a15c98e563536119fe5eabef738b0dfdc Mon Sep 17 00:00:00 2001 From: Matthew Bucci Date: Wed, 21 Feb 2024 01:12:35 -0800 Subject: [PATCH 3/5] add test for single quotes --- tests/rewriteTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/rewriteTest.php b/tests/rewriteTest.php index 931d14f..307ca89 100644 --- a/tests/rewriteTest.php +++ b/tests/rewriteTest.php @@ -433,6 +433,22 @@ final class rewriteTest extends TestCase $this->assertSame(trim($expected), trim($postgresql)); } + public function test_it_doesnt_remove_single_quotes() + { + $sql = <<assertSame(trim($expected), trim($postgresql)); + } + + + protected function setUp(): void { From b7be1aa89a3486d2f80456e83d090e5ce273ec17 Mon Sep 17 00:00:00 2001 From: Matthew Bucci Date: Tue, 27 Feb 2024 00:53:05 -0800 Subject: [PATCH 4/5] add rewriting for protected column names --- pg4wp/rewriters/AlterTableSQLRewriter.php | 25 +++++++++ pg4wp/rewriters/CreateTableSQLRewriter.php | 25 +++++++++ tests/rewriteTest.php | 63 ++++++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/pg4wp/rewriters/AlterTableSQLRewriter.php b/pg4wp/rewriters/AlterTableSQLRewriter.php index db819e7..27a0c95 100644 --- a/pg4wp/rewriters/AlterTableSQLRewriter.php +++ b/pg4wp/rewriters/AlterTableSQLRewriter.php @@ -30,6 +30,7 @@ class AlterTableSQLRewriter extends AbstractSQLRewriter $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); @@ -262,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, 1); + return $prefix . $columnsAndKeys . $suffix; + } + + return $sql; + } } diff --git a/pg4wp/rewriters/CreateTableSQLRewriter.php b/pg4wp/rewriters/CreateTableSQLRewriter.php index 8bd2b4f..eb4be6e 100644 --- a/pg4wp/rewriters/CreateTableSQLRewriter.php +++ b/pg4wp/rewriters/CreateTableSQLRewriter.php @@ -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, 1); + return $prefix . $columnsAndKeys . $suffix; + } + + return $sql; + } } diff --git a/tests/rewriteTest.php b/tests/rewriteTest.php index 118ade0..b2de577 100644 --- a/tests/rewriteTest.php +++ b/tests/rewriteTest.php @@ -530,6 +530,69 @@ final class rewriteTest extends TestCase $this->assertSame(trim($expected), trim($postgresql)); } + public function test_it_rewrites_protected_column_names() + { + $sql = <<assertSame(trim($expected), trim($postgresql)); + } + + public function test_it_rewrites_advanced_protected_column_names() + { + $sql = <<assertSame(trim($expected), trim($postgresql)); + } + + public function test_it_doesnt_remove_single_quotes() { $sql = << Date: Tue, 27 Feb 2024 00:58:58 -0800 Subject: [PATCH 5/5] update expected results for create table tests with protected names --- pg4wp/rewriters/AlterTableSQLRewriter.php | 2 +- pg4wp/rewriters/CreateTableSQLRewriter.php | 2 +- tests/rewriteTest.php | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pg4wp/rewriters/AlterTableSQLRewriter.php b/pg4wp/rewriters/AlterTableSQLRewriter.php index 27a0c95..ac3d639 100644 --- a/pg4wp/rewriters/AlterTableSQLRewriter.php +++ b/pg4wp/rewriters/AlterTableSQLRewriter.php @@ -281,7 +281,7 @@ class AlterTableSQLRewriter extends AbstractSQLRewriter }; // Replace protected column names with quoted versions within columns and keys part - $columnsAndKeys = preg_replace_callback($regex, $callback, $columnsAndKeys, 1); + $columnsAndKeys = preg_replace_callback($regex, $callback, $columnsAndKeys); return $prefix . $columnsAndKeys . $suffix; } diff --git a/pg4wp/rewriters/CreateTableSQLRewriter.php b/pg4wp/rewriters/CreateTableSQLRewriter.php index eb4be6e..948c9ce 100644 --- a/pg4wp/rewriters/CreateTableSQLRewriter.php +++ b/pg4wp/rewriters/CreateTableSQLRewriter.php @@ -152,7 +152,7 @@ class CreateTableSQLRewriter extends AbstractSQLRewriter }; // Replace protected column names with quoted versions within columns and keys part - $columnsAndKeys = preg_replace_callback($regex, $callback, $columnsAndKeys, 1); + $columnsAndKeys = preg_replace_callback($regex, $callback, $columnsAndKeys); return $prefix . $columnsAndKeys . $suffix; } diff --git a/tests/rewriteTest.php b/tests/rewriteTest.php index b2de577..d9172f2 100644 --- a/tests/rewriteTest.php +++ b/tests/rewriteTest.php @@ -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)