Passed
Push — develop ( 2d5edd...a33225 )
by Felipe
05:40
created

Postgres::browseQuery()   D

Complexity

Conditions 24
Paths 182

Size

Total Lines 102
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 102
rs 4.345
c 0
b 0
f 0
cc 24
eloc 51
nc 182
nop 8

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.47
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\Traits\HelperTrait;
20
    use \PHPPgAdmin\Traits\SequenceTrait;
21
    use \PHPPgAdmin\Traits\ViewTrait;
22
    use \PHPPgAdmin\Traits\IndexTrait;
23
    use \PHPPgAdmin\Traits\RoleTrait;
24
    use \PHPPgAdmin\Traits\AggregateTrait;
25
    use \PHPPgAdmin\Traits\TableTrait;
26
    use \PHPPgAdmin\Traits\DomainTrait;
27
    use \PHPPgAdmin\Traits\FtsTrait;
28
    use \PHPPgAdmin\Traits\FunctionTrait;
29
    use \PHPPgAdmin\Traits\DatabaseTrait;
30
    use \PHPPgAdmin\Traits\SchemaTrait;
31
    use \PHPPgAdmin\Traits\TypeTrait;
32
    use \PHPPgAdmin\Traits\TablespaceTrait;
33
    use \PHPPgAdmin\Traits\OperatorTrait;
34
35
    public $lang;
36
    public $conf;
37
    protected $container;
38
    protected $server_info;
39
40
    public function __construct(&$conn, $container, $server_info)
41
    {
42
        //$this->prtrace('major_version :' . $this->major_version);
43
        $this->conn      = $conn;
44
        $this->container = $container;
45
46
        $this->lang        = $container->get('lang');
47
        $this->conf        = $container->get('conf');
48
        $this->server_info = $server_info;
49
    }
50
51
    /**
52
     * Fetch a URL (or array of URLs) for a given help page.
53
     *
54
     * @param string $help
55
     *
56
     * @return null|array|string the help page or pages related to the $help topic, or null if none exists
57
     */
58
    public function getHelp($help)
59
    {
60
        $this->getHelpPages();
61
62
        if (isset($this->help_page[$help])) {
63
            if (is_array($this->help_page[$help])) {
64
                $urls = [];
65
                foreach ($this->help_page[$help] as $link) {
66
                    $urls[] = $this->help_base.$link;
67
                }
68
69
                return $urls;
70
            }
71
72
            return $this->help_base.$this->help_page[$help];
73
        }
74
75
        return null;
76
    }
77
78
    /**
79
     * Gets the help pages.
80
     * get help page by instancing the corresponding help class
81
     * if $this->help_page and $this->help_base are set, this function is a noop.
82
     */
83
    public function getHelpPages()
84
    {
85
        if ($this->help_page === null || $this->help_base === null) {
86
            $help_classname = '\PHPPgAdmin\Help\PostgresDoc'.str_replace('.', '', $this->major_version);
87
88
            $help_class = new $help_classname($this->conf, $this->major_version);
89
90
            $this->help_base = $help_class->getHelpBase();
91
        }
92
    }
93
94
    // Formatting functions
95
96
    /**
97
     * Outputs the HTML code for a particular field.
98
     *
99
     * @param string $name   The name to give the field
100
     * @param mixed  $value  The value of the field.  Note this could be 'numeric(7,2)' sort of thing...
101
     * @param string $type   The database type of the field
102
     * @param array  $extras An array of attributes name as key and attributes' values as value
103
     */
104
    public function printField($name, $value, $type, $extras = [])
105
    {
106
        $lang = $this->lang;
107
108
        // Determine actions string
109
        $extra_str = '';
110
        foreach ($extras as $k => $v) {
111
            $extra_str .= " {$k}=\"".htmlspecialchars($v).'"';
112
        }
113
114
        switch (substr($type, 0, 9)) {
115
            case 'bool':
116
            case 'boolean':
117
                if ($value !== null && $value == '') {
118
                    $value = null;
119
                } elseif ($value == 'true') {
120
                    $value = 't';
121
                } elseif ($value == 'false') {
122
                    $value = 'f';
123
                }
124
125
                // If value is null, 't' or 'f'...
126
                if ($value === null || $value == 't' || $value == 'f') {
127
                    echo '<select name="', htmlspecialchars($name), "\"{$extra_str}>\n";
128
                    echo '<option value=""', ($value === null) ? ' selected="selected"' : '', "></option>\n";
129
                    echo '<option value="t"', ($value == 't') ? ' selected="selected"' : '', ">{$lang['strtrue']}</option>\n";
130
                    echo '<option value="f"', ($value == 'f') ? ' selected="selected"' : '', ">{$lang['strfalse']}</option>\n";
131
                    echo "</select>\n";
132
                } else {
133
                    echo '<input name="', htmlspecialchars($name), '" value="', htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
134
                }
135
136
                break;
137
            case 'bytea':
138
            case 'bytea[]':
139
                if (!is_null($value)) {
140
                    $value = $this->escapeBytea($value);
141
                }
142
            // no break
143
            case 'text':
144
            case 'text[]':
145
            case 'json':
146
            case 'jsonb':
147
            case 'xml':
148
            case 'xml[]':
149
                $n = substr_count($value, "\n");
150
                $n = $n < 5 ? max(2, $n) : $n;
151
                $n = $n > 20 ? 20 : $n;
152
                echo '<textarea name="', htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"85\"{$extra_str}>\n";
153
                echo htmlspecialchars($value);
154
                echo "</textarea>\n";
155
156
                break;
157
            case 'character':
158
            case 'character[]':
159
                $n = substr_count($value, "\n");
160
                $n = $n < 5 ? 5 : $n;
161
                $n = $n > 20 ? 20 : $n;
162
                echo '<textarea name="', htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"35\"{$extra_str}>\n";
163
                echo htmlspecialchars($value);
164
                echo "</textarea>\n";
165
166
                break;
167
            default:
168
                echo '<input name="', htmlspecialchars($name), '" value="', htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
169
170
                break;
171
        }
172
    }
