Passed
Push — develop ( 7af434...de9bfb )
by Felipe
05:43
created

DatabaseTrait::isSuperUser()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 5
nop 1
1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.47
5
 */
6
7
namespace PHPPgAdmin\DatabaseTraits;
8
9
/**
10
 * Common trait for tables manipulation.
11
 */
12
trait DatabaseTrait
13
{
14
    /**
15
     * Determines whether or not a user is a super user.
16
     *
17
     * @param string $username The username of the user
18
     *
19
     * @return bool true if is a super user, false otherwise
20
     */
21
    public function isSuperUser($username = '')
22
    {
23
        $this->clean($username);
24
25
        if (empty($username)) {
26
            $val = pg_parameter_status($this->conn->_connectionID, 'is_superuser');
27
            if ($val !== false) {
28
                return $val == 'on';
29
            }
30
        }
31
32
        $sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'";
33
34
        $usesuper = $this->selectField($sql, 'usesuper');
0 ignored issues
show
Bug introduced by
The method selectField() does not exist on PHPPgAdmin\DatabaseTraits\DatabaseTrait. Did you maybe mean selectSet()? ( Ignorable by Annotation )

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

34
        /** @scrutinizer ignore-call */ 
35
        $usesuper = $this->selectField($sql, 'usesuper');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
35
        if ($usesuper == -1) {
36
            return false;
37
        }
38
39
        return $usesuper == 't';
40
    }
41
42
    /**
43
     * Analyze a database.
44
     *
45
     * @param string $table (optional) The table to analyze
46
     *
47
     * @return bool 0 if successful
48
     */
49
    public function analyzeDB($table = '')
50
    {
51
        if ($table != '') {
52
            $f_schema = $this->_schema;
53
            $this->fieldClean($f_schema);
54
            $this->fieldClean($table);
55
56
            $sql = "ANALYZE \"{$f_schema}\".\"{$table}\"";
57
        } else {
58
            $sql = 'ANALYZE';
59
        }
60
61
        return $this->execute($sql);
62
    }
63
64
    /**
65
     * Return all information about a particular database.
66
     *
67
     * @param string $database The name of the database to retrieve
68
     *
69
     * @return \PHPPgAdmin\ADORecordSet The database info
70
     */
71
    public function getDatabase($database)
72
    {
73
        $this->clean($database);
74
        $sql = "SELECT * FROM pg_database WHERE datname='{$database}'";
75
76
        return $this->selectSet($sql);
77
    }
78
79
    /**
80
     * Return all database available on the server.
81
     *
82
     * @param null|string $currentdatabase database name that should be on top of the resultset
83
     *
84
     * @return \PHPPgAdmin\ADORecordSet A list of databases, sorted alphabetically
85
     */
86
    public function getDatabases($currentdatabase = null)
87
    {
88
        $conf        = $this->conf;
89
        $server_info = $this->server_info;
90
91
        //$this->prtrace('server_info', $server_info);
92
93
        if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser()) {
94
            $username = $server_info['username'];
95
            $this->clean($username);
96
            $clause = " AND pr.rolname='{$username}'";
97
        } else {
98
            $clause = '';
99
        }
100
        if (isset($server_info['useonlydefaultdb']) && $server_info['useonlydefaultdb']) {
101
            $currentdatabase = $server_info['defaultdb'];
102
            $clause .= " AND pdb.datname = '{$currentdatabase}' ";
103
        }
104
105
        if (isset($server_info['hiddendbs']) && $server_info['hiddendbs']) {
106
            $hiddendbs = $server_info['hiddendbs'];
107
108
            $not_in = "('".implode("','", $hiddendbs)."')";
109
            $clause .= " AND pdb.datname NOT IN {$not_in} ";
110
        }
111
112
        if ($currentdatabase != null) {
113
            $this->clean($currentdatabase);
114
            $orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
115
        } else {
116
            $orderby = 'ORDER BY pdb.datname';
117
        }
118
119
        if (!$conf['show_system']) {
120
            $where = ' AND NOT pdb.datistemplate';
121
        } else {
122
            $where = ' AND pdb.datallowconn';
123
        }
124
125
        $sql = "
126
            SELECT pdb.datname AS datname,
127
                    pr.rolname AS datowner,
128
                    pg_encoding_to_char(encoding) AS datencoding,
129
                    (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pdb.oid=pd.objoid AND pd.classoid='pg_database'::regclass) AS datcomment,
130
                    (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=pdb.dattablespace) AS tablespace,
131
                CASE WHEN pg_catalog.has_database_privilege(current_user, pdb.oid, 'CONNECT')
132
                    THEN pg_catalog.pg_database_size(pdb.oid)
133
                    ELSE -1 -- set this magic value, which we will convert to no access later
134
                END as dbsize,
135
                pdb.datcollate,
136
                pdb.datctype
137
            FROM pg_catalog.pg_database pdb
138
            LEFT JOIN pg_catalog.pg_roles pr ON (pdb.datdba = pr.oid)
139
            WHERE true
140
                {$where}
141
                {$clause}
142
            {$orderby}";
143
144
        return $this->selectSet($sql);
145
    }
