Passed
Pull Request — develop (#212)
by Felipe
05:01
created

TableTrait::saveAutovacuum()   B

Complexity

Conditions 8
Paths 128

Size

Total Lines 50
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.49
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
54
        if (isset($conf['display_sizes']) && $conf['display_sizes'] === true) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $conf seems to never exist and therefore isset should always be false.
Loading history...
55
            $sql .= ' pg_size_pretty(pg_total_relation_size(c.oid)) as table_size ';
56
        } else {
57
            $sql .= "   'N/A' as table_size ";
58
        }
59
60
        $sql .= " FROM pg_catalog.pg_class c
61
                LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
62
                LEFT JOIN  pg_catalog.pg_tablespace pt ON  pt.oid=c.reltablespace
63
                WHERE c.relkind = 'r'
64
                AND nspname='{$c_schema}'
65
                ORDER BY c.relname";
66
67
        return $this->selectSet($sql);
68
    }
69
70
    /**
71
     * Finds the names and schemas of parent tables (in order).
72
     *
73
     * @param string $table The table to find the parents for
74
     *
75
     * @return \PHPPgAdmin\ADORecordSet A recordset
76
     */
77
    public function getTableParents($table)
78
    {
79
        $c_schema = $this->_schema;
80
        $this->clean($c_schema);
81
        $this->clean($table);
82
83
        $sql = "
84
            SELECT
85
                pn.nspname, relname
86
            FROM
87
                pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
88
            WHERE
89
                pc.oid=pi.inhparent
90
                AND pc.relnamespace=pn.oid
91
                AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
92
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
93
            ORDER BY
94
                pi.inhseqno
95
        ";
96
97
        return $this->selectSet($sql);
98
    }
99
100
    /**
101
     * Finds the names and schemas of child tables.
102
     *
103
     * @param string $table The table to find the children for
104
     *
105
     * @return \PHPPgAdmin\ADORecordSet A recordset
106
     */
107
    public function getTableChildren($table)
108
    {
109
        $c_schema = $this->_schema;
110
        $this->clean($c_schema);
111
        $this->clean($table);
112
113
        $sql = "
114
            SELECT
115
                pn.nspname, relname
116
            FROM
117
                pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
118
            WHERE
119
                pc.oid=pi.inhrelid
120
                AND pc.relnamespace=pn.oid
121
                AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
122
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
123
        ";
124
125
        return $this->selectSet($sql);
126
    }
127
128
    /**
129
     * Returns the SQL definition for the table.
130
     * MUST be run within a transaction.
131
     *
132
     * @param string $table       The table to define
133
     * @param string $cleanprefix set to '-- ' to avoid issuing DROP statement
134
     *
135
     * @return string A string containing the formatted SQL code
136
     */
137
    public function getTableDefPrefix($table, $cleanprefix = '')
