Passed
Push — develop ( c2019a...2cab77 )
by Felipe
04:50
created

DatabaseTrait::getProcesses()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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