Passed
Push — master ( f3b317...5b2bef )
by Felipe
07:25 queued 03:38
created

Postgres::setFunction()   C

Complexity

Conditions 11
Paths 29

Size

Total Lines 82
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 35
nc 29
nop 16
dl 0
loc 82
rs 5.2653
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.41
5
 */
6
7
namespace PHPPgAdmin\Database;
8
9
/**
10
 * A Class that implements the DB Interface for Postgres
11
 * Note: This Class uses ADODB and returns RecordSets.
12
 *
13
 * Id: Postgres.php,v 1.320 2008/02/20 20:43:09 ioguix Exp $
14
 *
15
 * @package PHPPgAdmin
16
 */
17
class Postgres extends ADOdbBase
18
{
19
    use \PHPPgAdmin\HelperTrait;
1 ignored issue
show
introduced by
The trait PHPPgAdmin\HelperTrait requires some properties which are not provided by PHPPgAdmin\Database\Postgres: $responseobj, $requestobj
Loading history...
20
    use \PHPPgAdmin\Database\SequenceTrait;
21
    use \PHPPgAdmin\Database\ViewTrait;
22
    use \PHPPgAdmin\Database\IndexTrait;
1 ignored issue
show
Bug introduced by
The trait PHPPgAdmin\Database\IndexTrait requires the property $EOF which is not provided by PHPPgAdmin\Database\Postgres.
Loading history...
23
    use \PHPPgAdmin\Database\RoleTrait;
24
    use \PHPPgAdmin\Database\AggregateTrait;
25
    use \PHPPgAdmin\Database\TableTrait;
1 ignored issue
show
Bug introduced by
The trait PHPPgAdmin\Database\TableTrait requires the property $EOF which is not provided by PHPPgAdmin\Database\Postgres.
Loading history...
26
    use \PHPPgAdmin\Database\DomainTrait;
27
    use \PHPPgAdmin\Database\FtsTrait;
28
29
    public $lang;
30
    public $conf;
31
    protected $container;
32
33
    public function __construct(&$conn, $container)
34
    {
35
        //$this->prtrace('major_version :' . $this->major_version);
36
        $this->conn      = $conn;
37
        $this->container = $container;
38
39
        $this->lang = $container->get('lang');
40
        $this->conf = $container->get('conf');
41
    }
42
43
    /**
44
     * Fetch a URL (or array of URLs) for a given help page.
45
     *
46
     * @param $help
47
     *
48
     * @return null|array|string
49
     */
50
    public function getHelp($help)
51
    {
52
        $this->getHelpPages();
53
54
        if (isset($this->help_page[$help])) {
55
            if (is_array($this->help_page[$help])) {
56
                $urls = [];
57
                foreach ($this->help_page[$help] as $link) {
58
                    $urls[] = $this->help_base.$link;
59
                }
60
61
                return $urls;
62
            }
63
64
            return $this->help_base.$this->help_page[$help];
65
        }
66
67
        return null;
68
    }
69
70
    /**
71
     * Gets the help pages.
72
     * get help page by instancing the corresponding help class
73
     * if $this->help_page and $this->help_base are set, this function is a noop.
74
     */
75
    public function getHelpPages()
76
    {
77
        if ($this->help_page === null || $this->help_base === null) {
78
            $help_classname = '\PHPPgAdmin\Help\PostgresDoc'.str_replace('.', '', $this->major_version);
79
80
            $help_class = new $help_classname($this->conf, $this->major_version);
81
82
            $this->help_base = $help_class->getHelpBase();
83
        }
84
    }
85
86
    // Formatting functions
87
88
    /**
89
     * Outputs the HTML code for a particular field.
90
     *
91
     * @param       $name   The name to give the field
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
92
     * @param       $value  The value of the field.  Note this could be 'numeric(7,2)' sort of thing...
93
     * @param       $type   The database type of the field
94
     * @param array $extras An array of attributes name as key and attributes' values as value
95
     */
96
    public function printField($name, $value, $type, $extras = [])
97
    {
98
        $lang = $this->lang;
99
100
        // Determine actions string
101
        $extra_str = '';
102
        foreach ($extras as $k => $v) {
103
            $extra_str .= " {$k}=\"".htmlspecialchars($v).'"';
104
        }
105
106
        switch (substr($type, 0, 9)) {
107
            case 'bool':
108
            case 'boolean':
109
                if ($value !== null && $value == '') {
110
                    $value = null;
111
                } elseif ($value == 'true') {
112
                    $value = 't';
113
                } elseif ($value == 'false') {
114
                    $value = 'f';
115
                }
116
117
                // If value is null, 't' or 'f'...
118
                if ($value === null || $value == 't' || $value == 'f') {
119
                    echo '<select name="', htmlspecialchars($name), "\"{$extra_str}>\n";
120
                    echo '<option value=""', ($value === null) ? ' selected="selected"' : '', "></option>\n";
121
                    echo '<option value="t"', ($value == 't') ? ' selected="selected"' : '', ">{$lang['strtrue']}</option>\n";
122
                    echo '<option value="f"', ($value == 'f') ? ' selected="selected"' : '', ">{$lang['strfalse']}</option>\n";
123
                    echo "</select>\n";
124
                } else {
125
                    echo '<input name="', htmlspecialchars($name), '" value="', htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
126
                }
127
128
                break;
129
            case 'bytea':
130
            case 'bytea[]':
131
                if (!is_null($value)) {
132
                    $value = $this->escapeBytea($value);
133
                }
134
            // no break
135
            case 'text':
136
            case 'text[]':
137
            case 'json':
138
            case 'jsonb':
139
            case 'xml':
140
            case 'xml[]':
141
                $n = substr_count($value, "\n");
142
                $n = $n < 5 ? max(2, $n) : $n;
143
                $n = $n > 20 ? 20 : $n;
144
                echo '<textarea name="', htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"85\"{$extra_str}>\n";
145
                echo htmlspecialchars($value);
146
                echo "</textarea>\n";
147
148
                break;
149
            case 'character':
150
            case 'character[]':
151
                $n = substr_count($value, "\n");
152
                $n = $n < 5 ? 5 : $n;
153
                $n = $n > 20 ? 20 : $n;
154
                echo '<textarea name="', htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"35\"{$extra_str}>\n";
155
                echo htmlspecialchars($value);
156
                echo "</textarea>\n";
157
158
                break;
159
            default:
160
                echo '<input name="', htmlspecialchars($name), '" value="', htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
161
162
                break;
163
        }
164
    }
165
166
    /**
167
     * Return all information about a particular database.
168
     *
169
     * @param $database The name of the database to retrieve
170
     *
171
     * @return \PHPPgAdmin\ADORecordSet The database info
172
     */
173
    public function getDatabase($database)
174
    {
175
        $this->clean($database);
176
        $sql = "SELECT * FROM pg_database WHERE datname='{$database}'";
177
178
        return $this->selectSet($sql);
179
    }
180
181
    /**
182
     * Return all database available on the server.
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\database was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
183
     *
184
     * @param $currentdatabase database name that should be on top of the resultset
185
     *
186
     * @return \PHPPgAdmin\ADORecordSet A list of databases, sorted alphabetically
187
     */
188
    public function getDatabases($currentdatabase = null)
189
    {
190
        $conf        = $this->conf;
191
        $server_info = $this->server_info;
192
193
        if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser()) {
194
            $username = $server_info['username'];
195
            $this->clean($username);
196
            $clause = " AND pr.rolname='{$username}'";
197
        } else {
198
            $clause = '';
199
        }
200
        if (isset($server_info['useonlydefaultdb']) && $server_info['useonlydefaultdb']) {
201
            $currentdatabase = $server_info['defaultdb'];
202
            $clause .= " AND pdb.datname = '{$currentdatabase}' ";
203
        }
204
205
        if (isset($server_info['hiddendbs']) && $server_info['hiddendbs']) {
206
            $hiddendbs = $server_info['hiddendbs'];
207
            $not_in    = "('".implode("','", $hiddendbs)."')";
208
            $clause .= " AND pdb.datname NOT IN {$not_in} ";
209
        }
210
211
        if ($currentdatabase != null) {
212
            $this->clean($currentdatabase);
213
            $orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
214
        } else {
215
            $orderby = 'ORDER BY pdb.datname';
216
        }
217
218
        if (!$conf['show_system']) {
219
            $where = ' AND NOT pdb.datistemplate';
220
        } else {
221
            $where = ' AND pdb.datallowconn';
222
        }
223
224
        $sql = "
225
			SELECT pdb.datname AS datname,
226
                    pr.rolname AS datowner,
227
                    pg_encoding_to_char(encoding) AS datencoding,
228
				    (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pdb.oid=pd.objoid AND pd.classoid='pg_database'::regclass) AS datcomment,
229
				    (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=pdb.dattablespace) AS tablespace,
230
				CASE WHEN pg_catalog.has_database_privilege(current_user, pdb.oid, 'CONNECT')
231
					THEN pg_catalog.pg_database_size(pdb.oid)
232
					ELSE -1 -- set this magic value, which we will convert to no access later
233
				END as dbsize,
234
                pdb.datcollate,
235
                pdb.datctype
236
			FROM pg_catalog.pg_database pdb
237
            LEFT JOIN pg_catalog.pg_roles pr ON (pdb.datdba = pr.oid)
238
			WHERE true
239
				{$where}
240
				{$clause}
241
			{$orderby}";
242
243
        return $this->selectSet($sql);
244
    }
245
246
    /**
247
     * Determines whether or not a user is a super user.
248
     *
249
     * @param string $username The username of the user
250
     *
251
     * @return bool true if is a super user, false otherwise
252
     */
253
    public function isSuperUser($username = '')
254
    {
255
        $this->clean($username);
256
257
        if (empty($usename)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $usename does not exist. Did you maybe mean $username?
Loading history...
258
            $val = pg_parameter_status($this->conn->_connectionID, 'is_superuser');
259
            if ($val !== false) {
260
                return $val == 'on';
261
            }
262
        }
263
264
        $sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'";
265
266
        $usesuper = $this->selectField($sql, 'usesuper');
267
        if ($usesuper == -1) {
268
            return false;
269
        }
270
271
        return $usesuper == 't';
272
    }
273
274
    /**
275
     * Return the database comment of a db from the shared description table.
276
     *
277
     * @param string $database the name of the database to get the comment for
278
     *
279
     * @return \PHPPgAdmin\ADORecordSet recordset of the db comment info
280
     */
281
    public function getDatabaseComment($database)
282
    {
283
        $this->clean($database);
284
        $sql =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
285
            "SELECT description FROM pg_catalog.pg_database JOIN pg_catalog.pg_shdescription ON (oid=objoid AND classoid='pg_database'::regclass) WHERE pg_database.datname = '{$database}' ";
286
287
        return $this->selectSet($sql);
288
    }
289
290
    /**
291
     * Return the database owner of a db.
292
     *
293
     * @param string $database the name of the database to get the owner for
294
     *
295
     * @return \PHPPgAdmin\ADORecordSet recordset of the db owner info
296
     */
297
    public function getDatabaseOwner($database)
298
    {
299
        $this->clean($database);
300
        $sql = "SELECT usename FROM pg_user, pg_database WHERE pg_user.usesysid = pg_database.datdba AND pg_database.datname = '{$database}' ";
301
302
        return $this->selectSet($sql);
303
    }
304
305
    // Help functions
306
307
    // Database functions
308
309
    /**
310
     * Returns the current database encoding.
311
     *
312
     * @return string The encoding.  eg. SQL_ASCII, UTF-8, etc.
313
     */
314
    public function getDatabaseEncoding()
315
    {
316
        return pg_parameter_status($this->conn->_connectionID, 'server_encoding');
317
    }
318
319
    /**
320
     * Returns the current default_with_oids setting.
321
     *
322
     * @return string default_with_oids setting
323
     */
324
    public function getDefaultWithOid()
325
    {
326
        $sql = 'SHOW default_with_oids';
327
328
        return $this->selectField($sql, 'default_with_oids');
329
    }
330
331
    /**
332
     * Creates a database.
333
     *
334
     * @param        $database   The name of the database to create
335
     * @param        $encoding   Encoding of the database
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Encoding was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
336
     * @param string $tablespace (optional) The tablespace name
337
     * @param string $comment
338
     * @param string $template
339
     * @param string $lc_collate
340
     * @param string $lc_ctype
341
     *
342
     * @return int 0 success
343
     */
344
    public function createDatabase(
345
        $database,
346
        $encoding,
347
        $tablespace = '',
348
        $comment = '',
349
        $template = 'template1',
350
        $lc_collate = '',
351
        $lc_ctype = ''
352
    ) {
353
        $this->fieldClean($database);
354
        $this->clean($encoding);
355
        $this->fieldClean($tablespace);
356
        $this->fieldClean($template);
357
        $this->clean($lc_collate);
358
        $this->clean($lc_ctype);
359
360
        $sql = "CREATE DATABASE \"{$database}\" WITH TEMPLATE=\"{$template}\"";
361
362
        if ($encoding != '') {
363
            $sql .= " ENCODING='{$encoding}'";
364
        }
365
366
        if ($lc_collate != '') {
367
            $sql .= " LC_COLLATE='{$lc_collate}'";
368
        }
369
370
        if ($lc_ctype != '') {
371
            $sql .= " LC_CTYPE='{$lc_ctype}'";
372
        }
373
374
        if ($tablespace != '' && $this->hasTablespaces()) {
375
            $sql .= " TABLESPACE \"{$tablespace}\"";
376
        }
377
378
        $status = $this->execute($sql);
379
        if ($status != 0) {
380
            return -1;
381
        }
382
383
        if ($comment != '' && $this->hasSharedComments()) {
384
            $status = $this->setComment('DATABASE', $database, '', $comment);
385
            if ($status != 0) {
386
                return -2;
387
            }
388
        }
389
390
        return 0;
391
    }
392
393
    /**
394
     * Cleans (escapes) an object name (eg. table, field).
395
     *
396
     * @param string $str The string to clean, by reference
397
     *
398
     * @return string The cleaned string
399
     */
400
    public function fieldClean(&$str)
401
    {
402
        if ($str === null) {
0 ignored issues
show
introduced by
The condition $str === null is always false.
Loading history...
403
            return null;
404
        }
405
406
        $str = str_replace('"', '""', $str);
407
408
        return $str;
409
    }
410
411
    /**
412
     * Drops a database.
413
     *
414
     * @param $database The name of the database to drop
415
     *
416
     * @return int 0 if operation was successful
417
     */
418
    public function dropDatabase($database)
419
    {
420
        $this->fieldClean($database);
421
        $sql = "DROP DATABASE \"{$database}\"";
422
423
        return $this->execute($sql);
424
    }
425
426
    /**
427
     * Alters a database
428
     * the multiple return vals are for postgres 8+ which support more functionality in alter database.
429
     *
430
     * @param        $dbName   The name of the database
431
     * @param        $newName  new name for the database
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\new was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
432
     * @param string $newOwner The new owner for the database
433
     * @param string $comment
434
     *
435
     * @return bool|int 0 success
436
     */
437
    public function alterDatabase($dbName, $newName, $newOwner = '', $comment = '')
438
    {
439
        $status = $this->beginTransaction();
440
        if ($status != 0) {
441
            $this->rollbackTransaction();
442
443
            return -1;
444
        }
445
446
        if ($dbName != $newName) {
447
            $status = $this->alterDatabaseRename($dbName, $newName);
448
            if ($status != 0) {
449
                $this->rollbackTransaction();
450
451
                return -3;
452
            }
453
            $dbName = $newName;
454
        }
455
456
        if ($newOwner != '') {
457
            $status = $this->alterDatabaseOwner($newName, $newOwner);
458
            if ($status != 0) {
459
                $this->rollbackTransaction();
460
461
                return -2;
462
            }
463
        }
464
465
        $this->fieldClean($dbName);
466
        $status = $this->setComment('DATABASE', $dbName, '', $comment);
467
        if ($status != 0) {
468
            $this->rollbackTransaction();
469
470
            return -4;
471
        }
472
473
        return $this->endTransaction();
474
    }
475
476
    /**
477
     * Renames a database, note that this operation cannot be
478
     * performed on a database that is currently being connected to.
479
     *
480
     * @param string $oldName name of database to rename
481
     * @param string $newName new name of database
482
     *
483
     * @return int 0 on success
484
     */
485
    public function alterDatabaseRename($oldName, $newName)
486
    {
487
        $this->fieldClean($oldName);
488
        $this->fieldClean($newName);
489
490
        if ($oldName != $newName) {
491
            $sql = "ALTER DATABASE \"{$oldName}\" RENAME TO \"{$newName}\"";
492
493
            return $this->execute($sql);
494
        }
495
496
        return 0;
497
    }