138
    {
139
        // Fetch table
140
        $t = $this->getTable($table);
141
        if (!is_object($t) || $t->RecordCount() != 1) {
0 ignored issues
show
introduced by
The condition $t->RecordCount() != 1 is always true.
Loading history...
142
            $this->rollbackTransaction();
143
144
            return null;
145
        }
146
        $this->fieldClean($t->fields['relname']);
147
        $this->fieldClean($t->fields['nspname']);
148
149
        // Fetch attributes
150
        $atts = $this->getTableAttributes($table);
151
        if (!is_object($atts)) {
152
            $this->rollbackTransaction();
153
154
            return null;
155
        }
156
157
        // Fetch constraints
158
        $cons = $this->getConstraints($table);
159
        if (!is_object($cons)) {
160
            $this->rollbackTransaction();
161
162
            return null;
163
        }
164
165
        // Output a reconnect command to create the table as the correct user
166
        $sql = $this->getChangeUserSQL($t->fields['relowner'])."\n\n";
167
168
        // Set schema search path
169
        $sql .= "SET search_path = \"{$t->fields['nspname']}\", pg_catalog;\n\n";
170
171
        // Begin CREATE TABLE definition
172
        $sql .= "-- Definition\n\n";
173
        // DROP TABLE must be fully qualified in case a table with the same name exists
174
        $sql .= $cleanprefix.'DROP TABLE ';
175
        $sql .= "\"{$t->fields['nspname']}\".\"{$t->fields['relname']}\";\n";
176
        $sql .= "CREATE TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" (\n";
177
178
        // Output all table columns
179
        $col_comments_sql = ''; // Accumulate comments on columns
180
        $num              = $atts->RecordCount() + $cons->RecordCount();
181
        $i                = 1;
182
        while (!$atts->EOF) {
183
            $this->fieldClean($atts->fields['attname']);
184
            $sql .= "    \"{$atts->fields['attname']}\"";
185
            // Dump SERIAL and BIGSERIAL columns correctly
186
            if ($this->phpBool($atts->fields['attisserial']) &&
187
                ($atts->fields['type'] == 'integer' || $atts->fields['type'] == 'bigint')) {
188
                if ($atts->fields['type'] == 'integer') {
189
                    $sql .= ' SERIAL';
190
                } else {
191
                    $sql .= ' BIGSERIAL';
192
                }
193
            } else {
194
                $sql .= ' '.$this->formatType($atts->fields['type'], $atts->fields['atttypmod']);
195
196
                // Add NOT NULL if necessary
197
                if ($this->phpBool($atts->fields['attnotnull'])) {
198
                    $sql .= ' NOT NULL';
199
                }
200
201
                // Add default if necessary
202
                if ($atts->fields['adsrc'] !== null) {
203
                    $sql .= " DEFAULT {$atts->fields['adsrc']}";
204
                }
205
            }
206
207
            // Output comma or not
208
            if ($i < $num) {
209
                $sql .= ",\n";
210
            } else {
211
                $sql .= "\n";
212
            }
213
214
            // Does this column have a comment?
215
            if ($atts->fields['comment'] !== null) {
216
                $this->clean($atts->fields['comment']);
217
                $col_comments_sql .= "COMMENT ON COLUMN \"{$t->fields['relname']}\".\"{$atts->fields['attname']}\"  IS '{$atts->fields['comment']}';\n";
218
            }
219
220
            $atts->moveNext();
221
            ++$i;
222
        }
223
        // Output all table constraints
224
        while (!$cons->EOF) {
225
            $this->fieldClean($cons->fields['conname']);
226
            $sql .= "    CONSTRAINT \"{$cons->fields['conname']}\" ";
227
            // Nasty hack to support pre-7.4 PostgreSQL
228
            if ($cons->fields['consrc'] !== null) {
229
                $sql .= $cons->fields['consrc'];
230
            } else {
231
                switch ($cons->fields['contype']) {
232
                    case 'p':
233
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
234
                        $sql .= 'PRIMARY KEY ('.join(',', $keys).')';
235
236
                        break;
237
                    case 'u':
238
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
239
                        $sql .= 'UNIQUE ('.join(',', $keys).')';
240
241
                        break;
242
                    default:
243
                        // Unrecognised constraint
244
                        $this->rollbackTransaction();
245
246
                        return null;
247
                }
248
            }
249
250
            // Output comma or not
251
            if ($i < $num) {
252
                $sql .= ",\n";
253
            } else {
254
                $sql .= "\n";
255
            }
256
257
            $cons->moveNext();
258
            ++$i;
259
        }
260
261
        $sql .= ')';
262
263
        // @@@@ DUMP CLUSTERING INFORMATION
264
265
        // Inherits
266
        /**
267
         * XXX: This is currently commented out as handling inheritance isn't this simple.
268
         * You also need to make sure you don't dump inherited columns and defaults, as well
269
         * as inherited NOT NULL and CHECK constraints.  So for the time being, we just do
270
         * not claim to support inheritance.
271
         * $parents = $this->getTableParents($table);
272
         * if ($parents->RecordCount() > 0) {
273
         * $sql .= " INHERITS (";
274
         * while (!$parents->EOF) {
275
         * $this->fieldClean($parents->fields['relname']);
276
         * // Qualify the parent table if it's in another schema
277
         * if ($parents->fields['schemaname'] != $this->_schema) {
278
         * $this->fieldClean($parents->fields['schemaname']);
279
         * $sql .= "\"{$parents->fields['schemaname']}\".";
280
         * }
281
         * $sql .= "\"{$parents->fields['relname']}\"";.
282
         *
283
         * $parents->moveNext();
284
         * if (!$parents->EOF) $sql .= ', ';
285
         * }
286
         * $sql .= ")";
287
         * }
288
         */
289
290
        // Handle WITHOUT OIDS
291
        if ($this->hasObjectID($table)) {
292
            $sql .= ' WITH OIDS';
293
        } else {
294
            $sql .= ' WITHOUT OIDS';
295
        }
296
297
        $sql .= ";\n";
298
299
        // Column storage and statistics
300
        $atts->moveFirst();
301
        $first = true;
302
        while (!$atts->EOF) {
303
            $this->fieldClean($atts->fields['attname']);
304
            // Statistics first
305
            if ($atts->fields['attstattarget'] >= 0) {
306
                if ($first) {
307
                    $sql .= "\n";
308
                    $first = false;
309
                }
310
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STATISTICS {$atts->fields['attstattarget']};\n";
311
            }
312
            // Then storage
313
            if ($atts->fields['attstorage'] != $atts->fields['typstorage']) {
314
                switch ($atts->fields['attstorage']) {
315
                    case 'p':
316
                        $storage = 'PLAIN';
317
318
                        break;
319
                    case 'e':
320
                        $storage = 'EXTERNAL';
321
322
                        break;
323
                    case 'm':
324
                        $storage = 'MAIN';
325
326
                        break;
327
                    case 'x':
328
                        $storage = 'EXTENDED';
329
330
                        break;
331
                    default:
332
                        // Unknown storage type
333
                        $this->rollbackTransaction();
334
335
                        return null;
336
                }
337
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STORAGE {$storage};\n";
338
            }
339
340
            $atts->moveNext();
341
        }
342
343
        // Comment
344
        if ($t->fields['relcomment'] !== null) {
345
            $this->clean($t->fields['relcomment']);
346
            $sql .= "\n-- Comment\n\n";
347
            $sql .= "COMMENT ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" IS '{$t->fields['relcomment']}';\n";
348
        }
349
350
        // Add comments on columns, if any
351
        if ($col_comments_sql != '') {
352
            $sql .= $col_comments_sql;
353
        }
354
355
        // Privileges
356
        $privs = $this->getPrivileges($table, 'table');
357
        if (!is_array($privs)) {
358
            $this->rollbackTransaction();
359
360
            return null;
361
        }
362
363
        if (sizeof($privs) > 0) {
364
            $sql .= "\n-- Privileges\n\n";
365
            /*
366
             * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
367
             * wire-in knowledge about the default public privileges for different
368
             * kinds of objects.
369
             */
370
            $sql .= "REVOKE ALL ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" FROM PUBLIC;\n";
371
            foreach ($privs as $v) {
372
                // Get non-GRANT OPTION privs
373
                $nongrant = array_diff($v[2], $v[4]);
374
375
                // Skip empty or owner ACEs
376
                if (sizeof($v[2]) == 0 || ($v[0] == 'user' && $v[1] == $t->fields['relowner'])) {
377
                    continue;
378
                }
379
380
                // Change user if necessary
381
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
382
                    $grantor = $v[3];
383
                    $this->clean($grantor);
384
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
385
                }
386
387
                // Output privileges with no GRANT OPTION
388
                $sql .= 'GRANT '.join(', ', $nongrant)." ON TABLE \"{$t->fields['relname']}\" TO ";
389
                switch ($v[0]) {
390
                    case 'public':
391
                        $sql .= "PUBLIC;\n";
392
393
                        break;
394
                    case 'user':
395
                    case 'role':
396
                        $this->fieldClean($v[1]);
397
                        $sql .= "\"{$v[1]}\";\n";
398
399
                        break;
400
                    case 'group':
401
                        $this->fieldClean($v[1]);
402
                        $sql .= "GROUP \"{$v[1]}\";\n";
403
404
                        break;
405
                    default:
406
                        // Unknown privilege type - fail
407
                        $this->rollbackTransaction();
408
409
                        return null;
410
                }
411
412
                // Reset user if necessary
413
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
414
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
415
                }
416
417
                // Output privileges with GRANT OPTION
418
419
                // Skip empty or owner ACEs
420
                if (!$this->hasGrantOption() || sizeof($v[4]) == 0) {
421
                    continue;
422
                }
423
424
                // Change user if necessary
425
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
426
                    $grantor = $v[3];
427
                    $this->clean($grantor);
428
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
429
                }
430
431
                $sql .= 'GRANT '.join(', ', $v[4])." ON \"{$t->fields['relname']}\" TO ";
432
                switch ($v[0]) {
433
                    case 'public':
434
                        $sql .= 'PUBLIC';
435
436
                        break;
437
                    case 'user':
438
                    case 'role':
439
                        $this->fieldClean($v[1]);
440
                        $sql .= "\"{$v[1]}\"";
441
442
                        break;
443
                    case 'group':
444
                        $this->fieldClean($v[1]);
445
                        $sql .= "GROUP \"{$v[1]}\"";
446
447
                        break;
448
                    default:
449
                        // Unknown privilege type - fail
450
                        return null;
451
                }
452
                $sql .= " WITH GRANT OPTION;\n";
453
454
                // Reset user if necessary
455
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
456
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
457
                }
