Passed
Push — develop ( ca4520...e7398d )
by Felipe
04:50
created

TableTrait::saveAutovacuum()   C

Complexity

Conditions 8
Paths 128

Size

Total Lines 50
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 50
rs 5.6
c 0
b 0
f 0
cc 8
eloc 28
nc 128
nop 8

How to fix   Many Parameters   

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.48
5
 */
6
7
namespace PHPPgAdmin\Database\Traits;
8
9
/**
10
 * Common trait for tables manipulation.
11
 */
12
trait TableTrait
13
{
14
    use \PHPPgAdmin\Database\Traits\ColumnTrait;
15
    use \PHPPgAdmin\Database\Traits\RowTrait;
16
    use \PHPPgAdmin\Database\Traits\TriggerTrait;
17
18
    /**
19
     * Return all tables in current database excluding schemas 'pg_catalog', 'information_schema' and 'pg_toast'.
20
     *
21
     *
22
     * @return \PHPPgAdmin\ADORecordSet All tables, sorted alphabetically
23
     */
24
    public function getAllTables()
25
    {
26
        $sql = "SELECT
27
                        schemaname AS nspname,
28
                        tablename AS relname,
29
                        tableowner AS relowner
30
                    FROM pg_catalog.pg_tables
31
                    WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
32
                    ORDER BY schemaname, tablename";
33
34
        return $this->selectSet($sql);
35
    }
36
37
    /**
38
     * Return all tables in current database (and schema).
39
     *
40
     * @return \PHPPgAdmin\ADORecordSet All tables, sorted alphabetically
41
     */
42
    public function getTables()
43
    {
44
        $c_schema = $this->_schema;
45
        $this->clean($c_schema);
46
47
        $sql = "
48
                SELECT c.relname,
49
                    pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
50
                    pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
51
                    reltuples::bigint as reltuples,
52
                    pt.spcname as tablespace,
53
                    pg_size_pretty(pg_total_relation_size(c.oid)) as table_size
54
                FROM pg_catalog.pg_class c
55
                LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
56
                LEFT JOIN  pg_catalog.pg_tablespace pt ON  pt.oid=c.reltablespace
57
                WHERE c.relkind = 'r'
58
                AND nspname='{$c_schema}'
59
                ORDER BY c.relname";
60
61
        return $this->selectSet($sql);
62
    }
63
64
    /**
65
     * Finds the names and schemas of parent tables (in order).
66
     *
67
     * @param string $table The table to find the parents for
68
     *
69
     * @return \PHPPgAdmin\ADORecordSet A recordset
70
     */
71
    public function getTableParents($table)
72
    {
73
        $c_schema = $this->_schema;
74
        $this->clean($c_schema);
75
        $this->clean($table);
76
77
        $sql = "
78
            SELECT
79
                pn.nspname, relname
80
            FROM
81
                pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
82
            WHERE
83
                pc.oid=pi.inhparent
84
                AND pc.relnamespace=pn.oid
85
                AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
86
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
87
            ORDER BY
88
                pi.inhseqno
89
        ";
90
91
        return $this->selectSet($sql);
92
    }
93
94
    /**
95
     * Finds the names and schemas of child tables.
96
     *
97
     * @param string $table The table to find the children for
98
     *
99
     * @return \PHPPgAdmin\ADORecordSet A recordset
100
     */
101
    public function getTableChildren($table)
102
    {
103
        $c_schema = $this->_schema;
104
        $this->clean($c_schema);
105
        $this->clean($table);
106
107
        $sql = "
108
            SELECT
109
                pn.nspname, relname
110
            FROM
111
                pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
112
            WHERE
113
                pc.oid=pi.inhrelid
114
                AND pc.relnamespace=pn.oid
115
                AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
116
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
117
        ";
118
119
        return $this->selectSet($sql);
120
    }
121
122
    /**
123
     * Returns the SQL definition for the table.
124
     * MUST be run within a transaction.
125
     *
126
     * @param string    $table The table to define
127
     * @param bool|true $clean True to issue drop command, false otherwise
128
     *
129
     * @return string A string containing the formatted SQL code
130
     */
131
    public function getTableDefPrefix($table, $clean = false)
132
    {
133
        // Fetch table
134
        $t = $this->getTable($table);
135
        if (!is_object($t) || $t->RecordCount() != 1) {
136
            $this->rollbackTransaction();
137
138
            return null;
139
        }
140
        $this->fieldClean($t->fields['relname']);
141
        $this->fieldClean($t->fields['nspname']);
142
143
        // Fetch attributes
144
        $atts = $this->getTableAttributes($table);
145
        if (!is_object($atts)) {
146
            $this->rollbackTransaction();
147
148
            return null;
149
        }
150
151
        // Fetch constraints
152
        $cons = $this->getConstraints($table);
153
        if (!is_object($cons)) {
154
            $this->rollbackTransaction();
155
156
            return null;
157
        }
158
159
        // Output a reconnect command to create the table as the correct user
160
        $sql = $this->getChangeUserSQL($t->fields['relowner'])."\n\n";
161
162
        // Set schema search path
163
        $sql .= "SET search_path = \"{$t->fields['nspname']}\", pg_catalog;\n\n";
164
165
        // Begin CREATE TABLE definition
166
        $sql .= "-- Definition\n\n";
167
        // DROP TABLE must be fully qualified in case a table with the same name exists
168
        // in pg_catalog.
169
        if (!$clean) {
170
            $sql .= '-- ';
171
        }
172
173
        $sql .= 'DROP TABLE ';
174
        $sql .= "\"{$t->fields['nspname']}\".\"{$t->fields['relname']}\";\n";
175
        $sql .= "CREATE TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" (\n";
176
177
        // Output all table columns
178
        $col_comments_sql = ''; // Accumulate comments on columns
179
        $num              = $atts->RecordCount() + $cons->RecordCount();
180
        $i                = 1;
181
        while (!$atts->EOF) {
182
            $this->fieldClean($atts->fields['attname']);
183
            $sql .= "    \"{$atts->fields['attname']}\"";
184
            // Dump SERIAL and BIGSERIAL columns correctly
185
            if ($this->phpBool($atts->fields['attisserial']) &&
186
                ($atts->fields['type'] == 'integer' || $atts->fields['type'] == 'bigint')) {
187
                if ($atts->fields['type'] == 'integer') {
188
                    $sql .= ' SERIAL';
189
                } else {
190
                    $sql .= ' BIGSERIAL';
191
                }
192
            } else {
193
                $sql .= ' '.$this->formatType($atts->fields['type'], $atts->fields['atttypmod']);
194
195
                // Add NOT NULL if necessary
196
                if ($this->phpBool($atts->fields['attnotnull'])) {
197
                    $sql .= ' NOT NULL';
198
                }
199
200
                // Add default if necessary
201
                if ($atts->fields['adsrc'] !== null) {
202
                    $sql .= " DEFAULT {$atts->fields['adsrc']}";
203
                }
204
            }
205
206
            // Output comma or not
207
            if ($i < $num) {
208
                $sql .= ",\n";
209
            } else {
210
                $sql .= "\n";
211
            }
212
213
            // Does this column have a comment?
214
            if ($atts->fields['comment'] !== null) {
215
                $this->clean($atts->fields['comment']);
216
                $col_comments_sql .= "COMMENT ON COLUMN \"{$t->fields['relname']}\".\"{$atts->fields['attname']}\"  IS '{$atts->fields['comment']}';\n";
217
            }
218
219
            $atts->moveNext();
220
            ++$i;
221
        }
222
        // Output all table constraints
223
        while (!$cons->EOF) {
224
            $this->fieldClean($cons->fields['conname']);
225
            $sql .= "    CONSTRAINT \"{$cons->fields['conname']}\" ";
226
            // Nasty hack to support pre-7.4 PostgreSQL
227
            if ($cons->fields['consrc'] !== null) {
228
                $sql .= $cons->fields['consrc'];
229
            } else {
230
                switch ($cons->fields['contype']) {
231
                    case 'p':
232
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
233
                        $sql .= 'PRIMARY KEY ('.join(',', $keys).')';
234
235
                        break;
236
                    case 'u':
237
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
238
                        $sql .= 'UNIQUE ('.join(',', $keys).')';
239
240
                        break;
241
                    default:
242
                        // Unrecognised constraint
243
                        $this->rollbackTransaction();
244
245
                        return null;
246
                }
247
            }
248
249
            // Output comma or not
250
            if ($i < $num) {
251
                $sql .= ",\n";
252
            } else {
253
                $sql .= "\n";
254
            }
255
256
            $cons->moveNext();
257
            ++$i;
258
        }
259
260
        $sql .= ')';
261
262
        // @@@@ DUMP CLUSTERING INFORMATION
263
264
        // Inherits
265
        /**
266
         * XXX: This is currently commented out as handling inheritance isn't this simple.
267
         * You also need to make sure you don't dump inherited columns and defaults, as well
268
         * as inherited NOT NULL and CHECK constraints.  So for the time being, we just do
269
         * not claim to support inheritance.
270
         * $parents = $this->getTableParents($table);
271
         * if ($parents->RecordCount() > 0) {
272
         * $sql .= " INHERITS (";
273
         * while (!$parents->EOF) {
274
         * $this->fieldClean($parents->fields['relname']);
275
         * // Qualify the parent table if it's in another schema
276
         * if ($parents->fields['schemaname'] != $this->_schema) {
277
         * $this->fieldClean($parents->fields['schemaname']);
278
         * $sql .= "\"{$parents->fields['schemaname']}\".";
279
         * }
280
         * $sql .= "\"{$parents->fields['relname']}\"";.
281
         *
282
         * $parents->moveNext();
283
         * if (!$parents->EOF) $sql .= ', ';
284
         * }
285
         * $sql .= ")";
286
         * }
287
         */
288
289
        // Handle WITHOUT OIDS
290
        if ($this->hasObjectID($table)) {
291
            $sql .= ' WITH OIDS';
292
        } else {
293
            $sql .= ' WITHOUT OIDS';
294
        }
295
296
        $sql .= ";\n";
297
298
        // Column storage and statistics
299
        $atts->moveFirst();
300
        $first = true;
301
        while (!$atts->EOF) {
302
            $this->fieldClean($atts->fields['attname']);
303
            // Statistics first
304
            if ($atts->fields['attstattarget'] >= 0) {
305
                if ($first) {
306
                    $sql .= "\n";
307
                    $first = false;
308
                }
309
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STATISTICS {$atts->fields['attstattarget']};\n";
310
            }
311
            // Then storage
312
            if ($atts->fields['attstorage'] != $atts->fields['typstorage']) {
313
                switch ($atts->fields['attstorage']) {
314
                    case 'p':
315
                        $storage = 'PLAIN';
316
317
                        break;
318
                    case 'e':
319
                        $storage = 'EXTERNAL';
320
321
                        break;
322
                    case 'm':
323
                        $storage = 'MAIN';
324
325
                        break;
326
                    case 'x':
327
                        $storage = 'EXTENDED';
328
329
                        break;
330
                    default:
331
                        // Unknown storage type
332
                        $this->rollbackTransaction();
333
334
                        return null;
335
                }
336
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STORAGE {$storage};\n";
337
            }
338
339
            $atts->moveNext();
340
        }
341
342
        // Comment
343
        if ($t->fields['relcomment'] !== null) {
344
            $this->clean($t->fields['relcomment']);
345
            $sql .= "\n-- Comment\n\n";
346
            $sql .= "COMMENT ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" IS '{$t->fields['relcomment']}';\n";
347
        }
348
349
        // Add comments on columns, if any
350
        if ($col_comments_sql != '') {
351
            $sql .= $col_comments_sql;
352
        }
353
354
        // Privileges
355
        $privs = $this->getPrivileges($table, 'table');
356
        if (!is_array($privs)) {
357
            $this->rollbackTransaction();
358
359
            return null;
360
        }
361
362
        if (sizeof($privs) > 0) {
363
            $sql .= "\n-- Privileges\n\n";
364
            /*
365
             * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
366
             * wire-in knowledge about the default public privileges for different
367
             * kinds of objects.
368
             */
369
            $sql .= "REVOKE ALL ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" FROM PUBLIC;\n";
370
            foreach ($privs as $v) {
371
                // Get non-GRANT OPTION privs
372
                $nongrant = array_diff($v[2], $v[4]);
373
374
                // Skip empty or owner ACEs
375
                if (sizeof($v[2]) == 0 || ($v[0] == 'user' && $v[1] == $t->fields['relowner'])) {
376
                    continue;
377
                }
378
379
                // Change user if necessary
380
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
381
                    $grantor = $v[3];
382
                    $this->clean($grantor);
383
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
384
                }
385
386
                // Output privileges with no GRANT OPTION
387
                $sql .= 'GRANT '.join(', ', $nongrant)." ON TABLE \"{$t->fields['relname']}\" TO ";
388
                switch ($v[0]) {
389
                    case 'public':
390
                        $sql .= "PUBLIC;\n";
391
392
                        break;
393
                    case 'user':
394
                        $this->fieldClean($v[1]);
395
                        $sql .= "\"{$v[1]}\";\n";
396
397
                        break;
398
                    case 'group':
399
                        $this->fieldClean($v[1]);
400
                        $sql .= "GROUP \"{$v[1]}\";\n";
401
402
                        break;
403
                    default:
404
                        // Unknown privilege type - fail
405
                        $this->rollbackTransaction();
406
407
                        return null;
408
                }
409
410
                // Reset user if necessary
411
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
412
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
413
                }
414
415
                // Output privileges with GRANT OPTION
416
417
                // Skip empty or owner ACEs
418
                if (!$this->hasGrantOption() || sizeof($v[4]) == 0) {
419
                    continue;
420
                }
421
422
                // Change user if necessary
423
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
424
                    $grantor = $v[3];
425
                    $this->clean($grantor);
426
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
427
                }
428
429
                $sql .= 'GRANT '.join(', ', $v[4])." ON \"{$t->fields['relname']}\" TO ";
430
                switch ($v[0]) {
431
                    case 'public':
432
                        $sql .= 'PUBLIC';
433
434
                        break;
435
                    case 'user':
436
                        $this->fieldClean($v[1]);
437
                        $sql .= "\"{$v[1]}\"";
438
439
                        break;
440
                    case 'group':
441
                        $this->fieldClean($v[1]);
442
                        $sql .= "GROUP \"{$v[1]}\"";
443
444
                        break;
445
                    default:
446
                        // Unknown privilege type - fail
447
                        return null;
448
                }
449
                $sql .= " WITH GRANT OPTION;\n";
450
451
                // Reset user if necessary
452
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
453
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
454
                }
455
            }
456
        }
457
458
        // Add a newline to separate data that follows (if any)
459
        $sql .= "\n";
460
461
        return $sql;
462
    }
463
464
    /**
465
     * Returns table information.
466
     *
467
     * @param string $table The name of the table
468
     *
469
     * @return \PHPPgAdmin\ADORecordSet A recordset
470
     */
471
    public function getTable($table)
472
    {
473
        $c_schema = $this->_schema;
474
        $this->clean($c_schema);
475
        $this->clean($table);
476
477
        $sql = "
478
            SELECT
479
              c.relname, n.nspname, u.usename AS relowner,
480
              pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
481
              (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
482
            FROM pg_catalog.pg_class c
483
                 LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
484
                 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
485
            WHERE c.relkind = 'r'
486
                  AND n.nspname = '{$c_schema}'
487
                  AND n.oid = c.relnamespace
488
                  AND c.relname = '{$table}'";
489
490
        return $this->selectSet($sql);
491
    }
492
493
    /**
494
     * Retrieve the attribute definition of a table.
495
     *
496
     * @param string $table The name of the table
497
     * @param string $field (optional) The name of a field to return
498
     *
499
     * @return \PHPPgAdmin\ADORecordSet All attributes in order
500
     */
501
    public function getTableAttributes($table, $field = '')
502
    {
503
        $c_schema = $this->_schema;
504
        $this->clean($c_schema);
505
        $this->clean($table);
506
        $this->clean($field);
507
508
        if ($field == '') {
509
            // This query is made much more complex by the addition of the 'attisserial' field.
510
            // The subquery to get that field checks to see if there is an internally dependent
511
            // sequence on the field.
512
            $sql = "
513
                SELECT
514
                    a.attname, a.attnum,
515
                    pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
516
                    a.atttypmod,
517
                    a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc,
518
                    a.attstattarget, a.attstorage, t.typstorage,
519
                    (
520
                        SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
521
                        WHERE pd.objid=pc.oid
522
                        AND pd.classid=pc.tableoid
523
                        AND pd.refclassid=pc.tableoid
524
                        AND pd.refobjid=a.attrelid
525
                        AND pd.refobjsubid=a.attnum
526
                        AND pd.deptype='i'
527
                        AND pc.relkind='S'
528
                    ) IS NOT NULL AS attisserial,
529
                    pg_catalog.col_description(a.attrelid, a.attnum) AS comment
530
                FROM
531
                    pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
532
                    ON a.attrelid=adef.adrelid
533
                    AND a.attnum=adef.adnum
534
                    LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
535
                WHERE
536
                    a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
537
                        AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
538
                        nspname = '{$c_schema}'))
539
                    AND a.attnum > 0 AND NOT a.attisdropped
540
                ORDER BY a.attnum";
541
        } else {
542
            $sql = "
543
                SELECT
544
                    a.attname, a.attnum,
545
                    pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
546
                    pg_catalog.format_type(a.atttypid, NULL) as base_type,
547
                    a.atttypmod,
548
                    a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc,
549
                    a.attstattarget, a.attstorage, t.typstorage,
550
                    pg_catalog.col_description(a.attrelid, a.attnum) AS comment
551
                FROM
552
                    pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
553
                    ON a.attrelid=adef.adrelid
554
                    AND a.attnum=adef.adnum
555
                    LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
556
                WHERE
557
                    a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
558
                        AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
559
                        nspname = '{$c_schema}'))
560
                    AND a.attname = '{$field}'";
561
        }
562
563
        return $this->selectSet($sql);
564
    }
565
566
    /**
567
     * Returns a list of all constraints on a table.
568
     *
569
     * @param string $table The table to find rules for
570
     *
571
     * @return \PHPPgAdmin\ADORecordSet A recordset
572
     */
573
    public function getConstraints($table)
574
    {
575
        $c_schema = $this->_schema;
576
        $this->clean($c_schema);
577
        $this->clean($table);
578
579
        // This SQL is greatly complicated by the need to retrieve
580
        // index clustering information for primary and unique constraints
581
        $sql = "SELECT
582
                pc.conname,
583
                pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc,
584
                pc.contype,
585
                CASE WHEN pc.contype='u' OR pc.contype='p' THEN (
586
                    SELECT
587
                        indisclustered
588
                    FROM
589
                        pg_catalog.pg_depend pd,
590
                        pg_catalog.pg_class pl,
591
                        pg_catalog.pg_index pi
592
                    WHERE
593
                        pd.refclassid=pc.tableoid
594
                        AND pd.refobjid=pc.oid
595
                        AND pd.objid=pl.oid
596
                        AND pl.oid=pi.indexrelid
597
                ) ELSE
598
                    NULL
599
                END AS indisclustered
600
            FROM
601
                pg_catalog.pg_constraint pc
602
            WHERE
603
                pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
604
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
605
                    WHERE nspname='{$c_schema}'))
606
            ORDER BY
607
                1
608
        ";
609
610
        return $this->selectSet($sql);
611
    }
612
613
    /**
614
     * Checks to see whether or not a table has a unique id column.
615
     *
616
     * @param string $table The table name
617
     *
618
     * @return true if it has a unique id, false otherwise
619
     */
620
    public function hasObjectID($table)
621
    {
622
        $c_schema = $this->_schema;
623
        $this->clean($c_schema);
624
        $this->clean($table);
625
626
        $sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}'
627
            AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')";
628
629
        $rs = $this->selectSet($sql);
630
        if ($rs->RecordCount() != 1) {
631
            return null;
632
        }
633
634
        $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']);
635
636
        return $rs->fields['relhasoids'];
637
    }
638
639
    /**
640
     * Returns extra table definition information that is most usefully
641
     * dumped after the table contents for speed and efficiency reasons.
642
     *
643
     * @param string $table The table to define
644
     *
645
     * @return string A string containing the formatted SQL code
646
     */
647
    public function getTableDefSuffix($table)
648
    {
649
        $sql = '';
650
651
        // Indexes
652
        $indexes = $this->getIndexes($table);
653
        if (!is_object($indexes)) {
654
            $this->rollbackTransaction();
655
656
            return null;
657
        }
658
659
        if ($indexes->RecordCount() > 0) {
660
            $sql .= "\n-- Indexes\n\n";
661
            while (!$indexes->EOF) {
662
                $sql .= $indexes->fields['inddef'].";\n";
663
664
                $indexes->moveNext();
665
            }
666
        }
667
668
        // Triggers
669
        $triggers = $this->getTriggers($table);
670
        if (!is_object($triggers)) {
671
            $this->rollbackTransaction();
672
673
            return null;
674
        }
675
676
        if ($triggers->RecordCount() > 0) {
677
            $sql .= "\n-- Triggers\n\n";
678
            while (!$triggers->EOF) {
679
                $sql .= $triggers->fields['tgdef'];
680
                $sql .= ";\n";
681
682
                $triggers->moveNext();
683
            }
684
        }
685
686
        // Rules
687
        $rules = $this->getRules($table);
688
        if (!is_object($rules)) {
689
            $this->rollbackTransaction();
690
691
            return null;
692
        }
693
694
        if ($rules->RecordCount() > 0) {
695
            $sql .= "\n-- Rules\n\n";
696
            while (!$rules->EOF) {
697
                $sql .= $rules->fields['definition']."\n";
698
699
                $rules->moveNext();
700
            }
701
        }
702
703
        return $sql;
704
    }
705
706
    /**
707
     * Grabs a list of indexes for a table.
708
     *
709
     * @param string $table  The name of a table whose indexes to retrieve
710
     * @param bool   $unique Only get unique/pk indexes
711
     *
712
     * @return \PHPPgAdmin\ADORecordSet A recordset
713
     */
714
    public function getIndexes($table = '', $unique = false)
715
    {
716
        $this->clean($table);
717
718
        $sql = "
719
            SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered,
720
                pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef
721
            FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
722
            WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid)
723
                AND c.oid = i.indrelid AND i.indexrelid = c2.oid
724
        ";
725
        if ($unique) {
726
            $sql .= ' AND i.indisunique ';
727
        }
728
729
        $sql .= ' ORDER BY c2.relname';
730
731
        return $this->selectSet($sql);
732
    }
733
734
    /**
735
     * Grabs a list of triggers on a table.
736
     *
737
     * @param string $table The name of a table whose triggers to retrieve
738
     *
739
     * @return \PHPPgAdmin\ADORecordSet A recordset
740
     */
741
    public function getTriggers($table = '')
742
    {
743
        $c_schema = $this->_schema;
744
        $this->clean($c_schema);
745
        $this->clean($table);
746
747
        $sql = "SELECT
748
                t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef,
749
                CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid,
750
                p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
751
                ns.nspname AS pronamespace
752
            FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
753
            WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
754
                AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}'))