498
499
    /**
500
     * Changes ownership of a database
501
     * This can only be done by a superuser or the owner of the database.
502
     *
503
     * @param string $dbName   database to change ownership of
504
     * @param string $newOwner user that will own the database
505
     *
506
     * @return int 0 on success
507
     */
508
    public function alterDatabaseOwner($dbName, $newOwner)
509
    {
510
        $this->fieldClean($dbName);
511
        $this->fieldClean($newOwner);
512
513
        $sql = "ALTER DATABASE \"{$dbName}\" OWNER TO \"{$newOwner}\"";
514
515
        return $this->execute($sql);
516
    }
517
518
    /**
519
     * Returns prepared transactions information.
520
     *
521
     * @param $database (optional) Find only prepared transactions executed in a specific database
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\optional was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
522
     *
523
     * @return \PHPPgAdmin\ADORecordSet A recordset
524
     */
525
    public function getPreparedXacts($database = null)
526
    {
527
        if ($database === null) {
528
            $sql = 'SELECT * FROM pg_prepared_xacts';
529
        } else {
530
            $this->clean($database);
531
            $sql = "SELECT transaction, gid, prepared, owner FROM pg_prepared_xacts
532
				WHERE database='{$database}' ORDER BY owner";
533
        }
534
535
        return $this->selectSet($sql);
536
    }
537
538
    /**
539
     * Searches all system catalogs to find objects that match a certain name.
540
     *
541
     * @param $term   The search term
542
     * @param $filter The object type to restrict to ('' means no restriction)
543
     *
544
     * @return \PHPPgAdmin\ADORecordSet A recordset
545
     */
546
    public function findObject($term, $filter)
547
    {
548
        $conf = $this->conf;
549
550
        /*about escaping:
551
         * SET standard_conforming_string is not available before 8.2
552
         * So we must use PostgreSQL specific notation :/
553
         * E'' notation is not available before 8.1
554
         * $$ is available since 8.0
555
         * Nothing specific from 7.4
556
         */
557
558
        // Escape search term for ILIKE match
559
        $this->clean($term);
560
        $this->clean($filter);
561
        $term = str_replace('_', '\_', $term);
562
        $term = str_replace('%', '\%', $term);
563
564
        // Exclude system relations if necessary
565
        if (!$conf['show_system']) {
566
            // XXX: The mention of information_schema here is in the wrong place, but
567
            // it's the quickest fix to exclude the info schema from 7.4
568
            $where     = " AND pn.nspname NOT LIKE \$_PATERN_\$pg\\_%\$_PATERN_\$ AND pn.nspname != 'information_schema'";
569
            $lan_where = 'AND pl.lanispl';
570
        } else {
571
            $where     = '';
572
            $lan_where = '';
573
        }
574
575
        // Apply outer filter
576
        $sql = '';
577
        if ($filter != '') {
578
            $sql = 'SELECT * FROM (';
579
        }
580
581
        $term = "\$_PATERN_\$%{$term}%\$_PATERN_\$";
582
583
        $sql .= "
584
			SELECT 'SCHEMA' AS type, oid, NULL AS schemaname, NULL AS relname, nspname AS name
585
				FROM pg_catalog.pg_namespace pn WHERE nspname ILIKE {$term} {$where}
586
			UNION ALL
587
			SELECT CASE WHEN relkind='r' THEN 'TABLE' WHEN relkind='v' THEN 'VIEW' WHEN relkind='S' THEN 'SEQUENCE' END, pc.oid,
588
				pn.nspname, NULL, pc.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn
589
				WHERE pc.relnamespace=pn.oid AND relkind IN ('r', 'v', 'S') AND relname ILIKE {$term} {$where}
590
			UNION ALL
591
			SELECT CASE WHEN pc.relkind='r' THEN 'COLUMNTABLE' ELSE 'COLUMNVIEW' END, NULL, pn.nspname, pc.relname, pa.attname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
592
				pg_catalog.pg_attribute pa WHERE pc.relnamespace=pn.oid AND pc.oid=pa.attrelid
593
				AND pa.attname ILIKE {$term} AND pa.attnum > 0 AND NOT pa.attisdropped AND pc.relkind IN ('r', 'v') {$where}
594
			UNION ALL
595
			SELECT 'FUNCTION', pp.oid, pn.nspname, NULL, pp.proname || '(' || pg_catalog.oidvectortypes(pp.proargtypes) || ')' FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pn
596
				WHERE pp.pronamespace=pn.oid AND NOT pp.proisagg AND pp.proname ILIKE {$term} {$where}
597
			UNION ALL
598
			SELECT 'INDEX', NULL, pn.nspname, pc.relname, pc2.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
599
				pg_catalog.pg_index pi, pg_catalog.pg_class pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pi.indrelid
600
				AND pi.indexrelid=pc2.oid
601
				AND NOT EXISTS (
602
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
603
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
604
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
605
				)
606
				AND pc2.relname ILIKE {$term} {$where}
607
			UNION ALL
608
			SELECT 'CONSTRAINTTABLE', NULL, pn.nspname, pc.relname, pc2.conname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
609
				pg_catalog.pg_constraint pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pc2.conrelid AND pc2.conrelid != 0
610
				AND CASE WHEN pc2.contype IN ('f', 'c') THEN TRUE ELSE NOT EXISTS (
611
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
612
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
613
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
614
				) END
615
				AND pc2.conname ILIKE {$term} {$where}
616
			UNION ALL
617
			SELECT 'CONSTRAINTDOMAIN', pt.oid, pn.nspname, pt.typname, pc.conname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn,
618
				pg_catalog.pg_constraint pc WHERE pt.typnamespace=pn.oid AND pt.oid=pc.contypid AND pc.contypid != 0
619
				AND pc.conname ILIKE {$term} {$where}
620
			UNION ALL
621
			SELECT 'TRIGGER', NULL, pn.nspname, pc.relname, pt.tgname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
622
				pg_catalog.pg_trigger pt WHERE pc.relnamespace=pn.oid AND pc.oid=pt.tgrelid
623
					AND ( pt.tgconstraint = 0 OR NOT EXISTS
624
					(SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
625
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
626
					WHERE d.classid = pt.tableoid AND d.objid = pt.oid AND d.deptype = 'i' AND c.contype = 'f'))
627
				AND pt.tgname ILIKE {$term} {$where}
628
			UNION ALL
629
			SELECT 'RULETABLE', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
630
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
631
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
632
				WHERE c.relkind='r' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where}
633
			UNION ALL
634
			SELECT 'RULEVIEW', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
635
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
636
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
637
				WHERE c.relkind='v' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where}
638
		";
639
640
        // Add advanced objects if show_advanced is set
641
        if ($conf['show_advanced']) {
642
            $sql .= "
643
				UNION ALL
644
				SELECT CASE WHEN pt.typtype='d' THEN 'DOMAIN' ELSE 'TYPE' END, pt.oid, pn.nspname, NULL,
645
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
646
					WHERE pt.typnamespace=pn.oid AND typname ILIKE {$term}
647
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
648
					{$where}
649
			 	UNION ALL
650
				SELECT 'OPERATOR', po.oid, pn.nspname, NULL, po.oprname FROM pg_catalog.pg_operator po, pg_catalog.pg_namespace pn
651
					WHERE po.oprnamespace=pn.oid AND oprname ILIKE {$term} {$where}
652
				UNION ALL
653
				SELECT 'CONVERSION', pc.oid, pn.nspname, NULL, pc.conname FROM pg_catalog.pg_conversion pc,
654
					pg_catalog.pg_namespace pn WHERE pc.connamespace=pn.oid AND conname ILIKE {$term} {$where}
655
				UNION ALL
656
				SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_catalog.pg_language pl
657
					WHERE lanname ILIKE {$term} {$lan_where}
658
				UNION ALL
659
				SELECT DISTINCT ON (p.proname) 'AGGREGATE', p.oid, pn.nspname, NULL, p.proname FROM pg_catalog.pg_proc p
660
					LEFT JOIN pg_catalog.pg_namespace pn ON p.pronamespace=pn.oid
661
					WHERE p.proisagg AND p.proname ILIKE {$term} {$where}
662
				UNION ALL
663
				SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, pn.nspname, NULL, po.opcname FROM pg_catalog.pg_opclass po,
664
					pg_catalog.pg_namespace pn WHERE po.opcnamespace=pn.oid
665
					AND po.opcname ILIKE {$term} {$where}
666
			";
667
        } else {
668
            // Otherwise just add domains
669
            $sql .= "
670
				UNION ALL
671
				SELECT 'DOMAIN', pt.oid, pn.nspname, NULL,
672
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
673
					WHERE pt.typnamespace=pn.oid AND pt.typtype='d' AND typname ILIKE {$term}
674
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
675
					{$where}
676
			";
677
        }
678
679
        if ($filter != '') {
680
            // We use like to make RULE, CONSTRAINT and COLUMN searches work
681
            $sql .= ") AS sub WHERE type LIKE '{$filter}%' ";
682
        }
683
684
        $sql .= 'ORDER BY type, schemaname, relname, name';
685
686
        return $this->selectSet($sql);
687
    }
688
689
    /**
690
     * Returns all available variable information.
691
     *
692
     * @return \PHPPgAdmin\ADORecordSet A recordset
693
     */
694
    public function getVariables()
695
    {
696
        $sql = 'SHOW ALL';
697
698
        return $this->selectSet($sql);
699
    }
700
701
    // Schema functons
702
703
    /**
704
     * Return all schemas in the current database.
705
     *
706
     * @return \PHPPgAdmin\ADORecordSet All schemas, sorted alphabetically
707
     */
708
    public function getSchemas()
709
    {
710
        $conf = $this->conf;
711
712
        if (!$conf['show_system']) {
713
            $where = "WHERE nspname NOT LIKE 'pg@_%' ESCAPE '@' AND nspname != 'information_schema'";
714
        } else {
715
            $where = "WHERE nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
716
        }
717
718
        $sql = "
719
			SELECT pn.nspname,
720
                   pu.rolname AS nspowner,
721
				   pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment,
722
                   pg_size_pretty(SUM(pg_total_relation_size(pg_class.oid))) as schema_size
723
			FROM pg_catalog.pg_namespace pn
724
            LEFT JOIN pg_catalog.pg_class  ON relnamespace = pn.oid
725
			LEFT JOIN pg_catalog.pg_roles pu ON (pn.nspowner = pu.oid)
726
			{$where}
727
            GROUP BY pn.nspname, pu.rolname, pg_catalog.obj_description(pn.oid, 'pg_namespace')
728
			ORDER BY nspname";
729
730
        return $this->selectSet($sql);
731
    }
732
733
    /**
734
     * Sets the current working schema.  Will also set Class variable.
735
     *
736
     * @param $schema The the name of the schema to work in
737
     *
738
     * @return int 0 if operation was successful
739
     */
740
    public function setSchema($schema)