458
            }
459
        }
460
461
        // Add a newline to separate data that follows (if any)
462
        $sql .= "\n";
463
464
        return $sql;
465
    }
466
467
    /**
468
     * Returns table information.
469
     *
470
     * @param string $table The name of the table
471
     *
472
     * @return \PHPPgAdmin\ADORecordSet A recordset
473
     */
474
    public function getTable($table)
475
    {
476
        $c_schema = $this->_schema;
477
        $this->clean($c_schema);
478
        $this->clean($table);
479
480
        $sql = '
481
            SELECT
482
              c.relname, n.nspname, ';
483
484
        $sql .= ($this->hasRoles() ? ' coalesce(u.usename,r.rolname) ' : ' u.usename')." AS relowner,
0 ignored issues
show
Bug introduced by
It seems like hasRoles() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

484
        $sql .= ($this->/** @scrutinizer ignore-call */ hasRoles() ? ' coalesce(u.usename,r.rolname) ' : ' u.usename')." AS relowner,
Loading history...
485
              pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
486
              pt.spcname  AS tablespace
487
            FROM pg_catalog.pg_class c
488
                LEFT JOIN pg_catalog.pg_tablespace pt ON pt.oid=c.reltablespace
489
                 LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
490
                 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace ";
491
492
        $sql .= ($this->hasRoles() ? ' LEFT JOIN pg_catalog.pg_roles r ON c.relowner = r.oid ' : '').
493
            " WHERE c.relkind = 'r'
494
                  AND n.nspname = '{$c_schema}'
495
                  AND n.oid = c.relnamespace
496
                  AND c.relname = '{$table}'";
497
498
        return $this->selectSet($sql);
499
    }
500
501
    /**
502
     * Retrieve all attributes definition of a table.
503
     *
504
     * @param string $table    The name of the table
505
     * @param string $c_schema The name of the schema
506
     *
507
     * @return \PHPPgAdmin\ADORecordSet All attributes in order
508
     */
509
    private function _getTableAttributesAll($table, $c_schema)
510
    {
511
        $sql = "
512
            SELECT
513
                a.attname,
514
                a.attnum,
515
                pg_catalog.format_type(a.atttypid, a.atttypmod) AS TYPE,
516
                a.atttypmod,
517
                a.attnotnull,
518
                a.atthasdef,
519
                pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, TRUE) AS adsrc,
520
                a.attstattarget,
521
                a.attstorage,
522
                t.typstorage,
523
                CASE
524
                WHEN pc.oid IS NULL THEN FALSE
525
                ELSE TRUE
526
                END AS attisserial,
527
                pg_catalog.col_description(a.attrelid, a.attnum) AS COMMENT
528
529
            FROM pg_catalog.pg_tables tbl
530
            JOIN pg_catalog.pg_class tbl_class ON tbl.tablename=tbl_class.relname
531
            JOIN  pg_catalog.pg_attribute a ON tbl_class.oid = a.attrelid
532
            JOIN pg_catalog.pg_namespace    ON pg_namespace.oid = tbl_class.relnamespace
533
                                            AND pg_namespace.nspname=tbl.schemaname
534
            LEFT JOIN pg_catalog.pg_attrdef adef    ON a.attrelid=adef.adrelid
535
                                                    AND a.attnum=adef.adnum
536
            LEFT JOIN pg_catalog.pg_type t  ON a.atttypid=t.oid
537
            LEFT JOIN  pg_catalog.pg_depend pd  ON pd.refobjid=a.attrelid
538
                                                AND pd.refobjsubid=a.attnum
539
                                                AND pd.deptype='i'
540
            LEFT JOIN pg_catalog.pg_class pc ON pd.objid=pc.oid
541
                                            AND pd.classid=pc.tableoid
542
                                            AND pd.refclassid=pc.tableoid
543
                                            AND pc.relkind='S'
544
            WHERE tbl.tablename='{$table}'
545
            AND tbl.schemaname='{$c_schema}'
546
            AND a.attnum > 0 AND NOT a.attisdropped
547
            ORDER BY a.attnum";
548
549
        return $this->selectSet($sql);
550
    }
551
552
    /**
553
     * Retrieve single attribute definition of a table.
554
     *
555
     * @param string $table    The name of the table
556
     * @param string $c_schema The schema of the table
557
     * @param string $field    (optional) The name of a field to return
558
     *
559
     * @return \PHPPgAdmin\ADORecordSet All attributes in order
560
     */
561
    private function _getTableAttribute($table, $c_schema, $field)
562
    {
563
        $sql = "
564
                SELECT
565
                    a.attname, a.attnum,
566
                    pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
567
                    pg_catalog.format_type(a.atttypid, NULL) as base_type,
568
                    a.atttypmod,
569
                    a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc,
570
                    a.attstattarget, a.attstorage, t.typstorage,
571
                    pg_catalog.col_description(a.attrelid, a.attnum) AS comment
572
                FROM
573
                    pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
574
                    ON a.attrelid=adef.adrelid
575
                    AND a.attnum=adef.adnum
576
                    LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
577
                WHERE
578
                    a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
579
                        AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
580
                        nspname = '{$c_schema}'))
581
                    AND a.attname = '{$field}'";
582
583
        return $this->selectSet($sql);
584
    }
585
586
    /**
587
     * Retrieve the attribute definition of a table.
588
     *
589
     * @param string $table The name of the table
590
     * @param string $field (optional) The name of a field to return
591
     *
592
     * @return \PHPPgAdmin\ADORecordSet All attributes in order
593
     */
