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

Postgres::getOperator()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
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