741
    {
742
        // Get the current schema search path, including 'pg_catalog'.
743
        $search_path = $this->getSearchPath();
744
        // Prepend $schema to search path
745
        array_unshift($search_path, $schema);
0 ignored issues
show
Bug introduced by
$search_path of type PHPPgAdmin\ADORecordSet is incompatible with the type array expected by parameter $array of array_unshift(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

745
        array_unshift(/** @scrutinizer ignore-type */ $search_path, $schema);
Loading history...
746
        $status = $this->setSearchPath($search_path);
747
        if ($status == 0) {
748
            $this->_schema = $schema;
749
750
            return 0;
751
        }
752
753
        return $status;
754
    }
755
756
    /**
757
     * Return the current schema search path.
758
     *
759
     * @return \PHPPgAdmin\ADORecordSet array of schema names
760
     */
761
    public function getSearchPath()
762
    {
763
        $sql = 'SELECT current_schemas(false) AS search_path';
764
765
        return $this->phpArray($this->selectField($sql, 'search_path'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->phpArray($...d($sql, 'search_path')) returns the type array which is incompatible with the documented return type PHPPgAdmin\ADORecordSet.
Loading history...
766
    }
767
768
    /**
769
     * Sets the current schema search path.
770
     *
771
     * @param $paths An array of schemas in required search order
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\An was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
772
     *
773
     * @return int 0 if operation was successful
774
     */
775
    public function setSearchPath($paths)
776
    {
777
        if (!is_array($paths)) {
778
            return -1;
779
        }
780
781
        if (sizeof($paths) == 0) {
782
            return -2;
783
        }
784
        if (sizeof($paths) == 1 && $paths[0] == '') {
785
            // Need to handle empty paths in some cases
786
            $paths[0] = 'pg_catalog';
787
        }
788
789
        // Loop over all the paths to check that none are empty
790
        $temp = [];
791
        foreach ($paths as $schema) {
792
            if ($schema != '') {
793
                $temp[] = $schema;
794
            }
795
        }
796
        $this->fieldArrayClean($temp);
797
798
        $sql = 'SET SEARCH_PATH TO "'.implode('","', $temp).'"';
799
800
        return $this->execute($sql);
801
    }
802
803
    /**
804
     * Creates a new schema.
805
     *
806
     * @param        $schemaname    The name of the schema to create
807
     * @param string $authorization (optional) The username to create the schema for
808
     * @param string $comment       (optional) If omitted, defaults to nothing
809
     *
810
     * @return bool|int 0 success
811
     */
812
    public function createSchema($schemaname, $authorization = '', $comment = '')
813
    {
814
        $this->fieldClean($schemaname);
815
        $this->fieldClean($authorization);
816
817
        $sql = "CREATE SCHEMA \"{$schemaname}\"";
818
        if ($authorization != '') {
819
            $sql .= " AUTHORIZATION \"{$authorization}\"";
820
        }
821
822
        if ($comment != '') {
823
            $status = $this->beginTransaction();
824
            if ($status != 0) {
825
                return -1;
826
            }
827
        }
828
829
        // Create the new schema
830
        $status = $this->execute($sql);
831
        if ($status != 0) {
832
            $this->rollbackTransaction();
833
834
            return -1;
835
        }
836
837
        // Set the comment
838
        if ($comment != '') {
839
            $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
840
            if ($status != 0) {
841
                $this->rollbackTransaction();
842
843
                return -1;
844
            }
845
846
            return $this->endTransaction();
847
        }
848
849
        return 0;
850
    }
851
852
    /**
853
     * Updates a schema.
854
     *
855
     * @param $schemaname The name of the schema to drop
856
     * @param $comment    The new comment for this schema
857
     * @param $name
858
     * @param $owner      The new owner for this schema
859
     *
860
     * @return bool|int 0 success
861
     */
862
    public function updateSchema($schemaname, $comment, $name, $owner)
863
    {
864
        $this->fieldClean($schemaname);
865
        $this->fieldClean($name);
866
        $this->fieldClean($owner);
867
868
        $status = $this->beginTransaction();
869
        if ($status != 0) {
870
            $this->rollbackTransaction();
871
872
            return -1;
873
        }
874
875
        $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
876
        if ($status != 0) {
877
            $this->rollbackTransaction();
878
879
            return -1;
880
        }
881
882
        $schema_rs = $this->getSchemaByName($schemaname);
883
        /* Only if the owner change */
884
        if ($schema_rs->fields['ownername'] != $owner) {
885
            $sql    = "ALTER SCHEMA \"{$schemaname}\" OWNER TO \"{$owner}\"";
886
            $status = $this->execute($sql);
887
            if ($status != 0) {
888
                $this->rollbackTransaction();
889
890
                return -1;
891
            }
892
        }
893
894
        // Only if the name has changed
895
        if ($name != $schemaname) {
896
            $sql    = "ALTER SCHEMA \"{$schemaname}\" RENAME TO \"{$name}\"";
897
            $status = $this->execute($sql);
898
            if ($status != 0) {
899
                $this->rollbackTransaction();
900
901
                return -1;
902
            }
903
        }
904
905
        return $this->endTransaction();
906
    }
907
908
    /**
909
     * Return all information relating to a schema.
910
     *
911
     * @param $schema The name of the schema
912
     *
913
     * @return Schema information
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Schema was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
914
     */
915
    public function getSchemaByName($schema)
916
    {
917
        $this->clean($schema);
918
        $sql = "
919
			SELECT nspname, nspowner, r.rolname AS ownername, nspacl,
920
				pg_catalog.obj_description(pn.oid, 'pg_namespace') as nspcomment
921
			FROM pg_catalog.pg_namespace pn
922
				LEFT JOIN pg_roles as r ON pn.nspowner = r.oid
923
			WHERE nspname='{$schema}'";
924
925
        return $this->selectSet($sql);
926
    }
927
928
    // Table functions
929
930
    /**
931
     * Drops a schema.
932
     *
933
     * @param $schemaname The name of the schema to drop
934
     * @param $cascade    True to cascade drop, false to restrict
935
     *
936
     * @return int 0 if operation was successful
937
     */
938
    public function dropSchema($schemaname, $cascade)
939
    {
940
        $this->fieldClean($schemaname);
941
942
        $sql = "DROP SCHEMA \"{$schemaname}\"";
943
        if ($cascade) {
944
            $sql .= ' CASCADE';
945
        }
946
947
        return $this->execute($sql);
948
    }
949
950
    /**
951
     * Formats a type correctly for display.  Postgres 7.0 had no 'format_type'
952
     * built-in function, and hence we need to do it manually.
953
     *
954
     * @param $typname The name of the type
955
     * @param $typmod  The contents of the typmod field
956
     *
957
     * @return bool|string
958
     */
959
    public function formatType($typname, $typmod)
960
    {
961
        // This is a specific constant in the 7.0 source
962
        $varhdrsz = 4;
963
964
        // If the first character is an underscore, it's an array type
965
        $is_array = false;
966
        if (substr($typname, 0, 1) == '_') {
967
            $is_array = true;
968
            $typname  = substr($typname, 1);
969
        }
970
971
        // Show lengths on bpchar and varchar
972
        if ($typname == 'bpchar') {
973
            $len  = $typmod - $varhdrsz;
974
            $temp = 'character';
975
            if ($len > 1) {
976
                $temp .= "({$len})";
977
            }
978
        } elseif ($typname == 'varchar') {
979
            $temp = 'character varying';
980
            if ($typmod != -1) {
981
                $temp .= '('.($typmod - $varhdrsz).')';
982
            }
983
        } elseif ($typname == 'numeric') {
984
            $temp = 'numeric';
985
            if ($typmod != -1) {
986
                $tmp_typmod = $typmod - $varhdrsz;
987
                $precision  = ($tmp_typmod >> 16) & 0xffff;
988
                $scale      = $tmp_typmod & 0xffff;
989
                $temp .= "({$precision}, {$scale})";
990
            }
991
        } else {
992
            $temp = $typname;
993
        }
994
995
        // Add array qualifier if it's an array
996
        if ($is_array) {
997
            $temp .= '[]';
998
        }
999
1000
        return $temp;
1001
    }
1002
1003
    /**
1004
     * Given an array of attnums and a relation, returns an array mapping
1005
     * attribute number to attribute name.
1006
     *
1007
     * @param $table The table to get attributes for
1008
     * @param $atts  An array of attribute numbers
1009
     *
1010
     * @return An array mapping attnum to attname
1011
     * @return -1 $atts must be an array
0 ignored issues
show
Coding Style introduced by
Only 1 @return tag is allowed in a function comment
Loading history...
1012
     * @return -2 wrong number of attributes found
1013
     */
1014
    public function getAttributeNames($table, $atts)
1015
    {
1016
        $c_schema = $this->_schema;
1017
        $this->clean($c_schema);
1018
        $this->clean($table);
1019
        $this->arrayClean($atts);
1020
1021
        if (!is_array($atts)) {
1022
            return -1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -1 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1023
        }
1024
1025
        if (sizeof($atts) == 0) {
1026
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1027
        }
1028
1029
        $sql = "SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
1030
			attrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
1031
			relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}'))
1032
			AND attnum IN ('".join("','", $atts)."')";
1033
1034
        $rs = $this->selectSet($sql);
1035
        if ($rs->recordCount() != sizeof($atts)) {
1036
            return -2;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -2 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1037
        }
1038
1039
        $temp = [];
1040
        while (!$rs->EOF) {
1041
            $temp[$rs->fields['attnum']] = $rs->fields['attname'];
1042
            $rs->moveNext();
1043
        }
1044
1045
        return $temp;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $temp returns the type array which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1046
    }
1047
1048
    /**
1049
     * Cleans (escapes) an array.
1050
     *
1051
     * @param $arr The array to clean, by reference
1052
     *
1053
     * @return The cleaned array
1054
     */
1055
    public function arrayClean(&$arr)
1056
    {
1057
        foreach ($arr as $k => $v) {
1058
            if ($v === null) {
1059
                continue;
1060
            }
1061
1062
            $arr[$k] = pg_escape_string($v);
1 ignored issue
show
Bug introduced by
The call to pg_escape_string() has too few arguments starting with data. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1062
            $arr[$k] = /** @scrutinizer ignore-call */ pg_escape_string($v);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1063
        }
1064
1065
        return $arr;
1066
    }
1067
1068
    /**
1069
     * Grabs an array of users and their privileges for an object,
1070
     * given its type.
1071
     *
1072
     * @param $object The name of the object whose privileges are to be retrieved
1073
     * @param $type   The type of the object (eg. database, schema, relation, function or language)
1074
     * @param $table  Optional, column's table if type = column
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Optional was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1075
     *
1076
     * @return arrray|int Privileges array
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\arrray was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1077
     * @return -1         invalid type
0 ignored issues
show
Coding Style introduced by
Only 1 @return tag is allowed in a function comment
Loading history...
1078
     * @return -2         object not found
1079
     * @return -3         unknown privilege type
1080
     */
1081
    public function getPrivileges($object, $type, $table = null)
1082
    {
1083
        $c_schema = $this->_schema;
1084
        $this->clean($c_schema);
1085
        $this->clean($object);
1086
1087
        switch ($type) {
1088
            case 'column':
1089
                $this->clean($table);
1090
                $sql = "
1091
					SELECT E'{' || pg_catalog.array_to_string(attacl, E',') || E'}' as acl
1092
					FROM pg_catalog.pg_attribute a
1093
						LEFT JOIN pg_catalog.pg_class c ON (a.attrelid = c.oid)
1094
						LEFT JOIN pg_catalog.pg_namespace n ON (c.relnamespace=n.oid)
1095
					WHERE n.nspname='{$c_schema}'
1096
						AND c.relname='{$table}'
1097
						AND a.attname='{$object}'";
1098
1099
                break;
1100
            case 'table':
1101
            case 'view':
1102
            case 'sequence':
1103
                $sql = "
1104
					SELECT relacl AS acl FROM pg_catalog.pg_class
1105
					WHERE relname='{$object}'
1106
						AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace
1107
							WHERE nspname='{$c_schema}')";
1108
1109
                break;
1110
            case 'database':
1111
                $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";
1112
1113
                break;
1114
            case 'function':
1115
                // Since we fetch functions by oid, they are already constrained to
1116
                // the current schema.
1117
                $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";
1118
1119
                break;
1120
            case 'language':
1121
                $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";
1122
1123
                break;
1124
            case 'schema':
1125
                $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";
1126
1127
                break;
1128
            case 'tablespace':
1129
                $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";
1130
1131
                break;
1132
            default:
1133
                return -1;
1134
        }
1135
1136
        // Fetch the ACL for object
1137
        $acl = $this->selectField($sql, 'acl');
1138
        if ($acl == -1) {
1139
            return -2;
1140
        }
1141
1142
        if ($acl == '' || $acl === null || !(bool) $acl) {
1143
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type PHPPgAdmin\Database\arrray|integer.
Loading history...
1144
        }
1145
1146
        return $this->_parseACL($acl);
1147
    }
1148
1149
    /**
1150
     * Internal function used for parsing ACLs.
1151
     *
1152
     * @param $acl The ACL to parse (of type aclitem[])
1153
     *
1154
     * @return Privileges array
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Privileges was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1155
     */
1156
    public function _parseACL($acl)
1 ignored issue
show
Coding Style introduced by
Public method name "Postgres::_parseACL" must not be prefixed with an underscore
Loading history...
1157
    {
1158
        // Take off the first and last characters (the braces)
1159
        $acl = substr($acl, 1, strlen($acl) - 2);
1160
1161
        // Pick out individual ACE's by carefully parsing.  This is necessary in order
1162
        // to cope with usernames and stuff that contain commas
1163
        $aces      = [];
1164
        $i         = $j         = 0;
1165
        $in_quotes = false;
1166
        while ($i < strlen($acl)) {
1167
            // If current char is a double quote and it's not escaped, then
1168
            // enter quoted bit
1169
            $char = substr($acl, $i, 1);
1170
            if ($char == '"' && ($i == 0 || substr($acl, $i - 1, 1) != '\\')) {
1171
                $in_quotes = !$in_quotes;
1 ignored issue
show
introduced by
$in_quotes is of type mixed, thus it always evaluated to false.
Loading history...
1172
            } elseif ($char == ',' && !$in_quotes) {
1173
                // Add text so far to the array
1174
                $aces[] = substr($acl, $j, $i - $j);
1175
                $j      = $i + 1;
1176
            }
1177
            ++$i;
1178
        }
1179
        // Add final text to the array
1180
        $aces[] = substr($acl, $j);
1181
1182
        // Create the array to be returned
1183
        $temp = [];
1184
1185
        // For each ACE, generate an entry in $temp
1186
        foreach ($aces as $v) {
1187
            // If the ACE begins with a double quote, strip them off both ends
1188
            // and unescape backslashes and double quotes
1189
            $unquote = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $unquote is dead and can be removed.
Loading history...
1190
            if (strpos($v, '"') === 0) {
1191
                $v = substr($v, 1, strlen($v) - 2);
1192
                $v = str_replace('\\"', '"', $v);
1193
                $v = str_replace('\\\\', '\\', $v);
1194
            }
1195
1196
            // Figure out type of ACE (public, user or group)
1197
            if (strpos($v, '=') === 0) {
1198
                $atype = 'public';
1199
            } else {
1200
                if ($this->hasRoles()) {
1201
                    $atype = 'role';
1202
                } else {
1203
                    if (strpos($v, 'group ') === 0) {
1204
                        $atype = 'group';
1205
                        // Tear off 'group' prefix
1206
                        $v = substr($v, 6);
1207
                    } else {
1208
                        $atype = 'user';
1209
                    }
1210
                }
1211
            }
1212
1213
            // Break on unquoted equals sign...
1214
            $i         = 0;
1215
            $in_quotes = false;
1216
            $entity    = null;
1217
            $chars     = null;
1218
            while ($i < strlen($v)) {
1219
                // If current char is a double quote and it's not escaped, then
1220
                // enter quoted bit
1221
                $char      = substr($v, $i, 1);
1222
                $next_char = substr($v, $i + 1, 1);
1223
                if ($char == '"' && ($i == 0 || $next_char != '"')) {
1224
                    $in_quotes = !$in_quotes;
1 ignored issue
show
introduced by
The condition $in_quotes is always false.
Loading history...
1225
                } // Skip over escaped double quotes
1226
                elseif ($char == '"' && $next_char == '"') {
0 ignored issues
show
Coding Style introduced by
Expected "} elseif (...) \n"; found " // Skip over escaped double quotes\n elseif (...) {\n"
Loading history...
1227
                    ++$i;
1228
                } elseif ($char == '=' && !$in_quotes) {
1229
                    // Split on current equals sign
1230
                    $entity = substr($v, 0, $i);
1231
                    $chars  = substr($v, $i + 1);
1232
1233
                    break;
1234
                }
1235
                ++$i;
1236
            }
1237
1238
            // Check for quoting on entity name, and unescape if necessary
1239
            if (strpos($entity, '"') === 0) {
1240
                $entity = substr($entity, 1, strlen($entity) - 2);
1241
                $entity = str_replace('""', '"', $entity);
1242
            }
1243
1244
            // New row to be added to $temp
1245
            // (type, grantee, privileges, grantor, grant option?
1246
            $row = [$atype, $entity, [], '', []];
1247
1248
            // Loop over chars and add privs to $row
1249
            for ($i = 0; $i < strlen($chars); ++$i) {
1250
                // Append to row's privs list the string representing
1251
                // the privilege
1252
                $char = substr($chars, $i, 1);
1253
                if ($char == '*') {
1254
                    $row[4][] = $this->privmap[substr($chars, $i - 1, 1)];
1255
                } elseif ($char == '/') {
1256
                    $grantor = substr($chars, $i + 1);
1257
                    // Check for quoting
1258
                    if (strpos($grantor, '"') === 0) {
1259
                        $grantor = substr($grantor, 1, strlen($grantor) - 2);
1260
                        $grantor = str_replace('""', '"', $grantor);
1261
                    }
1262
                    $row[3] = $grantor;
1263
1264
                    break;
1265
                } else {
1266
                    if (!isset($this->privmap[$char])) {
1267
                        return -3;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -3 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\Privileges.
Loading history...
1268
                    }
1269
1270
                    $row[2][] = $this->privmap[$char];
1271
                }
1272
            }
1273
1274
            // Append row to temp
1275
            $temp[] = $row;
1276
        }
1277
1278
        return $temp;
1279
    }
1280
1281
    // Rule functions
1282
1283
    /**
1284
     * Returns an array containing a function's properties.
1285
     *
1286
     * @param array $f The array of data for the function
1287
     *
1288
     * @return array An array containing the properties
1289
     */
1290
    public function getFunctionProperties($f)
1291
    {
1292
        $temp = [];
1293
1294
        // Volatility
1295
        if ($f['provolatile'] == 'v') {
1296
            $temp[] = 'VOLATILE';
1297
        } elseif ($f['provolatile'] == 'i') {
1298
            $temp[] = 'IMMUTABLE';
1299
        } elseif ($f['provolatile'] == 's') {
1300
            $temp[] = 'STABLE';
1301
        } else {
1302
            return -1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -1 returns the type integer which is incompatible with the documented return type array.
Loading history...
1303
        }
1304
1305
        // Null handling
1306
        $f['proisstrict'] = $this->phpBool($f['proisstrict']);
1307
        if ($f['proisstrict']) {
1308
            $temp[] = 'RETURNS NULL ON NULL INPUT';
1309
        } else {
1310
            $temp[] = 'CALLED ON NULL INPUT';
1311
        }
1312
1313
        // Security
1314
        $f['prosecdef'] = $this->phpBool($f['prosecdef']);
1315
        if ($f['prosecdef']) {
1316
            $temp[] = 'SECURITY DEFINER';
1317
        } else {
1318
            $temp[] = 'SECURITY INVOKER';
1319
        }
1320
1321
        return $temp;
1322
    }
1323
1324
    /**
1325
     * Updates (replaces) a function.
1326
     *
1327
     * @param int    $function_oid The OID of the function
1328
     * @param string $funcname     The name of the function to create
1329
     * @param string $newname      The new name for the function
1330
     * @param array  $args         The array of argument types
1331
     * @param string $returns      The return type
1332
     * @param string $definition   The definition for the new function
1333
     * @param string $language     The language the function is written for
1334
     * @param array  $flags        An array of optional flags
1335
     * @param bool   $setof        True if returns a set, false otherwise
1336
     * @param string $funcown
1337
     * @param string $newown
1338
     * @param string $funcschema
1339
     * @param string $newschema
1340
     * @param float  $cost
1341
     * @param int    $rows
1342
     * @param string $comment      The comment on the function
1343
     *
1344
     * @return bool|int 0 success
1345
     */
1346
    public function setFunction(
1347
        $function_oid,
0 ignored issues
show
Unused Code introduced by
The parameter $function_oid is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1347
        /** @scrutinizer ignore-unused */ $function_oid,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1348
        $funcname,
1349
        $newname,
1350
        $args,
1351
        $returns,
1352
        $definition,
1353
        $language,
1354
        $flags,
1355
        $setof,
1356
        $funcown,
1357
        $newown,
1358
        $funcschema,
1359
        $newschema,
1360
        $cost,
1361
        $rows,
1362
        $comment
1363
    ) {
1364
        // Begin a transaction
1365
        $status = $this->beginTransaction();
1366
        if ($status != 0) {
1367
            $this->rollbackTransaction();
1368
1369
            return -1;
1370
        }
1371
1372
        // Replace the existing function
1373
        $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true);
0 ignored issues
show
Bug introduced by
$args of type array is incompatible with the type string expected by parameter $args of PHPPgAdmin\Database\Postgres::createFunction(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1373
        $status = $this->createFunction($funcname, /** @scrutinizer ignore-type */ $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true);
Loading history...
1374
        if ($status != 0) {
1375
            $this->rollbackTransaction();
1376
1377
            return $status;
1378
        }
1379
1380
        $f_schema = $this->_schema;
1381
        $this->fieldClean($f_schema);
1382
1383
        // Rename the function, if necessary
1384
        $this->fieldClean($newname);
1385
        /* $funcname is escaped in createFunction */
1386
        if ($funcname != $newname) {
1387
            $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\"";
1388
            $status = $this->execute($sql);
1389
            if ($status != 0) {
1390
                $this->rollbackTransaction();
1391
1392
                return -5;
1393
            }
1394
1395
            $funcname = $newname;
1396
        }
1397
1398
        // Alter the owner, if necessary
1399
        if ($this->hasFunctionAlterOwner()) {
1400
            $this->fieldClean($newown);
1401
            if ($funcown != $newown) {
1402
                $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\"";
1403
                $status = $this->execute($sql);
1404
                if ($status != 0) {
1405
                    $this->rollbackTransaction();
1406
1407
                    return -6;
1408
                }
1409
            }
1410
        }
1411
1412
        // Alter the schema, if necessary
1413
        if ($this->hasFunctionAlterSchema()) {
1414
            $this->fieldClean($newschema);
1415
            /* $funcschema is escaped in createFunction */
1416
            if ($funcschema != $newschema) {
1417
                $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\"";
1418
                $status = $this->execute($sql);
1419
                if ($status != 0) {
1420
                    $this->rollbackTransaction();
1421
1422
                    return -7;
1423
                }
1424
            }
1425
        }
1426
1427
        return $this->endTransaction();
1428
    }
1429
1430
    /**
1431
     * Creates a new function.
1432
     *
1433
     * @param string $funcname   The name of the function to create
1434
     * @param string $args       A comma separated string of types
1435
     * @param string $returns    The return type
1436
     * @param string $definition The definition for the new function
1437
     * @param string $language   The language the function is written for
1438
     * @param array  $flags      An array of optional flags
1439
     * @param bool   $setof      True if it returns a set, false otherwise
1440
     * @param string $cost       cost the planner should use in the function  execution step
1441
     * @param int    $rows       number of rows planner should estimate will be returned
1442
     * @param string $comment    Comment for the function
1443
     * @param bool   $replace    (optional) True if OR REPLACE, false for
1444
     *                           normal
1445
     *
1446
     * @return bool|int 0 success
1447
     */
1448
    public function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, $replace = false)
