Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: isWriteType() to recognize CTE; always excluding RETURNING #8599

Merged
merged 8 commits into from
Mar 7, 2024
2 changes: 1 addition & 1 deletion system/Database/BaseConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1657,7 +1657,7 @@ public function resetDataCache()
*/
public function isWriteType($sql): bool
{
return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s/i', $sql);
return (bool) preg_match('/^\s*(WITH\s.+(\s|[)]))?"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s/is', $sql);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion system/Database/Postgre/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ protected function _transRollback(): bool
*/
public function isWriteType($sql): bool
{
if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql)) {
if (preg_match('#^\s*(WITH\s.+(\s|[)]))?(INSERT|UPDATE|DELETE).*RETURNING\s.+(\,\s?.+)*$#is', $sql)) {
return false;
}

Expand Down
141 changes: 135 additions & 6 deletions tests/system/Database/Live/WriteTypeQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,49 @@

$this->assertTrue($this->db->isWriteType($sql));

if ($this->db->DBDriver === 'Postgre') {
$sql = "INSERT INTO my_table (col1, col2) VALUES ('Joe', 'Cool') RETURNING id;";
$sql = "WITH seqvals AS (SELECT '3' AS seqval)INSERT INTO my_table (col1, col2) SELECT 'Joe', seqval FROM seqvals;";
kenjis marked this conversation as resolved.
Show resolved Hide resolved

$this->assertTrue($this->db->isWriteType($sql));

$sql = <<<'SQL'
WITH seqvals AS (SELECT '3' AS seqval)
INSERT INTO my_table (col1, col2)
SELECT 'Joe', seqval
FROM seqvals;
SQL;

$this->assertFalse($this->db->isWriteType($sql));
$this->assertTrue($this->db->isWriteType($sql));

$assertionType = 'assertTrue';
if ($this->db->DBDriver === 'Postgre') {
$assertionType = 'assertFalse';
}
kenjis marked this conversation as resolved.
Show resolved Hide resolved

$sql = "INSERT INTO my_table (col1, col2) VALUES ('Joe', 'Cool') RETURNING id;";

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = "WITH seqvals AS (SELECT '3' AS seqval)INSERT INTO my_table (col1, col2) SELECT 'Joe', seqval FROM seqvals RETURNING id;";

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = <<<'SQL'
INSERT INTO my_table (col1, col2)
VALUES ('Joe', 'Cool')
RETURNING id;
SQL;

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = <<<'SQL'
WITH seqvals AS (SELECT '3' AS seqval)
INSERT INTO my_table (col1, col2)
SELECT 'Joe', seqval
FROM seqvals
RETURNING id;
SQL;

$this->{$assertionType}($this->db->isWriteType($sql));
}

public function testUpdate(): void
Expand All @@ -63,11 +101,52 @@

$this->assertTrue($this->db->isWriteType($sql));

if ($this->db->DBDriver === 'Postgre') {
$sql = "UPDATE my_table SET col1 = 'foo' WHERE id = 2 RETURNING *;";
$sql = "WITH seqvals AS (SELECT '3' AS seqval)UPDATE my_table SET col1 = seqval FROM seqvals WHERE id = 2;";

$this->assertTrue($this->db->isWriteType($sql));

$sql = <<<'SQL'
WITH seqvals AS (SELECT '3' AS seqval)
UPDATE my_table
SET col1 = seqval
FROM seqvals
WHERE id = 2;
SQL;

$this->assertFalse($this->db->isWriteType($sql));
$this->assertTrue($this->db->isWriteType($sql));

$assertionType = 'assertTrue';
if ($this->db->DBDriver === 'Postgre') {
$assertionType = 'assertFalse';
}

$sql = "UPDATE my_table SET col1 = 'foo' WHERE id = 2 RETURNING *;";

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = "WITH seqvals AS (SELECT '3' AS seqval)UPDATE my_table SET col1 = seqval FROM seqvals WHERE id = 2 RETURNING *;";

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = <<<'SQL'
UPDATE my_table
SET col1 = 'foo'
WHERE id = 2
RETURNING *;
SQL;

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = <<<'SQL'
WITH seqvals AS (SELECT '3' AS seqval)
UPDATE my_table
SET col1 = seqval
FROM seqvals
WHERE id = 2
RETURNING *;
SQL;

$this->{$assertionType}($this->db->isWriteType($sql));
}

public function testDelete(): void
Expand All @@ -76,6 +155,56 @@
$sql = $builder->testMode()->delete(['id' => 1], null, true);

$this->assertTrue($this->db->isWriteType($sql));

$sql = 'DELETE FROM my_table WHERE id = 2;';

$this->assertTrue($this->db->isWriteType($sql));

$sql = "WITH seqvals AS (SELECT '3' AS seqval)DELETE FROM my_table JOIN seqvals ON col1 = seqval;";

$this->assertTrue($this->db->isWriteType($sql));

$sql = <<<'SQL'
WITH seqvals AS
(SELECT '3' AS seqval)
DELETE FROM my_table
JOIN seqvals
ON col1 = seqval;
SQL;

$this->assertTrue($this->db->isWriteType($sql));

$assertionType = 'assertTrue';
if ($this->db->DBDriver === 'Postgre') {
$assertionType = 'assertFalse';
}

$sql = 'DELETE FROM my_table WHERE id = 2 RETURNING *;';

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = "WITH seqvals AS (SELECT '3' AS seqval)DELETE FROM my_table JOIN seqvals ON col1 = seqval RETURNING *;";

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = <<<'SQL'
DELETE FROM my_table
WHERE id = 2
RETURNING *;
SQL;

$this->{$assertionType}($this->db->isWriteType($sql));

$sql = <<<'SQL'
WITH seqvals AS
(SELECT '3' AS seqval)
DELETE FROM my_table
JOIN seqvals
ON col1 = seqval
RETURNING *;
SQL;

$this->{$assertionType}($this->db->isWriteType($sql));
}

public function testReplace(): void
Expand Down Expand Up @@ -177,7 +306,7 @@
$this->assertTrue($this->db->isWriteType($sql));
}

public function testUnlock(): void

Check warning on line 309 in tests/system/Database/Live/WriteTypeQueryTest.php

View workflow job for this annotation

GitHub Actions / DatabaseLive (8.1, SQLite3, 8.0) / tests

Took 3.08s from 0.50s limit to run CodeIgniter\\Database\\Live\\WriteTypeQueryTest::testUnlock
{
// i think this is only a valid command for MySQL?
$sql = 'UNLOCK TABLES;';
Expand Down
Loading