173
174
    /**
175
     * Determines whether or not a user is a super user.
176
     *
177
     * @param string $username The username of the user
178
     *
179
     * @return bool true if is a super user, false otherwise
180
     */
181
    public function isSuperUser($username = '')
182
    {
183
        $this->clean($username);
184
185
        if (empty($username)) {
186
            $val = pg_parameter_status($this->conn->_connectionID, 'is_superuser');
187
            if ($val !== false) {
188
                return $val == 'on';
189
            }
190
        }
191
192
        $sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'";
193
194
        $usesuper = $this->selectField($sql, 'usesuper');
195
        if ($usesuper == -1) {
196
            return false;
197
        }
198
199
        return $usesuper == 't';
200
    }
201
202
    /**
203
     * Returns the current default_with_oids setting.
204
     *
205
     * @return string default_with_oids setting
206
     */
207
    public function getDefaultWithOid()
208
    {
209
        $sql = 'SHOW default_with_oids';
210
211
        return $this->selectField($sql, 'default_with_oids');
212
    }
213
214
    /**
215
     * Cleans (escapes) an object name (eg. table, field).
216
     *
217
     * @param null|string $str The string to clean, by reference
218
     *
219
     * @return null|string The cleaned string
220
     */
221
    public function fieldClean(&$str)
222
    {
223
        if (!$str) {
224
            return null;
225
        }
226
227
        $str = str_replace('"', '""', $str);
228
229
        return $str;
230
    }
231
232
    /**
233
     * Searches all system catalogs to find objects that match a certain name.
234
     *
235
     * @param string $term   The search term
236
     * @param string $filter The object type to restrict to ('' means no restriction)
237
     *
238
     * @return \PHPPgAdmin\ADORecordSet A recordset
239
     */
240
    public function findObject($term, $filter)
241
    {
242
        $conf = $this->conf;
243
244
        /*about escaping:
245
         * SET standard_conforming_string is not available before 8.2
246
         * So we must use PostgreSQL specific notation :/
247
         * E'' notation is not available before 8.1
248
         * $$ is available since 8.0
249
         * Nothing specific from 7.4
250
         */
251
252
        // Escape search term for ILIKE match
253
        $this->clean($term);
254
        $this->clean($filter);
255
        $term = str_replace('_', '\_', $term);
256
        $term = str_replace('%', '\%', $term);
257
258
        // Exclude system relations if necessary
259
        if (!$conf['show_system']) {
260
            // XXX: The mention of information_schema here is in the wrong place, but
261
            // it's the quickest fix to exclude the info schema from 7.4
262
            $where     = " AND pn.nspname NOT LIKE \$_PATERN_\$pg\\_%\$_PATERN_\$ AND pn.nspname != 'information_schema'";
263
            $lan_where = 'AND pl.lanispl';
264
        } else {
265
            $where     = '';
266
            $lan_where = '';
267
        }
268
269
        // Apply outer filter
270
        $sql = '';
271
        if ($filter != '') {
272
            $sql = 'SELECT * FROM (';
273
        }
274
275
        $term = "\$_PATERN_\$%{$term}%\$_PATERN_\$";
276
277
        $sql .= "
278
			SELECT 'SCHEMA' AS type, oid, NULL AS schemaname, NULL AS relname, nspname AS name
279
				FROM pg_catalog.pg_namespace pn WHERE nspname ILIKE {$term} {$where}
280
			UNION ALL
281
			SELECT CASE WHEN relkind='r' THEN 'TABLE' WHEN relkind='v' THEN 'VIEW' WHEN relkind='S' THEN 'SEQUENCE' END, pc.oid,
282
				pn.nspname, NULL, pc.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn
283
				WHERE pc.relnamespace=pn.oid AND relkind IN ('r', 'v', 'S') AND relname ILIKE {$term} {$where}
284
			UNION ALL
285
			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,
286
				pg_catalog.pg_attribute pa WHERE pc.relnamespace=pn.oid AND pc.oid=pa.attrelid
287
				AND pa.attname ILIKE {$term} AND pa.attnum > 0 AND NOT pa.attisdropped AND pc.relkind IN ('r', 'v') {$where}
288
			UNION ALL
289
			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
290
				WHERE pp.pronamespace=pn.oid AND NOT pp.proisagg AND pp.proname ILIKE {$term} {$where}
291
			UNION ALL
292
			SELECT 'INDEX', NULL, pn.nspname, pc.relname, pc2.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
293
				pg_catalog.pg_index pi, pg_catalog.pg_class pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pi.indrelid
294
				AND pi.indexrelid=pc2.oid
295
				AND NOT EXISTS (
296
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
297
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
298
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
299
				)
300
				AND pc2.relname ILIKE {$term} {$where}
301
			UNION ALL
302
			SELECT 'CONSTRAINTTABLE', NULL, pn.nspname, pc.relname, pc2.conname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
303
				pg_catalog.pg_constraint pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pc2.conrelid AND pc2.conrelid != 0
304
				AND CASE WHEN pc2.contype IN ('f', 'c') THEN TRUE ELSE NOT EXISTS (
305
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
306
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
307
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
308
				) END
309
				AND pc2.conname ILIKE {$term} {$where}
310
			UNION ALL
311
			SELECT 'CONSTRAINTDOMAIN', pt.oid, pn.nspname, pt.typname, pc.conname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn,
312
				pg_catalog.pg_constraint pc WHERE pt.typnamespace=pn.oid AND pt.oid=pc.contypid AND pc.contypid != 0
313
				AND pc.conname ILIKE {$term} {$where}
314
			UNION ALL
315
			SELECT 'TRIGGER', NULL, pn.nspname, pc.relname, pt.tgname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
316
				pg_catalog.pg_trigger pt WHERE pc.relnamespace=pn.oid AND pc.oid=pt.tgrelid
317
					AND ( pt.tgconstraint = 0 OR NOT EXISTS
318
					(SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
319
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
320
					WHERE d.classid = pt.tableoid AND d.objid = pt.oid AND d.deptype = 'i' AND c.contype = 'f'))
321
				AND pt.tgname ILIKE {$term} {$where}
322
			UNION ALL
323
			SELECT 'RULETABLE', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
324
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
325
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
326
				WHERE c.relkind='r' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where}