146
147
    /**
148
     * Return the database comment of a db from the shared description table.
149
     *
150
     * @param string $database the name of the database to get the comment for
151
     *
152
     * @return \PHPPgAdmin\ADORecordSet recordset of the db comment info
153
     */
154
    public function getDatabaseComment($database)
155
    {
156
        $this->clean($database);
157
        $sql = "SELECT description
158
                FROM pg_catalog.pg_database
159
                JOIN pg_catalog.pg_shdescription
160
                ON (oid=objoid AND classoid='pg_database'::regclass)
161
                WHERE pg_database.datname = '{$database}' ";
162
163
        return $this->selectSet($sql);
164
    }
165
166
    /**
167
     * Return the database owner of a db.
168
     *
169
     * @param string $database the name of the database to get the owner for
170
     *
171
     * @return \PHPPgAdmin\ADORecordSet recordset of the db owner info
172
     */
173
    public function getDatabaseOwner($database)
174
    {
175
        $this->clean($database);
176
        $sql = "SELECT usename FROM pg_user, pg_database WHERE pg_user.usesysid = pg_database.datdba AND pg_database.datname = '{$database}' ";
177
178
        return $this->selectSet($sql);
179
    }
180
181
    // Help functions
182
183
    // Database functions
184
185
    /**
186
     * Returns the current database encoding.
187
     *
188
     * @return string The encoding.  eg. SQL_ASCII, UTF-8, etc.
189
     */
190
    public function getDatabaseEncoding()
191
    {
192
        return pg_parameter_status($this->conn->_connectionID, 'server_encoding');
193
    }
194
195
    /**
196
     * Creates a database.
197
     *
198
     * @param string $database   The name of the database to create
199
     * @param string $encoding   Encoding of the database
200
     * @param string $tablespace (optional) The tablespace name
201
     * @param string $comment
202
     * @param string $template
203
     * @param string $lc_collate
204
     * @param string $lc_ctype
205
     *
206
     * @return int 0 success
207
     */
208
    public function createDatabase(
209
        $database,
210
        $encoding,
211
        $tablespace = '',
212
        $comment = '',
213
        $template = 'template1',
214
        $lc_collate = '',
215
        $lc_ctype = ''
216
    ) {
217
        $this->fieldClean($database);
218
        $this->clean($encoding);
219
        $this->fieldClean($tablespace);
220
        $this->fieldClean($template);
221
        $this->clean($lc_collate);
222
        $this->clean($lc_ctype);
223
224
        $sql = "CREATE DATABASE \"{$database}\" WITH TEMPLATE=\"{$template}\"";
225
226
        if ($encoding != '') {
227
            $sql .= " ENCODING='{$encoding}'";
228
        }
229
230
        if ($lc_collate != '') {
231
            $sql .= " LC_COLLATE='{$lc_collate}'";
232
        }
233
234
        if ($lc_ctype != '') {
235
            $sql .= " LC_CTYPE='{$lc_ctype}'";
236
        }
237
238
        if ($tablespace != '' && $this->hasTablespaces()) {
239
            $sql .= " TABLESPACE \"{$tablespace}\"";
240
        }
241
242
        $status = $this->execute($sql);
243
        if ($status != 0) {
244
            return -1;
245
        }
246
247
        if ($comment != '' && $this->hasSharedComments()) {
248
            $status = $this->setComment('DATABASE', $database, '', $comment);
249
            if ($status != 0) {
250
                return -2;
251
            }
252
        }
253
254
        return 0;
255
    }
256
257
    /**
258
     * Drops a database.
259
     *
260
     * @param string $database The name of the database to drop
261
     *
262
     * @return int 0 if operation was successful
263
     */
264
    public function dropDatabase($database)
265
    {
266
        $this->fieldClean($database);
267
        $sql = "DROP DATABASE \"{$database}\"";
268
269
        return $this->execute($sql);
270
    }
271
272
    /**
273
     * Alters a database
274
     * the multiple return vals are for postgres 8+ which support more functionality in alter database.
275
     *
276
     * @param string $dbName   The name of the database
277
     * @param string $newName  new name for the database
278
     * @param string $newOwner The new owner for the database
279
     * @param string $comment
280
     *
281
     * @return bool|int 0 success
282
     */
283
    public function alterDatabase($dbName, $newName, $newOwner = '', $comment = '')
