Skip to content

Commit 0d45be6

Browse files
authored
Bug 4069 - query returns Result even when query result is FALSE (#4176)
* Initial commit fixing wrong return value in Database/BaseConnection::query. When underlying resultID if false, this should return false. Added BadQueryTest to tests. * changes to make query() function return boolean for write-type queries. centralize isWriteType functionality in one place. Change tests impacted by this change * improved BadQueryTest, simplified return value in BaseUtils::optimizeTable * tiny change made by record...remove of one line in ConnectionInterface * Removed static sqlIsWriteType method from Query class and instead to mimic CI3 added isWriteType declaration to ConnectionInterface, defined isWriteType fn in BaseConnection, added specific isWriteType to Postgres, modified various unit tests, and added WriteTypeQueryTest. * Added RENAME back into BaseConnectin::isWriteType check and moved EXEC sp_rename into SQLSRV\Connection, tweaked WriteTypeQueryTest * Tweaked BadQueryTest and DBUtilsTest to alter/restore DBDebug value for certain tests. Added changelog and upgrade notes. * Update upgrade_412.rst remove incorrect markdown
1 parent 852b5d2 commit 0d45be6

File tree

18 files changed

+440
-73
lines changed

18 files changed

+440
-73
lines changed

system/BaseModel.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ abstract protected function doFirst();
357357
*
358358
* @param array $data Data
359359
*
360-
* @return object|integer|string|false
360+
* @return integer|string|boolean
361361
*/
362362
abstract protected function doInsert(array $data);
363363

@@ -407,7 +407,7 @@ abstract protected function doUpdateBatch(array $set = null, string $index = nul
407407
* @param integer|string|array|null $id The rows primary key(s)
408408
* @param boolean $purge Allows overriding the soft deletes setting.
409409
*
410-
* @return object|boolean
410+
* @return string|boolean
411411
*
412412
* @throws DatabaseException
413413
*/
@@ -666,11 +666,7 @@ public function save($data): bool
666666
{
667667
$response = $this->insert($data, false);
668668

669-
if ($response instanceof BaseResult)
670-
{
671-
$response = $response->resultID !== false;
672-
}
673-
elseif ($response !== false)
669+
if ($response !== false)
674670
{
675671
$response = true;
676672
}
@@ -708,7 +704,7 @@ public function getInsertID()
708704
* @param array|object|null $data Data
709705
* @param boolean $returnID Whether insert ID should be returned or not.
710706
*
711-
* @return BaseResult|object|integer|string|false
707+
* @return integer|string|boolean
712708
*
713709
* @throws ReflectionException
714710
*/

system/Database/BaseBuilder.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,7 +2252,7 @@ public function getCompiledInsert(bool $reset = true)
22522252
*
22532253
* @throws DatabaseException
22542254
*
2255-
* @return BaseResult|Query|false
2255+
* @return Query|boolean
22562256
*/
22572257
public function insert(array $set = null, bool $escape = null)
22582258
{
@@ -2483,7 +2483,7 @@ public function update(array $set = null, $where = null, int $limit = null): boo
24832483

24842484
$result = $this->db->query($sql, $this->binds, false);
24852485

2486-
if ($result->resultID !== false)
2486+
if ($result !== false)
24872487
{
24882488
// Clear our binds so we don't eat up memory
24892489
$this->binds = [];
@@ -2835,7 +2835,7 @@ public function getCompiledDelete(bool $reset = true): string
28352835
* @param integer $limit The limit clause
28362836
* @param boolean $resetData
28372837
*
2838-
* @return mixed
2838+
* @return string|boolean
28392839
* @throws DatabaseException
28402840
*/
28412841
public function delete($where = '', int $limit = null, bool $resetData = true)

system/Database/BaseConnection.php

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ abstract protected function execute(string $sql);
612612
* @param boolean $setEscapeFlags
613613
* @param string $queryClass
614614
*
615-
* @return BaseResult|Query|false
615+
* @return BaseResult|Query|boolean
616616
*
617617
* @todo BC set $queryClass default as null in 4.1
618618
*/
@@ -625,7 +625,6 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s
625625
$this->initialize();
626626
}
627627

628-
$resultClass = str_replace('Connection', 'Result', get_class($this));
629628
/**
630629
* @var Query $query
631630
*/
@@ -682,7 +681,7 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s
682681
Events::trigger('DBQuery', $query);
683682
}
684683

685-
return new $resultClass($this->connID, $this->resultID);
684+
return false;
686685
}
687686

688687
$query->setDuration($startTime);
@@ -696,7 +695,20 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s
696695
// If $pretend is true, then we just want to return
697696
// the actual query object here. There won't be
698697
// any results to return.
699-
return $this->pretend ? $query : new $resultClass($this->connID, $this->resultID);
698+
if ($this->pretend)
699+
{
700+
return $query;
701+
}
702+
703+
// resultID is not false, so it must be successful
704+
if ($this->isWriteType($sql))
705+
{
706+
return true;
707+
}
708+
709+
// query is not write-type, so it must be read-type query; return QueryResult
710+
$resultClass = str_replace('Connection', 'Result', get_class($this));
711+
return new $resultClass($this->connID, $this->resultID);
700712
}
701713

702714
//--------------------------------------------------------------------
@@ -1755,6 +1767,19 @@ public function resetDataCache()
17551767

17561768
//--------------------------------------------------------------------
17571769

1770+
/**
1771+
* Determines if the statement is a write-type query or not.
1772+
*
1773+
* @param string $sql
1774+
* @return boolean
1775+
*/
1776+
public function isWriteType($sql): bool
1777+
{
1778+
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);
1779+
}
1780+
1781+
//--------------------------------------------------------------------
1782+
17581783
/**
17591784
* Returns the last error code and message.
17601785
*

system/Database/BaseUtils.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function databaseExists(string $databaseName): bool
120120
* Optimize Table
121121
*
122122
* @param string $tableName
123-
* @return mixed
123+
* @return boolean
124124
* @throws DatabaseException
125125
*/
126126
public function optimizeTable(string $tableName)
@@ -135,13 +135,8 @@ public function optimizeTable(string $tableName)
135135
}
136136