327
			UNION ALL
328
			SELECT 'RULEVIEW', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
329
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
330
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
331
				WHERE c.relkind='v' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where}
332
		";
333
334
        //\Kint::dump($sql);
335
336
        // Add advanced objects if show_advanced is set
337
        if ($conf['show_advanced']) {
338
            $sql .= "
339
				UNION ALL
340
				SELECT CASE WHEN pt.typtype='d' THEN 'DOMAIN' ELSE 'TYPE' END, pt.oid, pn.nspname, NULL,
341
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
342
					WHERE pt.typnamespace=pn.oid AND typname ILIKE {$term}
343
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
344
					{$where}
345
			 	UNION ALL
346
				SELECT 'OPERATOR', po.oid, pn.nspname, NULL, po.oprname FROM pg_catalog.pg_operator po, pg_catalog.pg_namespace pn
347
					WHERE po.oprnamespace=pn.oid AND oprname ILIKE {$term} {$where}
348
				UNION ALL
349
				SELECT 'CONVERSION', pc.oid, pn.nspname, NULL, pc.conname FROM pg_catalog.pg_conversion pc,
350
					pg_catalog.pg_namespace pn WHERE pc.connamespace=pn.oid AND conname ILIKE {$term} {$where}
351
				UNION ALL
352
				SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_catalog.pg_language pl
353
					WHERE lanname ILIKE {$term} {$lan_where}
354
				UNION ALL
355
				SELECT DISTINCT ON (p.proname) 'AGGREGATE', p.oid, pn.nspname, NULL, p.proname FROM pg_catalog.pg_proc p
356
					LEFT JOIN pg_catalog.pg_namespace pn ON p.pronamespace=pn.oid
357
					WHERE p.proisagg AND p.proname ILIKE {$term} {$where}
358
				UNION ALL
359
				SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, pn.nspname, NULL, po.opcname FROM pg_catalog.pg_opclass po,
360
					pg_catalog.pg_namespace pn WHERE po.opcnamespace=pn.oid
361
					AND po.opcname ILIKE {$term} {$where}
362
			";
363
        } else {
364
            // Otherwise just add domains
365
            $sql .= "
366
				UNION ALL
367
				SELECT 'DOMAIN', pt.oid, pn.nspname, NULL,
368
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
369
					WHERE pt.typnamespace=pn.oid AND pt.typtype='d' AND typname ILIKE {$term}
370
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
371
					{$where}
372
			";
373
        }
374
375
        if ($filter != '') {
376
            // We use like to make RULE, CONSTRAINT and COLUMN searches work
377
            $sql .= ") AS sub WHERE type LIKE '{$filter}%' ";
378
        }
379
380
        $sql .= 'ORDER BY type, schemaname, relname, name';
381
382
        return $this->selectSet($sql);
383
    }
384
385
    /**
386
     * Given an array of attnums and a relation, returns an array mapping
387
     * attribute number to attribute name.
388
     *
389
     * @param string $table The table to get attributes for
390
     * @param array  $atts  An array of attribute numbers
391
     *
392
     * @return array|int An array mapping attnum to attname or error code
393
     *                   - -1 $atts must be an array
394
     *                   - -2 wrong number of attributes found
395
     */
396
    public function getAttributeNames($table, $atts)
397
    {
398
        $c_schema = $this->_schema;
399
        $this->clean($c_schema);
400
        $this->clean($table);
401
        $this->arrayClean($atts);
402
403
        if (!is_array($atts)) {
404
            return -1;
405
        }
406
407
        if (sizeof($atts) == 0) {
408
            return [];
409
        }
410
411
        $sql = "SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
412
			attrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
413
			relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}'))
414
			AND attnum IN ('".join("','", $atts)."')";
415
416
        $rs = $this->selectSet($sql);
417
        if ($rs->RecordCount() != sizeof($atts)) {
418
            return -2;
419
        }
420
421
        $temp = [];
422
        while (!$rs->EOF) {
423
            $temp[$rs->fields['attnum']] = $rs->fields['attname'];
424
            $rs->moveNext();
425
        }
426
427
        return $temp;
428
    }
429
430
    /**
431
     * Cleans (escapes) an array.
432
     *
433
     * @param array $arr The array to clean, by reference
434
     *
435
     * @return array The cleaned array
436
     */
437
    public function arrayClean(&$arr)
438
    {
439
        foreach ($arr as $k => $v) {
440
            if ($v === null) {
441
                continue;
442
            }
443
444
            $arr[$k] = pg_escape_string($v);
445
        }
446
447
        return $arr;
448
    }
449
450
    /**
451
     * Grabs an array of users and their privileges for an object,
452
     * given its type.
453
     *
454
     * @param string      $object The name of the object whose privileges are to be retrieved
455
     * @param string      $type   The type of the object (eg. database, schema, relation, function or language)
456
     * @param null|string $table  Optional, column's table if type = column
457
     *
458
     * @return array|int Privileges array or error code
459
     *                   - -1         invalid type
460
     *                   - -2         object not found
461
     *                   - -3         unknown privilege type
462
     */
463
    public function getPrivileges($object, $type, $table = null)
464
    {
465
        $c_schema = $this->_schema;
466
        $this->clean($c_schema);
467
        $this->clean($object);
468
469
        switch ($type) {
470
            case 'column':
471
                $this->clean($table);
472
                $sql = "
473
					SELECT E'{' || pg_catalog.array_to_string(attacl, E',') || E'}' as acl
474
					FROM pg_catalog.pg_attribute a
475
						LEFT JOIN pg_catalog.pg_class c ON (a.attrelid = c.oid)
476
						LEFT JOIN pg_catalog.pg_namespace n ON (c.relnamespace=n.oid)
477
					WHERE n.nspname='{$c_schema}'
478
						AND c.relname='{$table}'
479
						AND a.attname='{$object}'";
480
481
                break;
482
            case 'table':
483
            case 'view':
484
            case 'sequence':
485
                $sql = "
486
					SELECT relacl AS acl FROM pg_catalog.pg_class
487
					WHERE relname='{$object}'
488
						AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace
489
							WHERE nspname='{$c_schema}')";
490
491
                break;
492
            case 'database':
493
                $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";
494
495
                break;
496
            case 'function':
497
                // Since we fetch functions by oid, they are already constrained to
498
                // the current schema.
499
                $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";
500
501
                break;
502
            case 'language':
503
                $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";
504
505
                break;
506
            case 'schema':
507
                $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";
508
509
                break;
510
            case 'tablespace':
511
                $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";
512
513
                break;
514
            default:
515
                return -1;
516
        }
