Skip to content

Commit 0e3d5cc

Browse files
authored
Merge pull request #6750 from michalsn/fix/preparedQuery
fix: BasePreparedQuery class to return boolean values for write-type queries
2 parents 2a6fd1c + 9901cb1 commit 0e3d5cc

File tree

10 files changed

+175
-68
lines changed

10 files changed

+175
-68
lines changed

system/Database/BasePreparedQuery.php

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,28 +111,74 @@ abstract public function _prepare(string $sql, array $options = []);
111111
* Takes a new set of data and runs it against the currently
112112
* prepared query. Upon success, will return a Results object.
113113
*
114-
* @return ResultInterface
114+
* @return bool|ResultInterface
115+
*
116+
* @throws DatabaseException
115117
*/
116118
public function execute(...$data)
117119
{
118120
// Execute the Query.
119121
$startTime = microtime(true);
120122

121123
try {
122-
$this->_execute($data);
123-
} catch (ArgumentCountError|ErrorException $e) {
124-
throw new DatabaseException($e->getMessage(), $e->getCode(), $e);
124+
$exception = null;
125+
$result = $this->_execute($data);
126+
} catch (ArgumentCountError|ErrorException $exception) {
127+
$result = false;
125128
}
126129

127130
// Update our query object
128131
$query = clone $this->query;
129132
$query->setBinds($data);
130133

134+
if ($result === false) {
135+
$query->setDuration($startTime, $startTime);
136+
137+
// This will trigger a rollback if transactions are being used
138+
if ($this->db->transDepth !== 0) {
139+
$this->db->transStatus = false;
140+
}
141+
142+
if ($this->db->DBDebug) {
143+
// We call this function in order to roll-back queries
144+
// if transactions are enabled. If we don't call this here
145+
// the error message will trigger an exit, causing the
146+
// transactions to remain in limbo.
147+
while ($this->db->transDepth !== 0) {
148+
$transDepth = $this->db->transDepth;
149+
$this->db->transComplete();
150+
151+
if ($transDepth === $this->db->transDepth) {
152+
log_message('error', 'Database: Failure during an automated transaction commit/rollback!');
153+
break;
154+
}
155+
}
156+
157+
// Let others do something with this query.
158+
Events::trigger('DBQuery', $query);
159+
160+
if ($exception !== null) {
161+
throw new DatabaseException($exception->getMessage(), $exception->getCode(), $exception);
162+
}
163+
164+
return false;
165+
}
166+
167+
// Let others do something with this query.
168+
Events::trigger('DBQuery', $query);
169+
170+
return false;
171+
}
172+
131173
$query->setDuration($startTime);
132174

133175
// Let others do something with this query
134176
Events::trigger('DBQuery', $query);
135177

178+
if ($this->db->isWriteType($query)) {
179+
return true;
180+
}
181+
136182
// Return a result object
137183
$resultClass = str_replace('PreparedQuery', 'Result', static::class);
138184

system/Database/MySQLi/PreparedQuery.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use CodeIgniter\Database\BasePreparedQuery;
1616
use CodeIgniter\Database\Exceptions\DatabaseException;
1717
use mysqli;
18+
use mysqli_sql_exception;
1819
use mysqli_stmt;
1920

2021
/**
@@ -33,10 +34,8 @@ class PreparedQuery extends BasePreparedQuery
3334
*
3435
* @param array $options Passed to the connection's prepare statement.
3536
* Unused in the MySQLi driver.
36-
*
37-
* @return mixed
3837
*/
39-
public function _prepare(string $sql, array $options = [])
38+
public function _prepare(string $sql, array $options = []): PreparedQuery
4039
{
4140
// Mysqli driver doesn't like statements
4241
// with terminating semicolons.
@@ -81,11 +80,19 @@ public function _execute(array $data): bool
8180
// Bind it
8281
$this->statement->bind_param($bindTypes, ...$data);
8382

84-
return $this->statement->execute();
83+
try {
84+
return $this->statement->execute();
85+
} catch (mysqli_sql_exception $e) {
86+
if ($this->db->DBDebug) {
87+
throw new DatabaseException($e->getMessage(), $e->getCode(), $e);
88+
}
89+
90+
return false;
91+
}
8592
}
8693

8794
/**
88-
* Returns the result object for the prepared query.
95+
* Returns the result object for the prepared query or false on failure.
8996
*
9097
* @return mixed
9198
*/
@@ -95,7 +102,7 @@ public function _getResult()
95102
}
96103