755
                AND ( tgconstraint = 0 OR NOT EXISTS
756
                        (SELECT 1 FROM pg_catalog.pg_depend d    JOIN pg_catalog.pg_constraint c
757
                            ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
758
                        WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
759
                AND p.oid=t.tgfoid
760
                AND p.pronamespace = ns.oid";
761
762
        return $this->selectSet($sql);
763
    }
764
765
    /**
766
     * Returns a list of all rules on a table OR view.
767
     *
768
     * @param string $table The table to find rules for
769
     *
770
     * @return \PHPPgAdmin\ADORecordSet A recordset
771
     */
772
    public function getRules($table)
773
    {
774
        $c_schema = $this->_schema;
775
        $this->clean($c_schema);
776
        $this->clean($table);
777
778
        $sql = "
779
            SELECT *
780
            FROM pg_catalog.pg_rules
781
            WHERE
782
                schemaname='{$c_schema}' AND tablename='{$table}'
783
            ORDER BY rulename
784
        ";
785
786
        return $this->selectSet($sql);
787
    }
788
789
    /**
790
     * Creates a new table in the database.
791
     *
792
     * @param string $name        The name of the table
793
     * @param int    $fields      The number of fields
794
     * @param array  $field       An array of field names
795
     * @param array  $type        An array of field types
796
     * @param array  $array       An array of '' or '[]' for each type if it's an array or not
797
     * @param array  $length      An array of field lengths
798
     * @param array  $notnull     An array of not null
799
     * @param array  $default     An array of default values
800
     * @param bool   $withoutoids True if WITHOUT OIDS, false otherwise
801
     * @param array  $colcomment  An array of comments
802
     * @param string $tblcomment  the comment for the table
803
     * @param string $tablespace  The tablespace name ('' means none/default)
804
     * @param array  $uniquekey   An Array indicating the fields that are unique (those indexes that are set)
805
     * @param array  $primarykey  An Array indicating the field used for the primarykey (those indexes that are set)
806
     *
807
     * @return bool|int 0 success
808
     */
809
    public function createTable(
810
        $name,
811
        $fields,
812
        $field,
813
        $type,
814
        $array,
815
        $length,
816
        $notnull,
817
        $default,
818
        $withoutoids,
819
        $colcomment,
820
        $tblcomment,
821
        $tablespace,
822
        $uniquekey,
823
        $primarykey
824
    ) {
825
        $f_schema = $this->_schema;
826
        $this->fieldClean($f_schema);
827
        $this->fieldClean($name);
828
829
        $status = $this->beginTransaction();
830
        if ($status != 0) {
831
            return -1;
832
        }
833
834
        $found       = false;
835
        $first       = true;
836
        $comment_sql = ''; //Accumulate comments for the columns
837
        $sql         = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (";
838
        for ($i = 0; $i < $fields; ++$i) {
839
            $this->fieldClean($field[$i]);
840
            $this->clean($type[$i]);
841
            $this->clean($length[$i]);
842
            $this->clean($colcomment[$i]);
843
844
            // Skip blank columns - for user convenience
845
            if ($field[$i] == '' || $type[$i] == '') {
846
                continue;
847
            }
848
849
            // If not the first column, add a comma
850
            if (!$first) {
851
                $sql .= ', ';
852
            } else {
853
                $first = false;
854
            }
855
856
            switch ($type[$i]) {
857
                // Have to account for weird placing of length for with/without
858
                // time zone types
859
                case 'timestamp with time zone':
860
                case 'timestamp without time zone':
861
                    $qual = substr($type[$i], 9);
862
                    $sql .= "\"{$field[$i]}\" timestamp";
863
                    if ($length[$i] != '') {
864
                        $sql .= "({$length[$i]})";
865
                    }
866
867
                    $sql .= $qual;
868
869
                    break;
870
                case 'time with time zone':
871
                case 'time without time zone':
872
                    $qual = substr($type[$i], 4);
873
                    $sql .= "\"{$field[$i]}\" time";
874
                    if ($length[$i] != '') {
875
                        $sql .= "({$length[$i]})";
876
                    }
877
878
                    $sql .= $qual;
879
880
                    break;
881
                default:
882
                    $sql .= "\"{$field[$i]}\" {$type[$i]}";
883
                    if ($length[$i] != '') {
884
                        $sql .= "({$length[$i]})";
885
                    }
886
            }
887
            // Add array qualifier if necessary
888
            if ($array[$i] == '[]') {
889
                $sql .= '[]';
890
            }
891
892
            // Add other qualifiers
893
            if (!isset($primarykey[$i])) {
894
                if (isset($uniquekey[$i])) {
895
                    $sql .= ' UNIQUE';
896
                }
897
898
                if (isset($notnull[$i])) {
899
                    $sql .= ' NOT NULL';
900
                }
901
            }
902
            if ($default[$i] != '') {
903
                $sql .= " DEFAULT {$default[$i]}";
904
            }
905
906
            if ($colcomment[$i] != '') {
907
                $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
908
            }
909
910
            $found = true;
911
        }
912
913
        if (!$found) {
914
            return -1;
915
        }
916
917
        // PRIMARY KEY
918
        $primarykeycolumns = [];
919
        for ($i = 0; $i < $fields; ++$i) {
920
            if (isset($primarykey[$i])) {
921
                $primarykeycolumns[] = "\"{$field[$i]}\"";
922
            }
923
        }
924
        if (count($primarykeycolumns) > 0) {
925
            $sql .= ', PRIMARY KEY ('.implode(', ', $primarykeycolumns).')';
926
        }
927
928
        $sql .= ')';
929
930
        // WITHOUT OIDS
931
        if ($withoutoids) {
932
            $sql .= ' WITHOUT OIDS';
933
        } else {
934
            $sql .= ' WITH OIDS';
935
        }
936
937
        // Tablespace
938
        if ($this->hasTablespaces() && $tablespace != '') {
939
            $this->fieldClean($tablespace);
940
            $sql .= " TABLESPACE \"{$tablespace}\"";
941
        }
942
943
        $status = $this->execute($sql);
944
        if ($status) {
945
            $this->rollbackTransaction();
946
947
            return -1;
948
        }
949
950
        if ($tblcomment != '') {
951
            $status = $this->setComment('TABLE', '', $name, $tblcomment, true);
952
            if ($status) {
953
                $this->rollbackTransaction();
954
955
                return -1;
956
            }
957
        }
958
959
        if ($comment_sql != '') {
960
            $status = $this->execute($comment_sql);
961
            if ($status) {
962
                $this->rollbackTransaction();
963
964
                return -1;
965
            }
966
        }
967
968
        return $this->endTransaction();
969
    }
970
971
    /**
972
     * Creates a new table in the database copying attribs and other properties from another table.
973
     *
974
     * @param string $name        The name of the table
975
     * @param array  $like        an array giving the schema ans the name of the table from which attribs are copying
976
     *                            from: array(
977
     *                            'table' => table name,
978
     *                            'schema' => the schema name,
979
     *                            )
980
     * @param bool   $defaults    if true, copy the defaults values as well
981
     * @param bool   $constraints if true, copy the constraints as well (CHECK on table & attr)
982
     * @param bool   $idx
983
     * @param string $tablespace  The tablespace name ('' means none/default)
984
     *
985
     * @return bool|int
986
     */
987
    public function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '')
988
    {
989
        $f_schema = $this->_schema;
990
        $this->fieldClean($f_schema);
991
        $this->fieldClean($name);
992
        $this->fieldClean($like['schema']);
993
        $this->fieldClean($like['table']);
994
        $like = "\"{$like['schema']}\".\"{$like['table']}\"";
995
996
        $status = $this->beginTransaction();
997
        if ($status != 0) {
998
            return -1;
999
        }
1000
1001
        $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (LIKE {$like}";
1002
1003
        if ($defaults) {
1004
            $sql .= ' INCLUDING DEFAULTS';
1005
        }
1006
1007
        if ($this->hasCreateTableLikeWithConstraints() && $constraints) {
1008
            $sql .= ' INCLUDING CONSTRAINTS';
1009
        }
1010
1011
        if ($this->hasCreateTableLikeWithIndexes() && $idx) {
1012
            $sql .= ' INCLUDING INDEXES';
1013
        }
1014
1015
        $sql .= ')';
1016
1017
        if ($this->hasTablespaces() && $tablespace != '') {
1018
            $this->fieldClean($tablespace);
1019
            $sql .= " TABLESPACE \"{$tablespace}\"";
1020
        }
1021
1022
        $status = $this->execute($sql);
1023
        if ($status) {
1024
            $this->rollbackTransaction();
1025
1026
            return -1;
1027
        }
1028
1029
        return $this->endTransaction();
1030
    }
1031
1032
    /**
1033
     * Alter table properties.
1034
     *
1035
     * @param string $table      The name of the table
1036
     * @param string $name       The new name for the table
1037
     * @param string $owner      The new owner for the table
1038
     * @param string $schema     The new schema for the table
1039
     * @param string $comment    The comment on the table
1040
     * @param string $tablespace The new tablespace for the table ('' means leave as is)
1041
     *
1042
     * @return bool|int 0 success
1043
     */
1044
    public function alterTable($table, $name, $owner, $schema, $comment, $tablespace)
1045
    {
1046
        $data = $this->getTable($table);
1047
1048
        if ($data->RecordCount() != 1) {
1049
            return -2;
1050
        }
1051
1052
        $status = $this->beginTransaction();
1053
        if ($status != 0) {
1054
            $this->rollbackTransaction();
1055
1056
            return -1;
1057
        }
1058
1059
        $status = $this->_alterTable($data, $name, $owner, $schema, $comment, $tablespace);
1060
1061
        if ($status != 0) {
1062
            $this->rollbackTransaction();
1063
1064
            return $status;
1065
        }
1066
1067
        return $this->endTransaction();
1068
    }
1069
1070
    /**
1071
     * Protected method which alter a table
1072
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION.
1073
     *
1074
     * @param \PHPPgAdmin\ADORecordSet $tblrs      The table recordSet returned by getTable()
1075
     * @param string                   $name       The new name for the table
1076
     * @param string                   $owner      The new owner for the table
1077
     * @param string                   $schema     The new schema for the table
1078
     * @param string                   $comment    The comment on the table
1079
     * @param string                   $tablespace The new tablespace for the table ('' means leave as is)
1080
     *
1081
     * @return int 0 success
1082
     */
1083
    protected function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace)
1084
    {
1085
        $this->fieldArrayClean($tblrs->fields);
1086
1087
        // Comment
1088
        $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
1089
        if ($status != 0) {
1090
            return -4;
1091
        }
1092
1093
        // Owner
1094
        $this->fieldClean($owner);
1095
        $status = $this->alterTableOwner($tblrs, $owner);
1096
        if ($status != 0) {
1097
            return -5;
1098
        }
1099
1100
        // Tablespace
1101
        $this->fieldClean($tablespace);
1102
        $status = $this->alterTableTablespace($tblrs, $tablespace);
1103
        if ($status != 0) {
1104
            return -6;
1105
        }
1106
1107
        // Rename
1108
        $this->fieldClean($name);
1109
        $status = $this->alterTableName($tblrs, $name);
1110
        if ($status != 0) {
1111
            return -3;
1112
        }
1113
1114
        // Schema
1115
        $this->fieldClean($schema);
1116
        $status = $this->alterTableSchema($tblrs, $schema);
1117
        if ($status != 0) {
1118
            return -7;
1119
        }
1120
1121
        return 0;
1122
    }
1123
1124
    /**
1125
     * Alter a table's owner
1126
     * /!\ this function is called from _alterTable which take care of escaping fields.
1127
     *
1128
     * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable()
1129
     * @param null|string              $owner
1130
     *
1131
     * @return int 0 if operation was successful
1132
     */
1133
    public function alterTableOwner($tblrs, $owner = null)
1134
    {
1135
        /* vars cleaned in _alterTable */
1136
        if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
1137
            $f_schema = $this->_schema;
1138
            $this->fieldClean($f_schema);
1139
            // If owner has been changed, then do the alteration.  We are
1140
            // careful to avoid this generally as changing owner is a
1141
            // superuser only function.
1142
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" OWNER TO \"{$owner}\"";
1143
1144
            return $this->execute($sql);
1145
        }
1146
1147
        return 0;
1148
    }
