• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 25752811411

12 May 2026 06:02PM UTC coverage: 88.375% (-0.06%) from 88.434%
25752811411

push

github

web-flow
fix: classify prepared query exceptions (#10182)

77 of 110 new or added lines in 9 files covered. (70.0%)

1 existing line in 1 file now uncovered.

23931 of 27079 relevant lines covered (88.37%)

218.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.39
/system/Database/Postgre/PreparedQuery.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Database\Postgre;
15

16
use CodeIgniter\Database\BasePreparedQuery;
17
use CodeIgniter\Exceptions\BadMethodCallException;
18
use Exception;
19
use PgSql\Connection as PgSqlConnection;
20
use PgSql\Result as PgSqlResult;
21

22
/**
23
 * Prepared query for Postgre
24
 *
25
 * @extends BasePreparedQuery<PgSqlConnection, PgSqlResult, PgSqlResult>
26
 */
27
class PreparedQuery extends BasePreparedQuery
28
{
29
    /**
30
     * Stores the name this query can be
31
     * used under by postgres. Only used internally.
32
     *
33
     * @var string
34
     */
35
    protected $name;
36

37
    /**
38
     * The result resource from a successful
39
     * pg_exec. Or false.
40
     *
41
     * @var false|PgSqlResult
42
     */
43
    protected $result;
44

45
    /**
46
     * Prepares the query against the database, and saves the connection
47
     * info necessary to execute the query later.
48
     *
49
     * NOTE: This version is based on SQL code. Child classes should
50
     * override this method.
51
     *
52
     * @param array $options Passed to the connection's prepare statement.
53
     *                       Unused in the MySQLi driver.
54
     *
55
     * @throws Exception
56
     */
57
    public function _prepare(string $sql, array $options = []): PreparedQuery
58
    {
59
        $this->name = (string) random_int(1, 10_000_000_000_000_000);
16✔
60

61
        $sql = $this->parameterize($sql);
16✔
62

63
        // Update the query object since the parameters are slightly different
64
        // than what was put in.
65
        $this->query->setQuery($sql);
16✔
66

67
        if (! $this->statement = pg_prepare($this->db->connID, $this->name, $sql)) {
16✔
68
            $this->errorCode   = 0;
×
69
            $this->errorString = pg_last_error($this->db->connID);
×
70

71
            if ($this->db->DBDebug) {
×
NEW
72
                throw $this->db->createDatabaseException($this->errorString, $this->errorCode);
×
73
            }
74
        }
75

76
        return $this;
16✔
77
    }
78

79
    /**
80
     * Takes a new set of data and runs it against the currently
81
     * prepared query. Upon success, will return a Results object.
82
     */
83
    public function _execute(array $data): bool
84
    {
85
        if (! isset($this->statement)) {
13✔
86
            throw new BadMethodCallException('You must call prepare before trying to execute a prepared statement.');
1✔
87
        }
88

89
        foreach ($data as &$item) {
12✔
90
            if (is_string($item) && $this->isBinary($item)) {
12✔
91
                $item = pg_escape_bytea($this->db->connID, $item);
1✔
92
            }
93
        }
94

95
        $sent = pg_send_execute($this->db->connID, $this->name, $data);
12✔
96

97
        if ($sent === false || $sent === 0) {
12✔
NEW
98
            $this->errorCode         = 0;
×
NEW
99
            $this->errorString       = pg_last_error($this->db->connID);
×
NEW
100
            $this->databaseException = $this->db->createDatabaseException($this->errorString, $this->errorCode);
×
101

NEW
102
            return false;
×
103
        }
104

105
        $this->result = pg_get_result($this->db->connID);
12✔
106

107
        if ($this->result === false) {
12✔
NEW
108
            $this->errorCode         = 0;
×
NEW
109
            $this->errorString       = pg_last_error($this->db->connID);
×
NEW
110
            $this->databaseException = $this->db->createDatabaseException($this->errorString, $this->errorCode);
×
111

NEW
112
            return false;
×
113
        }
114

115
        $lastResult   = $this->result;
12✔
116
        $failedResult = pg_result_status($this->result) === PGSQL_FATAL_ERROR ? $this->result : null;
12✔
117

118
        while (($next = pg_get_result($this->db->connID)) !== false) {
12✔
NEW
119
            $lastResult = $next;
×
120

NEW
121
            if (! $failedResult instanceof PgSqlResult && pg_result_status($next) === PGSQL_FATAL_ERROR) {
×
NEW
122
                $failedResult = $next;
×
123
            }
124
        }
125

126
        $this->result = $lastResult;
12✔
127

128
        if ($failedResult instanceof PgSqlResult) {
12✔
129
            $sqlstate                = (string) pg_result_error_field($failedResult, PGSQL_DIAG_SQLSTATE);
5✔
130
            $this->errorCode         = 0;
5✔
131
            $this->errorString       = (string) pg_result_error($failedResult);
5✔
132
            $this->databaseException = $this->db->createDatabaseException($this->errorString, $sqlstate);
5✔
133

134
            if ($this->db->DBDebug) {
5✔
135
                throw $this->databaseException;
2✔
136
            }
137

138
            return false;
3✔
139
        }
140

141
        return true;
11✔
142
    }
143

144
    /**
145
     * Returns the result object for the prepared query or false on failure.
146
     *
147
     * @return PgSqlResult|null
148
     */
149
    public function _getResult()
150
    {
151
        return $this->result;
4✔
152
    }
153

154
    /**
155
     * Deallocate prepared statements.
156
     */
157
    protected function _close(): bool
158
    {
159
        return pg_query($this->db->connID, 'DEALLOCATE "' . $this->db->escapeIdentifiers($this->name) . '"') !== false;
16✔
160
    }
161

162
    /**
163
     * Replaces the ? placeholders with $1, $2, etc parameters for use
164
     * within the prepared query.
165
     */
166
    public function parameterize(string $sql): string
167
    {
168
        // Track our current value
169
        $count = 0;
16✔
170

171
        return preg_replace_callback('/\?/', static function () use (&$count): string {
16✔
172
            $count++;
16✔
173

174
            return "\${$count}";
16✔
175
        }, $sql);
16✔
176
    }
177
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc