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

TableTrait::createTable()   F

Complexity

Conditions 31
Paths > 20000

Size

Total Lines 160
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 160
rs 2
c 0
b 0
f 0
cc 31
eloc 85
nc 154787
nop 14

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