594
    public function getTableAttributes($table, $field = '')
595
    {
596
        $c_schema = $this->_schema;
597
        $this->clean($c_schema);
598
        $this->clean($table);
599
600
        if ($field == '') {
601
            // This query is made much more complex by the addition of the 'attisserial' field.
602
            // The subquery to get that field checks to see if there is an internally dependent
603
            // sequence on the field.
604
            return $this->_getTableAttributesAll($table, $c_schema);
605
        }
606
        $this->clean($field);
607
608
        return $this->_getTableAttribute($table, $c_schema, $field);
609
    }
610
611
    /**
612
     * Returns a list of all constraints on a table.
613
     *
614
     * @param string $table The table to find rules for
615
     *
616
     * @return \PHPPgAdmin\ADORecordSet A recordset
617
     */
618
    public function getConstraints($table)
619
    {
620
        $c_schema = $this->_schema;
621
        $this->clean($c_schema);
622
        $this->clean($table);
623
624
        // This SQL is greatly complicated by the need to retrieve
625
        // index clustering information for primary and unique constraints
626
        $sql = "SELECT
627
                pc.conname,
628
                pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc,
629
                pc.contype,
630
                CASE WHEN pc.contype='u' OR pc.contype='p' THEN (
631
                    SELECT
632
                        indisclustered
633
                    FROM
634
                        pg_catalog.pg_depend pd,
635
                        pg_catalog.pg_class pl,
636
                        pg_catalog.pg_index pi
637
                    WHERE
638
                        pd.refclassid=pc.tableoid
639
                        AND pd.refobjid=pc.oid
640
                        AND pd.objid=pl.oid
641
                        AND pl.oid=pi.indexrelid
642
                ) ELSE
643
                    NULL
644
                END AS indisclustered
645
            FROM
646
                pg_catalog.pg_constraint pc
647
            WHERE
648
                pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
649
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
650
                    WHERE nspname='{$c_schema}'))
651
            ORDER BY
652
                1
653
        ";
654
655
        return $this->selectSet($sql);
656
    }
657
658
    /**
659
     * Checks to see whether or not a table has a unique id column.
660
     *
661
     * @param string $table The table name
662
     *
663
     * @return true if it has a unique id, false otherwise
664
     */
665
    public function hasObjectID($table)
666
    {
667
        $c_schema = $this->_schema;
668
        $this->clean($c_schema);
669
        $this->clean($table);
670
671
        $sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}'
672
            AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')";
673
674
        $rs = $this->selectSet($sql);
675
        if ($rs->RecordCount() != 1) {
676
            return null;
677
        }
678
679
        $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']);
680
681
        return $rs->fields['relhasoids'];
682
    }
683
684
    /**
685
     * Returns extra table definition information that is most usefully
686
     * dumped after the table contents for speed and efficiency reasons.
687
     *
688
     * @param string $table The table to define
689
     *
690
     * @return string A string containing the formatted SQL code
691
     */
692
    public function getTableDefSuffix($table)
693
    {
694
        $sql = '';
695
696
        // Indexes
697
        $indexes = $this->getIndexes($table);
698
        if (!is_object($indexes)) {
699
            $this->rollbackTransaction();
700
701
            return null;
702
        }
703
704
        if ($indexes->RecordCount() > 0) {
705
            $sql .= "\n-- Indexes\n\n";
706
            while (!$indexes->EOF) {
707
                $sql .= $indexes->fields['inddef'].";\n";
708
709
                $indexes->moveNext();
710
            }
711
        }
712
713
        // Triggers
714
        $triggers = $this->getTriggers($table);
715
        if (!is_object($triggers)) {
716
            $this->rollbackTransaction();
717
718
            return null;
719
        }
720
721
        if ($triggers->RecordCount() > 0) {
722
            $sql .= "\n-- Triggers\n\n";
723
            while (!$triggers->EOF) {
724
                $sql .= $triggers->fields['tgdef'];
725
                $sql .= ";\n";
726
727
                $triggers->moveNext();
728
            }
729
        }
730
731
        // Rules
732
        $rules = $this->getRules($table);
733
        if (!is_object($rules)) {
734
            $this->rollbackTransaction();
735
736
            return null;
737
        }
738
739
        if ($rules->RecordCount() > 0) {
740
            $sql .= "\n-- Rules\n\n";
741
            while (!$rules->EOF) {
742
                $sql .= $rules->fields['definition']."\n";
743
744
                $rules->moveNext();
745
            }
746
        }
747
748
        return $sql;
749
    }
750
751
    /**
752
     * Grabs a list of indexes for a table.
753
     *
754
     * @param string $table  The name of a table whose indexes to retrieve
755
     * @param bool   $unique Only get unique/pk indexes
756
     *
757
     * @return \PHPPgAdmin\ADORecordSet A recordset
758
     */
759
    public function getIndexes($table = '', $unique = false)
760
    {
761
        $this->clean($table);
762
763
        $sql = "
764
            SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered,
765
                pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef
766
            FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
767
            WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid)
768
                AND c.oid = i.indrelid AND i.indexrelid = c2.oid
769
        ";
770
        if ($unique) {
771
            $sql .= ' AND i.indisunique ';
772
        }
773
774
        $sql .= ' ORDER BY c2.relname';
775
776
        return $this->selectSet($sql);
777
    }
778
779
    /**
780
     * Grabs a list of triggers on a table.
781
     *
782
     * @param string $table The name of a table whose triggers to retrieve
783
     *
784
     * @return \PHPPgAdmin\ADORecordSet A recordset
785
     */
786
    public function getTriggers($table = '')
787
    {
788
        $c_schema = $this->_schema;
789
        $this->clean($c_schema);
790
        $this->clean($table);
791
792
        $sql = "SELECT
793
                t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef,
794
                CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid,
795
                p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
796
                ns.nspname AS pronamespace
797
            FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
798
            WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
799
                AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}'))