517
518
        // Fetch the ACL for object
519
        $acl = $this->selectField($sql, 'acl');
520
        if ($acl == -1) {
521
            return -2;
522
        }
523
524
        if ($acl == '' || $acl === null || !(bool) $acl) {
525
            return [];
526
        }
527
528
        return $this->parseACL($acl);
529
    }
530
531
    /**
532
     * Internal function used for parsing ACLs.
533
     *
534
     * @param string $acl The ACL to parse (of type aclitem[])
535
     *
536
     * @return array|int Privileges array or integer with error code
537
     *
538
     * @internal bool $in_quotes toggles acl in_quotes attribute
539
     */
540
    protected function parseACL($acl)
541
    {
542
        // Take off the first and last characters (the braces)
543
        $acl = substr($acl, 1, strlen($acl) - 2);
544
545
        // Pick out individual ACE's by carefully parsing.  This is necessary in order
546
        // to cope with usernames and stuff that contain commas
547
        $aces      = [];
548
        $i         = $j         = 0;
549
        $in_quotes = false;
550
        while ($i < strlen($acl)) {
551
            // If current char is a double quote and it's not escaped, then
552
            // enter quoted bit
553
            $char = substr($acl, $i, 1);
554
            if ($char == '"' && ($i == 0 || substr($acl, $i - 1, 1) != '\\')) {
555
                $in_quotes = !$in_quotes;
556
            } elseif ($char == ',' && !$in_quotes) {
557
                // Add text so far to the array
558
                $aces[] = substr($acl, $j, $i - $j);
559
                $j      = $i + 1;
560
            }
561
            ++$i;
562
        }
563
        // Add final text to the array
564
        $aces[] = substr($acl, $j);
565
566
        // Create the array to be returned
567
        $temp = [];
568
569
        // For each ACE, generate an entry in $temp
570
        foreach ($aces as $v) {
571
            // If the ACE begins with a double quote, strip them off both ends
572
            // and unescape backslashes and double quotes
573
            // $unquote = false;
574
            if (strpos($v, '"') === 0) {
575
                $v = substr($v, 1, strlen($v) - 2);
576
                $v = str_replace('\\"', '"', $v);
577
                $v = str_replace('\\\\', '\\', $v);
578
            }
579
580
            // Figure out type of ACE (public, user or group)
581
            if (strpos($v, '=') === 0) {
582
                $atype = 'public';
583
            } else {
584
                if ($this->hasRoles()) {
585
                    $atype = 'role';
586
                } else {
587
                    if (strpos($v, 'group ') === 0) {
588
                        $atype = 'group';
589
                        // Tear off 'group' prefix
590
                        $v = substr($v, 6);
591
                    } else {
592
                        $atype = 'user';
593
                    }
594
                }
595
            }
596
597
            // Break on unquoted equals sign...
598
            $i         = 0;
599
            $in_quotes = false;
600
            $entity    = null;
601
            $chars     = null;
602
            while ($i < strlen($v)) {
603
                // If current char is a double quote and it's not escaped, then
604
                // enter quoted bit
605
                $char      = substr($v, $i, 1);
606
                $next_char = substr($v, $i + 1, 1);
607
                if ($char == '"' && ($i == 0 || $next_char != '"')) {
608
                    $in_quotes = !$in_quotes;
609
                } elseif ($char == '"' && $next_char == '"') {
610
                    // Skip over escaped double quotes
611
                    ++$i;
612
                } elseif ($char == '=' && !$in_quotes) {
613
                    // Split on current equals sign
614
                    $entity = substr($v, 0, $i);
615
                    $chars  = substr($v, $i + 1);
616
617
                    break;
618
                }
619
                ++$i;
620
            }
621
622
            // Check for quoting on entity name, and unescape if necessary
623
            if (strpos($entity, '"') === 0) {
624
                $entity = substr($entity, 1, strlen($entity) - 2);
625
                $entity = str_replace('""', '"', $entity);
626
            }
627
628
            // New row to be added to $temp
629
            // (type, grantee, privileges, grantor, grant option?
630
            $row = [$atype, $entity, [], '', []];
631
632
            // Loop over chars and add privs to $row
633
            for ($i = 0; $i < strlen($chars); ++$i) {
634
                // Append to row's privs list the string representing
635
                // the privilege
636
                $char = substr($chars, $i, 1);
637
                if ($char == '*') {
638
                    $row[4][] = $this->privmap[substr($chars, $i - 1, 1)];
639
                } elseif ($char == '/') {
640
                    $grantor = substr($chars, $i + 1);
641
                    // Check for quoting
642
                    if (strpos($grantor, '"') === 0) {
643
                        $grantor = substr($grantor, 1, strlen($grantor) - 2);
644
                        $grantor = str_replace('""', '"', $grantor);
645
                    }
646
                    $row[3] = $grantor;
647
648
                    break;
649
                } else {
650
                    if (!isset($this->privmap[$char])) {
651
                        return -3;
652
                    }
653
654
                    $row[2][] = $this->privmap[$char];
655
                }
656
            }
657
658
            // Append row to temp
659
            $temp[] = $row;
660
        }
661
662
        return $temp;
663
    }
664
665
    /**
666
     * Gets all languages.
667
     *
668
     * @param bool $all True to get all languages, regardless of show_system
669
     *
670
     * @return \PHPPgAdmin\ADORecordSet A recordset
671
     */
672
    public function getLanguages($all = false)
