From 6901c501b3d8f10d9fd5985c729bcd1834db511c Mon Sep 17 00:00:00 2001 From: dantleech Date: Thu, 4 Jun 2015 12:37:02 +0200 Subject: [PATCH] Enforce termination of queries --- CHANGELOG.md | 8 ++- features/all/phpcr_query_delete.feature | 10 ++-- features/all/phpcr_query_select.feature | 6 ++- features/all/phpcr_query_update.feature | 54 ++++++++++--------- features/shell/shell_alias.feature | 2 +- .../Command/Phpcr/BaseQueryCommand.php | 33 ++++++++++++ .../Command/Phpcr/QueryDeleteCommand.php | 15 ++---- .../Command/Phpcr/QuerySelectCommand.php | 10 ++-- .../Command/Phpcr/QueryUpdateCommand.php | 9 +--- src/PHPCR/Shell/Query/Validator.php | 29 ++++++++++ 10 files changed, 121 insertions(+), 55 deletions(-) create mode 100644 src/PHPCR/Shell/Console/Command/Phpcr/BaseQueryCommand.php create mode 100644 src/PHPCR/Shell/Query/Validator.php diff --git a/CHANGELOG.md b/CHANGELOG.md index eba24ef1..1616d913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ Changelog ========= - dev-master ---------- +### Bug fixes + +- [query] Enforce termination of queries with ";": fixes #162 + +beta2 +----- + ### Features - [profile:show] Added command to display current profile diff --git a/features/all/phpcr_query_delete.feature b/features/all/phpcr_query_delete.feature index 21330274..cbdbd1f5 100644 --- a/features/all/phpcr_query_delete.feature +++ b/features/all/phpcr_query_delete.feature @@ -19,6 +19,10 @@ Feature: Execute a a raw DELETE query in JCR_SQL2 """ Examples: | query | path | - | DELETE FROM [nt:unstructured] AS a WHERE localname() = 'product1' | /cms/products/product1 | - | delete FROM [nt:unstructured] as a where localname() = 'product1' | /cms/products/product1 | - | DELETE FROM nt:unstructured AS a WHERE localname() = 'product1' | /cms/products/product1 | + | DELETE FROM [nt:unstructured] AS a WHERE localname() = 'product1'; | /cms/products/product1 | + | delete FROM [nt:unstructured] as a where localname() = 'product1'; | /cms/products/product1 | + | DELETE FROM nt:unstructured AS a WHERE localname() = 'product1'; | /cms/products/product1 | + + Scenario: It should fail if a non terminated query is executed + Given I execute the "DELETE FROM [nt:unstructured] WHERE bar = 'product1'" command + Then the command should fail diff --git a/features/all/phpcr_query_select.feature b/features/all/phpcr_query_select.feature index 2917f1ac..96edb3ab 100644 --- a/features/all/phpcr_query_select.feature +++ b/features/all/phpcr_query_select.feature @@ -8,8 +8,12 @@ Feature: Execute a raw query in JCR_SQL2 And the "session_data.xml" fixtures are loaded Scenario: Execute query - Given I execute the "SELECT a.[jcr:createdBy], a.[jcr:primaryType] from [nt:folder] AS a WHERE localname() = 'emptyExample'" command + Given I execute the "SELECT a.[jcr:createdBy], a.[jcr:primaryType] from [nt:folder] AS a WHERE localname() = 'emptyExample';" command Then the command should not fail And I should see a table containing the following rows: | a.jcr:createdBy | a.jcr:primaryType | | admin | nt:folder | + + Scenario: It should fail if a non terminated query is executed + Given I execute the "SELECT * FROM [nt:unstructured] WHERE bar = 'product1'" command + Then the command should fail diff --git a/features/all/phpcr_query_update.feature b/features/all/phpcr_query_update.feature index cac81685..0b1fda83 100644 --- a/features/all/phpcr_query_update.feature +++ b/features/all/phpcr_query_update.feature @@ -18,21 +18,21 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 """ Examples: | query | path | property | expectedValue | - | UPDATE [nt:unstructured] AS a SET a.title = 'DTL' WHERE localname() = 'article1' | /cms/articles/article1 | title | DTL | - | update [nt:unstructured] as a set a.title = 'dtl' where localname() = 'article1' | /cms/articles/article1 | title | dtl | - | UPDATE nt:unstructured AS a SET a.title = 'DTL' WHERE localname() = 'article1' | /cms/articles/article1 | title | DTL | - | UPDATE nt:unstructured AS a SET title = 'DTL' WHERE localname() = 'article1' | /cms/articles/article1 | title | DTL | - | UPDATE nt:unstructured AS a SET title = 'DTL', foobar='barfoo' WHERE localname() = 'article1' | /cms/articles/article1 | foobar | barfoo | + | UPDATE [nt:unstructured] AS a SET a.title = 'DTL' WHERE localname() = 'article1'; | /cms/articles/article1 | title | DTL | + | update [nt:unstructured] as a set a.title = 'dtl' where localname() = 'article1'; | /cms/articles/article1 | title | dtl | + | UPDATE nt:unstructured AS a SET a.title = 'DTL' WHERE localname() = 'article1'; | /cms/articles/article1 | title | DTL | + | UPDATE nt:unstructured AS a SET title = 'DTL' WHERE localname() = 'article1'; | /cms/articles/article1 | title | DTL | + | UPDATE nt:unstructured AS a SET title = 'DTL', foobar='barfoo' WHERE localname() = 'article1'; | /cms/articles/article1 | foobar | barfoo | Scenario: Replace a multivalue index by value - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace(a.tags, 'Trains', 'Rockets') WHERE a.tags = 'Trains'" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace(a.tags, 'Trains', 'Rockets') WHERE a.tags = 'Trains';" command Then the command should not fail And I save the session Then the command should not fail And the node at "/cms/articles/article1" should have the property "tags" with value "Rockets" at index "1" Scenario: Set a multivalue value - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array('Rockets', 'Dragons')" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array('Rockets', 'Dragons');" command Then the command should not fail And I save the session Then the command should not fail @@ -40,7 +40,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 And the node at "/cms/articles/article1" should have the property "tags" with value "Dragons" at index "1" Scenario: Update single multivalue without selector - Given I execute the "UPDATE [nt:unstructured] SET tags = array_replace(tags, 'Planes', 'Rockets') WHERE tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] SET tags = array_replace(tags, 'Planes', 'Rockets') WHERE tags = 'Planes';" command Then the command should not fail And I save the session Then the command should not fail @@ -52,7 +52,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 And the node at "/cms/articles/article1" should have the property "tags" with value "Automobiles" at index "2" Scenario: Remove single multivalue - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_remove(a.tags, 'Planes') WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_remove(a.tags, 'Planes') WHERE a.tags = 'Planes';" command And I save the session Then the command should not fail And I should see the following: @@ -63,7 +63,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 And the node at "/cms/articles/article1" should have the property "tags" with value "Automobiles" at index "1" Scenario: Remove single multivalue by index - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, 0, NULL) WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, 0, NULL) WHERE a.tags = 'Planes';" command And I save the session Then the command should not fail And I should see the following: @@ -74,7 +74,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 And the node at "/cms/articles/article1" should have the property "tags" with value "Automobiles" at index "1" Scenario: Add a multivalue property - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_append(a.tags, 'Kite') WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_append(a.tags, 'Kite') WHERE a.tags = 'Planes';" command And I save the session Then the command should not fail And I should see the following: @@ -86,7 +86,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 And the node at "/cms/articles/article1" should have the property "tags" with value "Kite" at index "3" Scenario: Replace a multivalue property by index - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, 1, 'Kite'), a.tags = array_replace_at(a.tags, 2, 'foobar') WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, 1, 'Kite'), a.tags = array_replace_at(a.tags, 2, 'foobar') WHERE a.tags = 'Planes';" command And I save the session Then the command should not fail And I should see the following: @@ -98,7 +98,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 And the node at "/cms/articles/article1" should have the property "tags" with value "foobar" at index "2" Scenario: Replace a multivalue property by invalid index - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, 10, 'Kite') WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, 10, 'Kite') WHERE a.tags = 'Planes';" command Then the command should fail And I should see the following: """ @@ -106,7 +106,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 """ Scenario: Attempt to update a numerically named property (must use a selector) - Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, a.10, 'Kite') WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_replace_at(a.tags, a.10, 'Kite') WHERE a.tags = 'Planes';" command Then the command should fail And I should see the following: """ @@ -114,27 +114,27 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 """ Scenario: Apply mixin_remove - Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_remove('mix:title') WHERE a.name = 'Product Two'" command + Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_remove('mix:title') WHERE a.name = 'Product Two';" command Then the command should not fail And I save the session Then the command should not fail Then the node at "/cms/products/product2" should not have the mixin "mix:title" Scenario: Apply mixin_add - Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_add('mix:mimeType') WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_add('mix:mimeType') WHERE a.tags = 'Planes';" command Then the command should not fail And I save the session And the node at "/cms/articles/article1" should have the mixin "mix:mimeType" Scenario: Apply mixin_add existing - Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_add('mix:title') WHERE a.name = 'Product Two'" command + Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_add('mix:title') WHERE a.name = 'Product Two';" command Then the command should not fail And I save the session Then the command should not fail Then the node at "/cms/products/product2" should have the mixin "mix:title" Scenario: Apply multiple functions - Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_add('mix:mimeType'), mixin_add('mix:lockable') WHERE a.tags = 'Planes'" command + Given I execute the "UPDATE [nt:unstructured] AS a APPLY mixin_add('mix:mimeType'), mixin_add('mix:lockable') WHERE a.tags = 'Planes';" command Then the command should not fail And I save the session And the node at "/cms/articles/article1" should have the mixin "mix:mimeType" @@ -149,10 +149,10 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 """ Examples: | query | - | UPDATE foo FOR fi | - | UPDATE [nt:unstructured] mixin_foo('bar') | - | UPDATE [nt:unstructured] APPLY mixin_foo('bar') | - | UPDATE [nt:unstructured] mixin_foo'bar') | + | UPDATE foo FOR fi; | + | UPDATE [nt:unstructured] mixin_foo('bar'); | + | UPDATE [nt:unstructured] APPLY mixin_foo('bar'); | + | UPDATE [nt:unstructured] mixin_foo'bar'); | Scenario Outline: Execute update query with expressions When I execute the "" command @@ -165,13 +165,13 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 """ Examples: | query | path | property | expectedValue | - | UPDATE [nt:unstructured] AS a SET a.title = expr('row.getNode().getName()') WHERE localname() = 'article1' | /cms/articles/article1 | title | article1 | - | UPDATE [nt:unstructured] AS a SET a.title = expr('row.getPath()') WHERE localname() = 'article1' | /cms/articles/article1 | title | /cms/articles/article1 | + | UPDATE [nt:unstructured] AS a SET a.title = expr('row.getNode().getName()') WHERE localname() = 'article1'; | /cms/articles/article1 | title | article1 | + | UPDATE [nt:unstructured] AS a SET a.title = expr('row.getPath()') WHERE localname() = 'article1'; | /cms/articles/article1 | title | /cms/articles/article1 | Scenario: Execute an update with a quoted expression (can't do this in Examples above) When I execute the following command: """ - UPDATE [nt:unstructured] AS a SET a.weight = expr('row.getNode().getPropertyValue("weight") * 2') WHERE a.name = 'Product One' + UPDATE [nt:unstructured] AS a SET a.weight = expr('row.getNode().getPropertyValue("weight") * 2') WHERE a.name = 'Product One'; """ Then the command should not fail And I save the session @@ -180,3 +180,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2 """ 1 row(s) affected """ + + Scenario: It should fail if a non terminated query is executed + Given I execute the "UPDATE [nt:unstructured] SET foo = 'bar'" command + Then the command should fail diff --git a/features/shell/shell_alias.feature b/features/shell/shell_alias.feature index 5058552e..cd96f0cd 100644 --- a/features/shell/shell_alias.feature +++ b/features/shell/shell_alias.feature @@ -18,7 +18,7 @@ Feature: Command aliases Examples: | command | - | select * from [nt:unstructured] | + | select * from [nt:unstructured]; | | cd cms | | rm cms | | mv cms smc | diff --git a/src/PHPCR/Shell/Console/Command/Phpcr/BaseQueryCommand.php b/src/PHPCR/Shell/Console/Command/Phpcr/BaseQueryCommand.php new file mode 100644 index 00000000..f2383725 --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/Phpcr/BaseQueryCommand.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PHPCR\Shell\Console\Command\Phpcr; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use PHPCR\Util\QOM\Sql2ToQomQueryConverter; + +class BaseQueryCommand extends BasePhpcrCommand +{ + public function getQuery(InputInterface $input) + { + $sql = $input->getRawCommand(); + + if (substr($sql, -1) !== ';') { + throw new \InvalidArgumentException( + 'Queries must be terminated with ";"' + ); + } + + return substr($sql, 0, -1); + } +} diff --git a/src/PHPCR/Shell/Console/Command/Phpcr/QueryDeleteCommand.php b/src/PHPCR/Shell/Console/Command/Phpcr/QueryDeleteCommand.php index f16b0f15..9b62237a 100644 --- a/src/PHPCR/Shell/Console/Command/Phpcr/QueryDeleteCommand.php +++ b/src/PHPCR/Shell/Console/Command/Phpcr/QueryDeleteCommand.php @@ -16,7 +16,7 @@ use Symfony\Component\Console\Output\OutputInterface; use PHPCR\Util\QOM\Sql2ToQomQueryConverter; -class QueryDeleteCommand extends BasePhpcrCommand +class QueryDeleteCommand extends BaseQueryCommand { protected function configure() { @@ -38,15 +38,7 @@ protected function configure() public function execute(InputInterface $input, OutputInterface $output) { - $sql = $input->getRawCommand(); - - // trim ";" for people used to MysQL - if (substr($sql, -1) == ';') { - $sql = substr($sql, 0, -1); - } - - $session = $this->get('phpcr.session'); - $qm = $session->getWorkspace()->getQueryManager(); + $sql = $this->getQuery($input); if (!preg_match('{^delete from}', strtolower($sql))) { throw new \PHPCR\Query\InvalidQueryException(sprintf( @@ -54,6 +46,9 @@ public function execute(InputInterface $input, OutputInterface $output) )); } + $session = $this->get('phpcr.session'); + $qm = $session->getWorkspace()->getQueryManager(); + $sql = 'SELECT * FROM' . substr($sql, 11); $selectParser = new Sql2ToQomQueryConverter($qm->getQOMFactory()); diff --git a/src/PHPCR/Shell/Console/Command/Phpcr/QuerySelectCommand.php b/src/PHPCR/Shell/Console/Command/Phpcr/QuerySelectCommand.php index 6f36f209..74f35b1d 100644 --- a/src/PHPCR/Shell/Console/Command/Phpcr/QuerySelectCommand.php +++ b/src/PHPCR/Shell/Console/Command/Phpcr/QuerySelectCommand.php @@ -15,7 +15,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class QuerySelectCommand extends BasePhpcrCommand +class QuerySelectCommand extends BaseQueryCommand { protected function configure() { @@ -34,12 +34,8 @@ protected function configure() public function execute(InputInterface $input, OutputInterface $output) { - $sql = $input->getRawCommand(); - - // trim ";" for people used to MysQL - if (substr($sql, -1) == ';') { - $sql = substr($sql, 0, -1); - } + $sql = $this->getQuery($input); + $input = $this->getQuery($input); $session = $this->get('phpcr.session'); $qm = $session->getWorkspace()->getQueryManager(); diff --git a/src/PHPCR/Shell/Console/Command/Phpcr/QueryUpdateCommand.php b/src/PHPCR/Shell/Console/Command/Phpcr/QueryUpdateCommand.php index a23971d8..79848749 100644 --- a/src/PHPCR/Shell/Console/Command/Phpcr/QueryUpdateCommand.php +++ b/src/PHPCR/Shell/Console/Command/Phpcr/QueryUpdateCommand.php @@ -17,7 +17,7 @@ use PHPCR\Shell\Query\UpdateParser; use PHPCR\Shell\Query\UpdateProcessor; -class QueryUpdateCommand extends BasePhpcrCommand +class QueryUpdateCommand extends BaseQueryCommand { /** * @var OutputInterface @@ -69,12 +69,7 @@ protected function configure() public function execute(InputInterface $input, OutputInterface $output) { $this->output = $output; - $sql = $input->getRawCommand(); - - // trim ";" for people used to MysQL - if (substr($sql, -1) == ';') { - $sql = substr($sql, 0, -1); - } + $sql = $this->getQuery($input); $session = $this->get('phpcr.session'); $qm = $session->getWorkspace()->getQueryManager(); diff --git a/src/PHPCR/Shell/Query/Validator.php b/src/PHPCR/Shell/Query/Validator.php new file mode 100644 index 00000000..f64047e3 --- /dev/null +++ b/src/PHPCR/Shell/Query/Validator.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace \PHPCR\Shell\Query; + +class Validator +{ + /** + * Assert that queries are terminated with ";" + * + * @param string $sql2 + */ + public static function validateQuery($sql2) + { + if (substr($sql2, -1) !== ';') { + throw new \InvalidArgumentException( + 'Queries must be terminated with ";"' + ); + } + } +}