800
                AND ( tgconstraint = 0 OR NOT EXISTS
801
                        (SELECT 1 FROM pg_catalog.pg_depend d    JOIN pg_catalog.pg_constraint c
802
                            ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
803
                        WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
804
                AND p.oid=t.tgfoid
805
                AND p.pronamespace = ns.oid";
806
807
        return $this->selectSet($sql);
808
    }
809
810
    /**
811
     * Returns a list of all rules on a table OR view.
812
     *
813
     * @param string $table The table to find rules for
814
     *
815
     * @return \PHPPgAdmin\ADORecordSet A recordset
816
     */
817
    public function getRules($table)
818
    {
819
        $c_schema = $this->_schema;
820
        $this->clean($c_schema);
821
        $this->clean($table);
822
823
        $sql = "
824
            SELECT *
825
            FROM pg_catalog.pg_rules
826
            WHERE
827
                schemaname='{$c_schema}' AND tablename='{$table}'
828
            ORDER BY rulename
829
        ";
830
831
        return $this->selectSet($sql);
832
    }
833
834
    /**
835
     * Creates a new table in the database.
836
     *
837
     * @param string $name        The name of the table
838
     * @param int    $fields      The number of fields
839
     * @param array  $field       An array of field names
840
     * @param array  $type        An array of field types
841
     * @param array  $array       An array of '' or '[]' for each type if it's an array or not
842
     * @param array  $length      An array of field lengths
843
     * @param array  $notnull     An array of not null
844
     * @param array  $default     An array of default values
845
     * @param bool   $withoutoids True if WITHOUT OIDS, false otherwise
846
     * @param array  $colcomment  An array of comments
847
     * @param string $tblcomment  the comment for the table
848
     * @param string $tablespace  The tablespace name ('' means none/default)
849
     * @param array  $uniquekey   An Array indicating the fields that are unique (those indexes that are set)
850
     * @param array  $primarykey  An Array indicating the field used for the primarykey (those indexes that are set)
851
     *
852
     * @return bool|int 0 success
853
     */
854
    public function createTable(
855
        $name,
856
        $fields,
857
        $field,
858
        $type,
859
        $array,
860
        $length,
861
        $notnull,
862
        $default,
863
        $withoutoids,
864
        $colcomment,
865
        $tblcomment,
866
        $tablespace,
867
        $uniquekey,
868
        $primarykey
869
    ) {
870
        $f_schema = $this->_schema;
871
        $this->fieldClean($f_schema);
872
        $this->fieldClean($name);
873
874
        $status = $this->beginTransaction();
875
        if ($status != 0) {
876
            return -1;
877
        }
878
879
        $found       = false;
880
        $first       = true;
881
        $comment_sql = ''; //Accumulate comments for the columns
882
        $sql         = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (";
883
        for ($i = 0; $i < $fields; ++$i) {
884
            $this->fieldClean($field[$i]);
885
            $this->clean($type[$i]);
886
            $this->clean($length[$i]);
887
            $this->clean($colcomment[$i]);
888
889
            // Skip blank columns - for user convenience
890
            if ($field[$i] == '' || $type[$i] == '') {
891
                continue;
892
            }
893
894
            // If not the first column, add a comma
895
            if (!$first) {
896
                $sql .= ', ';
897
            } else {
898
                $first = false;
899
            }
900
901
            switch ($type[$i]) {
902
                // Have to account for weird placing of length for with/without
903
                // time zone types
904
                case 'timestamp with time zone':
905
                case 'timestamp without time zone':
906
                    $qual = substr($type[$i], 9);
907
                    $sql .= "\"{$field[$i]}\" timestamp";
908
                    if ($length[$i] != '') {
909
                        $sql .= "({$length[$i]})";
910
                    }
911
912
                    $sql .= $qual;
913
914
                    break;
915
                case 'time with time zone':
916
                case 'time without time zone':
917
                    $qual = substr($type[$i], 4);
918
                    $sql .= "\"{$field[$i]}\" time";
919
                    if ($length[$i] != '') {
920
                        $sql .= "({$length[$i]})";
921
                    }
922
923
                    $sql .= $qual;
924
925
                    break;
926
                default:
927
                    $sql .= "\"{$field[$i]}\" {$type[$i]}";
928
                    if ($length[$i] != '') {
929
                        $sql .= "({$length[$i]})";
930
                    }
931
            }
932
            // Add array qualifier if necessary
933
            if ($array[$i] == '[]') {
934
                $sql .= '[]';
935
            }
936
937
            // Add other qualifiers
938
            if (!isset($primarykey[$i])) {
939
                if (isset($uniquekey[$i])) {
940
                    $sql .= ' UNIQUE';
941
                }
942
943
                if (isset($notnull[$i])) {
944
                    $sql .= ' NOT NULL';
945
                }
946
            }
947
            if ($default[$i] != '') {
948
                $sql .= " DEFAULT {$default[$i]}";
949
            }
950
951
            if ($colcomment[$i] != '') {
952
                $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
953
            }
954
955
            $found = true;
956
        }
957
958
        if (!$found) {
0 ignored issues
show
introduced by
The condition $found is always false.
Loading history...
959
            return -1;
960
        }
961
962
        // PRIMARY KEY
963
        $primarykeycolumns = [];
964
        for ($i = 0; $i < $fields; ++$i) {
965
            if (isset($primarykey[$i])) {
966
                $primarykeycolumns[] = "\"{$field[$i]}\"";
967
            }
968
        }
969
        if (count($primarykeycolumns) > 0) {
970
            $sql .= ', PRIMARY KEY ('.implode(', ', $primarykeycolumns).')';
971
        }
972
973
        $sql .= ')';
974
975
        // WITHOUT OIDS
976
        if ($withoutoids) {
977
            $sql .= ' WITHOUT OIDS';
978
        } else {
979
            $sql .= ' WITH OIDS';
980
        }
981
982
        // Tablespace
983
        if ($this->hasTablespaces() && $tablespace != '') {
984
            $this->fieldClean($tablespace);
985
            $sql .= " TABLESPACE \"{$tablespace}\"";
986
        }
987
988
        $status = $this->execute($sql);
989
        if ($status) {
990
            $this->rollbackTransaction();
991
992
            return -1;
993
        }
994
995
        if ($tblcomment != '') {
996
            $status = $this->setComment('TABLE', '', $name, $tblcomment, true);
997
            if ($status) {
998
                $this->rollbackTransaction();
999
1000
                return -1;
1001
            }
1002
        }
1003
1004
        if ($comment_sql != '') {
1005
            $status = $this->execute($comment_sql);
1006
            if ($status) {
1007
                $this->rollbackTransaction();
1008
1009
                return -1;
1010
            }
1011
        }
1012
1013
        return $this->endTransaction();
1014
    }
1015
1016
    /**
1017
     * Creates a new table in the database copying attribs and other properties from another table.
1018
     *
1019
     * @param string $name        The name of the table
1020
     * @param array  $like        an array giving the schema ans the name of the table from which attribs are copying
1021
     *                            from: array(
1022
     *                            'table' => table name,
1023
     *                            'schema' => the schema name,
1024
     *                            )
1025
     * @param bool   $defaults    if true, copy the defaults values as well
1026
     * @param bool   $constraints if true, copy the constraints as well (CHECK on table & attr)
1027
     * @param bool   $idx
1028
     * @param string $tablespace  The tablespace name ('' means none/default)
1029
     *
1030
     * @return bool|int
1031
     */
1032
    public function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '')
1033
    {
1034
        $f_schema = $this->_schema;
1035
        $this->fieldClean($f_schema);
1036
        $this->fieldClean($name);
1037
        $this->fieldClean($like['schema']);
1038
        $this->fieldClean($like['table']);
1039
        $like = "\"{$like['schema']}\".\"{$like['table']}\"";
1040
1041
        $status = $this->beginTransaction();
1042
        if ($status != 0) {
1043
            return -1;
1044
        }
1045
1046
        $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (LIKE {$like}";
1047
1048
        if ($defaults) {
1049
            $sql .= ' INCLUDING DEFAULTS';
1050
        }
1051
1052
        if ($this->hasCreateTableLikeWithConstraints() && $constraints) {
1053
            $sql .= ' INCLUDING CONSTRAINTS';
1054
        }
1055
1056
        if ($this->hasCreateTableLikeWithIndexes() && $idx) {
1057
            $sql .= ' INCLUDING INDEXES';
1058
        }
1059
1060
        $sql .= ')';
1061
1062
        if ($this->hasTablespaces() && $tablespace != '') {
1063
            $this->fieldClean($tablespace);
1064
            $sql .= " TABLESPACE \"{$tablespace}\"";
1065
        }
1066
1067
        $status = $this->execute($sql);
1068
        if ($status) {
1069
            $this->rollbackTransaction();
1070
1071
            return -1;
1072
        }
1073
1074
        return $this->endTransaction();
1075
    }
1076
1077
    /**
1078
     * Alter table properties.
1079
     *
1080
     * @param string $table      The name of the table
1081
     * @param string $name       The new name for the table
1082
     * @param string $owner      The new owner for the table
1083
     * @param string $schema     The new schema for the table
1084
     * @param string $comment    The comment on the table
1085
     * @param string $tablespace The new tablespace for the table ('' means leave as is)
1086
     *
1087
     * @return bool|int 0 success
1088
     */
1089
    public function alterTable($table, $name, $owner, $schema, $comment, $tablespace)
1090
    {
1091
        $data = $this->getTable($table);
1092
1093
        if ($data->RecordCount() != 1) {
1094
            return -2;
1095
        }
1096
1097
        $status = $this->beginTransaction();
1098
        if ($status != 0) {
1099
            $this->rollbackTransaction();
1100
1101
            return -1;
1102
        }
1103
1104
        $status = $this->_alterTable($data, $name, $owner, $schema, $comment, $tablespace);
1105
1106
        if ($status != 0) {
1107
            $this->rollbackTransaction();
1108
1109
            return $status;
1110
        }
1111
1112
        return $this->endTransaction();
1113
    }
1114
1115
    /**
1116
     * Protected method which alter a table
1117
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION.
1118
     *
1119
     * @param \PHPPgAdmin\ADORecordSet $tblrs      The table recordSet returned by getTable()
1120
     * @param string                   $name       The new name for the table
1121
     * @param string                   $owner      The new owner for the table
1122
     * @param string                   $schema     The new schema for the table
1123
     * @param string                   $comment    The comment on the table
1124
     * @param string                   $tablespace The new tablespace for the table ('' means leave as is)
1125
     *
1126
     * @return int 0 success
1127
     */
1128
    protected function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace)
1129
    {
1130
        $this->fieldArrayClean($tblrs->fields);
1131
1132
        // Comment
1133
        $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
1134
        if ($status != 0) {
1135
            return -4;
1136
        }
1137
1138
        // Owner
1139
        $this->fieldClean($owner);
1140
        $status = $this->alterTableOwner($tblrs, $owner);
1141
        if ($status != 0) {
1142
            return -5;
1143
        }
1144
1145
        // Tablespace
1146
        $this->fieldClean($tablespace);
1147
        $status = $this->alterTableTablespace($tblrs, $tablespace);
1148
        if ($status != 0) {
1149
            return -6;
1150
        }
1151
1152
        // Rename
1153
        $this->fieldClean($name);
1154
        $status = $this->alterTableName($tblrs, $name);
1155
        if ($status != 0) {
1156
            return -3;
1157
        }
1158
1159
        // Schema
1160
        $this->fieldClean($schema);
1161
        $status = $this->alterTableSchema($tblrs, $schema);
1162
        if ($status != 0) {
1163
            return -7;
1164
        }
1165
1166
        return 0;
1167
    }
1168
1169
    /**
1170
     * Alter a table's owner
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 null|string              $owner
1175
     *
1176
     * @return int 0 if operation was successful
1177
     */
1178
    public function alterTableOwner($tblrs, $owner = null)
1179
    {
1180
        /* vars cleaned in _alterTable */
1181
        if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
1182
            $f_schema = $this->_schema;
1183
            $this->fieldClean($f_schema);
1184
            // If owner has been changed, then do the alteration.  We are
1185
            // careful to avoid this generally as changing owner is a
1186
            // superuser only function.
1187
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" OWNER TO \"{$owner}\"";
1188
1189
            return $this->execute($sql);
1190
        }
1191
1192
        return 0;
1193
    }