673
    {
674
        $conf = $this->conf;
675
676
        if ($conf['show_system'] || $all) {
677
            $where = '';
678
        } else {
679
            $where = 'WHERE lanispl';
680
        }
681
682
        $sql = "
683
			SELECT
684
				lanname, lanpltrusted,
685
				lanplcallfoid::pg_catalog.regproc AS lanplcallf
686
			FROM
687
				pg_catalog.pg_language
688
			{$where}
689
			ORDER BY lanname
690
		";
691
692
        return $this->selectSet($sql);
693
    }
694
695
    /**
696
     * Executes an SQL script as a series of SQL statements.  Returns
697
     * the result of the final step.  This is a very complicated lexer
698
     * based on the REL7_4_STABLE src/bin/psql/mainloop.c lexer in
699
     * the PostgreSQL source code.
700
     * XXX: It does not handle multibyte languages properly.
701
     *
702
     * @param string        $name     Entry in $_FILES to use
703
     * @param null|callable $callback (optional) Callback function to call with each query, its result and line number
704
     *
705
     * @return bool true for general success, false on any failure
706
     */
707
    public function executeScript($name, $callback = null)
708
    {
709
        // This whole function isn't very encapsulated, but hey...
710
        $conn = $this->conn->_connectionID;
711
        if (!is_uploaded_file($_FILES[$name]['tmp_name'])) {
712
            return false;
713
        }
714
715
        $fd = fopen($_FILES[$name]['tmp_name'], 'rb');
716
        if ($fd === false) {
717
            return false;
718
        }
719
720
        // Build up each SQL statement, they can be multiline
721
        $query_buf    = null;
722
        $query_start  = 0;
723
        $in_quote     = 0;
724
        $in_xcomment  = 0;
725
        $bslash_count = 0;
726
        $dol_quote    = '';
727
        $paren_level  = 0;
728
        $len          = 0;
729
        $i            = 0;
730
        $prevlen      = 0;
731
        $thislen      = 0;
732
        $lineno       = 0;
733
734
        // Loop over each line in the file
735
        while (!feof($fd)) {
736
            $line = fgets($fd);
737
            ++$lineno;
738
739
            // Nothing left on line? Then ignore...
740
            if (trim($line) == '') {
741
                continue;
742
            }
743
744
            $len         = strlen($line);
745
            $query_start = 0;
746
747
            /**
748
             * Parse line, looking for command separators.
749
             *
750
             * The current character is at line[i], the prior character at line[i
751
             * - prevlen], the next character at line[i + thislen].
752
             */
753
            $prevlen = 0;
754
            $thislen = ($len > 0) ? 1 : 0;
755
756
            for ($i = 0; $i < $len; $this->advance_1($i, $prevlen, $thislen)) {
757
                /* was the previous character a backslash? */
758
                if ($i > 0 && substr($line, $i - $prevlen, 1) == '\\') {
759
                    $this->prtrace('bslash_count', $bslash_count, $line);
760
                    ++$bslash_count;
761
                } else {
762
                    $bslash_count = 0;
763
                }
764
765
                /*
766
                 * It is important to place the in_* test routines before the
767
                 * in_* detection routines. i.e. we have to test if we are in
768
                 * a quote before testing for comments.
769
                 */
770
771
                /* in quote? */
772
                if ($in_quote !== 0) {
773
                    //$this->prtrace('in_quote', $in_quote, $line);
774
                    /*
775
                     * end of quote if matching non-backslashed character.
776
                     * backslashes don't count for double quotes, though.
777
                     */
778
                    if (substr($line, $i, 1) == $in_quote &&
779
                        ($bslash_count % 2 == 0 || $in_quote == '"')
780
                    ) {
781
                        $in_quote = 0;
782
                    }
783
                } elseif ($dol_quote) {
784
                    $this->prtrace('dol_quote', $dol_quote, $line);
785
                    if (strncmp(substr($line, $i), $dol_quote, strlen($dol_quote)) == 0) {
786
                        $this->advance_1($i, $prevlen, $thislen);
787
                        while (substr($line, $i, 1) != '$') {
788
                            $this->advance_1($i, $prevlen, $thislen);
789
                        }
790
791
                        $dol_quote = '';
792
                    }
793
                } elseif (substr($line, $i, 2) == '/*') {
794
                    $this->prtrace('open_xcomment', $in_xcomment, $line, $i, $prevlen, $thislen);
795
                    if ($in_xcomment == 0) {
796
                        ++$in_xcomment;
797
                        $finishpos = strpos(substr($line, $i, $len), '*/');
798
                        if ($finishpos === false) {
799
                            $line = substr($line, 0, $i); /* remove comment */
800
                            break;
801
                        }
802
                        $pre         = substr($line, 0, $i);
803
                        $post        = substr($line, $i + 2 + $finishpos, $len);
804
                        $line        = $pre.' '.$post;
805
                        $in_xcomment = 0;
806
                        $i           = 0;
807
                    }
808
                } elseif ($in_xcomment) {
809
                    $position = strpos(substr($line, $i, $len), '*/');
810
                    if ($position === false) {
811
                        $line = '';
812
813
                        break;
814
                    }
815
816
                    $substr = substr($line, $i, 2);
817
818
                    if ($substr == '*/' && !--$in_xcomment) {
819
                        $line = substr($line, $i + 2, $len);
820
                        $i += 2;
821
                        $this->advance_1($i, $prevlen, $thislen);
822
                    }
823
                    // old logic
824
                    //  } else if (substr($line, $i, 2) == '/*') {
825
                    //      if ($in_xcomment == 0) {
826
                    //          ++$in_xcomment;
827
                    //          $this->advance_1($i, $prevlen, $thislen);
828
                    //      }
829
                    //  } else if ($in_xcomment) {
830
                    //      $substr = substr($line, $i, 2);
831
                    //      if ($substr == '*/' && !--$in_xcomment) {
832
                    //          $this->advance_1($i, $prevlen, $thislen);
833
                    //      }
834
                } elseif (substr($line, $i, 1) == '\'' || substr($line, $i, 1) == '"') {
835
                    $in_quote = substr($line, $i, 1);
836
                } elseif (!$dol_quote && $this->valid_dolquote(substr($line, $i))) {
837
                    $dol_end   = strpos(substr($line, $i + 1), '$');
838
                    $dol_quote = substr($line, $i, $dol_end + 1);
839
                    $this->advance_1($i, $prevlen, $thislen);
840
                    while (substr($line, $i, 1) != '$') {
841
                        $this->advance_1($i, $prevlen, $thislen);
842
                    }
843
                } else {
844
                    if (substr($line, $i, 2) == '--') {
845
                        $line = substr($line, 0, $i); /* remove comment */
846
                        break;
847
                    } /* count nested parentheses */
848
849
                    if (substr($line, $i, 1) == '(') {
850
                        ++$paren_level;
851
                    } elseif (substr($line, $i, 1) == ')' && $paren_level > 0) {
852
                        --$paren_level;
853
                    } elseif (substr($line, $i, 1) == ';' && !$bslash_count && !$paren_level) {
854
                        $subline = substr(substr($line, 0, $i), $query_start);
855
                        /*
856
                         * insert a cosmetic newline, if this is not the first
857
                         * line in the buffer
858
                         */
859
                        if (strlen($query_buf) > 0) {
860
                            $query_buf .= "\n";
861
                        }
862
863
                        /* append the line to the query buffer */
864
                        $query_buf .= $subline;
865
                        /* is there anything in the query_buf? */
866
                        if (trim($query_buf)) {
867
                            $query_buf .= ';';
868
869
                            // Execute the query. PHP cannot execute
870
                            // empty queries, unlike libpq
871
                            $res = @pg_query($conn, $query_buf);
872
873
                            // Call the callback function for display
874
                            if ($callback !== null) {
875
                                $callback($query_buf, $res, $lineno);
876
                            }
877
878
                            // Check for COPY request
879
                            if (pg_result_status($res) == 4) {
880
                                // 4 == PGSQL_COPY_FROM
881
                                while (!feof($fd)) {
882
                                    $copy = fgets($fd, 32768);
883
                                    ++$lineno;
884
                                    pg_put_line($conn, $copy);
885
                                    if ($copy == "\\.\n" || $copy == "\\.\r\n") {
886
                                        pg_end_copy($conn);
887
888
                                        break;
889
                                    }
890
                                }
891
                            }
892
                        }
893
                        $query_buf   = null;
894
                        $query_start = $i + $thislen;
895
                    } elseif (preg_match('/^[_[:alpha:]]$/', substr($line, $i, 1))) {
896
                        $sub = substr($line, $i, $thislen);
897
                        while (preg_match('/^[\$_A-Za-z0-9]$/', $sub)) {
898
                            /* keep going while we still have identifier chars */
899
                            $this->advance_1($i, $prevlen, $thislen);
900
                            $sub = substr($line, $i, $thislen);
901
                        }
902
                        // Since we're now over the next character to be examined, it is necessary
903
                        // to move back one space.
904
                        $i -= $prevlen;
905
                    }
906
                }
907
            } // end for
908
909
            /* Put the rest of the line in the query buffer. */
910
            $subline = substr($line, $query_start);
911
912
            if ($in_quote || $dol_quote || strspn($subline, " \t\n\r") != strlen($subline)) {
913
                if (strlen($query_buf) > 0) {
914
                    $query_buf .= "\n";
915
                }
916
917
                $query_buf .= $subline;
918
            }
919
920
            $line = null;
921
        } // end while
922
923
        /*
924
         * Process query at the end of file without a semicolon, so long as
925
         * it's non-empty.
926
         */
927
        if (strlen($query_buf) > 0 && strspn($query_buf, " \t\n\r") != strlen($query_buf)) {
928
            // Execute the query
929
            $res = @pg_query($conn, $query_buf);
930
931
            // Call the callback function for display
932
            if ($callback !== null) {
933
                $callback($query_buf, $res, $lineno);
934
            }
935
936
            // Check for COPY request
937
            if (pg_result_status($res) == 4) {
938
                // 4 == PGSQL_COPY_FROM
939
                while (!feof($fd)) {
940
                    $copy = fgets($fd, 32768);
941
                    ++$lineno;
942
                    pg_put_line($conn, $copy);
943
                    if ($copy == "\\.\n" || $copy == "\\.\r\n") {
944
                        pg_end_copy($conn);
945
946
                        break;
947
                    }
948
                }
949
            }
950
        }
951
952
        fclose($fd);
953
954
        return true;
955
    }