137137
$query = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($tableName)));
138-
if ($query !== false)
139-
{
140-
$query = $query->getResultArray();
141-
return current($query);
142-
}
143138

144-
return false;
139+
return ($query !== false) ? true : false;
145140
}
146141

147142
//--------------------------------------------------------------------

system/Database/ConnectionInterface.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ public function getPlatform(): string;
116116
public function getVersion(): string;
117117

118118
//--------------------------------------------------------------------
119-
120119
/**
121120
* Orchestrates a query against the database. Queries must use
122121
* Database\Statement objects to store the query and build it.
@@ -128,7 +127,7 @@ public function getVersion(): string;
128127
* @param string $sql
129128
* @param mixed ...$binds
130129
*
131-
* @return mixed
130+
* @return BaseResult|Query|boolean
132131
*/
133132
public function query(string $sql, $binds = null);
134133

@@ -193,4 +192,12 @@ public function escape($str);
193192
public function callFunction(string $functionName, ...$params);
194193

195194
//--------------------------------------------------------------------
195+
196+
/**
197+
* Determines if the statement is a write-type query or not.
198+
*
199+
* @param string $sql
200+
* @return boolean
201+
*/
202+
public function isWriteType($sql): bool;
196203
}

system/Database/Postgre/Connection.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,4 +609,24 @@ protected function _transRollback(): bool
609609
}
610610

611611
// --------------------------------------------------------------------
612+
613+
/**
614+
* Determines if a query is a "write" type.
615+
*
616+
* Overrides BaseConnection::isWriteType, adding additional read query types.
617+
*
618+
* @param string $sql An SQL query string
619+
* @return boolean
620+
*/
621+
public function isWriteType($sql): bool
622+
{
623+
if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
624+
{
625+
return false;
626+
}
627+
628+
return parent::isWriteType($sql);
629+
}
630+
631+
// --------------------------------------------------------------------
612632
}

