Test Failed
Push — master ( 34dd7c...325a03 )
by Felipe
11:17 queued 06:14
created

TableTrait::getTableAutovacuum()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 49
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1861
            default:
1862
                // Checking variable fields is difficult as there might be a size
1863
                // attribute...
1864
                if (strpos($type, 'time') === 0) {
1865
                    // Assume it's one of the time types...
1866
                    if ($value == '') {
1867
                        return "''";
1868
                    }
1869
1870
                    if (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0
1871
                        || strcasecmp($value, 'CURRENT_TIME') == 0
1872
                        || strcasecmp($value, 'CURRENT_DATE') == 0
1873
                        || strcasecmp($value, 'LOCALTIME') == 0
1874
                        || strcasecmp($value, 'LOCALTIMESTAMP') == 0) {
1 ignored issue
show
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
1875
                        return $value;
1876
                    }
1877
                    if ($format == 'EXPRESSION') {
1878
                        return $value;
1879
                    }
1880
                    $this->clean($value);
1881
1882
                    return "'{$value}'";
1883
                }
1884
                if ($format == 'VALUE') {
1885
                    $this->clean($value);
1886
1887
                    return "'{$value}'";
1888
                }
1889
1890
                return $value;
1891
        }
1892
    }
1893
1894
    // View functions
1895
1896
    /**
1897
     * Updates a row in a table.
1898
     *
1899
     * @param string $table  The table in which to update
1900
     * @param array  $vars   An array mapping new values for the row
1901
     * @param array  $nulls  An array mapping column => something if it is to be null
1902
     * @param array  $format An array of the data type (VALUE or EXPRESSION)
1903
     * @param array  $types  An array of field types
1904
     * @param array  $keyarr An array mapping column => value to update
1905
     *
1906
     * @return bool|int 0 success
1907
     */
1908
    public function editRow($table, $vars, $nulls, $format, $types, $keyarr)
1909
    {
1910
        if (!is_array($vars) || !is_array($nulls) || !is_array($format) || !is_array($types)) {
4 ignored issues
show
introduced by
The condition is_array($format) is always true.
Loading history...
introduced by
The condition is_array($nulls) is always true.
Loading history...
introduced by
The condition is_array($vars) is always true.
Loading history...
introduced by
The condition is_array($types) is always true.
Loading history...
1911
            return -1;
1912
        }
1913
1914
        $f_schema = $this->_schema;
1915
        $this->fieldClean($f_schema);
1916
        $this->fieldClean($table);
1917
1918
        // Build clause
1919
        if (sizeof($vars) > 0) {
1920
            foreach ($vars as $key => $value) {
1921
                $this->fieldClean($key);
1922
1923
                // Handle NULL values
1924
                if (isset($nulls[$key])) {
1925
                    $tmp = 'NULL';
1926
                } else {
1927
                    $tmp = $this->formatValue($types[$key], $format[$key], $value);
1928
                }
1929
1930
                if (isset($sql)) {
1931
                    $sql .= ", \"{$key}\"={$tmp}";
1932
                } else {
1933
                    $sql = "UPDATE \"{$f_schema}\".\"{$table}\" SET \"{$key}\"={$tmp}";
1934
                }
1935
            }
1936
            $first = true;
1937
            foreach ($keyarr as $k => $v) {
1938
                $this->fieldClean($k);
1939
                $this->clean($v);
1940
                if ($first) {
1941
                    $sql .= " WHERE \"{$k}\"='{$v}'";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sql does not seem to be defined for all execution paths leading up to this point.
Loading history...
1942
                    $first = false;
1943
                } else {
1944
                    $sql .= " AND \"{$k}\"='{$v}'";
1945
                }
1946
            }
1947
        }
1948
1949
        // Begin transaction.  We do this so that we can ensure only one row is
1950
        // edited
1951
        $status = $this->beginTransaction();
1952
        if ($status != 0) {
1953
            $this->rollbackTransaction();
1954
1955
            return -1;
1956
        }
1957
1958
        $status = $this->execute($sql);
1959
        if ($status != 0) {
1960
            // update failed
1961
            $this->rollbackTransaction();
1962
1963
            return -1;
1964
        }
1965
1966
        if ($this->conn->Affected_Rows() != 1) {
1967
            // more than one row could be updated
1968
            $this->rollbackTransaction();
1969
1970
            return -2;
1971
        }
1972
1973
        // End transaction
1974
        return $this->endTransaction();
1975
    }
1976
1977
    /**
1978
     * Delete a row from a table.
1979
     *
1980
     * @param string $table  The table from which to delete
1981
     * @param array  $key    An array mapping column => value to delete
1982
     * @param string $schema the schema of the table
1983
     *
1984
     * @return bool|int 0 success
1985
     */
1986
    public function deleteRow($table, $key, $schema = '')
1987
    {
1988
        if (!is_array($key)) {
1 ignored issue
show
introduced by
The condition is_array($key) is always true.
Loading history...
1989
            return -1;
1990
        }
1991
1992
        // Begin transaction.  We do this so that we can ensure only one row is
1993
        // deleted
1994
        $status = $this->beginTransaction();
1995
        if ($status != 0) {
1996
            $this->rollbackTransaction();
1997
1998
            return -1;
1999
        }
2000
2001
        if ($schema === '') {
2002
            $schema = $this->_schema;
2003
        }
2004
2005
        $status = $this->delete($table, $key, $schema);
2006
        if ($status != 0 || $this->conn->Affected_Rows() != 1) {
2007
            $this->rollbackTransaction();
2008
2009
            return -2;
2010
        }
2011
2012
        // End transaction
2013
        return $this->endTransaction();
2014
    }
2015
2016
    /**
2017
     * Returns the SQL for changing the current user.
2018
     *
2019
     * @param string $user The user to change to
2020
     *
2021
     * @return string The SQL
2022
     */
2023
    public function getChangeUserSQL($user)
2024
    {
2025
        $this->clean($user);
2026
2027
        return "SET SESSION AUTHORIZATION '{$user}';";
2028
    }
2029
2030
    abstract public function fieldClean(&$str);
2031
2032
    abstract public function beginTransaction();
2033
2034
    abstract public function rollbackTransaction();
2035
2036
    abstract public function endTransaction();
2037
2038
    abstract public function execute($sql);
2039
2040
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
2041
2042
    abstract public function selectSet($sql);
2043
2044
    abstract public function clean(&$str);
2045
2046
    abstract public function phpBool($parameter);
2047
2048
    abstract public function hasCreateTableLikeWithConstraints();
2049
2050
    abstract public function hasCreateTableLikeWithIndexes();
2051
2052
    abstract public function hasTablespaces();
2053
2054
    abstract public function delete($table, $conditions, $schema = '');
2055
2056
    abstract public function fieldArrayClean(&$arr);
2057
2058
    abstract public function hasCreateFieldWithConstraints();
2059
2060
    abstract public function getAttributeNames($table, $atts);
2061
}
2062