1194
1195
    /**
1196
     * Alter a table's tablespace
1197
     * /!\ this function is called from _alterTable which take care of escaping fields.
1198
     *
1199
     * @param \PHPPgAdmin\ADORecordSet $tblrs      The table RecordSet returned by getTable()
1200
     * @param null|string              $tablespace
1201
     *
1202
     * @return int 0 if operation was successful
1203
     */
1204
    public function alterTableTablespace($tblrs, $tablespace = null)
1205
    {
1206
        /* vars cleaned in _alterTable */
1207
        if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) {
1208
            $f_schema = $this->_schema;
1209
            $this->fieldClean($f_schema);
1210
1211
            // If tablespace has been changed, then do the alteration.  We
1212
            // don't want to do this unnecessarily.
1213
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET TABLESPACE \"{$tablespace}\"";
1214
1215
            return $this->execute($sql);
1216
        }
1217
1218
        return 0;
1219
    }
1220
1221
    /**
1222
     * Alter a table's name
1223
     * /!\ this function is called from _alterTable which take care of escaping fields.
1224
     *
1225
     * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable()
1226
     * @param string                   $name  The new table's name
1227
     *
1228
     * @return int 0 if operation was successful
1229
     */
1230
    public function alterTableName($tblrs, $name = null)
1231
    {
1232
        /* vars cleaned in _alterTable */
1233
        // Rename (only if name has changed)
1234
        if (!empty($name) && ($name != $tblrs->fields['relname'])) {
1235
            $f_schema = $this->_schema;
1236
            $this->fieldClean($f_schema);
1237
1238
            $sql    = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" RENAME TO \"{$name}\"";
1239
            $status = $this->execute($sql);
1240
            if ($status == 0) {
1241
                $tblrs->fields['relname'] = $name;
1242
            } else {
1243
                return $status;
1244
            }
1245
        }
1246
1247
        return 0;
1248
    }