1449
    {
1450
        // Begin a transaction
1451
        $status = $this->beginTransaction();
1452
        if ($status != 0) {
1453
            $this->rollbackTransaction();
1454
1455
            return -1;
1456
        }
1457
1458
        $this->fieldClean($funcname);
1459
        $this->clean($args);
1460
        $this->fieldClean($language);
1461
        $this->arrayClean($flags);
1462
        $this->clean($cost);
1463
        $this->clean($rows);
1464
        $f_schema = $this->_schema;
1465
        $this->fieldClean($f_schema);
1466
1467
        $sql = 'CREATE';
1468
        if ($replace) {
1469
            $sql .= ' OR REPLACE';
1470
        }
1471
1472
        $sql .= " FUNCTION \"{$f_schema}\".\"{$funcname}\" (";
1473
1474
        if ($args != '') {
1475
            $sql .= $args;
1476
        }
1477
1478
        // For some reason, the returns field cannot have quotes...
1479
        $sql .= ') RETURNS ';
1480
        if ($setof) {
1481
            $sql .= 'SETOF ';
1482
        }
1483
1484
        $sql .= "{$returns} AS ";
1485
1486
        if (is_array($definition)) {
1 ignored issue
show
introduced by
The condition is_array($definition) is always false.
Loading history...
1487
            $this->arrayClean($definition);
1488
            $sql .= "'".$definition[0]."'";
1489
            if ($definition[1]) {
1490
                $sql .= ",'".$definition[1]."'";
1491
            }
1492
        } else {
1493
            $this->clean($definition);
1494
            $sql .= "'".$definition."'";
1495
        }
1496
1497
        $sql .= " LANGUAGE \"{$language}\"";
1498
1499
        // Add costs
1500
        if (!empty($cost)) {
1501
            $sql .= " COST {$cost}";
1502
        }
1503
1504
        if ($rows != 0) {
1505
            $sql .= " ROWS {$rows}";
1506
        }
1507
1508
        // Add flags
1509
        foreach ($flags as $v) {
1510
            // Skip default flags
1511
            if ($v == '') {
1512
                continue;
1513
            }
1514
1515
            $sql .= "\n{$v}";
1516
        }
1517
1518
        $status = $this->execute($sql);
1519
        if ($status != 0) {
1520
            $this->rollbackTransaction();
1521
1522
            return -3;
1523
        }
1524
1525
        /* set the comment */
1526
        $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment);
1527
        if ($status != 0) {
1528
            $this->rollbackTransaction();
1529
1530
            return -4;
1531
        }
1532
1533
        return $this->endTransaction();
1534
    }
1535
1536
    /**
1537
     * Drops a function.
1538
     *
1539
     * @param int  $function_oid The OID of the function to drop
1540
     * @param bool $cascade      True to cascade drop, false to restrict
1541
     *
1542
     * @return int 0 if operation was successful
1543
     */
1544
    public function dropFunction($function_oid, $cascade)
1545
    {
1546
        // Function comes in with $object as function OID
1547
        $fn       = $this->getFunction($function_oid);
1548
        $f_schema = $this->_schema;
1549
        $this->fieldClean($f_schema);
1550
        $this->fieldClean($fn->fields['proname']);
1551
1552
        $sql = "DROP FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
1553
        if ($cascade) {
1554
            $sql .= ' CASCADE';
1555
        }
1556
1557
        return $this->execute($sql);
1558
    }
1559
1560
    /**
1561
     * Returns all details for a particular function.
1562
     *
1563
     * @param int $function_oid
1564
     *
1565
     * @return \PHPPgAdmin\ADORecordSet Function info
1566
     *
1567
     * @internal param string The $func name of the function to retrieve
1568
     */
1569
    public function getFunction($function_oid)
1570
    {
1571
        $this->clean($function_oid);
1572
1573
        $sql = "
1574
			SELECT
1575
				pc.oid AS prooid, proname,
1576
				pg_catalog.pg_get_userbyid(proowner) AS proowner,
1577
				nspname as proschema, lanname as prolanguage, procost, prorows,
1578
				pg_catalog.format_type(prorettype, NULL) as proresult, prosrc,
1579
				probin, proretset, proisstrict, provolatile, prosecdef,
1580
				pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
1581
				proargnames AS proargnames,
1582
				pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment,
1583
				proconfig,
1584
				(select array_agg( (select typname from pg_type pt
1585
					where pt.oid = p.oid) ) from unnest(proallargtypes) p)
1586
				AS proallarguments,
1587
				proargmodes
1588
			FROM
1589
				pg_catalog.pg_proc pc, pg_catalog.pg_language pl,
1590
				pg_catalog.pg_namespace pn
1591
			WHERE
1592
				pc.oid = '{$function_oid}'::oid AND pc.prolang = pl.oid
1593
				AND pc.pronamespace = pn.oid
1594
			";
1595
1596
        return $this->selectSet($sql);
1597
    }
1598
1599
    /**
1600
     * Returns all details for a particular type.
1601
     *
1602
     * @param string $typname The name of the view to retrieve
1603
     *
1604
     * @return \PHPPgAdmin\ADORecordSet info
1605
     */
1606
    public function getType($typname)
1607
    {
1608
        $this->clean($typname);
1609
1610
        $sql = "SELECT typtype, typbyval, typname, typinput AS typin, typoutput AS typout, typlen, typalign
1611
			FROM pg_type WHERE typname='{$typname}'";
1612
1613
        return $this->selectSet($sql);
1614
    }
1615
1616
    /**
1617
     * Returns a list of all types in the database.
1618
     *
1619
     * @param bool $all        If true, will find all available types, if false just those in search path
1620
     * @param bool $tabletypes If true, will include table types
1621
     * @param bool $domains    If true, will include domains
1622
     *
1623
     * @return \PHPPgAdmin\ADORecordSet A recordset
1624
     */
1625
    public function getTypes($all = false, $tabletypes = false, $domains = false)
1626
    {
1627
        if ($all) {
1628
            $where = '1 = 1';
1629
        } else {
1630
            $c_schema = $this->_schema;
1631
            $this->clean($c_schema);
1632
            $where = "n.nspname = '{$c_schema}'";
1633
        }
1634
        // Never show system table types
1635
        $where2 = "AND c.relnamespace NOT IN (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname LIKE 'pg@_%' ESCAPE '@')";
1636
1637
        // Create type filter
1638
        $tqry = "'c'";
1639
        if ($tabletypes) {
1640
            $tqry .= ", 'r', 'v'";
1641
        }
1642
1643
        // Create domain filter
1644
        if (!$domains) {
1645
            $where .= " AND t.typtype != 'd'";
1646
        }
1647
1648
        $sql = "SELECT
1649
				t.typname AS basename,
1650
				pg_catalog.format_type(t.oid, NULL) AS typname,
1651
				pu.usename AS typowner,
1652
				t.typtype,
1653
				pg_catalog.obj_description(t.oid, 'pg_type') AS typcomment
1654
			FROM (pg_catalog.pg_type t
1655
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace)
1656
				LEFT JOIN pg_catalog.pg_user pu ON t.typowner = pu.usesysid
1657
			WHERE (t.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid {$where2}))
1658
			AND t.typname !~ '^_'
1659
			AND {$where}
1660
			ORDER BY typname
1661
		";
1662
1663
        return $this->selectSet($sql);
1664
    }
1665
1666
    /**
1667
     * Creates a new type.
1668
     *
1669
     * @param string $typname
1670
     * @param string $typin
1671
     * @param string $typout
1672
     * @param string $typlen
1673
     * @param string $typdef
1674
     * @param string $typelem
1675
     * @param string $typdelim
1676
     * @param string $typbyval
1677
     * @param string $typalign
1678
     * @param string $typstorage
1679
     *
1680
     * @return int 0 if operation was successful
1681
     *
1682
     * @internal param $ ...
1683
     */
1684
    public function createType(
1685
        $typname,
1686
        $typin,
1687
        $typout,
1688
        $typlen,
1689
        $typdef,
1690
        $typelem,
1691
        $typdelim,
1692
        $typbyval,
1693
        $typalign,
1694
        $typstorage
1695
    ) {
1696
        $f_schema = $this->_schema;
1697
        $this->fieldClean($f_schema);
1698
        $this->fieldClean($typname);
1699
        $this->fieldClean($typin);
1700
        $this->fieldClean($typout);
1701
1702
        $sql = "
1703
			CREATE TYPE \"{$f_schema}\".\"{$typname}\" (
1704
				INPUT = \"{$typin}\",
1705
				OUTPUT = \"{$typout}\",
1706
				INTERNALLENGTH = {$typlen}";
1707
        if ($typdef != '') {
1708
            $sql .= ", DEFAULT = {$typdef}";
1709
        }
1710
1711
        if ($typelem != '') {
1712
            $sql .= ", ELEMENT = {$typelem}";
1713
        }
1714
1715
        if ($typdelim != '') {
1716
            $sql .= ", DELIMITER = {$typdelim}";
1717
        }
1718
1719
        if ($typbyval) {
1720
            $sql .= ', PASSEDBYVALUE, ';
1721
        }
1722
1723
        if ($typalign != '') {
1724
            $sql .= ", ALIGNMENT = {$typalign}";
1725
        }
1726
1727
        if ($typstorage != '') {
1728
            $sql .= ", STORAGE = {$typstorage}";
1729
        }
1730
1731
        $sql .= ')';
1732
1733
        return $this->execute($sql);
1734
    }
1735
1736
    /**
1737
     * Drops a type.
1738
     *
1739
     * @param $typname The name of the type to drop
1740
     * @param $cascade True to cascade drop, false to restrict
1741
     *
1742
     * @return int 0 if operation was successful
1743
     */
1744
    public function dropType($typname, $cascade)
1745
    {
1746
        $f_schema = $this->_schema;
1747
        $this->fieldClean($f_schema);
1748
        $this->fieldClean($typname);
1749
1750
        $sql = "DROP TYPE \"{$f_schema}\".\"{$typname}\"";
1751
        if ($cascade) {
1752
            $sql .= ' CASCADE';
1753
        }
1754
1755
        return $this->execute($sql);
1756
    }
1757
1758
    /**
1759
     * Creates a new enum type in the database.
1760
     *
1761
     * @param string $name       The name of the type
1762
     * @param array  $values     An array of values
1763
     * @param string $typcomment Type comment
1764
     *
1765
     * @return bool|int 0 success
1766
     */
1767
    public function createEnumType($name, $values, $typcomment)
1768
    {
1769
        $f_schema = $this->_schema;
1770
        $this->fieldClean($f_schema);
1771
        $this->fieldClean($name);
1772
1773
        if (empty($values)) {
1774
            return -2;
1775
        }
1776
1777
        $status = $this->beginTransaction();
1778
        if ($status != 0) {
1779
            return -1;
1780
        }
1781
1782
        $values = array_unique($values);
1783
1784
        $nbval = count($values);
1785
1786
        for ($i = 0; $i < $nbval; ++$i) {
1787
            $this->clean($values[$i]);
1788
        }
1789
1790
        $sql = "CREATE TYPE \"{$f_schema}\".\"{$name}\" AS ENUM ('";
1791
        $sql .= implode("','", $values);
1792
        $sql .= "')";
1793
1794
        $status = $this->execute($sql);
1795
        if ($status) {
1796
            $this->rollbackTransaction();
1797
1798
            return -1;
1799
        }
1800
1801
        if ($typcomment != '') {
1802
            $status = $this->setComment('TYPE', $name, '', $typcomment, true);
1803
            if ($status) {
1804
                $this->rollbackTransaction();
1805
1806
                return -1;
1807
            }
1808
        }
1809
1810
        return $this->endTransaction();
1811
    }
1812
1813
    /**
1814
     * Get defined values for a given enum.
1815
     *
1816
     * @param string $name
1817
     *
1818
     * @return \PHPPgAdmin\ADORecordSet A recordset
1819
     */
1820
    public function getEnumValues($name)
1821
    {
1822
        $this->clean($name);
1823
1824
        $sql = "SELECT enumlabel AS enumval
1825
		FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON (t.oid=e.enumtypid)
1826
		WHERE t.typname = '{$name}' ORDER BY e.oid";
1827
1828
        return $this->selectSet($sql);
1829
    }
1830
1831
    // Operator functions
1832
1833
    /**
1834
     * Creates a new composite type in the database.
1835
     *
1836
     * @param string $name       The name of the type
1837
     * @param int    $fields     The number of fields
1838
     * @param array  $field      An array of field names
1839
     * @param array  $type       An array of field types
1840
     * @param array  $array      An array of '' or '[]' for each type if it's an array or not
1841
     * @param array  $length     An array of field lengths
1842
     * @param array  $colcomment An array of comments
1843
     * @param string $typcomment Type comment
1844
     *
1845
     * @return bool|int 0 success
1846
     */
1847
    public function createCompositeType($name, $fields, $field, $type, $array, $length, $colcomment, $typcomment)
1848
    {
1849
        $f_schema = $this->_schema;
1850
        $this->fieldClean($f_schema);
1851
        $this->fieldClean($name);
1852
1853
        $status = $this->beginTransaction();
1854
        if ($status != 0) {
1855
            return -1;
1856
        }
1857
1858
        $found       = false;
1859
        $first       = true;
1860
        $comment_sql = ''; // Accumulate comments for the columns
1861
        $sql         = "CREATE TYPE \"{$f_schema}\".\"{$name}\" AS (";
1862
        for ($i = 0; $i < $fields; ++$i) {
1863
            $this->fieldClean($field[$i]);
1864
            $this->clean($type[$i]);
1865
            $this->clean($length[$i]);
1866
            $this->clean($colcomment[$i]);
1867
1868
            // Skip blank columns - for user convenience
1869
            if ($field[$i] == '' || $type[$i] == '') {
1870
                continue;
1871
            }
1872
1873
            // If not the first column, add a comma
1874
            if (!$first) {
1875
                $sql .= ', ';
1876
            } else {
1877
                $first = false;
1878
            }
1879
1880
            switch ($type[$i]) {
1881
                // Have to account for weird placing of length for with/without
1882
                // time zone types
1883
                case 'timestamp with time zone':
1884
                case 'timestamp without time zone':
1885
                    $qual = substr($type[$i], 9);
1886
                    $sql .= "\"{$field[$i]}\" timestamp";
1887
                    if ($length[$i] != '') {
1888
                        $sql .= "({$length[$i]})";
1889
                    }
1890
1891
                    $sql .= $qual;
1892
1893
                    break;
1894
                case 'time with time zone':
1895
                case 'time without time zone':
1896
                    $qual = substr($type[$i], 4);
1897
                    $sql .= "\"{$field[$i]}\" time";
1898
                    if ($length[$i] != '') {
1899
                        $sql .= "({$length[$i]})";
1900
                    }
1901
1902
                    $sql .= $qual;
1903
1904
                    break;
1905
                default:
1906
                    $sql .= "\"{$field[$i]}\" {$type[$i]}";
1907
                    if ($length[$i] != '') {
1908
                        $sql .= "({$length[$i]})";
1909
                    }
1910
            }
1911
            // Add array qualifier if necessary
1912
            if ($array[$i] == '[]') {
1913
                $sql .= '[]';
1914
            }
1915
1916
            if ($colcomment[$i] != '') {
1917
                $comment_sql .= "COMMENT ON COLUMN \"{$f_schema}\".\"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
1918
            }
1919
1920
            $found = true;
1921
        }
1922
1923
        if (!$found) {
0 ignored issues
show
introduced by
The condition $found is always false.
Loading history...
1924
            return -1;
1925
        }
1926
1927
        $sql .= ')';
1928
1929
        $status = $this->execute($sql);
1930
        if ($status) {
1931
            $this->rollbackTransaction();
1932
1933
            return -1;
1934
        }
1935
1936
        if ($typcomment != '') {
1937
            $status = $this->setComment('TYPE', $name, '', $typcomment, true);
1938
            if ($status) {
1939
                $this->rollbackTransaction();
1940
1941
                return -1;
1942
            }
1943
        }
1944
1945
        if ($comment_sql != '') {
1946
            $status = $this->execute($comment_sql);
1947
            if ($status) {
1948
                $this->rollbackTransaction();
1949
1950
                return -1;
1951
            }
1952
        }
1953
1954
        return $this->endTransaction();
1955
    }