284
    {
285
        $status = $this->beginTransaction();
286
        if ($status != 0) {
287
            $this->rollbackTransaction();
288
289
            return -1;
290
        }
291
292
        if ($dbName != $newName) {
293
            $status = $this->alterDatabaseRename($dbName, $newName);
294
            if ($status != 0) {
295
                $this->rollbackTransaction();
296
297
                return -3;
298
            }
299
            $dbName = $newName;
300
        }
301
302
        if ($newOwner != '') {
303
            $status = $this->alterDatabaseOwner($newName, $newOwner);
304
            if ($status != 0) {
305
                $this->rollbackTransaction();
306
307
                return -2;
308
            }
309
        }
310
311
        $this->fieldClean($dbName);
312
        $status = $this->setComment('DATABASE', $dbName, '', $comment);
313
        if ($status != 0) {
314
            $this->rollbackTransaction();
315
316
            return -4;
317
        }
318
319
        return $this->endTransaction();
320
    }
321
322
    /**
323
     * Renames a database, note that this operation cannot be
324
     * performed on a database that is currently being connected to.
325
     *
326
     * @param string $oldName name of database to rename
327
     * @param string $newName new name of database
328
     *
329
     * @return int 0 on success
330
     */
331
    public function alterDatabaseRename($oldName, $newName)
332
    {
333
        $this->fieldClean($oldName);
334
        $this->fieldClean($newName);
335
336
        if ($oldName != $newName) {
337
            $sql = "ALTER DATABASE \"{$oldName}\" RENAME TO \"{$newName}\"";
338
339
            return $this->execute($sql);
340
        }
341
342
        return 0;
343
    }
344
345
    /**
346
     * Changes ownership of a database
347
     * This can only be done by a superuser or the owner of the database.
348
     *
349
     * @param string $dbName   database to change ownership of
350
     * @param string $newOwner user that will own the database
351
     *
352
     * @return int 0 on success
353
     */
354
    public function alterDatabaseOwner($dbName, $newOwner)
355
    {
356
        $this->fieldClean($dbName);
357
        $this->fieldClean($newOwner);
358
359
        $sql = "ALTER DATABASE \"{$dbName}\" OWNER TO \"{$newOwner}\"";
360
361
        return $this->execute($sql);
362
    }
363
364
    /**
365
     * Returns prepared transactions information.
366
     *
367
     * @param null|string $database (optional) Find only prepared transactions executed in a specific database
368
     *
369
     * @return \PHPPgAdmin\ADORecordSet A recordset
370
     */
371
    public function getPreparedXacts($database = null)
372
    {
373
        if ($database === null) {
374
            $sql = 'SELECT * FROM pg_prepared_xacts';
375
        } else {
376
            $this->clean($database);
377
            $sql = "SELECT transaction, gid, prepared, owner FROM pg_prepared_xacts
378
                WHERE database='{$database}' ORDER BY owner";
379
        }
380
381
        return $this->selectSet($sql);
382
    }
383
384
    /**
385
     * Returns all available process information.
386
     *
387
     * @param null|string $database (optional) Find only connections to specified database
388
     *
389
     * @return \PHPPgAdmin\ADORecordSet A recordset
390
     */
391
    public function getProcesses($database = null)
392
    {
393
        if ($database === null) {
394
            $sql = "SELECT datname, usename, pid, waiting, state_change as query_start,
395
                  case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query
396
                FROM pg_catalog.pg_stat_activity
397
                ORDER BY datname, usename, pid";
398
        } else {
399
            $this->clean($database);
400
            $sql = "SELECT datname, usename, pid, waiting, state_change as query_start,
401
                  case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query
402
                FROM pg_catalog.pg_stat_activity
403
                WHERE datname='{$database}'
404
                ORDER BY usename, pid";
405
        }
406
407
        return $this->selectSet($sql);
408
    }
409
410
    // interfaces Statistics collector functions
411
412
    /**
413
     * Returns table locks information in the current database.
414
     *
415
     * @return \PHPPgAdmin\ADORecordSet A recordset
416
     */
417
    public function getLocks()
418
    {
419
        $conf = $this->conf;
420
421
        if (!$conf['show_system']) {
422
            $where = 'AND pn.nspname NOT LIKE $$pg\_%$$';
423
        } else {
424
            $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
425
        }
426
427
        $sql = "
428
            SELECT
429
                pn.nspname, pc.relname AS tablename, pl.pid, pl.mode, pl.granted, pl.virtualtransaction,
430
                (select transactionid from pg_catalog.pg_locks l2 where l2.locktype='transactionid'
431
                    and l2.mode='ExclusiveLock' and l2.virtualtransaction=pl.virtualtransaction) as transaction
432
            FROM
433
                pg_catalog.pg_locks pl,
434
                pg_catalog.pg_class pc,
435
                pg_catalog.pg_namespace pn
436
            WHERE
437
                pl.relation = pc.oid AND pc.relnamespace=pn.oid
438
            {$where}
439
            ORDER BY pid,nspname,tablename";
440
441
        return $this->selectSet($sql);
442
    }