1249
1250
    // Row functions
1251
1252
    /**
1253
     * Alter a table's schema
1254
     * /!\ this function is called from _alterTable which take care of escaping fields.
1255
     *
1256
     * @param \PHPPgAdmin\ADORecordSet $tblrs  The table RecordSet returned by getTable()
1257
     * @param null|string              $schema
1258
     *
1259
     * @return int 0 if operation was successful
1260
     */
1261
    public function alterTableSchema($tblrs, $schema = null)
1262
    {
1263
        /* vars cleaned in _alterTable */
1264
        if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) {
1265
            $f_schema = $this->_schema;
1266
            $this->fieldClean($f_schema);
1267
            // If tablespace has been changed, then do the alteration.  We
1268
            // don't want to do this unnecessarily.
1269
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET SCHEMA \"{$schema}\"";
1270
1271
            return $this->execute($sql);
1272
        }
1273
1274
        return 0;
1275
    }
1276
1277
    /**
1278
     * Empties a table in the database.
1279
     *
1280
     * @param string $table   The table to be emptied
1281
     * @param bool   $cascade True to cascade truncate, false to restrict
1282
     *
1283
     * @return array<integer,mixed|string> 0 if operation was successful
1284
     */
1285
    public function emptyTable($table, $cascade)
1286
    {
1287
        $f_schema = $this->_schema;
1288
        $this->fieldClean($f_schema);
1289
        $this->fieldClean($table);
1290
1291
        $sql = "TRUNCATE TABLE \"{$f_schema}\".\"{$table}\" ";
1292
        if ($cascade) {
1293
            $sql = $sql.' CASCADE';
1294
        }
1295
1296
        $status = $this->execute($sql);
1297
1298
        return [$status, $sql];
1299
    }
1300
1301
    /**
1302
     * Removes a table from the database.
1303
     *
1304
     * @param string $table   The table to drop
1305
     * @param bool   $cascade True to cascade drop, false to restrict
1306
     *
1307
     * @return int 0 if operation was successful
1308
     */
1309
    public function dropTable($table, $cascade)
1310
    {
1311
        $f_schema = $this->_schema;
1312
        $this->fieldClean($f_schema);
1313
        $this->fieldClean($table);
1314
1315
        $sql = "DROP TABLE \"{$f_schema}\".\"{$table}\"";
1316
        if ($cascade) {
1317
            $sql .= ' CASCADE';
1318
        }
1319
1320
        return $this->execute($sql);
1321
    }
1322
1323
    /**
1324
     * Sets up the data object for a dump.  eg. Starts the appropriate
1325
     * transaction, sets variables, etc.
1326
     *
1327
     * @return int 0 success
1328
     */
1329
    public function beginDump()
1330
    {
1331
        // Begin serializable transaction (to dump consistent data)
1332
        $status = $this->beginTransaction();
1333
        if ($status != 0) {
1334
            return -1;
1335
        }
1336
1337
        // Set serializable
1338
        $sql    = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE';
1339
        $status = $this->execute($sql);
1340
        if ($status != 0) {
1341
            $this->rollbackTransaction();
1342
1343
            return -1;
1344
        }
1345
1346
        // Set datestyle to ISO
1347
        $sql    = 'SET DATESTYLE = ISO';
1348
        $status = $this->execute($sql);
1349
        if ($status != 0) {
1350
            $this->rollbackTransaction();
1351
1352
            return -1;
1353
        }
1354
1355
        // Set extra_float_digits to 2
1356
        $sql    = 'SET extra_float_digits TO 2';
1357
        $status = $this->execute($sql);
1358
        if ($status != 0) {
1359
            $this->rollbackTransaction();
1360
1361
            return -1;
1362
        }
1363
1364
        return 0;
1365
    }
1366
1367
    /**
1368
     * Ends the data object for a dump.
1369
     *
1370
     * @return bool 0 success
1371
     */
1372
    public function endDump()
1373
    {
1374
        return $this->endTransaction();
1375
    }
1376
1377
    /**
1378
     * Returns a recordset of all columns in a relation.  Used for data export.
1379
     *
1380
     * @@ Note: Really needs to use a cursor
1381
     *
1382
     * @param string $relation The name of a relation
1383
     * @param bool   $oids     true to dump also the oids
1384
     *
1385
     * @return \PHPPgAdmin\ADORecordSet A recordset on success
1386
     */
1387
    public function dumpRelation($relation, $oids)
1388
    {
1389
        $this->fieldClean($relation);
1390
1391
        // Actually retrieve the rows
1392
        if ($oids) {
1393
            $oid_str = $this->id.', ';
1394
        } else {
1395
            $oid_str = '';
1396
        }
1397
1398
        return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
1399
    }
1400
1401
    /**
1402
     * Returns all available autovacuum per table information.
1403
     *
1404
     * @param string $table if given, return autovacuum info for the given table or return all informations for all table
1405
     *
1406
     * @return \PHPPgAdmin\ArrayRecordSet A recordset
1407
     */
1408
    public function getTableAutovacuum($table = '')