956
957
    /**
958
     * A private helper method for executeScript that advances the
959
     * character by 1.  In psql this is careful to take into account
960
     * multibyte languages, but we don't at the moment, so this function
961
     * is someone redundant, since it will always advance by 1.
962
     *
963
     * @param int $i       The current character position in the line
964
     * @param int $prevlen Length of previous character (ie. 1)
965
     * @param int $thislen Length of current character (ie. 1)
966
     */
967
    protected function advance_1(&$i, &$prevlen, &$thislen)
968
    {
969
        $prevlen = $thislen;
970
        $i += $thislen;
971
        $thislen = 1;
972
    }
973
974
    /**
975
     * Private helper method to detect a valid $foo$ quote delimiter at
976
     * the start of the parameter dquote.
977
     *
978
     * @param string $dquote
979
     *
980
     * @return bool true if valid, false otherwise
981
     */
982
    protected function valid_dolquote($dquote)
983
    {
984
        // XXX: support multibyte
985
        return preg_match('/^[$][$]/', $dquote) || preg_match('/^[$][_[:alpha:]][_[:alnum:]]*[$]/', $dquote);
986
    }
987
988
    // Capabilities
989
990
    /**
991
     * Returns a recordset of all columns in a query.  Supports paging.
992
     *
993
     * @param string   $type      Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier,
994
     *                            or 'SELECT" if it's a select query
995
     * @param string   $table     The base table of the query.  NULL for no table.
996
     * @param string   $query     The query that is being executed.  NULL for no query.
997
     * @param string   $sortkey   The column number to sort by, or '' or null for no sorting
998
     * @param string   $sortdir   The direction in which to sort the specified column ('asc' or 'desc')
999
     * @param null|int $page      The page of the relation to retrieve
1000
     * @param null|int $page_size The number of rows per page
1001
     * @param int      $max_pages (return-by-ref) The max number of pages in the relation
1002
     *
1003
     * @return int|\PHPPgAdmin\ADORecordSet A  recordset on success or an int with error code
1004
     *                                      - -1 transaction error
1005
     *                                      - -2 counting error
1006
     *                                      - -3 page or page_size invalid
1007
     *                                      - -4 unknown type
1008
     *                                      - -5 failed setting transaction read only
1009
     */
