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

dg / dibi / 23992436981

05 Apr 2026 02:21AM UTC coverage: 77.838% (+0.3%) from 77.523%
23992436981

push

github

dg
drivers: escape*() methods moved to Engine [WIP]

101 of 113 new or added lines in 8 files covered. (89.38%)

160 existing lines in 9 files now uncovered.

1721 of 2211 relevant lines covered (77.84%)

0.78 hits per line

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

74.42
/src/Dibi/Drivers/PgSQL/Connection.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * This file is part of the Dibi, smart database abstraction layer (https://dibi.nette.org)
5
 * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Dibi\Drivers\PgSQL;
9

10
use Dibi;
11
use Dibi\Drivers;
12
use Dibi\Helpers;
13
use PgSql;
14
use function in_array, is_array, strlen;
15

16

17
/**
18
 * The driver for PostgreSQL database.
19
 *
20
 * Driver options:
21
 *   - host, hostaddr, port, dbname, user, password, connect_timeout, options, sslmode, service => see PostgreSQL API
22
 *   - string => or use connection string
23
 *   - schema => the schema search path
24
 *   - charset => character encoding to set (default is utf8)
25
 *   - persistent (bool) => try to find a persistent link?
26
 *   - resource (PgSql\Connection) => existing connection resource
27
 *   - connect_type (int) => see pg_connect()
28
 */
29
class Connection implements Drivers\Connection
30
{
31
        private PgSql\Connection $connection;
32
        private ?int $affectedRows;
33

34

35
        /**
36
         * @param  array<string, mixed>  $config
37
         * @throws Dibi\NotSupportedException
38
         */
39
        public function __construct(array $config)
1✔
40
        {
41
                if (!extension_loaded('pgsql')) {
1✔
42
                        throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded.");
×
43
                }
44

45
                $error = null;
1✔
46
                if (isset($config['resource'])) {
1✔
47
                        $this->connection = $config['resource'];
×
48

49
                } else {
50
                        $config += [
51
                                'charset' => 'utf8',
1✔
52
                        ];
53
                        if (isset($config['string'])) {
1✔
54
                                $string = $config['string'];
×
55
                        } else {
56
                                $string = '';
1✔
57
                                Helpers::alias($config, 'user', 'username');
1✔
58
                                Helpers::alias($config, 'dbname', 'database');
1✔
59
                                foreach (['host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'] as $key) {
1✔
60
                                        if (isset($config[$key])) {
1✔
61
                                                $string .= $key . '=' . $config[$key] . ' ';
1✔
62
                                        }
63
                                }
64
                        }
65

66
                        $connectType = $config['connect_type'] ?? PGSQL_CONNECT_FORCE_NEW;
1✔
67

68
                        set_error_handler(function (int $severity, string $message, string $file, int $line) use (&$error): bool {
1✔
69
                                $error = $message;
70
                                return false;
71
                        });
1✔
72
                        $conn = empty($config['persistent'])
1✔
73
                                ? pg_connect($string, $connectType)
1✔
74
                                : pg_pconnect($string, $connectType);
×
75
                        restore_error_handler();
1✔
76

77
                        if (!$conn instanceof PgSql\Connection) {
1✔
78
                                throw new Dibi\DriverException($error ?: 'Connecting error.');
×
79
                        }
80

81
                        $this->connection = $conn;
1✔
82
                }
83

84
                pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE);
1✔
85

86
                if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) {
1✔
87
                        throw static::createException(pg_last_error($this->connection));
×
88
                }
89

90
                if (isset($config['schema'])) {
1✔
91
                        $this->query('SET search_path TO "' . implode('", "', (array) $config['schema']) . '"');
×
92
                }
93
        }
1✔
94

95

96
        /**
97
         * Disconnects from a database.
98
         */
99
        public function disconnect(): void
100
        {
101
                @pg_close($this->connection); // @ - connection can be already disconnected
1✔
102
        }
1✔
103

104

105
        /**
106
         * Pings database.
107
         */
108
        public function ping(): bool
109
        {
110
                return pg_ping($this->connection);
×
111
        }
112

113

114
        /**
115
         * Executes the SQL query.
116
         * @throws Dibi\DriverException
117
         */
118
        public function query(string $sql): ?Result
