1818use CodeIgniter \Events \Events ;
1919use CodeIgniter \Exceptions \BadMethodCallException ;
2020use ErrorException ;
21+ use Throwable ;
2122
2223/**
2324 * @template TConnection
@@ -49,6 +50,11 @@ abstract class BasePreparedQuery implements PreparedQueryInterface
4950 */
5051 protected $ errorString ;
5152
53+ /**
54+ * The typed exception for the last failed prepared query, if any.
55+ */
56+ protected ?DatabaseException $ databaseException = null ;
57+
5258 /**
5359 * Holds the prepared query object
5460 * that is cloned during execute.
@@ -121,8 +127,10 @@ public function execute(...$data)
121127
122128 try {
123129 $ exception = null ;
124- $ result = $ this ->_execute ($ data );
125- } catch (ArgumentCountError |ErrorException $ exception ) {
130+ $ this ->db ->setLastException (null );
131+ $ this ->databaseException = null ;
132+ $ result = $ this ->_execute ($ data );
133+ } catch (ArgumentCountError |DatabaseException |ErrorException $ exception ) {
126134 $ result = false ;
127135 }
128136
@@ -136,6 +144,8 @@ public function execute(...$data)
136144 // This will trigger a rollback if transactions are being used
137145 $ this ->db ->handleTransStatus ();
138146
147+ $ databaseException = $ this ->createDatabaseException ($ exception );
148+
139149 if ($ this ->db ->DBDebug ) {
140150 // We call this function in order to roll-back queries
141151 // if transactions are enabled. If we don't call this here
@@ -154,8 +164,8 @@ public function execute(...$data)
154164 // Let others do something with this query.
155165 Events::trigger ('DBQuery ' , $ query );
156166
157- if ($ exception !== null ) {
158- throw new DatabaseException ( $ exception -> getMessage (), $ exception -> getCode (), $ exception ) ;
167+ if ($ databaseException instanceof DatabaseException ) {
168+ throw $ databaseException ;
159169 }
160170
161171 return false ;
@@ -164,6 +174,8 @@ public function execute(...$data)
164174 // Let others do something with this query.
165175 Events::trigger ('DBQuery ' , $ query );
166176
177+ $ this ->db ->setLastException ($ databaseException );
178+
167179 return false ;
168180 }
169181
@@ -196,6 +208,34 @@ abstract public function _execute(array $data): bool;
196208 */
197209 abstract public function _getResult ();
198210
211+ /**
212+ * Creates the database exception for a failed prepared query.
213+ */
214+ private function createDatabaseException (?Throwable $ previous ): ?DatabaseException
215+ {
216+ if ($ previous instanceof DatabaseException) {
217+ return $ previous ;
218+ }
219+
220+ if ($ this ->databaseException instanceof DatabaseException) {
221+ return $ this ->databaseException ;
222+ }
223+
224+ if ($ previous instanceof Throwable) {
225+ return $ this ->db ->createDatabaseException (
226+ $ previous ->getMessage (),
227+ $ previous ->getCode (),
228+ $ previous ,
229+ );
230+ }
231+
232+ if ($ this ->errorString === null || $ this ->errorString === '' ) {
233+ return null ;
234+ }
235+
236+ return $ this ->db ->createDatabaseException ($ this ->errorString , $ this ->errorCode );
237+ }
238+
199239 /**
200240 * Explicitly closes the prepared statement.
201241 *
0 commit comments