443
444
    /**
445
     * Sends a cancel or kill command to a process.
446
     *
447
     * @param int    $pid    The ID of the backend process
448
     * @param string $signal 'CANCEL' or 'KILL'
449
     *
450
     * @return int 0 success
451
     */
452
    public function sendSignal($pid, $signal)
453
    {
454
        // Clean
455
        $pid = (int) $pid;
456
457
        if ($signal == 'CANCEL') {
458
            $sql = "SELECT pg_catalog.pg_cancel_backend({$pid}) AS val";
459
        } elseif ($signal == 'KILL') {
460
            $sql = "SELECT pg_catalog.pg_terminate_backend({$pid}) AS val";
461
        } else {
462
            return -1;
463
        }
464
465
        // Execute the query
466
        $val = $this->selectField($sql, 'val');
467
468
        if ($val === 'f') {
469
            return -1;
470
        }
471
472
        if ($val === 't') {
473
            return 0;
474
        }
475
476
        return -1;
477
    }
478
479
    /**
480
     * Vacuums a database.
481
     *
482
     * @param string $table   The table to vacuum
483
     * @param bool   $analyze If true, also does analyze
484
     * @param bool   $full    If true, selects "full" vacuum
485
     * @param bool   $freeze  If true, selects aggressive "freezing" of tuples
486
     *
487
     * @return bool 0 if successful
488
     */
489
    public function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false)
490
    {
491
        $sql = 'VACUUM';
492
        if ($full) {
493
            $sql .= ' FULL';
494
        }
495
496
        if ($freeze) {
497
            $sql .= ' FREEZE';
498
        }
499
500
        if ($analyze) {
501
            $sql .= ' ANALYZE';
502
        }
503
504
        if ($table != '') {
505
            $f_schema = $this->_schema;
506
            $this->fieldClean($f_schema);
507
            $this->fieldClean($table);
508
            $sql .= " \"{$f_schema}\".\"{$table}\"";
509
        }
510
511
        return $this->execute($sql);
512
    }
513
514
    /**
515
     * Returns all autovacuum global configuration.
516
     *
517
     * @return array associative array array( param => value, ...)
518
     */
519
    public function getAutovacuum()
520
    {
521
        $_defaults = $this->selectSet(
522
            "SELECT name, setting
523
            FROM pg_catalog.pg_settings
524
            WHERE
525
                name = 'autovacuum'
526
                OR name = 'autovacuum_vacuum_threshold'
527
                OR name = 'autovacuum_vacuum_scale_factor'
528
                OR name = 'autovacuum_analyze_threshold'
529
                OR name = 'autovacuum_analyze_scale_factor'
530
                OR name = 'autovacuum_vacuum_cost_delay'
531
                OR name = 'autovacuum_vacuum_cost_limit'
532
                OR name = 'vacuum_freeze_min_age'
533
                OR name = 'autovacuum_freeze_max_age'
534
            "
535
        );
536
537
        $ret = [];
538
        while (!$_defaults->EOF) {
539
            $ret[$_defaults->fields['name']] = $_defaults->fields['setting'];
540
            $_defaults->moveNext();
541
        }
542
543
        return $ret;
544
    }
545
546
    /**
547
     * Returns all available variable information.
548
     *
549
     * @return \PHPPgAdmin\ADORecordSet A recordset
550
     */
551
    public function getVariables()
552
    {
553
        $sql = 'SHOW ALL';
554
555
        return $this->selectSet($sql);
556
    }
557
558
    abstract public function fieldClean(&$str);
559
560
    abstract public function beginTransaction();
561
562
    abstract public function rollbackTransaction();
563
564
    abstract public function endTransaction();
565
566
    abstract public function execute($sql);
567
568
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
569
570
    abstract public function selectSet($sql);
571
572
    abstract public function clean(&$str);
573
574
    abstract public function phpBool($parameter);
575
576
    abstract public function hasCreateTableLikeWithConstraints();
577
578
    abstract public function hasCreateTableLikeWithIndexes();
579
580
    abstract public function hasTablespaces();
581
582
    abstract public function delete($table, $conditions, $schema = '');
583
584
    abstract public function fieldArrayClean(&$arr);
585
586
    abstract public function hasCreateFieldWithConstraints();
587
588
    abstract public function getAttributeNames($table, $atts);
589
590
    abstract public function hasSharedComments();
591
}
592