1✔
119
        {
120
                $this->affectedRows = null;
1✔
121
                $res = @pg_query($this->connection, $sql); // intentionally @
1✔
122
                if (!$res instanceof PgSql\Result) {
1✔
123
                        throw static::createException(pg_last_error($this->connection), sql: $sql);
1✔
124
                }
125

126
                $this->affectedRows = Helpers::false2Null(pg_affected_rows($res));
1✔
127
                if (pg_num_fields($res)) {
1✔
128
                        return $this->createResultDriver($res);
1✔
129
                }
130

131
                return null;
1✔
132
        }
133

134

135
        public static function createException(
1✔
136
                string $message,
137
                int|string $code = 0,
138
                ?string $sql = null,
139
        ): Dibi\DriverException
140
        {
141
                if ($code === 0 && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) {
1✔
142
                        $code = $m[1];
1✔
143
                        $message = substr($message, strlen($m[0]));
1✔
144
                }
145

146
                if ($code === '0A000' && str_contains($message, 'truncate')) {
1✔
147
                        return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
×
148

149
                } elseif ($code === '23502') {
1✔
150
                        return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
1✔
151

152
                } elseif ($code === '23503') {
1✔
153
                        return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
1✔
154

155
                } elseif ($code === '23505') {
1✔
156
                        return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
1✔
157

158
                } else {
159
                        return new Dibi\DriverException($message, $code, $sql);
1✔
160
                }
161
        }
162

163

164
        /**
165
         * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
166
         */
167
        public function getAffectedRows(): ?int
168
        {
169
                return $this->affectedRows;
1✔
170
        }
171

172

173
        /**
174
         * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
175
         */
176
        public function getInsertId(?string $sequence): ?int
177
        {
178
                $res = $sequence === null
×
179
                        ? $this->query('SELECT LASTVAL()') // PostgreSQL 8.1 is needed
×
180
                        : $this->query("SELECT CURRVAL('$sequence')");
×
181

182
                if (!$res) {
×
183
                        return null;
×
184
                }
185

186
                $row = $res->fetch(false);
×
187
                return is_array($row) ? (int) $row[0] : null;
×
188
        }
189

190

191
        /**
192
         * Begins a transaction (if supported).
193
         * @throws Dibi\DriverException
194
         */
195
        public function begin(?string $savepoint = null): void
1✔
196
        {
197
                $this->query($savepoint ? "SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'START TRANSACTION');
1✔
198
        }
1✔
199

200

201
        /**
202
         * Commits statements in a transaction.
203
         * @throws Dibi\DriverException
204
         */
205
        public function commit(?string $savepoint = null): void
1✔
206
        {
207
                $this->query($savepoint ? "RELEASE SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'COMMIT');
1✔
208
        }
1✔
209

210

211
        /**
212
         * Rollback changes in a transaction.
213
         * @throws Dibi\DriverException
214
         */
215
        public function rollback(?string $savepoint = null): void
1✔
216
        {
217
                $this->query($savepoint ? "ROLLBACK TO SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'ROLLBACK');
1✔
218
        }
1✔
219

220

221
        /**
222
         * Is in transaction?
223
         */
224
        public function inTransaction(): bool
225
        {
226
                return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], strict: true);
×
227
        }
228

229

230
        /**
231
         * Returns the connection resource.
232
         */
233
        public function getResource(): PgSql\Connection
234
        {
235
                return $this->connection;
1✔
236
        }
237

238

239
        /**
240
         * Returns the connection reflector.
241
         */
242
        public function getReflector(): Drivers\Engine
243
        {
244
                return new Drivers\Engines\PostgreSQLEngine($this);
1✔
245
        }
246

247

248
        /**
249
         * Result set driver factory.
250
         */
251
        public function createResultDriver(PgSql\Result $resource): Result
1✔
252
        {
253
                return new Result($resource);
1✔
254
        }
255

256

257
        /********************* SQL ****************d*g**/
258

259

260
        /**
261
         * Encodes data for use in a SQL statement.
262
         */
263
        public function escapeText(string $value): string
1✔
264
        {
265
                if (!$this->getResource()) {
1✔
266
                        throw new Dibi\Exception('Lost connection to server.');
×
267
                }
268

269
                return "'" . pg_escape_string($this->connection, $value) . "'";
1✔
270
        }
271

272

273
        public function escapeBinary(string $value): string
274
        {
275
                if (!$this->getResource()) {
×
276
                        throw new Dibi\Exception('Lost connection to server.');
×
277
                }
278

279
                return "'" . pg_escape_bytea($this->connection, $value) . "'";
×
280
        }
281

282

283
        private function escapeIdentifier(string $value): string
284
        {
UNCOV
285
                return '"' . str_replace('"', '""', $value) . '"';
×
286
        }
287
}
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