97104
/**
98-
* Deallocate prepared statements
105+
* Deallocate prepared statements.
99106
*/
100107
protected function _close(): bool
101108
{

system/Database/OCI8/PreparedQuery.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,8 @@ class PreparedQuery extends BasePreparedQuery
4343
*
4444
* @param array $options Passed to the connection's prepare statement.
4545
* Unused in the OCI8 driver.
46-
*
47-
* @return mixed
4846
*/
49-
public function _prepare(string $sql, array $options = [])
47+
public function _prepare(string $sql, array $options = []): PreparedQuery
5048
{
5149
if (! $this->statement = oci_parse($this->db->connID, $this->parameterize($sql))) {
5250
$error = oci_error($this->db->connID);
@@ -73,11 +71,8 @@ public function _execute(array $data): bool
7371
throw new BadMethodCallException('You must call prepare before trying to execute a prepared statement.');
7472
}
7573

76-
$lastKey = 0;
77-
7874
foreach (array_keys($data) as $key) {
7975
oci_bind_by_name($this->statement, ':' . $key, $data[$key]);
80-
$lastKey = $key;
8176
}
8277

8378
$result = oci_execute($this->statement, $this->db->commitMode);
@@ -90,7 +85,7 @@ public function _execute(array $data): bool
9085
}
9186

9287
/**
93-
* Returns the result object for the prepared query.
88+
* Returns the statement resource for the prepared query or false when preparing failed.
9489
*
9590
* @return mixed
9691
*/
@@ -100,7 +95,7 @@ public function _getResult()
10095
}
10196

10297
/**
103-
* Deallocate prepared statements
98+
* Deallocate prepared statements.
10499
*/
105100
protected function _close(): bool
106101
{

system/Database/Postgre/PreparedQuery.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,9 @@ class PreparedQuery extends BasePreparedQuery
5151
* @param array $options Passed to the connection's prepare statement.
5252
* Unused in the MySQLi driver.
5353
*
54-
* @return mixed
55-
*
5654
* @throws Exception
5755
*/
58-
public function _prepare(string $sql, array $options = [])
56+
public function _prepare(string $sql, array $options = []): PreparedQuery
5957
{
6058
$this->name = (string) random_int(1, 10_000_000_000_000_000);
6159

@@ -93,7 +91,7 @@ public function _execute(array $data): bool
9391
}
9492

9593
/**
96-
* Returns the result object for the prepared query.
94+
* Returns the result object for the prepared query or false on failure.
9795
*
9896
* @return mixed
9997
*/
@@ -103,7 +101,7 @@ public function _getResult()
103101
}
104102

105103
/**
106-
* Deallocate prepared statements
104+
* Deallocate prepared statements.
107105
*/
108106
protected function _close(): bool
109107
{

system/Database/PreparedQueryInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ interface PreparedQueryInterface
2525
* Takes a new set of data and runs it against the currently
2626
* prepared query. Upon success, will return a Results object.
2727
*
28-
* @return ResultInterface
28+
* @return bool|ResultInterface
2929
*/
3030
public function execute(...$data);
3131

system/Database/SQLSRV/PreparedQuery.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use BadMethodCallException;
1515
use CodeIgniter\Database\BasePreparedQuery;
1616
use CodeIgniter\Database\Exceptions\DatabaseException;
17-
use Exception;
1817

1918
/**
2019
* Prepared query for Postgre
@@ -51,11 +50,9 @@ public function __construct(Connection $db)
5150
*
5251
* @param array $options Options takes an associative array;
5352
*
54-
* @return mixed
55-
*
56-
* @throws Exception
53+
* @throws DatabaseException
5754
*/
58-
public function _prepare(string $sql, array $options = [])
55+
public function _prepare(string $sql, array $options = []): PreparedQuery
5956
{
6057
// Prepare parameters for the query
6158
$queryString = $this->getQueryString();
@@ -112,15 +109,15 @@ public function _getResult()
112109
}
113110

114111
/**
115-
* Deallocate prepared statements
112+
* Deallocate prepared statements.
116113
*/
117114
protected function _close(): bool
118115
{
119116
return sqlsrv_free_stmt($this->statement);
120117
}
121118

122119
/**
123-
* Handle parameters
120+
* Handle parameters.
124121
*/
125122
protected function parameterize(string $queryString): array
126123
{

system/Database/SQLite3/PreparedQuery.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,8 @@ class PreparedQuery extends BasePreparedQuery
4040
*
4141
* @param array $options Passed to the connection's prepare statement.
4242
* Unused in the MySQLi driver.
43-
*
44-
* @return $this
4543
*/
46-
public function _prepare(string $sql, array $options = [])
44+
public function _prepare(string $sql, array $options = []): PreparedQuery
4745
{
4846
if (! ($this->statement = $this->db->connID->prepare($sql))) {
4947
$this->errorCode = $this->db->connID->lastErrorCode();
@@ -87,7 +85,7 @@ public function _execute(array $data): bool
8785
}
8886

8987
/**
90-
* Returns the result object for the prepared query.
88+
* Returns the result object for the prepared query or false on failure.
9189
*
9290
* @return mixed
9391
*/
@@ -97,7 +95,7 @@ public function _getResult()
9795
}
9896

9997
/**
100-
* Deallocate prepared statements
98+
* Deallocate prepared statements.
10199
*/
102100
protected function _close(): bool
103101
{

0 commit comments

Comments
 (0)