1149
1150
    /**
1151
     * Alter a table's tablespace
1152
     * /!\ this function is called from _alterTable which take care of escaping fields.
1153
     *
1154
     * @param \PHPPgAdmin\ADORecordSet $tblrs      The table RecordSet returned by getTable()
1155
     * @param null|string              $tablespace
1156
     *
1157
     * @return int 0 if operation was successful
1158
     */
1159
    public function alterTableTablespace($tblrs, $tablespace = null)
1160
    {
1161
        /* vars cleaned in _alterTable */
1162
        if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) {
1163
            $f_schema = $this->_schema;
1164
            $this->fieldClean($f_schema);
1165
1166
            // If tablespace has been changed, then do the alteration.  We
1167
            // don't want to do this unnecessarily.
1168
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET TABLESPACE \"{$tablespace}\"";
1169
1170
            return $this->execute($sql);
1171
        }
1172
1173
        return 0;
1174
    }
1175
1176
    /**
1177
     * Alter a table's name
1178
     * /!\ this function is called from _alterTable which take care of escaping fields.
1179
     *
1180
     * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable()
1181
     * @param string                   $name  The new table's name
1182
     *
1183
     * @return int 0 if operation was successful
1184
     */
1185
    public function alterTableName($tblrs, $name = null)
1186
    {
1187
        /* vars cleaned in _alterTable */
1188
        // Rename (only if name has changed)
1189
        if (!empty($name) && ($name != $tblrs->fields['relname'])) {
1190
            $f_schema = $this->_schema;
1191
            $this->fieldClean($f_schema);
1192
1193
            $sql    = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" RENAME TO \"{$name}\"";
1194
            $status = $this->execute($sql);
1195
            if ($status == 0) {
1196
                $tblrs->fields['relname'] = $name;
1197
            } else {
1198
                return $status;
1199
            }
1200
        }
1201
1202
        return 0;
1203
    }