system/Database/Query.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,7 @@ public function getErrorMessage(): string
280280
*/
281281
public function isWriteType(): bool
282282
{
283-
return (bool) preg_match(
284-
'/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i',
285-
$this->originalQueryString
286-
);
283+
return $this->db->isWriteType($this->originalQueryString);
287284
}
288285

289286
/**

system/Database/SQLSRV/Connection.php

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace CodeIgniter\Database\SQLSRV;
1313

14+
use CodeIgniter\Database\Query;
1415
use CodeIgniter\Database\BaseConnection;
1516
use CodeIgniter\Database\Exceptions\DatabaseException;
1617
use Exception;
@@ -501,17 +502,6 @@ public function execute(string $sql)
501502
return $stmt;
502503
}
503504

504-
/**
505-
* Determines if a query is a "write" type.
506-
*
507-
* @param string $sql An SQL query string
508-
* @return boolean
509-
*/
510-
public function isWriteType($sql)
511-
{
512-
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);
513-
}
514-
515505
/**
516506
* Returns the last error encountered by this connection.
517507
*
@@ -578,4 +568,27 @@ public function getVersion(): string
578568

579569
return isset($info['SQLServerVersion']) ? $this->dataCache['version'] = $info['SQLServerVersion'] : false;
580570
}
571+
572+
// --------------------------------------------------------------------
573+
574+
/**
575+
* Determines if a query is a "write" type.
576+
*
577+
* Overrides BaseConnection::isWriteType, adding additional read query types.
578+
*
579+
* @param string $sql An SQL query string
580+
* @return boolean
581+
*/
582+
public function isWriteType($sql): bool
583+
{
584+
if (preg_match('/^\s*"?(EXEC\s*sp_rename)\s/i', $sql))
585+
{
586+
return true;
587+
}
588+
589+
return parent::isWriteType($sql);
590+
}
591+
592+
// --------------------------------------------------------------------
593+
581594
}

system/Database/SQLite3/Connection.php

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace CodeIgniter\Database\SQLite3;
1313

14+
use CodeIgniter\Database\Query;
1415
use CodeIgniter\Database\BaseConnection;
1516
use CodeIgniter\Database\Exceptions\DatabaseException;
1617
use ErrorException;
@@ -488,20 +489,6 @@ protected function _transRollback(): bool
488489

489490
//--------------------------------------------------------------------
490491

491-
/**
492-
* Determines if the statement is a write-type query or not.
493-
*
494-
* @return boolean
495-
*/
496-
public function isWriteType($sql): bool
497-
{
498-
return (bool) preg_match(
499-
'/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i',
500-
$sql);
501-
}
502-
503-
//--------------------------------------------------------------------
504-
505492
/**
506493
* Checks to see if the current install supports Foreign Keys
507494
* and has them enabled.

system/Model.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use CodeIgniter\Database\ConnectionInterface;
2020
use CodeIgniter\Database\Exceptions\DatabaseException;
2121
use CodeIgniter\Database\Exceptions\DataException;
22+
use CodeIgniter\Database\Query;
2223
use CodeIgniter\Exceptions\ModelException;
2324
use CodeIgniter\I18n\Time;
2425
use CodeIgniter\Validation\ValidationInterface;
@@ -250,7 +251,7 @@ protected function doFirst()
250251
*
251252
* @param array $data Data
252253
*
253-
* @return BaseResult|integer|string|false
254+
* @return Query|boolean
254255
*/
255256
protected function doInsert(array $data)
256257
{
@@ -275,7 +276,7 @@ protected function doInsert(array $data)
275276
$result = $builder->insert();
276277

277278
// If insertion succeeded then save the insert ID
278-
if ($result->resultID)
279+
if ($result)
279280
{
280281
if (! $this->useAutoIncrement)
281282
{
@@ -376,7 +377,7 @@ protected function doUpdateBatch(array $set = null, string $index = null, int $b
376377
* @param integer|string|array|null $id The rows primary key(s)
377378
* @param boolean $purge Allows overriding the soft deletes setting.
378379
*
379-
* @return BaseResult|boolean
380+
* @return string|boolean
380381
*
381382
* @throws DatabaseException
382383
*/

0 commit comments

Comments
 (0)