1956
1957
    /**
1958
     * Returns a list of all casts in the database.
1959
     *
1960
     * @return \PHPPgAdmin\ADORecordSet All casts
1961
     */
1962
    public function getCasts()
1963
    {
1964
        $conf = $this->conf;
1965
1966
        if ($conf['show_system']) {
1967
            $where = '';
1968
        } else {
1969
            $where = '
1970
				AND n1.nspname NOT LIKE $$pg\_%$$
1971
				AND n2.nspname NOT LIKE $$pg\_%$$
1972
				AND n3.nspname NOT LIKE $$pg\_%$$
1973
			';
1974
        }
1975
1976
        $sql = "
1977
			SELECT
1978
				c.castsource::pg_catalog.regtype AS castsource,
1979
				c.casttarget::pg_catalog.regtype AS casttarget,
1980
				CASE WHEN c.castfunc=0 THEN NULL
1981
				ELSE c.castfunc::pg_catalog.regprocedure END AS castfunc,
1982
				c.castcontext,
1983
				obj_description(c.oid, 'pg_cast') as castcomment
1984
			FROM
1985
				(pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p ON c.castfunc=p.oid JOIN pg_catalog.pg_namespace n3 ON p.pronamespace=n3.oid),
1986
				pg_catalog.pg_type t1,
1987
				pg_catalog.pg_type t2,
1988
				pg_catalog.pg_namespace n1,
1989
				pg_catalog.pg_namespace n2
1990
			WHERE
1991
				c.castsource=t1.oid
1992
				AND c.casttarget=t2.oid
1993
				AND t1.typnamespace=n1.oid
1994
				AND t2.typnamespace=n2.oid
1995
				{$where}
1996
			ORDER BY 1, 2
1997
		";
1998
1999
        return $this->selectSet($sql);
2000
    }
2001
2002
    /**
2003
     * Returns a list of all conversions in the database.
2004
     *
2005
     * @return \PHPPgAdmin\ADORecordSet All conversions
2006
     */
2007
    public function getConversions()
2008
    {
2009
        $c_schema = $this->_schema;
2010
        $this->clean($c_schema);
2011
        $sql = "
2012
			SELECT
2013
			       c.conname,
2014
			       pg_catalog.pg_encoding_to_char(c.conforencoding) AS conforencoding,
2015
			       pg_catalog.pg_encoding_to_char(c.contoencoding) AS contoencoding,
2016
			       c.condefault,
2017
			       pg_catalog.obj_description(c.oid, 'pg_conversion') AS concomment
2018
			FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n
2019
			WHERE n.oid = c.connamespace
2020
			      AND n.nspname='{$c_schema}'
2021
			ORDER BY 1;
2022
		";
2023
2024
        return $this->selectSet($sql);
2025
    }
2026
2027
    // Operator Class functions
2028
2029
    /**
2030
     * Edits a rule on a table OR view.
2031
     *
2032
     * @param string $name The name of the new rule
2033
     * @param $event   SELECT, INSERT, UPDATE or DELETE
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\SELECT was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2034
     * @param $table   Table on which to create the rule
2035
     * @param $where   When to execute the rule, '' indicates always
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\When was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2036
     * @param $instead True if an INSTEAD rule, false otherwise
2037
     * @param $type    NOTHING for a do nothing rule, SOMETHING to use given action
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\NOTHING was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2038
     * @param $action  The action to take
2039
     *
2040
     * @return int 0 if operation was successful
2041
     */
2042
    public function setRule($name, $event, $table, $where, $instead, $type, $action)
2043
    {
2044
        return $this->createRule($name, $event, $table, $where, $instead, $type, $action, true);
2045
    }
2046
2047
    // FTS functions
2048
2049
    /**
2050
     * Creates a rule.
2051
     *
2052
     * @param string $name    The name of the new rule
2053
     * @param string $event   SELECT, INSERT, UPDATE or DELETE
2054
     * @param string $table   Table on which to create the rule
2055
     * @param string $where   When to execute the rule, '' indicates always
2056
     * @param bool   $instead True if an INSTEAD rule, false otherwise
2057
     * @param string $type    NOTHING for a do nothing rule, SOMETHING to use given action
2058
     * @param string $action  The action to take
2059
     * @param bool   $replace (optional) True to replace existing rule, false
2060
     *                        otherwise
2061
     *
2062
     * @return int 0 if operation was successful
2063
     */
2064
    public function createRule($name, $event, $table, $where, $instead, $type, $action, $replace = false)
2065
    {
2066
        $f_schema = $this->_schema;
2067
        $this->fieldClean($f_schema);
2068
        $this->fieldClean($name);
2069
        $this->fieldClean($table);
2070
        if (!in_array($event, $this->rule_events, true)) {
2071
            return -1;
2072
        }
2073
2074
        $sql = 'CREATE';
2075
        if ($replace) {
2076
            $sql .= ' OR REPLACE';
2077
        }
2078
2079
        $sql .= " RULE \"{$name}\" AS ON {$event} TO \"{$f_schema}\".\"{$table}\"";
2080
        // Can't escape WHERE clause
2081
        if ($where != '') {
2082
            $sql .= " WHERE {$where}";
2083
        }
2084
2085
        $sql .= ' DO';
2086
        if ($instead) {
2087
            $sql .= ' INSTEAD';
2088
        }
2089
2090
        if ($type == 'NOTHING') {
2091
            $sql .= ' NOTHING';
2092
        } else {
2093
            $sql .= " ({$action})";
2094
        }
2095
2096
        return $this->execute($sql);
2097
    }
2098
2099
    /**
2100
     * Removes a rule from a table OR view.
2101
     *
2102
     * @param string $rule     The rule to drop
2103
     * @param string $relation The relation from which to drop
2104
     * @param string $cascade  True to cascade drop, false to restrict
2105
     *
2106
     * @return int 0 if operation was successful
2107
     */
2108
    public function dropRule($rule, $relation, $cascade)
2109
    {
2110
        $f_schema = $this->_schema;
2111
        $this->fieldClean($f_schema);
2112
        $this->fieldClean($rule);
2113
        $this->fieldClean($relation);
2114
2115
        $sql = "DROP RULE \"{$rule}\" ON \"{$f_schema}\".\"{$relation}\"";
2116
        if ($cascade) {
2117
            $sql .= ' CASCADE';
2118
        }
2119
2120
        return $this->execute($sql);
2121
    }
2122
2123
    /**
2124
     * Grabs a single trigger.
2125
     *
2126
     * @param string $table   The name of a table whose triggers to retrieve
2127
     * @param string $trigger The name of the trigger to retrieve
2128
     *
2129
     * @return \PHPPgAdmin\ADORecordSet A recordset
2130
     */
2131
    public function getTrigger($table, $trigger)
2132
    {
2133
        $c_schema = $this->_schema;
2134
        $this->clean($c_schema);
2135
        $this->clean($table);
2136
        $this->clean($trigger);
2137
2138
        $sql = "
2139
			SELECT * FROM pg_catalog.pg_trigger t, pg_catalog.pg_class c
2140
			WHERE t.tgrelid=c.oid AND c.relname='{$table}' AND t.tgname='{$trigger}'
2141
				AND c.relnamespace=(
2142
					SELECT oid FROM pg_catalog.pg_namespace
2143
					WHERE nspname='{$c_schema}')";
2144
2145
        return $this->selectSet($sql);
2146
    }
2147
2148
    /**
2149
     * A helper function for getTriggers that translates
2150
     * an array of attribute numbers to an array of field names.
2151
     * Note: Only needed for pre-7.4 servers, this function is deprecated.
2152
     *
2153
     * @param string $trigger An array containing fields from the trigger table
2154
     *
2155
     * @return string The trigger definition string
2156
     */
2157
    public function getTriggerDef($trigger)