1204
1205
    // Row functions
1206
1207
    /**
1208
     * Alter a table's schema
1209
     * /!\ this function is called from _alterTable which take care of escaping fields.
1210
     *
1211
     * @param \PHPPgAdmin\ADORecordSet $tblrs  The table RecordSet returned by getTable()
1212
     * @param null|string              $schema
1213
     *
1214
     * @return int 0 if operation was successful
1215
     */
1216
    public function alterTableSchema($tblrs, $schema = null)
1217
    {
1218
        /* vars cleaned in _alterTable */
1219
        if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) {
1220
            $f_schema = $this->_schema;
1221
            $this->fieldClean($f_schema);
1222
            // If tablespace has been changed, then do the alteration.  We
1223
            // don't want to do this unnecessarily.
1224
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET SCHEMA \"{$schema}\"";
1225
1226
            return $this->execute($sql);
1227
        }
1228
1229
        return 0;
1230
    }
1231
1232
    /**
1233
     * Empties a table in the database.
1234
     *
1235
     * @param string $table   The table to be emptied
1236
     * @param bool   $cascade True to cascade truncate, false to restrict
1237
     *
1238
     * @return array<integer,mixed|string> 0 if operation was successful
1239
     */
1240
    public function emptyTable($table, $cascade)
1241
    {
1242
        $f_schema = $this->_schema;
1243
        $this->fieldClean($f_schema);
1244
        $this->fieldClean($table);
1245
1246
        $sql = "TRUNCATE TABLE \"{$f_schema}\".\"{$table}\" ";
1247
        if ($cascade) {
1248
            $sql = $sql.' CASCADE';
1249
        }
1250
1251
        $status = $this->execute($sql);
1252
1253
        return [$status, $sql];
1254
    }
1255
1256
    /**
1257
     * Removes a table from the database.
1258
     *
1259
     * @param string $table   The table to drop
1260
     * @param bool   $cascade True to cascade drop, false to restrict
1261
     *
1262
     * @return int 0 if operation was successful
1263
     */
1264
    public function dropTable($table, $cascade)
1265
    {
1266
        $f_schema = $this->_schema;
1267
        $this->fieldClean($f_schema);
1268
        $this->fieldClean($table);
1269
1270
        $sql = "DROP TABLE \"{$f_schema}\".\"{$table}\"";
1271
        if ($cascade) {
1272
            $sql .= ' CASCADE';
1273
        }
1274
1275
        return $this->execute($sql);
1276
    }
1277
1278
    /**
1279
     * Sets up the data object for a dump.  eg. Starts the appropriate
1280
     * transaction, sets variables, etc.
1281
     *
1282
     * @return int 0 success
1283
     */
1284
    public function beginDump()
1285
    {
1286
        // Begin serializable transaction (to dump consistent data)
1287
        $status = $this->beginTransaction();
1288
        if ($status != 0) {
1289
            return -1;
1290
        }
1291
1292
        // Set serializable
1293
        $sql    = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE';
1294
        $status = $this->execute($sql);
1295
        if ($status != 0) {
1296
            $this->rollbackTransaction();
1297
1298
            return -1;
1299
        }
1300
1301
        // Set datestyle to ISO
1302
        $sql    = 'SET DATESTYLE = ISO';
1303
        $status = $this->execute($sql);
1304
        if ($status != 0) {
1305
            $this->rollbackTransaction();
1306
1307
            return -1;
1308
        }
1309
1310
        // Set extra_float_digits to 2
1311
        $sql    = 'SET extra_float_digits TO 2';
1312
        $status = $this->execute($sql);
1313
        if ($status != 0) {
1314
            $this->rollbackTransaction();
1315
1316
            return -1;
1317
        }
1318
1319
        return 0;
1320
    }
1321
1322
    /**
1323
     * Ends the data object for a dump.
1324
     *
1325
     * @return bool 0 success
1326
     */
1327
    public function endDump()
1328
    {
1329
        return $this->endTransaction();
1330
    }
1331
1332
    /**
1333
     * Returns a recordset of all columns in a relation.  Used for data export.
1334
     *
1335
     * @@ Note: Really needs to use a cursor
1336
     *
1337
     * @param string $relation The name of a relation
1338
     * @param bool   $oids     true to dump also the oids
1339
     *
1340
     * @return \PHPPgAdmin\ADORecordSet A recordset on success
1341
     */
1342
    public function dumpRelation($relation, $oids)
1343
    {
1344
        $this->fieldClean($relation);
1345
1346
        // Actually retrieve the rows
1347
        if ($oids) {
1348
            $oid_str = $this->id.', ';
1349
        } else {
1350
            $oid_str = '';
1351
        }
1352
1353
        return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
1354
    }
1355
1356
    /**
1357
     * Returns all available autovacuum per table information.
1358
     *
1359
     * @param string $table if given, return autovacuum info for the given table or return all informations for all table
1360
     *
1361
     * @return \PHPPgAdmin\ArrayRecordSet A recordset
1362
     */
1363
    public function getTableAutovacuum($table = '')
