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

TableTrait   F

Complexity

Total Complexity 167

Size/Duplication

Total Lines 1537
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 167
dl 0
loc 1537
rs 0.6314
c 0
b 0
f 0

30 Methods

Rating   Name   Duplication   Size   Complexity  
A dropAutovacuum() 0 9 1
A alterTableOwner() 0 15 3
D createTableLike() 0 43 10
F getTableDefPrefix() 0 331 53
B alterTable() 0 24 4
A getIndexes() 0 18 2
B getTableAutovacuum() 0 49 4
A getTables() 0 20 1
C getTableDefSuffix() 0 57 10
A getChangeUserSQL() 0 5 1
A emptyTable() 0 14 2
B _alterTable() 0 39 6
A alterTableName() 0 18 4
A dropTable() 0 12 2
A alterTableTablespace() 0 15 3
A dumpRelation() 0 12 2
A hasObjectID() 0 17 2
A getAllTables() 0 11 1
A getTriggers() 0 22 1
B getConstraints() 0 38 1
A getTableAttributes() 0 63 2
A getRules() 0 15 1
A alterTableSchema() 0 14 3
A getTableParents() 0 21 1
A getTableChildren() 0 19 1
A endDump() 0 3 1
B beginDump() 0 36 5
A getTable() 0 20 1
C saveAutovacuum() 0 50 8
F createTable() 0 160 31

How to fix   Complexity   

Complex Class

Complex classes like TableTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TableTrait, and based on these observations, apply Extract Interface, too.

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