2158
    {
2159
        $this->fieldArrayClean($trigger);
2160
        // Constants to figure out tgtype
2161
        if (!defined('TRIGGER_TYPE_ROW')) {
2162
            define('TRIGGER_TYPE_ROW', 1 << 0);
2163
        }
2164
2165
        if (!defined('TRIGGER_TYPE_BEFORE')) {
2166
            define('TRIGGER_TYPE_BEFORE', 1 << 1);
2167
        }
2168
2169
        if (!defined('TRIGGER_TYPE_INSERT')) {
2170
            define('TRIGGER_TYPE_INSERT', 1 << 2);
2171
        }
2172
2173
        if (!defined('TRIGGER_TYPE_DELETE')) {
2174
            define('TRIGGER_TYPE_DELETE', 1 << 3);
2175
        }
2176
2177
        if (!defined('TRIGGER_TYPE_UPDATE')) {
2178
            define('TRIGGER_TYPE_UPDATE', 1 << 4);
2179
        }
2180
2181
        $trigger['tgisconstraint'] = $this->phpBool($trigger['tgisconstraint']);
2182
        $trigger['tgdeferrable']   = $this->phpBool($trigger['tgdeferrable']);
2183
        $trigger['tginitdeferred'] = $this->phpBool($trigger['tginitdeferred']);
2184
2185
        // Constraint trigger or normal trigger
2186
        if ($trigger['tgisconstraint']) {
2187
            $tgdef = 'CREATE CONSTRAINT TRIGGER ';
2188
        } else {
2189
            $tgdef = 'CREATE TRIGGER ';
2190
        }
2191
2192
        $tgdef .= "\"{$trigger['tgname']}\" ";
2193
2194
        // Trigger type
2195
        $findx = 0;
2196
        if (($trigger['tgtype'] & TRIGGER_TYPE_BEFORE) == TRIGGER_TYPE_BEFORE) {
2197
            $tgdef .= 'BEFORE';
2198
        } else {
2199
            $tgdef .= 'AFTER';
2200
        }
2201
2202
        if (($trigger['tgtype'] & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT) {
2203
            $tgdef .= ' INSERT';
2204
            ++$findx;
2205
        }
2206
        if (($trigger['tgtype'] & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE) {
2207
            if ($findx > 0) {
2208
                $tgdef .= ' OR DELETE';
2209
            } else {
2210
                $tgdef .= ' DELETE';
2211
                ++$findx;
2212
            }
2213
        }
2214
        if (($trigger['tgtype'] & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) {
2215
            if ($findx > 0) {
2216
                $tgdef .= ' OR UPDATE';
2217
            } else {
2218
                $tgdef .= ' UPDATE';
2219
            }
2220
        }
2221
2222
        $f_schema = $this->_schema;
2223
        $this->fieldClean($f_schema);
2224
        // Table name
2225
        $tgdef .= " ON \"{$f_schema}\".\"{$trigger['relname']}\" ";
2226
2227
        // Deferrability
2228
        if ($trigger['tgisconstraint']) {
2229
            if ($trigger['tgconstrrelid'] != 0) {
2230
                // Assume constrelname is not null
2231
                $tgdef .= " FROM \"{$trigger['tgconstrrelname']}\" ";
2232
            }
2233
            if (!$trigger['tgdeferrable']) {
2234
                $tgdef .= 'NOT ';
2235
            }
2236
2237
            $tgdef .= 'DEFERRABLE INITIALLY ';
2238
            if ($trigger['tginitdeferred']) {
2239
                $tgdef .= 'DEFERRED ';
2240
            } else {
2241
                $tgdef .= 'IMMEDIATE ';
2242
            }
2243
        }
2244
2245
        // Row or statement
2246
        if ($trigger['tgtype'] & TRIGGER_TYPE_ROW == TRIGGER_TYPE_ROW) {
2247
            $tgdef .= 'FOR EACH ROW ';
2248
        } else {
2249
            $tgdef .= 'FOR EACH STATEMENT ';
2250
        }
2251
2252
        // Execute procedure
2253
        $tgdef .= "EXECUTE PROCEDURE \"{$trigger['tgfname']}\"(";
2254
2255
        // Parameters
2256
        // Escape null characters
2257
        $v = addcslashes($trigger['tgargs'], "\0");
2258
        // Split on escaped null characters
2259
        $params = explode('\\000', $v);
2260
        for ($findx = 0; $findx < $trigger['tgnargs']; ++$findx) {
2261
            $param = "'".str_replace('\'', '\\\'', $params[$findx])."'";
2262
            $tgdef .= $param;
2263
            if ($findx < ($trigger['tgnargs'] - 1)) {
2264
                $tgdef .= ', ';
2265
            }
2266
        }
2267
2268
        // Finish it off
2269
        $tgdef .= ')';
2270
2271
        return $tgdef;
2272
    }
2273
2274
    /**
2275
     * Returns a list of all functions that can be used in triggers.
2276
     */
2277
    public function getTriggerFunctions()
2278
    {
2279
        return $this->getFunctions(true, 'trigger');
2280
    }
2281
2282
    /**
2283
     * Returns a list of all functions in the database.
2284
     *
2285
     * @param bool $all  If true, will find all available functions, if false just those in search path
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\If was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2286
     * @param      $type If not null, will find all functions with return value = type
2287
     *
2288
     * @return \PHPPgAdmin\ADORecordSet All functions
2289
     */
2290
    public function getFunctions($all = false, $type = null)
2291
    {
2292
        if ($all) {
2293
            $where    = 'pg_catalog.pg_function_is_visible(p.oid)';
2294
            $distinct = 'DISTINCT ON (p.proname)';
2295
2296
            if ($type) {
2297
                $where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') ";
2298
            }
2299
        } else {
2300
            $c_schema = $this->_schema;
2301
            $this->clean($c_schema);
2302
            $where    = "n.nspname = '{$c_schema}'";
2303
            $distinct = '';
2304
        }
2305
2306
        $sql = "
2307
			SELECT
2308
				{$distinct}
2309
				p.oid AS prooid,
2310
				p.proname,
2311
				p.proretset,
2312
				pg_catalog.format_type(p.prorettype, NULL) AS proresult,
2313
				pg_catalog.oidvectortypes(p.proargtypes) AS proarguments,
2314
				pl.lanname AS prolanguage,
2315
				pg_catalog.obj_description(p.oid, 'pg_proc') AS procomment,
2316
				p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
2317
				CASE WHEN p.proretset THEN 'setof ' ELSE '' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns,
2318
				coalesce(u.usename::text,p.proowner::text) AS proowner
2319
2320
			FROM pg_catalog.pg_proc p
2321
				INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
2322
				INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang
2323
				LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner
2324
			WHERE NOT p.proisagg
2325
				AND {$where}
2326
			ORDER BY p.proname, proresult
2327
			";
2328
2329
        return $this->selectSet($sql);
2330
    }
2331
2332
    /**
2333
     * Creates a trigger.
2334
     *
2335
     * @param $tgname  The name of the trigger to create
2336
     * @param $table   The name of the table
2337
     * @param $tgproc  The function to execute
2338
     * @param $tgtime  BEFORE or AFTER
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\BEFORE was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2339
     * @param $tgevent Event
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Event was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2340
     * @param $tgfrequency
2341
     * @param $tgargs  The function arguments
2342
     *
2343
     * @return int 0 if operation was successful
2344
     */
2345
    public function createTrigger($tgname, $table, $tgproc, $tgtime, $tgevent, $tgfrequency, $tgargs)
2346
    {
2347
        $f_schema = $this->_schema;
2348
        $this->fieldClean($f_schema);
2349
        $this->fieldClean($tgname);
2350
        $this->fieldClean($table);
2351
        $this->fieldClean($tgproc);
2352
2353
        /* No Statement Level Triggers in PostgreSQL (by now) */
2354
        $sql = "CREATE TRIGGER \"{$tgname}\" {$tgtime}
2355
				{$tgevent} ON \"{$f_schema}\".\"{$table}\"
2356
				FOR EACH {$tgfrequency} EXECUTE PROCEDURE \"{$tgproc}\"({$tgargs})";
2357
2358
        return $this->execute($sql);
2359
    }
2360
2361
    /**
2362
     * Alters a trigger.
2363
     *
2364
     * @param $table   The name of the table containing the trigger
2365
     * @param $trigger The name of the trigger to alter
2366
     * @param $name    The new name for the trigger
2367
     *
2368
     * @return int 0 if operation was successful
2369
     */
2370
    public function alterTrigger($table, $trigger, $name)
2371
    {
2372
        $f_schema = $this->_schema;
2373
        $this->fieldClean($f_schema);
2374
        $this->fieldClean($table);
2375
        $this->fieldClean($trigger);
2376
        $this->fieldClean($name);
2377
2378
        $sql = "ALTER TRIGGER \"{$trigger}\" ON \"{$f_schema}\".\"{$table}\" RENAME TO \"{$name}\"";
2379
2380
        return $this->execute($sql);
2381
    }
2382
2383
    /**
2384
     * Drops a trigger.
2385
     *
2386
     * @param $tgname  The name of the trigger to drop
2387
     * @param $table   The table from which to drop the trigger
2388
     * @param $cascade True to cascade drop, false to restrict
2389
     *
2390
     * @return int 0 if operation was successful
2391
     */
2392
    public function dropTrigger($tgname, $table, $cascade)
2393
    {
2394
        $f_schema = $this->_schema;
2395
        $this->fieldClean($f_schema);
2396
        $this->fieldClean($tgname);
2397
        $this->fieldClean($table);
2398
2399
        $sql = "DROP TRIGGER \"{$tgname}\" ON \"{$f_schema}\".\"{$table}\"";
2400
        if ($cascade) {
2401
            $sql .= ' CASCADE';
2402
        }
2403
2404
        return $this->execute($sql);
2405
    }
2406
2407
    /**
2408
     * Enables a trigger.
2409
     *
2410
     * @param $tgname The name of the trigger to enable
2411
     * @param $table  The table in which to enable the trigger
2412
     *
2413
     * @return int 0 if operation was successful
2414
     */
2415
    public function enableTrigger($tgname, $table)
2416
    {
2417
        $f_schema = $this->_schema;
2418
        $this->fieldClean($f_schema);
2419
        $this->fieldClean($tgname);
2420
        $this->fieldClean($table);
2421
2422
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ENABLE TRIGGER \"{$tgname}\"";
2423
2424
        return $this->execute($sql);
2425
    }
2426
2427
    /**
2428
     * Disables a trigger.
2429
     *
2430
     * @param $tgname The name of the trigger to disable
2431
     * @param $table  The table in which to disable the trigger
2432
     *
2433
     * @return int 0 if operation was successful
2434
     */
2435
    public function disableTrigger($tgname, $table)
2436
    {
2437
        $f_schema = $this->_schema;
2438
        $this->fieldClean($f_schema);
2439
        $this->fieldClean($tgname);
2440
        $this->fieldClean($table);
2441
2442
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" DISABLE TRIGGER \"{$tgname}\"";
2443
2444
        return $this->execute($sql);
2445
    }
2446
2447
    /**
2448
     * Returns a list of all operators in the database.
2449
     *
2450
     * @return \PHPPgAdmin\ADORecordSet All operators
2451
     */
2452
    public function getOperators()
2453
    {
2454
        $c_schema = $this->_schema;
2455
        $this->clean($c_schema);
2456
        // We stick with the subselects here, as you cannot ORDER BY a regtype
2457
        $sql = "
2458
			SELECT
2459
            	po.oid,	po.oprname,
2460
				(SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
2461
				(SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
2462
				po.oprresult::pg_catalog.regtype AS resultname,
2463
		        pg_catalog.obj_description(po.oid, 'pg_operator') AS oprcomment
2464
			FROM
2465
				pg_catalog.pg_operator po
2466
			WHERE
2467
				po.oprnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')
2468
			ORDER BY
2469
				po.oprname, oprleftname, oprrightname
2470
		";
2471
2472
        return $this->selectSet($sql);
2473
    }
2474
2475
    /**
2476
     * Drops an operator.
2477
     *
2478
     * @param $operator_oid The OID of the operator to drop
2479
     * @param $cascade      True to cascade drop, false to restrict
2480
     *
2481
     * @return int 0 if operation was successful
2482
     */
2483
    public function dropOperator($operator_oid, $cascade)
2484
    {
2485
        // Function comes in with $object as operator OID
2486
        $opr      = $this->getOperator($operator_oid);
2487
        $f_schema = $this->_schema;
2488
        $this->fieldClean($f_schema);
2489
        $this->fieldClean($opr->fields['oprname']);
2490
2491
        $sql = "DROP OPERATOR \"{$f_schema}\".{$opr->fields['oprname']} (";
2492
        // Quoting or formatting here???
2493
        if ($opr->fields['oprleftname'] !== null) {
2494
            $sql .= $opr->fields['oprleftname'].', ';
2495
        } else {
2496
            $sql .= 'NONE, ';
2497
        }
2498
2499
        if ($opr->fields['oprrightname'] !== null) {
2500
            $sql .= $opr->fields['oprrightname'].')';
2501
        } else {
2502
            $sql .= 'NONE)';
2503
        }
2504
2505
        if ($cascade) {
2506
            $sql .= ' CASCADE';
2507
        }
2508
2509
        return $this->execute($sql);
2510
    }
2511
2512
    /**
2513
     * Returns all details for a particular operator.
2514
     *
2515
     * @param $operator_oid The oid of the operator
2516
     *
2517
     * @return Function info
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Function was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2518
     */
2519
    public function getOperator($operator_oid)
2520
    {
2521
        $this->clean($operator_oid);
2522
2523
        $sql = "
2524
			SELECT
2525
            	po.oid, po.oprname,
2526
				oprleft::pg_catalog.regtype AS oprleftname,
2527
				oprright::pg_catalog.regtype AS oprrightname,
2528
				oprresult::pg_catalog.regtype AS resultname,
2529
				po.oprcanhash,
2530
				oprcanmerge,
2531
				oprcom::pg_catalog.regoperator AS oprcom,
2532
				oprnegate::pg_catalog.regoperator AS oprnegate,
2533
				po.oprcode::pg_catalog.regproc AS oprcode,
2534
				po.oprrest::pg_catalog.regproc AS oprrest,
2535
				po.oprjoin::pg_catalog.regproc AS oprjoin
2536
			FROM
2537
				pg_catalog.pg_operator po
2538
			WHERE
2539
				po.oid='{$operator_oid}'
2540
		";
2541
2542
        return $this->selectSet($sql);
2543
    }
2544
2545
    /**
2546
     *  Gets all opclasses.
2547
     *
2548
     * @return \PHPPgAdmin\ADORecordSet A recordset
2549
     */
2550
    public function getOpClasses()
2551
    {
2552
        $c_schema = $this->_schema;
2553
        $this->clean($c_schema);
2554
        $sql = "
2555
			SELECT
2556
				pa.amname, po.opcname,
2557
				po.opcintype::pg_catalog.regtype AS opcintype,
2558
				po.opcdefault,
2559
				pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment
2560
			FROM
2561
				pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn
2562
			WHERE
2563
				po.opcmethod=pa.oid
2564
				AND po.opcnamespace=pn.oid
2565
				AND pn.nspname='{$c_schema}'
2566
			ORDER BY 1,2
2567
			";
2568
2569
        return $this->selectSet($sql);
2570
    }
2571
2572
    /**
2573
     * Gets all languages.
2574
     *
2575
     * @param bool|true $all True to get all languages, regardless of show_system
2576
     *
2577
     * @return \PHPPgAdmin\ADORecordSet A recordset
2578
     */
2579
    public function getLanguages($all = false)
2580
    {
2581
        $conf = $this->conf;
2582
2583
        if ($conf['show_system'] || $all) {
2584
            $where = '';
2585
        } else {
2586
            $where = 'WHERE lanispl';
2587
        }
2588
2589
        $sql = "
2590
			SELECT
2591
				lanname, lanpltrusted,
2592
				lanplcallfoid::pg_catalog.regproc AS lanplcallf
2593
			FROM
2594
				pg_catalog.pg_language
2595
			{$where}
2596
			ORDER BY lanname
2597
		";
2598
2599
        return $this->selectSet($sql);
2600
    }
2601
2602
    /**
2603
     * Retrieves information for all tablespaces.
2604
     *
2605
     * @param bool $all Include all tablespaces (necessary when moving objects back to the default space)
2606
     *
2607
     * @return \PHPPgAdmin\ADORecordSet A recordset
2608
     */
2609
    public function getTablespaces($all = false)
2610
    {
2611
        $conf = $this->conf;
2612
2613
        $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, pg_catalog.pg_tablespace_location(oid) as spclocation,
2614
                    (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid AND pd.classoid='pg_tablespace'::regclass) AS spccomment
2615
					FROM pg_catalog.pg_tablespace";
2616
2617
        if (!$conf['show_system'] && !$all) {
2618
            $sql .= ' WHERE spcname NOT LIKE $$pg\_%$$';
2619
        }
2620
2621
        $sql .= ' ORDER BY spcname';
2622
2623
        return $this->selectSet($sql);
2624
    }
2625
2626
    // Misc functions
2627
2628
    /**
2629
     * Retrieves a tablespace's information.
2630
     *
2631
     * @param $spcname
2632
     *
2633
     * @return \PHPPgAdmin\ADORecordSet A recordset
2634
     */
2635
    public function getTablespace($spcname)
2636
    {
2637
        $this->clean($spcname);
2638
2639
        $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, pg_catalog.pg_tablespace_location(oid) as spclocation,
2640
                    (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid AND pd.classoid='pg_tablespace'::regclass) AS spccomment
2641
					FROM pg_catalog.pg_tablespace WHERE spcname='{$spcname}'";
2642
2643
        return $this->selectSet($sql);
2644
    }
2645
2646
    /**
2647
     * Creates a tablespace.
2648
     *
2649
     * @param        $spcname  The name of the tablespace to create
2650
     * @param        $spcowner The owner of the tablespace. '' for current
2651
     * @param        $spcloc   The directory in which to create the tablespace
2652
     * @param string $comment
2653
     *
2654
     * @return int 0 success
2655
     */
2656
    public function createTablespace($spcname, $spcowner, $spcloc, $comment = '')
2657
    {
2658
        $this->fieldClean($spcname);
2659
        $this->clean($spcloc);
2660
2661
        $sql = "CREATE TABLESPACE \"{$spcname}\"";
2662
2663
        if ($spcowner != '') {
2664
            $this->fieldClean($spcowner);
2665
            $sql .= " OWNER \"{$spcowner}\"";
2666
        }
2667
2668
        $sql .= " LOCATION '{$spcloc}'";
2669
2670
        $status = $this->execute($sql);
2671
        if ($status != 0) {
2672
            return -1;
2673
        }
2674
2675
        if ($comment != '' && $this->hasSharedComments()) {
2676
            $status = $this->setComment('TABLESPACE', $spcname, '', $comment);
2677
            if ($status != 0) {
2678
                return -2;
2679
            }
2680
        }
2681
2682
        return 0;
2683
    }
2684
2685
    /**
2686
     * Alters a tablespace.
2687
     *
2688
     * @param        $spcname The name of the tablespace
2689
     * @param        $name    The new name for the tablespace
2690
     * @param        $owner   The new owner for the tablespace
2691
     * @param string $comment
2692
     *
2693
     * @return bool|int 0 success
2694
     */
2695
    public function alterTablespace($spcname, $name, $owner, $comment = '')
2696
    {
2697
        $this->fieldClean($spcname);
2698
        $this->fieldClean($name);
2699
        $this->fieldClean($owner);
2700
2701
        // Begin transaction
2702
        $status = $this->beginTransaction();
2703
        if ($status != 0) {
2704
            return -1;
2705
        }
2706
2707
        // Owner
2708
        $sql    = "ALTER TABLESPACE \"{$spcname}\" OWNER TO \"{$owner}\"";
2709
        $status = $this->execute($sql);
2710
        if ($status != 0) {
2711
            $this->rollbackTransaction();
2712
2713
            return -2;
2714
        }
2715
2716
        // Rename (only if name has changed)
2717
        if ($name != $spcname) {
2718
            $sql    = "ALTER TABLESPACE \"{$spcname}\" RENAME TO \"{$name}\"";
2719
            $status = $this->execute($sql);
2720
            if ($status != 0) {
2721
                $this->rollbackTransaction();
2722
2723
                return -3;
2724
            }
2725
2726
            $spcname = $name;
2727
        }
2728
2729
        // Set comment if it has changed
2730
        if (trim($comment) != '' && $this->hasSharedComments()) {
2731
            $status = $this->setComment('TABLESPACE', $spcname, '', $comment);
2732
            if ($status != 0) {
2733
                return -4;
2734
            }
2735
        }
2736
2737
        return $this->endTransaction();
2738
    }
2739
2740
    /**
2741
     * Drops a tablespace.
2742
     *
2743
     * @param $spcname The name of the domain to drop
2744
     *
2745
     * @return int 0 if operation was successful
2746
     */
2747
    public function dropTablespace($spcname)
2748
    {
2749
        $this->fieldClean($spcname);
2750
2751
        $sql = "DROP TABLESPACE \"{$spcname}\"";
2752
2753
        return $this->execute($sql);
2754
    }
2755
2756
    /**
2757
     * Analyze a database.
2758
     *
2759
     * @param string $table (optional) The table to analyze
2760
     *
2761
     * @return bool 0 if successful
2762
     */
2763
    public function analyzeDB($table = '')
2764
    {
2765
        if ($table != '') {
2766
            $f_schema = $this->_schema;
2767
            $this->fieldClean($f_schema);
2768
            $this->fieldClean($table);
2769
2770
            $sql = "ANALYZE \"{$f_schema}\".\"{$table}\"";
2771
        } else {
2772
            $sql = 'ANALYZE';
2773
        }
2774
2775
        return $this->execute($sql);
2776
    }
2777
2778
    /**
2779
     * Vacuums a database.
2780
     *
2781
     * @param string $table   The table to vacuum
2782
     * @param bool   $analyze If true, also does analyze
2783
     * @param bool   $full    If true, selects "full" vacuum
2784
     * @param bool   $freeze  If true, selects aggressive "freezing" of tuples
2785
     *
2786
     * @return bool 0 if successful
2787
     */
2788
    public function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false)
2789
    {
2790
        $sql = 'VACUUM';
2791
        if ($full) {
2792
            $sql .= ' FULL';
2793
        }
2794
2795
        if ($freeze) {
2796
            $sql .= ' FREEZE';
2797
        }
2798
2799
        if ($analyze) {
2800
            $sql .= ' ANALYZE';
2801
        }
2802
2803
        if ($table != '') {
2804
            $f_schema = $this->_schema;
2805
            $this->fieldClean($f_schema);
2806
            $this->fieldClean($table);
2807
            $sql .= " \"{$f_schema}\".\"{$table}\"";
2808
        }
2809
2810
        return $this->execute($sql);
2811
    }
2812
2813
    /**
2814
     * Returns all autovacuum global configuration.
2815
     *
2816
     * @return array associative array array( param => value, ...)
2817
     */
2818
    public function getAutovacuum()
2819
    {
2820
        $_defaults = $this->selectSet(
2821
            "SELECT name, setting
2822
			FROM pg_catalog.pg_settings
2823
			WHERE
2824
				name = 'autovacuum'
2825
				OR name = 'autovacuum_vacuum_threshold'
2826
				OR name = 'autovacuum_vacuum_scale_factor'
2827
				OR name = 'autovacuum_analyze_threshold'
2828
				OR name = 'autovacuum_analyze_scale_factor'
2829
				OR name = 'autovacuum_vacuum_cost_delay'
2830
				OR name = 'autovacuum_vacuum_cost_limit'
2831
				OR name = 'vacuum_freeze_min_age'
2832
				OR name = 'autovacuum_freeze_max_age'
2833
			"
2834
        );
2835
2836
        $ret = [];
2837
        while (!$_defaults->EOF) {
2838
            $ret[$_defaults->fields['name']] = $_defaults->fields['setting'];
2839
            $_defaults->moveNext();
2840
        }
2841
2842
        return $ret;
2843
    }
2844
2845
    /**
2846
     * Returns all available autovacuum per table information.
2847
     *
2848
     * @param string $table
2849
     * @param bool   $vacenabled
2850
     * @param int    $vacthreshold
2851
     * @param int    $vacscalefactor
2852
     * @param int    $anathresold
2853
     * @param int    $anascalefactor
2854
     * @param int    $vaccostdelay
2855
     * @param int    $vaccostlimit
2856
     *
2857
     * @return bool 0 if successful
2858
     */
2859
    public function saveAutovacuum(
2860
        $table,
2861
        $vacenabled,
2862
        $vacthreshold,
2863
        $vacscalefactor,
2864
        $anathresold,
2865
        $anascalefactor,
2866
        $vaccostdelay,
2867
        $vaccostlimit
2868
    ) {
2869
        $f_schema = $this->_schema;
2870
        $this->fieldClean($f_schema);
2871
        $this->fieldClean($table);
2872
2873
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" SET (";
2874
2875
        if (!empty($vacenabled)) {
2876
            $this->clean($vacenabled);
2877
            $params[] = "autovacuum_enabled='{$vacenabled}'";
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
2878
        }
2879
        if (!empty($vacthreshold)) {
2880
            $this->clean($vacthreshold);
2881
            $params[] = "autovacuum_vacuum_threshold='{$vacthreshold}'";
2882
        }
2883
        if (!empty($vacscalefactor)) {
2884
            $this->clean($vacscalefactor);
2885
            $params[] = "autovacuum_vacuum_scale_factor='{$vacscalefactor}'";
2886
        }
2887
        if (!empty($anathresold)) {
2888
            $this->clean($anathresold);
2889
            $params[] = "autovacuum_analyze_threshold='{$anathresold}'";
2890
        }
2891
        if (!empty($anascalefactor)) {
2892
            $this->clean($anascalefactor);
2893
            $params[] = "autovacuum_analyze_scale_factor='{$anascalefactor}'";
2894
        }
2895
        if (!empty($vaccostdelay)) {
2896
            $this->clean($vaccostdelay);
2897
            $params[] = "autovacuum_vacuum_cost_delay='{$vaccostdelay}'";
2898
        }
2899
        if (!empty($vaccostlimit)) {
2900
            $this->clean($vaccostlimit);
2901
            $params[] = "autovacuum_vacuum_cost_limit='{$vaccostlimit}'";
2902
        }
2903
2904
        $sql = $sql.implode(',', $params).');';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $params does not seem to be defined for all execution paths leading up to this point.
Loading history...
2905
2906
        return $this->execute($sql);
2907
    }
2908
2909
    // Type conversion routines
2910
2911
    /**
2912
     * Drops autovacuum config for a table.
2913
     *
2914
     * @param string $table The table
2915
     *
2916
     * @return bool 0 if successful
2917
     */
2918
    public function dropAutovacuum($table)
2919
    {
2920
        $f_schema = $this->_schema;
2921
        $this->fieldClean($f_schema);
2922
        $this->fieldClean($table);
2923
2924
        return $this->execute(
2925
            "
2926
			ALTER TABLE \"{$f_schema}\".\"{$table}\" RESET (autovacuum_enabled, autovacuum_vacuum_threshold,
2927
				autovacuum_vacuum_scale_factor, autovacuum_analyze_threshold, autovacuum_analyze_scale_factor,
2928
				autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_limit
2929
			);"
2930
        );
2931
    }
2932
2933
    /**
2934
     * Returns all available process information.
2935
     *
2936
     * @param $database (optional) Find only connections to specified database
2937
     *
2938
     * @return \PHPPgAdmin\ADORecordSet A recordset
2939
     */
2940
    public function getProcesses($database = null)
2941
    {
2942
        if ($database === null) {
2943
            $sql = "SELECT datname, usename, pid, waiting, state_change as query_start,
2944
                  case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query
2945
				FROM pg_catalog.pg_stat_activity
2946
				ORDER BY datname, usename, pid";
2947
        } else {
2948
            $this->clean($database);
2949
            $sql = "SELECT datname, usename, pid, waiting, state_change as query_start,
2950
                  case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query
2951
				FROM pg_catalog.pg_stat_activity
2952
				WHERE datname='{$database}'
2953
				ORDER BY usename, pid";
2954
        }
2955
2956
        return $this->selectSet($sql);
2957
    }
2958
2959
    // interfaces Statistics collector functions
2960
2961
    /**
2962
     * Returns table locks information in the current database.
2963
     *
2964
     * @return \PHPPgAdmin\ADORecordSet A recordset
2965
     */
2966
    public function getLocks()
2967
    {
2968
        $conf = $this->conf;
2969
2970
        if (!$conf['show_system']) {
2971
            $where = 'AND pn.nspname NOT LIKE $$pg\_%$$';
2972
        } else {
2973
            $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
2974
        }
2975
2976
        $sql = "
2977
			SELECT
2978
				pn.nspname, pc.relname AS tablename, pl.pid, pl.mode, pl.granted, pl.virtualtransaction,
2979
				(select transactionid from pg_catalog.pg_locks l2 where l2.locktype='transactionid'
2980
					and l2.mode='ExclusiveLock' and l2.virtualtransaction=pl.virtualtransaction) as transaction
2981
			FROM
2982
				pg_catalog.pg_locks pl,
2983
				pg_catalog.pg_class pc,
2984
				pg_catalog.pg_namespace pn
2985
			WHERE
2986
				pl.relation = pc.oid AND pc.relnamespace=pn.oid
2987
			{$where}
2988
			ORDER BY pid,nspname,tablename";
2989
2990
        return $this->selectSet($sql);
2991
    }
2992
2993
    /**
2994
     * Sends a cancel or kill command to a process.
2995
     *
2996
     * @param $pid    The ID of the backend process
2997
     * @param $signal 'CANCEL'
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'CANCEL' at position 0 could not be parsed: Unknown type name ''CANCEL'' at position 0 in 'CANCEL'.
Loading history...
2998
     *
2999
     * @return int 0 success
3000
     */
3001
    public function sendSignal($pid, $signal)
3002
    {
3003
        // Clean
3004
        $pid = (int) $pid;
3005
3006
        if ($signal == 'CANCEL') {
3007
            $sql = "SELECT pg_catalog.pg_cancel_backend({$pid}) AS val";
3008
        } elseif ($signal == 'KILL') {
3009
            $sql = "SELECT pg_catalog.pg_terminate_backend({$pid}) AS val";
3010
        } else {
3011
            return -1;
3012
        }
3013
3014
        // Execute the query
3015
        $val = $this->selectField($sql, 'val');
3016
3017
        if ($val === 'f') {
3018
            return -1;
3019
        }
3020
3021
        if ($val === 't') {
3022
            return 0;
3023
        }
3024
3025
        return -1;
3026
    }
3027
3028
    /**
3029
     * Executes an SQL script as a series of SQL statements.  Returns
3030
     * the result of the final step.  This is a very complicated lexer
3031
     * based on the REL7_4_STABLE src/bin/psql/mainloop.c lexer in
3032
     * the PostgreSQL source code.
3033
     * XXX: It does not handle multibyte languages properly.
3034
     *
3035
     * @param string        $name     Entry in $_FILES to use
3036
     * @param null|function $callback (optional) Callback function to call with each query, its result and line number
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\function was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
3037
     *
3038
     * @return bool true for general success, false on any failure
3039
     */
3040
    public function executeScript($name, $callback = null)
3041
    {
3042
        // This whole function isn't very encapsulated, but hey...
3043
        $conn = $this->conn->_connectionID;
3044
        if (!is_uploaded_file($_FILES[$name]['tmp_name'])) {
3045
            return false;
3046
        }
3047
3048
        $fd = fopen($_FILES[$name]['tmp_name'], 'rb');
3049
        if (!$fd) {
0 ignored issues
show
introduced by
$fd is of type resource|false, thus it always evaluated to false.
Loading history...
3050
            return false;
3051
        }
3052
3053
        // Build up each SQL statement, they can be multiline
3054
        $query_buf    = null;
3055
        $query_start  = 0;
1 ignored issue
show
Unused Code introduced by
The assignment to $query_start is dead and can be removed.
Loading history...
3056
        $in_quote     = 0;
3057
        $in_xcomment  = 0;
3058
        $bslash_count = 0;
3059
        $dol_quote    = null;
3060
        $paren_level  = 0;
3061
        $len          = 0;
1 ignored issue
show
Unused Code introduced by
The assignment to $len is dead and can be removed.
Loading history...
3062
        $i            = 0;
1 ignored issue
show
Unused Code introduced by
The assignment to $i is dead and can be removed.
Loading history...
3063
        $prevlen      = 0;
1 ignored issue
show
Unused Code introduced by
The assignment to $prevlen is dead and can be removed.
Loading history...
3064
        $thislen      = 0;
1 ignored issue
show
Unused Code introduced by
The assignment to $thislen is dead and can be removed.
Loading history...
3065
        $lineno       = 0;
3066
3067
        // Loop over each line in the file
3068
        while (!feof($fd)) {
3069
            $line = fgets($fd);
3070
            ++$lineno;
3071
3072
            // Nothing left on line? Then ignore...
3073
            if (trim($line) == '') {
3074
                continue;
3075
            }
3076
3077
            $len         = strlen($line);
3078
            $query_start = 0;
3079
3080
            /**
3081
             * Parse line, looking for command separators.
3082
             *
3083
             * The current character is at line[i], the prior character at line[i
3084
             * - prevlen], the next character at line[i + thislen].
3085
             */
3086
            $prevlen = 0;
3087
            $thislen = ($len > 0) ? 1 : 0;
3088
3089
            for ($i = 0; $i < $len; $this->advance_1($i, $prevlen, $thislen)) {
3090
                /* was the previous character a backslash? */
3091
                if ($i > 0 && substr($line, $i - $prevlen, 1) == '\\') {
3092
                    ++$bslash_count;
3093
                } else {
3094
                    $bslash_count = 0;
3095
                }
3096
3097
                /*
3098
                 * It is important to place the in_* test routines before the
3099
                 * in_* detection routines. i.e. we have to test if we are in
3100
                 * a quote before testing for comments.
3101
                 */
3102
3103
                /* in quote? */
3104
                if ($in_quote !== 0) {
3105
                    /*
3106
                     * end of quote if matching non-backslashed character.
3107
                     * backslashes don't count for double quotes, though.
3108
                     */
3109
                    if (substr($line, $i, 1) == $in_quote &&
3110
                        ($bslash_count % 2 == 0 || $in_quote == '"')) {
2 ignored issues
show
Coding Style introduced by
Each line in a multi-line IF statement must begin with a boolean operator
Loading history...
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
3111
                        $in_quote = 0;
3112
                    }
3113
                } else {
3114
                    if ($dol_quote) {
3115
                        if (strncmp(substr($line, $i), $dol_quote, strlen($dol_quote)) == 0) {
3116
                            $this->advance_1($i, $prevlen, $thislen);
3117
                            while (substr($line, $i, 1) != '$') {
3118
                                $this->advance_1($i, $prevlen, $thislen);
3119
                            }
3120
3121
                            $dol_quote = null;
3122
                        }
3123
                    } else {
3124
                        if (substr($line, $i, 2) == '/*') {
3125
                            ++$in_xcomment;
3126
                            if ($in_xcomment == 1) {
3127
                                $this->advance_1($i, $prevlen, $thislen);
3128
                            }
3129
                        } else {
3130
                            if ($in_xcomment) {
3131
                                if (substr($line, $i, 2) == '*/' && !--$in_xcomment) {
3132
                                    $this->advance_1($i, $prevlen, $thislen);
3133
                                }
3134
                            } else {
3135
                                if (substr($line, $i, 1) == '\'' || substr($line, $i, 1) == '"') {
3136
                                    $in_quote = substr($line, $i, 1);
3137
                                }/*
3138
                                 * start of $foo$ type quote?
3139
                                 */
3140
                                else {
0 ignored issues
show
Coding Style introduced by
Expected "} else \n"; found "/*\n * start of $foo$ type quote?\n */\n else {\n"
Loading history...
3141
                                    if (!$dol_quote && $this->valid_dolquote(substr($line, $i))) {
3142
                                        $dol_end   = strpos(substr($line, $i + 1), '$');
3143
                                        $dol_quote = substr($line, $i, $dol_end + 1);
3144
                                        $this->advance_1($i, $prevlen, $thislen);
3145
                                        while (substr($line, $i, 1) != '$') {
3146
                                            $this->advance_1($i, $prevlen, $thislen);
3147
                                        }
3148
                                    } else {
3149
                                        if (substr($line, $i, 2) == '--') {
3150
                                            $line = substr($line, 0, $i); /* remove comment */
3151
                                            break;
3152
                                        } /* count nested parentheses */
3153
3154
                                        if (substr($line, $i, 1) == '(') {
3155
                                            ++$paren_level;
3156
                                        } else {
3157
                                            if (substr($line, $i, 1) == ')' && $paren_level > 0) {
3158
                                                --$paren_level;
3159
                                            } else {
3160
                                                if (substr($line, $i, 1) == ';' && !$bslash_count && !$paren_level) {
3161
                                                    $subline = substr(substr($line, 0, $i), $query_start);
3162
                                                    /*
3163
                                                     * insert a cosmetic newline, if this is not the first
3164
                                                     * line in the buffer
3165
                                                     */
3166
                                                    if (strlen($query_buf) > 0) {
3167
                                                        $query_buf .= "\n";
3168
                                                    }
3169
3170
                                                    /* append the line to the query buffer */
3171
                                                    $query_buf .= $subline;
3172
                                                    /* is there anything in the query_buf? */
3173
                                                    if (trim($query_buf)) {
3174
                                                        $query_buf .= ';';
3175
3176
                                                        // Execute the query. PHP cannot execute
3177
                                                        // empty queries, unlike libpq
3178
                                                        $res = @pg_query($conn, $query_buf);
3179
3180
                                                        // Call the callback function for display
3181
                                                        if ($callback !== null) {
3182
                                                            $callback($query_buf, $res, $lineno);
3183
                                                        }
3184
3185
                                                        // Check for COPY request
3186
                                                        if (pg_result_status($res) == 4) {
3187
                                                            // 4 == PGSQL_COPY_FROM
3188
                                                            while (!feof($fd)) {
3189
                                                                $copy = fgets($fd, 32768);
3190
                                                                ++$lineno;
3191
                                                                pg_put_line($conn, $copy);
3192
                                                                if ($copy == "\\.\n" || $copy == "\\.\r\n") {
3193
                                                                    pg_end_copy($conn);
3194
3195
                                                                    break;
3196
                                                                }
3197
                                                            }
3198
                                                        }
3199
                                                    }
3200
                                                    $query_buf   = null;
3201
                                                    $query_start = $i + $thislen;
3202
                                                }
3203
3204
                                                /*
3205
                                                 * keyword or identifier?
3206
                                                 * We grab the whole string so that we don't
3207
                                                 * mistakenly see $foo$ inside an identifier as the start
3208
                                                 * of a dollar quote.
3209
                                                 */
3210
                                                // XXX: multibyte here
3211
                                                else {
0 ignored issues
show
Coding Style introduced by
Expected "} else \n"; found "\n\n /*\n * keyword or identifier?\n * We grab the whole string so that we don't\n * mistakenly see $foo$ inside an identifier as the start\n * of a dollar quote.\n */\n // XXX: multibyte here\n else {\n"
Loading history...
3212
                                                    if (preg_match('/^[_[:alpha:]]$/', substr($line, $i, 1))) {
3213
                                                        $sub = substr($line, $i, $thislen);
3214
                                                        while (preg_match('/^[\$_A-Za-z0-9]$/', $sub)) {
3215
                                                            /* keep going while we still have identifier chars */
3216
                                                            $this->advance_1($i, $prevlen, $thislen);
3217
                                                            $sub = substr($line, $i, $thislen);
3218
                                                        }
3219
                                                        // Since we're now over the next character to be examined, it is necessary
3220
                                                        // to move back one space.
3221
                                                        $i -= $prevlen;
3222
                                                    }
3223
                                                }
3224
                                            }
3225
                                        }
3226
                                    }
3227
                                }
3228
                            }
3229
                        }
3230
                    }
3231
                }
3232
            } // end for
3233
3234
            /* Put the rest of the line in the query buffer. */
3235
            $subline = substr($line, $query_start);
3236
            if ($in_quote || $dol_quote || strspn($subline, " \t\n\r") != strlen($subline)) {
3237
                if (strlen($query_buf) > 0) {
3238
                    $query_buf .= "\n";
3239
                }
3240
3241
                $query_buf .= $subline;
3242
            }
3243
3244
            $line = null;
1 ignored issue
show
Unused Code introduced by
The assignment to $line is dead and can be removed.
Loading history...
3245
        } // end while
3246
3247
        /*
3248
         * Process query at the end of file without a semicolon, so long as
3249
         * it's non-empty.
3250
         */
3251
        if (strlen($query_buf) > 0 && strspn($query_buf, " \t\n\r") != strlen($query_buf)) {
3252
            // Execute the query
3253
            $res = @pg_query($conn, $query_buf);
3254
3255
            // Call the callback function for display
3256
            if ($callback !== null) {
3257
                $callback($query_buf, $res, $lineno);
3258
            }
3259
3260
            // Check for COPY request
3261
            if (pg_result_status($res) == 4) {
3262
                // 4 == PGSQL_COPY_FROM
3263
                while (!feof($fd)) {
3264
                    $copy = fgets($fd, 32768);
3265
                    ++$lineno;
3266
                    pg_put_line($conn, $copy);
3267
                    if ($copy == "\\.\n" || $copy == "\\.\r\n") {
3268
                        pg_end_copy($conn);
3269
3270
                        break;
3271
                    }
3272
                }
3273
            }
3274
        }
3275
3276
        fclose($fd);
3277
3278
        return true;
3279
    }
3280
3281
    /**
3282
     * A private helper method for executeScript that advances the
3283
     * character by 1.  In psql this is careful to take into account
3284
     * multibyte languages, but we don't at the moment, so this function
3285
     * is someone redundant, since it will always advance by 1.
3286
     *
3287
     * @param int &$i       The current character position in the line
0 ignored issues
show
Coding Style introduced by
Doc comment for parameter &$i does not match actual variable name $i
Loading history...
3288
     * @param int &$prevlen Length of previous character (ie. 1)
0 ignored issues
show
Coding Style introduced by
Doc comment for parameter &$prevlen does not match actual variable name $prevlen
Loading history...
3289
     * @param int &$thislen Length of current character (ie. 1)
0 ignored issues
show
Coding Style introduced by
Doc comment for parameter &$thislen does not match actual variable name $thislen
Loading history...
3290
     */
3291
    private function advance_1(&$i, &$prevlen, &$thislen)
1 ignored issue
show
Coding Style introduced by
Private method name "Postgres::advance_1" must be prefixed with an underscore
Loading history...
3292
    {
3293
        $prevlen = $thislen;
3294
        $i += $thislen;
3295
        $thislen = 1;
3296
    }
3297
3298
    /**
3299
     * Private helper method to detect a valid $foo$ quote delimiter at
3300
     * the start of the parameter dquote.
3301
     *
3302
     * @param string $dquote
3303
     *
3304
     * @return true if valid, false otherwise
3305
     */
3306
    private function valid_dolquote($dquote)
1 ignored issue
show
Coding Style introduced by
Private method name "Postgres::valid_dolquote" must be prefixed with an underscore
Loading history...
3307
    {
3308
        // XXX: support multibyte
3309
        return preg_match('/^[$][$]/', $dquote) || preg_match('/^[$][_[:alpha:]][_[:alnum:]]*[$]/', $dquote);
0 ignored issues
show
Bug Best Practice introduced by
The expression return preg_match('/^[$]...lnum:]]*[$]/', $dquote) returns the type boolean which is incompatible with the documented return type true.
Loading history...
3310
    }
3311
3312
    // Capabilities
3313
3314
    /**
3315
     * Returns a recordset of all columns in a query.  Supports paging.
3316
     *
3317
     * @param string $type       Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier,
3318
     *                           or 'SELECT" if it's a select query
3319
     * @param string $table      The base table of the query.  NULL for no table.
3320
     * @param string $query      The query that is being executed.  NULL for no query.
3321
     * @param string $sortkey    The column number to sort by, or '' or null for no sorting
3322
     * @param string $sortdir    The direction in which to sort the specified column ('asc' or 'desc')
3323
     * @param int    $page       The page of the relation to retrieve
3324
     * @param int    $page_size  The number of rows per page
3325
     * @param int    &$max_pages (return-by-ref) The max number of pages in the relation
0 ignored issues
show
Coding Style introduced by
Doc comment for parameter &$max_pages does not match actual variable name $max_pages
Loading history...
3326
     *
3327
     * @return A  recordset on success
3328
     * @return -1 transaction error
0 ignored issues
show
Coding Style introduced by
Only 1 @return tag is allowed in a function comment
Loading history...
3329
     * @return -2 counting error
3330
     * @return -3 page or page_size invalid
3331
     * @return -4 unknown type
3332
     * @return -5 failed setting transaction read only
3333
     */
3334
    public function browseQuery($type, $table, $query, $sortkey, $sortdir, $page, $page_size, &$max_pages)
3335
    {
3336
        // Check that we're not going to divide by zero
3337
        if (!is_numeric($page_size) || $page_size != (int) $page_size || $page_size <= 0) {
0 ignored issues
show
introduced by
The condition is_numeric($page_size) is always true.
Loading history...
3338
            return -3;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -3 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
3339
        }
3340
3341
        // If $type is TABLE, then generate the query
3342
        switch ($type) {
3343
            case 'TABLE':
3344
                if (preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) {
3345
                    $orderby = [$sortkey => $sortdir];
3346
                } else {
3347
                    $orderby = [];
3348
                }
3349
3350
                $query = $this->getSelectSQL($table, [], [], [], $orderby);
3351
3352
                break;
3353
            case 'QUERY':
3354
            case 'SELECT':
3355
                // Trim query
3356
                $query = trim($query);
3357
                // Trim off trailing semi-colon if there is one
3358
                if (substr($query, strlen($query) - 1, 1) == ';') {
3359
                    $query = substr($query, 0, strlen($query) - 1);
3360
                }
3361
3362
                break;
3363
            default:
3364
                return -4;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -4 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
3365
        }
3366
3367
        // Generate count query
3368
        $count = "SELECT COUNT(*) AS total FROM (${query}) AS sub";
3369
3370
        // Open a transaction
3371
        $status = $this->beginTransaction();
3372
        if ($status != 0) {
3373
            return -1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -1 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
3374
        }
3375
3376
        // If backend supports read only queries, then specify read only mode
3377
        // to avoid side effects from repeating queries that do writes.
3378
        if ($this->hasReadOnlyQueries()) {
3379
            $status = $this->execute('SET TRANSACTION READ ONLY');
3380
            if ($status != 0) {
3381
                $this->rollbackTransaction();
3382
3383
                return -5;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -5 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
3384
            }
3385
        }
3386
3387
        // Count the number of rows
3388
        $total = $this->browseQueryCount($query, $count);
3389
        if ($total < 0) {
3390
            $this->rollbackTransaction();
3391
3392
            return -2;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -2 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
3393
        }
3394
3395
        // Calculate max pages
3396
        $max_pages = ceil($total / $page_size);
3397
3398
        // Check that page is less than or equal to max pages
3399
        if (!is_numeric($page) || $page != (int) $page || $page > $max_pages || $page < 1) {
0 ignored issues
show
introduced by
The condition is_numeric($page) is always true.
Loading history...
3400
            $this->rollbackTransaction();
3401
3402
            return -3;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -3 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
3403
        }
3404
3405
        // Set fetch mode to NUM so that duplicate field names are properly returned
3406
        // for non-table queries.  Since the SELECT feature only allows selecting one
3407
        // table, duplicate fields shouldn't appear.
3408
        if ($type == 'QUERY') {
3409
            $this->conn->setFetchMode(ADODB_FETCH_NUM);
3410
        }
3411
3412
        // Figure out ORDER BY.  Sort key is always the column number (based from one)
3413
        // of the column to order by.  Only need to do this for non-TABLE queries
3414
        if ($type != 'TABLE' && preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) {
3415
            $orderby = " ORDER BY {$sortkey}";
3416
            // Add sort order
3417
            if ($sortdir == 'desc') {
3418
                $orderby .= ' DESC';
3419
            } else {
3420
                $orderby .= ' ASC';
3421
            }
3422
        } else {
3423
            $orderby = '';
3424
        }
3425
3426
        // Actually retrieve the rows, with offset and limit
3427
        $rs     = $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET ".($page - 1) * $page_size);
3428
        $status = $this->endTransaction();
3429
        if ($status != 0) {
3430
            $this->rollbackTransaction();
3431
3432
            return -1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -1 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
3433
        }
3434
3435
        return $rs;
3436
    }
3437
3438
    /**
3439
     * Generates the SQL for the 'select' function.
3440
     *
3441
     * @param $table   The table from which to select
3442
     * @param $show    An array of columns to show.  Empty array means all columns.
3443
     * @param $values  An array mapping columns to values
3444
     * @param $ops     An array of the operators to use
3445
     * @param $orderby (optional) An array of column numbers or names (one based)
3446
     *                 mapped to sort direction (asc or desc or '' or null) to order by
3447
     *
3448
     * @return The SQL query
3449
     */
3450
    public function getSelectSQL($table, $show, $values, $ops, $orderby = [])
3451
    {
3452
        $this->fieldArrayClean($show);
3453
3454
        // If an empty array is passed in, then show all columns
3455
        if (sizeof($show) == 0) {
3456
            if ($this->hasObjectID($table)) {
3457
                $sql = "SELECT \"{$this->id}\", * FROM ";
3458
            } else {
3459
                $sql = 'SELECT * FROM ';
3460
            }
3461
        } else {
3462
            // Add oid column automatically to results for editing purposes
3463
            if (!in_array($this->id, $show, true) && $this->hasObjectID($table)) {
3464
                $sql = "SELECT \"{$this->id}\", \"";
3465
            } else {
3466
                $sql = 'SELECT "';
3467
            }
3468
3469
            $sql .= join('","', $show).'" FROM ';
3470
        }
3471
3472
        $this->fieldClean($table);
3473
3474
        if (isset($_REQUEST['schema'])) {
3475
            $f_schema = $_REQUEST['schema'];
3476
            $this->fieldClean($f_schema);
3477
            $sql .= "\"{$f_schema}\".";
3478
        }
3479
        $sql .= "\"{$table}\"";
3480
3481
        // If we have values specified, add them to the WHERE clause
3482
        $first = true;
3483
        if (is_array($values) && sizeof($values) > 0) {
3484
            foreach ($values as $k => $v) {
3485
                if ($v != '' || $this->selectOps[$ops[$k]] == 'p') {
3486
                    $this->fieldClean($k);
3487
                    if ($first) {
3488
                        $sql .= ' WHERE ';
3489
                        $first = false;
3490
                    } else {
3491
                        $sql .= ' AND ';
3492
                    }
3493
                    // Different query format depending on operator type
3494
                    switch ($this->selectOps[$ops[$k]]) {
3495
                        case 'i':
3496
                            // Only clean the field for the inline case
3497
                            // this is because (x), subqueries need to
3498
                            // to allow 'a','b' as input.
3499
                            $this->clean($v);
3500
                            $sql .= "\"{$k}\" {$ops[$k]} '{$v}'";
3501
3502
                            break;
3503
                        case 'p':
3504
                            $sql .= "\"{$k}\" {$ops[$k]}";
3505
3506
                            break;
3507
                        case 'x':
3508
                            $sql .= "\"{$k}\" {$ops[$k]} ({$v})";
3509
3510
                            break;
3511
                        case 't':
3512
                            $sql .= "\"{$k}\" {$ops[$k]}('{$v}')";
3513
3514
                            break;
3515
                        default:
3516
                            // Shouldn't happen
3517
                    }
3518
                }
3519
            }
3520
        }
3521
3522
        // ORDER BY
3523
        if (is_array($orderby) && sizeof($orderby) > 0) {
3524
            $sql .= ' ORDER BY ';
3525
            $first = true;
3526
            foreach ($orderby as $k => $v) {
3527
                if ($first) {
3528
                    $first = false;
3529
                } else {
3530
                    $sql .= ', ';
3531
                }
3532
3533
                if (preg_match('/^[0-9]+$/', $k)) {
3534
                    $sql .= $k;
3535
                } else {
3536
                    $this->fieldClean($k);
3537
                    $sql .= '"'.$k.'"';
3538
                }
3539
                if (strtoupper($v) == 'DESC') {
3540
                    $sql .= ' DESC';
3541
                }
3542
            }
3543
        }
3544
3545
        return $sql;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $sql returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\The.
Loading history...
3546
    }
3547
3548
    /**
3549
     * Finds the number of rows that would be returned by a
3550
     * query.
3551
     *
3552
     * @param $query The SQL query
3553
     * @param $count The count query
3554
     *
3555
     * @return int The count of rows or -1 of no rows are found
3556
     */
3557
    public function browseQueryCount($query, $count)
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

3557
    public function browseQueryCount(/** @scrutinizer ignore-unused */ $query, $count)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
3558
    {
3559
        return $this->selectField($count, 'total');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->selectField($count, 'total') also could return the type string which is incompatible with the documented return type integer.
Loading history...
3560
    }
3561
3562
    /**
3563
     * Returns a recordset of all columns in a table.
3564
     *
3565
     * @param $table The name of a table
3566
     * @param $key   The associative array holding the key to retrieve
3567
     *
3568
     * @return \PHPPgAdmin\ADORecordSet A recordset
3569
     */
3570
    public function browseRow($table, $key)
3571
    {
3572
        $f_schema = $this->_schema;
3573
        $this->fieldClean($f_schema);
3574
        $this->fieldClean($table);
3575
3576
        $sql = "SELECT * FROM \"{$f_schema}\".\"{$table}\"";
3577
        if (is_array($key) && sizeof($key) > 0) {
3578
            $sql .= ' WHERE true';
3579
            foreach ($key as $k => $v) {
3580
                $this->fieldClean($k);
3581
                $this->clean($v);
3582
                $sql .= " AND \"{$k}\"='{$v}'";
3583
            }
3584
        }
3585
3586
        return $this->selectSet($sql);
3587
    }
3588
3589
    /**
3590
     * Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false.
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
3591
     *
3592
     * @param $parameter the parameter
3593
     *
3594
     * @return string
3595
     */
3596
    public function dbBool(&$parameter)
3597
    {
3598
        if ($parameter) {
3599
            $parameter = 't';
3600
        } else {
3601
            $parameter = 'f';
3602
        }
3603
3604
        return $parameter;
3605
    }
3606
3607
    /**
3608
     * Fetches statistics for a database.
3609
     *
3610
     * @param $database The database to fetch stats for
3611
     *
3612
     * @return \PHPPgAdmin\ADORecordSet A recordset
3613
     */
3614
    public function getStatsDatabase($database)
3615
    {
3616
        $this->clean($database);
3617
3618
        $sql = "SELECT * FROM pg_stat_database WHERE datname='{$database}'";
3619
3620
        return $this->selectSet($sql);
3621
    }
3622
3623
    /**
3624
     * Fetches tuple statistics for a table.
3625
     *
3626
     * @param $table The table to fetch stats for
3627
     *
3628
     * @return \PHPPgAdmin\ADORecordSet A recordset
3629
     */
3630
    public function getStatsTableTuples($table)
3631
    {
3632
        $c_schema = $this->_schema;
3633
        $this->clean($c_schema);
3634
        $this->clean($table);
3635
3636
        $sql = "SELECT * FROM pg_stat_all_tables
3637
			WHERE schemaname='{$c_schema}' AND relname='{$table}'";
3638
3639
        return $this->selectSet($sql);
3640
    }
3641
3642
    /**
3643
     * Fetches I/0 statistics for a table.
3644
     *
3645
     * @param $table The table to fetch stats for
3646
     *
3647
     * @return \PHPPgAdmin\ADORecordSet A recordset
3648
     */
3649
    public function getStatsTableIO($table)
3650
    {
3651
        $c_schema = $this->_schema;
3652
        $this->clean($c_schema);
3653
        $this->clean($table);
3654
3655
        $sql = "SELECT * FROM pg_statio_all_tables
3656
			WHERE schemaname='{$c_schema}' AND relname='{$table}'";
3657
3658
        return $this->selectSet($sql);
3659
    }
3660
3661
    /**
3662
     * Fetches tuple statistics for all indexes on a table.
3663
     *
3664
     * @param $table The table to fetch index stats for
3665
     *
3666
     * @return \PHPPgAdmin\ADORecordSet A recordset
3667
     */
3668
    public function getStatsIndexTuples($table)
3669
    {
3670
        $c_schema = $this->_schema;
3671
        $this->clean($c_schema);
3672
        $this->clean($table);
3673
3674
        $sql = "SELECT * FROM pg_stat_all_indexes
3675
			WHERE schemaname='{$c_schema}' AND relname='{$table}' ORDER BY indexrelname";
3676
3677
        return $this->selectSet($sql);
3678
    }
3679
3680
    /**
3681
     * Fetches I/0 statistics for all indexes on a table.
3682
     *
3683
     * @param $table The table to fetch index stats for
3684
     *
3685
     * @return \PHPPgAdmin\ADORecordSet A recordset
3686
     */
3687
    public function getStatsIndexIO($table)
3688
    {
3689
        $c_schema = $this->_schema;
3690
        $this->clean($c_schema);
3691
        $this->clean($table);
3692
3693
        $sql = "SELECT * FROM pg_statio_all_indexes
3694
			WHERE schemaname='{$c_schema}' AND relname='{$table}'
3695
			ORDER BY indexrelname";
3696
3697
        return $this->selectSet($sql);
3698
    }
3699
}
3700