1364
    {
1365
        $sql = '';
1366
1367
        if ($table !== '') {
1368
            $this->clean($table);
1369
            $c_schema = $this->_schema;
1370
            $this->clean($c_schema);
1371
1372
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
1373
                FROM pg_class c
1374
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
1375
                WHERE c.relkind = 'r'::\"char\"
1376
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
1377
                    AND c.reloptions IS NOT NULL
1378
                    AND c.relname = '{$table}' AND n.nspname = '{$c_schema}'
1379
                ORDER BY nspname, relname";
1380
        } else {
1381
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
1382
                FROM pg_class c
1383
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
1384
                WHERE c.relkind = 'r'::\"char\"
1385
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
1386
                    AND c.reloptions IS NOT NULL
1387
                ORDER BY nspname, relname";
1388
        }
1389
1390
        /* tmp var to parse the results */
1391
        $_autovacs = $this->selectSet($sql);
1392
1393
        /* result aray to return as RS */
1394
        $autovacs = [];
1395
        while (!$_autovacs->EOF) {
1396
            $_ = [
1397
                'nspname' => $_autovacs->fields['nspname'],
1398
                'relname' => $_autovacs->fields['relname'],
1399
            ];
1400
1401
            foreach (explode(',', $_autovacs->fields['reloptions']) as $var) {
1402
                list($o, $v) = explode('=', $var);
1403
                $_[$o]       = $v;
1404
            }
1405
1406
            $autovacs[] = $_;
1407
1408
            $_autovacs->moveNext();
1409
        }
1410
1411
        return new \PHPPgAdmin\ArrayRecordSet($autovacs);
1412
    }
1413
1414
    /**
1415
     * Returns the SQL for changing the current user.
1416
     *
1417
     * @param string $user The user to change to
1418
     *
1419
     * @return string The SQL
1420
     */
1421
    public function getChangeUserSQL($user)
1422
    {
1423
        $this->clean($user);
1424
1425
        return "SET SESSION AUTHORIZATION '{$user}';";
1426
    }
1427
1428
    /**
1429
     * Returns all available autovacuum per table information.
1430
     *
1431
     * @param string $table          table name
1432
     * @param bool   $vacenabled     true if vacuum is enabled
1433
     * @param int    $vacthreshold   vacuum threshold
1434
     * @param int    $vacscalefactor vacuum scalefactor
1435
     * @param int    $anathresold    analyze threshold
1436
     * @param int    $anascalefactor analyze scale factor
1437
     * @param int    $vaccostdelay   vacuum cost delay
1438
     * @param int    $vaccostlimit   vacuum cost limit
1439
     *
1440
     * @return bool 0 if successful
1441
     */
1442
    public function saveAutovacuum(
1443
        $table,
1444
        $vacenabled,
1445
        $vacthreshold,
1446
        $vacscalefactor,
1447
        $anathresold,
1448
        $anascalefactor,
1449
        $vaccostdelay,
1450
        $vaccostlimit
1451
    ) {
1452
        $f_schema = $this->_schema;
1453
        $this->fieldClean($f_schema);
1454
        $this->fieldClean($table);
1455
1456
        $params = [];
1457
1458
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" SET (";
1459
1460
        if (!empty($vacenabled)) {
1461
            $this->clean($vacenabled);
1462
            $params[] = "autovacuum_enabled='{$vacenabled}'";
1463
        }
1464
        if (!empty($vacthreshold)) {
1465
            $this->clean($vacthreshold);
1466
            $params[] = "autovacuum_vacuum_threshold='{$vacthreshold}'";
1467
        }
1468
        if (!empty($vacscalefactor)) {
1469
            $this->clean($vacscalefactor);
1470
            $params[] = "autovacuum_vacuum_scale_factor='{$vacscalefactor}'";
1471
        }
1472
        if (!empty($anathresold)) {
1473
            $this->clean($anathresold);
1474
            $params[] = "autovacuum_analyze_threshold='{$anathresold}'";
1475
        }
1476
        if (!empty($anascalefactor)) {
1477
            $this->clean($anascalefactor);
1478
            $params[] = "autovacuum_analyze_scale_factor='{$anascalefactor}'";
1479
        }
1480
        if (!empty($vaccostdelay)) {
1481
            $this->clean($vaccostdelay);
1482
            $params[] = "autovacuum_vacuum_cost_delay='{$vaccostdelay}'";
1483
        }
1484
        if (!empty($vaccostlimit)) {
1485
            $this->clean($vaccostlimit);
1486
            $params[] = "autovacuum_vacuum_cost_limit='{$vaccostlimit}'";
1487
        }
1488
1489
        $sql = $sql.implode(',', $params).');';
1490
1491
        return $this->execute($sql);
1492
    }
1493
1494
    // Type conversion routines
1495
1496
    /**
1497
     * Drops autovacuum config for a table.
1498
     *
1499
     * @param string $table The table
1500
     *
1501
     * @return bool 0 if successful
1502
     */
1503
    public function dropAutovacuum($table)
1504
    {
1505
        $f_schema = $this->_schema;
1506
        $this->fieldClean($f_schema);
1507
        $this->fieldClean($table);
1508
1509
        return $this->execute(
1510
            "
1511
            ALTER TABLE \"{$f_schema}\".\"{$table}\" RESET (autovacuum_enabled, autovacuum_vacuum_threshold,
1512
                autovacuum_vacuum_scale_factor, autovacuum_analyze_threshold, autovacuum_analyze_scale_factor,
1513
                autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_limit
1514
            );"
1515
        );
1516
    }
1517
1518
    abstract public function fieldClean(&$str);
1519
1520
    abstract public function beginTransaction();
1521
1522
    abstract public function rollbackTransaction();
1523
1524
    abstract public function endTransaction();
1525
1526
    abstract public function execute($sql);
1527
1528
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
1529
1530
    abstract public function selectSet($sql);
1531
1532
    abstract public function clean(&$str);
1533
1534
    abstract public function phpBool($parameter);
1535
1536
    abstract public function hasCreateTableLikeWithConstraints();
1537
1538
    abstract public function hasCreateTableLikeWithIndexes();
1539
1540
    abstract public function hasTablespaces();
1541
1542
    abstract public function delete($table, $conditions, $schema = '');
1543
1544
    abstract public function fieldArrayClean(&$arr);
1545
1546
    abstract public function hasCreateFieldWithConstraints();
1547
1548
    abstract public function getAttributeNames($table, $atts);
1549
}
1550