1 | <?php |
||||
2 | /** |
||||
3 | * PostgreSQL Related Functionality |
||||
4 | * @author Joe Huss <[email protected]> |
||||
5 | * @copyright 2025 |
||||
6 | * @package MyAdmin |
||||
7 | * @category SQL |
||||
8 | */ |
||||
9 | |||||
10 | namespace MyDb\Pgsql; |
||||
11 | |||||
12 | use MyDb\Generic; |
||||
13 | use MyDb\Db_Interface; |
||||
14 | |||||
15 | /** |
||||
16 | * Db |
||||
17 | * |
||||
18 | * @access public |
||||
19 | */ |
||||
20 | class Db extends Generic implements Db_Interface |
||||
21 | { |
||||
22 | /* public: this is an api revision, not a CVS revision. */ |
||||
23 | public $type = 'pgsql'; |
||||
24 | public $port = ''; |
||||
25 | public $defaultPort = '5432'; |
||||
26 | |||||
27 | |||||
28 | /** |
||||
29 | * adds if not blank |
||||
30 | * |
||||
31 | * @param string $add the value to set |
||||
32 | * @param string $me the key/field to set the value for |
||||
33 | * @param false|string $quote optional indicate the value needs quoted |
||||
34 | * @return string |
||||
35 | */ |
||||
36 | public function ifadd($add, $me, $quote = false) |
||||
37 | { |
||||
38 | if ('' != $add) { |
||||
39 | return ' '.$me.($quote === false ? '' : $quote).$add.($quote === false ? '' : $quote); |
||||
40 | } |
||||
41 | return ''; |
||||
42 | } |
||||
43 | |||||
44 | /** |
||||
45 | * @param $string |
||||
46 | * @return string |
||||
47 | */ |
||||
48 | public function real_escape($string = '') |
||||
49 | { |
||||
50 | return $this->escape($string); |
||||
51 | } |
||||
52 | |||||
53 | /** |
||||
54 | * alias function of select_db, changes the database we are working with. |
||||
55 | * |
||||
56 | * @param string $database the name of the database to use |
||||
57 | * @return void |
||||
58 | */ |
||||
59 | public function useDb($database) |
||||
60 | { |
||||
61 | $this->selectDb($database); |
||||
62 | } |
||||
63 | |||||
64 | /** |
||||
65 | * changes the database we are working with. |
||||
66 | * |
||||
67 | * @param string $database the name of the database to use |
||||
68 | * @return void |
||||
69 | */ |
||||
70 | public function selectDb($database) |
||||
0 ignored issues
–
show
|
|||||
71 | { |
||||
72 | /*if ($database != $this->database) { |
||||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
54% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||||
73 | $this->database = $database; |
||||
74 | $this->linkId = null; |
||||
75 | $this->connect(); |
||||
76 | }*/ |
||||
77 | } |
||||
78 | |||||
79 | /** |
||||
80 | * Db::connect() |
||||
81 | * @return void |
||||
82 | */ |
||||
83 | public function connect() |
||||
84 | { |
||||
85 | if (0 == $this->linkId) { |
||||
86 | $connectString = trim($this->ifadd($this->host, 'host='). |
||||
87 | $this->ifadd($this->port, 'port='). |
||||
88 | $this->ifadd($this->database, 'dbname='). |
||||
89 | $this->ifadd($this->user, 'user='). |
||||
90 | $this->ifadd($this->password, 'password=', "'")); |
||||
91 | $this->linkId = pg_connect($connectString); |
||||
0 ignored issues
–
show
It seems like
pg_connect($connectString) of type resource is incompatible with the declared type integer|object of property $linkId .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||||
92 | if (!$this->linkId) { |
||||
93 | $this->halt('Link-ID == FALSE, connect failed'); |
||||
94 | } |
||||
95 | } |
||||
96 | } |
||||
97 | |||||
98 | /* This only affects systems not using persistent connections */ |
||||
99 | |||||
100 | /** |
||||
101 | * Db::disconnect() |
||||
102 | * @return bool |
||||
103 | */ |
||||
104 | public function disconnect() |
||||
105 | { |
||||
106 | return @pg_close($this->linkId); |
||||
0 ignored issues
–
show
$this->linkId of type integer|object is incompatible with the type resource expected by parameter $connection of pg_close() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * Db::queryReturn() |
||||
111 | * |
||||
112 | * Sends an SQL query to the server like the normal query() command but iterates through |
||||
113 | * any rows and returns the row or rows immediately or FALSE on error |
||||
114 | * |
||||
115 | * @param mixed $query SQL Query to be used |
||||
116 | * @param string $line optionally pass __LINE__ calling the query for logging |
||||
117 | * @param string $file optionally pass __FILE__ calling the query for logging |
||||
118 | * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only |
||||
119 | */ |
||||
120 | public function queryReturn($query, $line = '', $file = '') |
||||
121 | { |
||||
122 | $this->query($query, $line, $file); |
||||
123 | if ($this->num_rows() == 0) { |
||||
124 | return false; |
||||
125 | } elseif ($this->num_rows() == 1) { |
||||
126 | $this->next_record(MYSQL_ASSOC); |
||||
0 ignored issues
–
show
The constant
MYSQL_ASSOC has been deprecated: 5.5
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
127 | return $this->Record; |
||||
128 | } else { |
||||
129 | $out = []; |
||||
130 | while ($this->next_record(MYSQL_ASSOC)) { |
||||
0 ignored issues
–
show
The constant
MYSQL_ASSOC has been deprecated: 5.5
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
131 | $out[] = $this->Record; |
||||
132 | } |
||||
133 | return $out; |
||||
134 | } |
||||
135 | } |
||||
136 | |||||
137 | /** |
||||
138 | * db:qr() |
||||
139 | * |
||||
140 | * alias of queryReturn() |
||||
141 | * |
||||
142 | * @param mixed $query SQL Query to be used |
||||
143 | * @param string $line optionally pass __LINE__ calling the query for logging |
||||
144 | * @param string $file optionally pass __FILE__ calling the query for logging |
||||
145 | * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only |
||||
146 | */ |
||||
147 | public function qr($query, $line = '', $file = '') |
||||
148 | { |
||||
149 | return $this->queryReturn($query, $line, $file); |
||||
150 | } |
||||
151 | |||||
152 | /** |
||||
153 | * Db::query() |
||||
154 | * |
||||
155 | * Sends an SQL query to the database |
||||
156 | * |
||||
157 | * @param mixed $queryString |
||||
158 | * @param string $line |
||||
159 | * @param string $file |
||||
160 | * @return mixed 0 if no query or query id handler, safe to ignore this return |
||||
161 | */ |
||||
162 | public function query($queryString, $line = '', $file = '') |
||||
163 | { |
||||
164 | /* No empty queries, please, since PHP4 chokes on them. */ |
||||
165 | /* The empty query string is passed on from the constructor, |
||||
166 | * when calling the class without a query, e.g. in situations |
||||
167 | * like these: '$db = new db_Subclass;' |
||||
168 | */ |
||||
169 | if ($queryString == '') { |
||||
170 | return 0; |
||||
171 | } |
||||
172 | |||||
173 | $this->connect(); |
||||
174 | |||||
175 | /* printf("<br>Debug: query = %s<br>\n", $queryString); */ |
||||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
67% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||||
176 | |||||
177 | $this->queryId = @pg_exec($this->linkId, $queryString); |
||||
178 | $this->Row = 0; |
||||
179 | |||||
180 | $this->Error = pg_errormessage($this->linkId); |
||||
181 | $this->Errno = ($this->Error == '') ? 0 : 1; |
||||
182 | if (!$this->queryId) { |
||||
183 | $this->halt('Invalid SQL: '.$queryString, $line, $file); |
||||
184 | } |
||||
185 | |||||
186 | return $this->queryId; |
||||
187 | } |
||||
188 | |||||
189 | /** |
||||
190 | * Db::free() |
||||
191 | * |
||||
192 | * @return void |
||||
193 | */ |
||||
194 | public function free() |
||||
195 | { |
||||
196 | @pg_freeresult($this->queryId); |
||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
pg_freeresult() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||||
197 | $this->queryId = 0; |
||||
198 | } |
||||
199 | |||||
200 | /** |
||||
201 | * Db::next_record() |
||||
202 | * @param mixed $resultType |
||||
203 | * @return bool |
||||
204 | */ |
||||
205 | public function next_record($resultType = PGSQL_BOTH) |
||||
206 | { |
||||
207 | $this->Record = @pg_fetch_array($this->queryId, $this->Row++, $resultType); |
||||
0 ignored issues
–
show
$this->queryId of type integer is incompatible with the type resource expected by parameter $result of pg_fetch_array() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
208 | |||||
209 | $this->Error = pg_errormessage($this->linkId); |
||||
210 | $this->Errno = ($this->Error == '') ? 0 : 1; |
||||
211 | |||||
212 | $stat = is_array($this->Record); |
||||
213 | if (!$stat && $this->autoFree) { |
||||
0 ignored issues
–
show
|
|||||
214 | pg_freeresult($this->queryId); |
||||
215 | $this->queryId = 0; |
||||
216 | } |
||||
217 | return $stat; |
||||
218 | } |
||||
219 | |||||
220 | /** |
||||
221 | * Db::seek() |
||||
222 | * |
||||
223 | * @param mixed $pos |
||||
224 | * @return void |
||||
225 | */ |
||||
226 | public function seek($pos) |
||||
227 | { |
||||
228 | $this->Row = $pos; |
||||
229 | } |
||||
230 | |||||
231 | /** |
||||
232 | * Db::transactionBegin() |
||||
233 | * |
||||
234 | * @return mixed |
||||
235 | */ |
||||
236 | public function transactionBegin() |
||||
237 | { |
||||
238 | return $this->query('begin'); |
||||
239 | } |
||||
240 | |||||
241 | /** |
||||
242 | * Db::transactionCommit() |
||||
243 | * @return bool|mixed |
||||
244 | */ |
||||
245 | public function transactionCommit() |
||||
246 | { |
||||
247 | if (!$this->Errno) { |
||||
248 | return pg_exec($this->linkId, 'commit'); |
||||
249 | } else { |
||||
250 | return false; |
||||
251 | } |
||||
252 | } |
||||
253 | |||||
254 | /** |
||||
255 | * Db::transactionAbort() |
||||
256 | * @return mixed |
||||
257 | */ |
||||
258 | public function transactionAbort() |
||||
259 | { |
||||
260 | return pg_exec($this->linkId, 'rollback'); |
||||
261 | } |
||||
262 | |||||
263 | /** |
||||
264 | * Db::getLastInsertId() |
||||
265 | * @param mixed $table |
||||
266 | * @param mixed $field |
||||
267 | * @return int |
||||
268 | */ |
||||
269 | public function getLastInsertId($table, $field) |
||||
270 | { |
||||
271 | /* This will get the last insert ID created on the current connection. Should only be called |
||||
272 | * after an insert query is run on a table that has an auto incrementing field. Of note, table |
||||
273 | * and field are required because pgsql returns the last inserted OID, which is unique across |
||||
274 | * an entire installation. These params allow us to retrieve the sequenced field without adding |
||||
275 | * conditional code to the apps. |
||||
276 | */ |
||||
277 | if (!isset($table) || $table == '' || !isset($field) || $field == '') { |
||||
278 | return -1; |
||||
279 | } |
||||
280 | |||||
281 | $oid = pg_getlastoid($this->queryId); |
||||
282 | if ($oid == -1) { |
||||
283 | return -1; |
||||
284 | } |
||||
285 | |||||
286 | $result = @pg_exec($this->linkId, "select $field from $table where oid=$oid"); |
||||
287 | if (!$result) { |
||||
288 | return -1; |
||||
289 | } |
||||
290 | |||||
291 | $Record = @pg_fetch_array($result, 0); |
||||
292 | @pg_freeresult($result); |
||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
pg_freeresult() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||||
293 | if (!is_array($Record)) /* OID not found? */ |
||||
0 ignored issues
–
show
|
|||||
294 | { |
||||
295 | return -1; |
||||
296 | } |
||||
297 | |||||
298 | return $Record[0]; |
||||
299 | } |
||||
300 | |||||
301 | /** |
||||
302 | * Db::lock() |
||||
303 | * @param mixed $table |
||||
304 | * @param string $mode |
||||
305 | * @return int|mixed |
||||
306 | */ |
||||
307 | public function lock($table, $mode = 'write') |
||||
308 | { |
||||
309 | $result = $this->transactionBegin(); |
||||
310 | |||||
311 | if ($mode == 'write') { |
||||
312 | if (is_array($table)) { |
||||
313 | foreach ($table as $t) { |
||||
314 | $result = pg_exec($this->linkId, 'lock table '.$t[1].' in share mode'); |
||||
315 | } |
||||
316 | } else { |
||||
317 | $result = pg_exec($this->linkId, 'lock table '.$table.' in share mode'); |
||||
318 | } |
||||
319 | } else { |
||||
320 | $result = 1; |
||||
321 | } |
||||
322 | |||||
323 | return $result; |
||||
324 | } |
||||
325 | |||||
326 | /** |
||||
327 | * Db::unlock() |
||||
328 | * @return bool|mixed |
||||
329 | */ |
||||
330 | public function unlock() |
||||
331 | { |
||||
332 | return $this->transactionCommit(); |
||||
333 | } |
||||
334 | |||||
335 | /** |
||||
336 | * Db::affectedRows() |
||||
337 | * @return void |
||||
338 | */ |
||||
339 | public function affectedRows() |
||||
340 | { |
||||
341 | return pg_cmdtuples($this->queryId); |
||||
342 | } |
||||
343 | |||||
344 | /** |
||||
345 | * Db::num_rows() |
||||
346 | * @return int |
||||
347 | */ |
||||
348 | public function num_rows() |
||||
349 | { |
||||
350 | return pg_numrows($this->queryId); |
||||
351 | } |
||||
352 | |||||
353 | /** |
||||
354 | * Db::num_fields() |
||||
355 | * @return int |
||||
356 | */ |
||||
357 | public function num_fields() |
||||
358 | { |
||||
359 | return pg_numfields($this->queryId); |
||||
360 | } |
||||
361 | |||||
362 | /** |
||||
363 | * @param mixed $msg |
||||
364 | * @param string $line |
||||
365 | * @param string $file |
||||
366 | * @return mixed|void |
||||
367 | */ |
||||
368 | public function haltmsg($msg, $line = '', $file = '') |
||||
369 | { |
||||
370 | $this->log("Database error: $msg", $line, $file, 'error'); |
||||
371 | if ($this->Errno != '0' || $this->Error != '()') { |
||||
372 | $this->log('PostgreSQL Error: '.pg_last_error($this->linkId), $line, $file, 'error'); |
||||
0 ignored issues
–
show
$this->linkId of type integer|object is incompatible with the type resource expected by parameter $connection of pg_last_error() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
373 | } |
||||
374 | $this->logBackTrace($msg, $line, $file); |
||||
375 | } |
||||
376 | |||||
377 | /** |
||||
378 | * Db::tableNames() |
||||
379 | * |
||||
380 | * @return array |
||||
381 | */ |
||||
382 | public function tableNames() |
||||
383 | { |
||||
384 | $return = []; |
||||
385 | $this->query("select relname from pg_class where relkind = 'r' and not relname like 'pg_%'"); |
||||
386 | $i = 0; |
||||
387 | while ($this->next_record()) { |
||||
388 | $return[$i]['table_name'] = $this->f(0); |
||||
389 | $return[$i]['tablespace_name'] = $this->database; |
||||
390 | $return[$i]['database'] = $this->database; |
||||
391 | ++$i; |
||||
392 | } |
||||
393 | return $return; |
||||
394 | } |
||||
395 | |||||
396 | /** |
||||
397 | * Db::indexNames() |
||||
398 | * |
||||
399 | * @return array |
||||
400 | */ |
||||
401 | public function indexNames() |
||||
402 | { |
||||
403 | $return = []; |
||||
404 | $this->query("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relkind ='i' ORDER BY relname"); |
||||
405 | $i = 0; |
||||
406 | while ($this->next_record()) { |
||||
407 | $return[$i]['index_name'] = $this->f(0); |
||||
408 | $return[$i]['tablespace_name'] = $this->database; |
||||
409 | $return[$i]['database'] = $this->database; |
||||
410 | ++$i; |
||||
411 | } |
||||
412 | return $return; |
||||
413 | } |
||||
414 | } |
||||
415 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.