1010
    public function browseQuery($type, $table, $query, $sortkey, $sortdir, $page, $page_size, &$max_pages)
1011
    {
1012
        // Check that we're not going to divide by zero
1013
        if (!is_numeric($page_size) || $page_size != (int) $page_size || $page_size <= 0) {
1014
            return -3;
1015
        }
1016
1017
        // If $type is TABLE, then generate the query
1018
        switch ($type) {
1019
            case 'TABLE':
1020
                if (preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) {
1021
                    $orderby = [$sortkey => $sortdir];
1022
                } else {
1023
                    $orderby = [];
1024
                }
1025
1026
                $query = $this->getSelectSQL($table, [], [], [], $orderby);
1027
1028
                break;
1029
            case 'QUERY':
1030
            case 'SELECT':
1031
                // Trim query
1032
                $query = trim($query);
1033
                // Trim off trailing semi-colon if there is one
1034
                if (substr($query, strlen($query) - 1, 1) == ';') {
1035
                    $query = substr($query, 0, strlen($query) - 1);
1036
                }
1037
1038
                break;
1039
            default:
1040
                return -4;
1041
        }
1042
1043
        // Generate count query
1044
        $count = "SELECT COUNT(*) AS total FROM (${query}) AS sub";
1045
1046
        // Open a transaction
1047
        $status = $this->beginTransaction();
1048
        if ($status != 0) {
1049
            return -1;
1050
        }
1051
1052
        // If backend supports read only queries, then specify read only mode
1053
        // to avoid side effects from repeating queries that do writes.
1054
        if ($this->hasReadOnlyQueries()) {
1055
            $status = $this->execute('SET TRANSACTION READ ONLY');
1056
            if ($status != 0) {
1057
                $this->rollbackTransaction();
1058
1059
                return -5;
1060
            }
1061
        }
1062
1063
        // Count the number of rows
1064
        $total = $this->browseQueryCount($count);
1065
        if ($total < 0) {
1066
            $this->rollbackTransaction();
1067
1068
            return -2;
1069
        }
1070
1071
        // Calculate max pages
1072
        $max_pages = ceil($total / $page_size);
1073
1074
        // Check that page is less than or equal to max pages
1075
        if (!is_numeric($page) || $page != (int) $page || $page > $max_pages || $page < 1) {
1076
            $this->rollbackTransaction();
1077
1078
            return -3;
1079
        }
1080
1081
        // Set fetch mode to NUM so that duplicate field names are properly returned
1082
        // for non-table queries.  Since the SELECT feature only allows selecting one
1083
        // table, duplicate fields shouldn't appear.
1084
        if ($type == 'QUERY') {
1085
            $this->conn->setFetchMode(ADODB_FETCH_NUM);
1086
        }
1087
1088
        // Figure out ORDER BY.  Sort key is always the column number (based from one)
1089
        // of the column to order by.  Only need to do this for non-TABLE queries
1090
        if ($type != 'TABLE' && preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) {
1091
            $orderby = " ORDER BY {$sortkey}";
1092
            // Add sort order
1093
            if ($sortdir == 'desc') {
1094
                $orderby .= ' DESC';
1095
            } else {
1096
                $orderby .= ' ASC';
1097
            }
1098
        } else {
1099
            $orderby = '';
1100
        }
1101
1102
        // Actually retrieve the rows, with offset and limit
1103
        $rs     = $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET ".($page - 1) * $page_size);
1104
        $status = $this->endTransaction();
1105
        if ($status != 0) {
1106
            $this->rollbackTransaction();
1107
1108
            return -1;
1109
        }
1110
1111
        return $rs;
1112
    }
1113
1114
    /**
1115
     * Generates the SQL for the 'select' function.
1116
     *
1117
     * @param string $table   The table from which to select
1118
     * @param array  $show    An array of columns to show.  Empty array means all columns.
1119
     * @param array  $values  An array mapping columns to values
1120
     * @param array  $ops     An array of the operators to use
1121
     * @param array  $orderby (optional) An array of column numbers or names (one based)
1122
     *                        mapped to sort direction (asc or desc or '' or null) to order by
1123
     *
1124
     * @return string The SQL query
1125
     */
1126
    public function getSelectSQL($table, $show, $values, $ops, $orderby = [])