1409
    {
1410
        $sql = '';
1411
1412
        if ($table !== '') {
1413
            $this->clean($table);
1414
            $c_schema = $this->_schema;
1415
            $this->clean($c_schema);
1416
1417
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
1418
                FROM pg_class c
1419
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
1420
                WHERE c.relkind = 'r'::\"char\"
1421
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
1422
                    AND c.reloptions IS NOT NULL
1423
                    AND c.relname = '{$table}' AND n.nspname = '{$c_schema}'
1424
                ORDER BY nspname, relname";
1425
        } else {
1426
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
1427
                FROM pg_class c
1428
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
1429
                WHERE c.relkind = 'r'::\"char\"
1430
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
1431
                    AND c.reloptions IS NOT NULL
1432
                ORDER BY nspname, relname";
1433
        }
1434
1435
        /* tmp var to parse the results */
1436
        $_autovacs = $this->selectSet($sql);
1437
1438
        /* result aray to return as RS */
1439
        $autovacs = [];
1440
        while (!$_autovacs->EOF) {
1441
            $_ = [
1442
                'nspname' => $_autovacs->fields['nspname'],
1443
                'relname' => $_autovacs->fields['relname'],
1444
            ];
1445
1446
            foreach (explode(',', $_autovacs->fields['reloptions']) as $var) {
1447
                list($o, $v) = explode('=', $var);
1448
                $_[$o]       = $v;
1449
            }
1450
1451
            $autovacs[] = $_;
1452
1453
            $_autovacs->moveNext();
1454
        }
1455
1456
        return new \PHPPgAdmin\ArrayRecordSet($autovacs);
1457
    }
1458
1459
    /**
1460
     * Returns the SQL for changing the current user.
1461
     *
1462
     * @param string $user The user to change to
1463
     *
1464
     * @return string The SQL
1465
     */
1466
    public function getChangeUserSQL($user)
1467
    {
1468
        $this->clean($user);
1469
1470
        return "SET SESSION AUTHORIZATION '{$user}';";
1471
    }
1472
1473
    /**
1474
     * Returns all available autovacuum per table information.
1475
     *
1476
     * @param string $table          table name
1477
     * @param bool   $vacenabled     true if vacuum is enabled
1478
     * @param int    $vacthreshold   vacuum threshold
1479
     * @param int    $vacscalefactor vacuum scalefactor
1480
     * @param int    $anathresold    analyze threshold
1481
     * @param int    $anascalefactor analyze scale factor
1482
     * @param int    $vaccostdelay   vacuum cost delay
1483
     * @param int    $vaccostlimit   vacuum cost limit
1484
     *
1485
     * @return bool 0 if successful
1486
     */
1487
    public function saveAutovacuum(
1488
        $table,
1489
        $vacenabled,
1490
        $vacthreshold,
1491
        $vacscalefactor,
1492
        $anathresold,
1493
        $anascalefactor,
1494
        $vaccostdelay,
1495
        $vaccostlimit
1496
    ) {
1497
        $f_schema = $this->_schema;
1498
        $this->fieldClean($f_schema);
1499
        $this->fieldClean($table);
1500
1501
        $params = [];
1502
1503
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" SET (";
1504
1505
        if (!empty($vacenabled)) {
1506
            $this->clean($vacenabled);
1507
            $params[] = "autovacuum_enabled='{$vacenabled}'";
1508
        }
1509
        if (!empty($vacthreshold)) {
1510
            $this->clean($vacthreshold);
1511
            $params[] = "autovacuum_vacuum_threshold='{$vacthreshold}'";
1512
        }
1513
        if (!empty($vacscalefactor)) {
1514
            $this->clean($vacscalefactor);
1515
            $params[] = "autovacuum_vacuum_scale_factor='{$vacscalefactor}'";
1516
        }
1517
        if (!empty($anathresold)) {
1518
            $this->clean($anathresold);
1519
            $params[] = "autovacuum_analyze_threshold='{$anathresold}'";
1520
        }
1521
        if (!empty($anascalefactor)) {
1522
            $this->clean($anascalefactor);
1523
            $params[] = "autovacuum_analyze_scale_factor='{$anascalefactor}'";
1524
        }
1525
        if (!empty($vaccostdelay)) {
1526
            $this->clean($vaccostdelay);
1527
            $params[] = "autovacuum_vacuum_cost_delay='{$vaccostdelay}'";
1528
        }
1529
        if (!empty($vaccostlimit)) {
1530
            $this->clean($vaccostlimit);
1531
            $params[] = "autovacuum_vacuum_cost_limit='{$vaccostlimit}'";
1532
        }
1533
1534
        $sql = $sql.implode(',', $params).');';
1535
1536
        return $this->execute($sql);
1537
    }
1538
1539
    // Type conversion routines
1540
1541
    /**
1542
     * Drops autovacuum config for a table.
1543
     *
1544
     * @param string $table The table
1545
     *
1546
     * @return bool 0 if successful
1547
     */
1548
    public function dropAutovacuum($table)
1549
    {
1550
        $f_schema = $this->_schema;
1551
        $this->fieldClean($f_schema);
1552
        $this->fieldClean($table);
1553
1554
        return $this->execute(
1555
            "
1556
            ALTER TABLE \"{$f_schema}\".\"{$table}\" RESET (autovacuum_enabled, autovacuum_vacuum_threshold,
1557
                autovacuum_vacuum_scale_factor, autovacuum_analyze_threshold, autovacuum_analyze_scale_factor,
1558
                autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_limit
1559
            );"
1560
        );
1561
    }
1562
1563
    abstract public function fieldClean(&$str);
1564
1565
    abstract public function beginTransaction();
1566
1567
    abstract public function rollbackTransaction();
1568
1569
    abstract public function endTransaction();
1570
1571
    abstract public function execute($sql);
1572
1573
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
1574
1575
    abstract public function selectSet($sql);
1576
1577
    abstract public function clean(&$str);
1578
1579
    abstract public function phpBool($parameter);
1580
1581
    abstract public function hasCreateTableLikeWithConstraints();
1582
1583
    abstract public function hasCreateTableLikeWithIndexes();
1584
1585
    abstract public function hasTablespaces();
1586
1587
    abstract public function delete($table, $conditions, $schema = '');
1588
1589
    abstract public function fieldArrayClean(&$arr);
1590
1591
    abstract public function hasCreateFieldWithConstraints();
1592
1593
    abstract public function getAttributeNames($table, $atts);
1594
}
1595