1127
    {
1128
        $this->fieldArrayClean($show);
1129
1130
        // If an empty array is passed in, then show all columns
1131
        if (sizeof($show) == 0) {
1132
            if ($this->hasObjectID($table)) {
1133
                $sql = "SELECT \"{$this->id}\", * FROM ";
1134
            } else {
1135
                $sql = 'SELECT * FROM ';
1136
            }
1137
        } else {
1138
            // Add oid column automatically to results for editing purposes
1139
            if (!in_array($this->id, $show, true) && $this->hasObjectID($table)) {
1140
                $sql = "SELECT \"{$this->id}\", \"";
1141
            } else {
1142
                $sql = 'SELECT "';
1143
            }
1144
1145
            $sql .= join('","', $show).'" FROM ';
1146
        }
1147
1148
        $this->fieldClean($table);
1149
1150
        if (isset($_REQUEST['schema'])) {
1151
            $f_schema = $_REQUEST['schema'];
1152
            $this->fieldClean($f_schema);
1153
            $sql .= "\"{$f_schema}\".";
1154
        }
1155
        $sql .= "\"{$table}\"";
1156
1157
        // If we have values specified, add them to the WHERE clause
1158
        $first = true;
1159
        if (is_array($values) && sizeof($values) > 0) {
1160
            foreach ($values as $k => $v) {
1161
                if ($v != '' || $this->selectOps[$ops[$k]] == 'p') {
1162
                    $this->fieldClean($k);
1163
                    if ($first) {
1164
                        $sql .= ' WHERE ';
1165
                        $first = false;
1166
                    } else {
1167
                        $sql .= ' AND ';
1168
                    }
1169
                    // Different query format depending on operator type
1170
                    switch ($this->selectOps[$ops[$k]]) {
1171
                        case 'i':
1172
                            // Only clean the field for the inline case
1173
                            // this is because (x), subqueries need to
1174
                            // to allow 'a','b' as input.
1175
                            $this->clean($v);
1176
                            $sql .= "\"{$k}\" {$ops[$k]} '{$v}'";
1177
1178
                            break;
1179
                        case 'p':
1180
                            $sql .= "\"{$k}\" {$ops[$k]}";
1181
1182
                            break;
1183
                        case 'x':
1184
                            $sql .= "\"{$k}\" {$ops[$k]} ({$v})";
1185
1186
                            break;
1187
                        case 't':
1188
                            $sql .= "\"{$k}\" {$ops[$k]}('{$v}')";
1189
1190
                            break;
1191
                        default:
1192
                            // Shouldn't happen
1193
                    }
1194
                }
1195
            }
1196
        }
1197
1198
        // ORDER BY
1199
        if (is_array($orderby) && sizeof($orderby) > 0) {
1200
            $sql .= ' ORDER BY ';
1201
            $first = true;
1202
            foreach ($orderby as $k => $v) {
1203
                if ($first) {
1204
                    $first = false;
1205
                } else {
1206
                    $sql .= ', ';
1207
                }
1208
1209
                if (preg_match('/^[0-9]+$/', $k)) {
1210
                    $sql .= $k;
1211
                } else {
1212
                    $this->fieldClean($k);
1213
                    $sql .= '"'.$k.'"';
1214
                }
1215
                if (strtoupper($v) == 'DESC') {
1216
                    $sql .= ' DESC';
1217
                }
1218
            }
1219
        }
1220
1221
        return $sql;
1222
    }
1223
1224
    /**
1225
     * Finds the number of rows that would be returned by a
1226
     * query.
1227
     *
1228
     * @param string $count The count query
1229
     *
1230
     * @return int|string The count of rows or -1 of no rows are found
1231
     */
1232
    public function browseQueryCount($count)
1233
    {
1234
        return $this->selectField($count, 'total');
1235
    }
1236
1237
    /**
1238
     * Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false.
1239
     *
1240
     * @param null|bool|int|string $parameter the parameter
1241
     *
1242
     * @return string the parameter transformed to 't' of 'f'
1243
     */
1244
    public function dbBool(&$parameter)
1245
    {
1246
        if ($parameter) {
1247
            $parameter = 't';
1248
        } else {
1249
            $parameter = 'f';
1250
        }
1251
1252
        return $parameter;
1253
    }
1254
1255
    /**
1256
     * Fetches statistics for a database.
1257
     *
1258
     * @param string $database The database to fetch stats for
1259
     *
1260
     * @return \PHPPgAdmin\ADORecordSet A recordset
1261
     */
1262
    public function getStatsDatabase($database)
1263
    {
1264
        $this->clean($database);
1265
1266
        $sql = "SELECT * FROM pg_stat_database WHERE datname='{$database}'";
1267
1268
        return $this->selectSet($sql);
1269
    }
1270
1271
    /**
1272
     * Fetches tuple statistics for a table.
1273
     *
1274
     * @param string $table The table to fetch stats for
1275
     *
1276
     * @return \PHPPgAdmin\ADORecordSet A recordset
1277
     */
1278
    public function getStatsTableTuples($table)
1279
    {
1280
        $c_schema = $this->_schema;
1281
        $this->clean($c_schema);
1282
        $this->clean($table);
1283
1284
        $sql = "SELECT * FROM pg_stat_all_tables
1285
			WHERE schemaname='{$c_schema}' AND relname='{$table}'";
1286
1287
        return $this->selectSet($sql);
1288
    }
1289
1290
    /**
1291
     * Fetches I/0 statistics for a table.
1292
     *
1293
     * @param string $table The table to fetch stats for
1294
     *
1295
     * @return \PHPPgAdmin\ADORecordSet A recordset
1296
     */
1297
    public function getStatsTableIO($table)
1298
    {
1299
        $c_schema = $this->_schema;
1300
        $this->clean($c_schema);
1301
        $this->clean($table);
1302
1303
        $sql = "SELECT * FROM pg_statio_all_tables
1304
			WHERE schemaname='{$c_schema}' AND relname='{$table}'";
1305
1306
        return $this->selectSet($sql);
1307
    }
1308
1309
    /**
1310
     * Fetches tuple statistics for all indexes on a table.
1311
     *
1312
     * @param string $table The table to fetch index stats for
1313
     *
1314
     * @return \PHPPgAdmin\ADORecordSet A recordset
1315
     */
1316
    public function getStatsIndexTuples($table)
1317
    {
1318
        $c_schema = $this->_schema;
1319
        $this->clean($c_schema);
1320
        $this->clean($table);
1321
1322
        $sql = "SELECT * FROM pg_stat_all_indexes
1323
			WHERE schemaname='{$c_schema}' AND relname='{$table}' ORDER BY indexrelname";
1324
1325
        return $this->selectSet($sql);
1326
    }
1327
1328
    /**
1329
     * Fetches I/0 statistics for all indexes on a table.
1330
     *
1331
     * @param string $table The table to fetch index stats for
1332
     *
1333
     * @return \PHPPgAdmin\ADORecordSet A recordset
1334
     */
1335
    public function getStatsIndexIO($table)
1336
    {
1337
        $c_schema = $this->_schema;
1338
        $this->clean($c_schema);
1339
        $this->clean($table);
1340
1341
        $sql = "SELECT * FROM pg_statio_all_indexes
1342
			WHERE schemaname='{$c_schema}' AND relname='{$table}'
1343
			ORDER BY indexrelname";
1344
1345
        return $this->selectSet($sql);
1346
    }
1347
}
1348