Passed
Push — develop ( ce7da4...69770d )
by Felipe
05:27
created

TableTrait::addColumn()   D

Complexity

Conditions 13
Paths 240

Size

Total Lines 70
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 70
rs 4.3945
c 0
b 0
f 0
cc 13
eloc 41
nc 240
nop 8

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.40
5
 */
6
7
namespace PHPPgAdmin\Database;
8
9
/**
10
 * Common trait for tables manipulation.
11
 */
12
trait TableTrait
13
{
14
15
    /**
16
     * Return all tables in current database (and schema).
17
     *
18
     * @param bool|true $all True to fetch all tables, false for just in current schema
19
     *
20
     * @return \PHPPgAdmin\ADORecordSet All tables, sorted alphabetically
21
     */
22
    public function getTables($all = false)
23
    {
24
        $c_schema = $this->_schema;
25
        $this->clean($c_schema);
0 ignored issues
show
Bug introduced by
It seems like clean() 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

25
        $this->/** @scrutinizer ignore-call */ 
26
               clean($c_schema);
Loading history...
26
        if ($all) {
27
            // Exclude pg_catalog and information_schema tables
28
            $sql = "SELECT
29
                        schemaname AS nspname,
30
                        tablename AS relname,
31
                        tableowner AS relowner
32
                    FROM pg_catalog.pg_tables
33
                    WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
34
                    ORDER BY schemaname, tablename";
35
        } else {
36
            $sql = "
37
                SELECT c.relname,
38
                    pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
39
                    pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
40
                    reltuples::bigint,
41
                    pt.spcname as tablespace,
42
                    pg_size_pretty(pg_total_relation_size(c.oid)) as table_size
43
                FROM pg_catalog.pg_class c
44
                LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
45
                LEFT JOIN  pg_catalog.pg_tablespace pt ON  pt.oid=c.reltablespace
46
                WHERE c.relkind = 'r'
47
                AND nspname='{$c_schema}'
48
                ORDER BY c.relname";
49
        }
50
51
        return $this->selectSet($sql);
0 ignored issues
show
Bug introduced by
It seems like selectSet() 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

51
        return $this->/** @scrutinizer ignore-call */ selectSet($sql);
Loading history...
52
    }
53
54
    /**
55
     * Finds the names and schemas of parent tables (in order).
56
     *
57
     * @param $table The table to find the parents for
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
58
     *
59
     * @return \PHPPgAdmin\ADORecordSet A recordset
60
     */
61
    public function getTableParents($table)
62
    {
63
        $c_schema = $this->_schema;
64
        $this->clean($c_schema);
65
        $this->clean($table);
66
67
        $sql = "
68
            SELECT
69
                pn.nspname, relname
70
            FROM
71
                pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
72
            WHERE
73
                pc.oid=pi.inhparent
74
                AND pc.relnamespace=pn.oid
75
                AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
76
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
77
            ORDER BY
78
                pi.inhseqno
79
        ";
80
81
        return $this->selectSet($sql);
82
    }
83
84
    /**
85
     * Finds the names and schemas of child tables.
86
     *
87
     * @param $table The table to find the children for
88
     *
89
     * @return \PHPPgAdmin\ADORecordSet A recordset
90
     */
91
    public function getTableChildren($table)
92
    {
93
        $c_schema = $this->_schema;
94
        $this->clean($c_schema);
95
        $this->clean($table);
96
97
        $sql = "
98
            SELECT
99
                pn.nspname, relname
100
            FROM
101
                pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
102
            WHERE
103
                pc.oid=pi.inhrelid
104
                AND pc.relnamespace=pn.oid
105
                AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
106
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
107
        ";
108
109
        return $this->selectSet($sql);
110
    }
111
112
    /**
113
     * Returns the SQL definition for the table.
114
     *
115
     * @pre MUST be run within a transaction
116
     *
117
     * @param           $table The table to define
1 ignored issue
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
118
     * @param bool|true $clean True to issue drop command, false otherwise
119
     *
120
     * @return string A string containing the formatted SQL code
121
     */
122
    public function getTableDefPrefix($table, $clean = false)
123
    {
124
        // Fetch table
125
        $t = $this->getTable($table);
126
        if (!is_object($t) || $t->recordCount() != 1) {
0 ignored issues
show
introduced by
The condition $t->recordCount() != 1 is always true.
Loading history...
127
            $this->rollbackTransaction();
0 ignored issues
show
Bug introduced by
It seems like rollbackTransaction() 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

127
            $this->/** @scrutinizer ignore-call */ 
128
                   rollbackTransaction();
Loading history...
128
129
            return null;
130
        }
131
        $this->fieldClean($t->fields['relname']);
132
        $this->fieldClean($t->fields['nspname']);
133
134
        // Fetch attributes
135
        $atts = $this->getTableAttributes($table);
136
        if (!is_object($atts)) {
137
            $this->rollbackTransaction();
138
139
            return null;
140
        }
141
142
        // Fetch constraints
143
        $cons = $this->getConstraints($table);
144
        if (!is_object($cons)) {
145
            $this->rollbackTransaction();
146
147
            return null;
148
        }
149
150
        // Output a reconnect command to create the table as the correct user
151
        $sql = $this->getChangeUserSQL($t->fields['relowner']) . "\n\n";
152
153
        // Set schema search path
154
        $sql .= "SET search_path = \"{$t->fields['nspname']}\", pg_catalog;\n\n";
155
156
        // Begin CREATE TABLE definition
157
        $sql .= "-- Definition\n\n";
158
        // DROP TABLE must be fully qualified in case a table with the same name exists
159
        // in pg_catalog.
160
        if (!$clean) {
161
            $sql .= '-- ';
162
        }
163
164
        $sql .= 'DROP TABLE ';
165
        $sql .= "\"{$t->fields['nspname']}\".\"{$t->fields['relname']}\";\n";
166
        $sql .= "CREATE TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" (\n";
167
168
        // Output all table columns
169
        $col_comments_sql = ''; // Accumulate comments on columns
170
        $num              = $atts->recordCount() + $cons->recordCount();
171
        $i                = 1;
172
        while (!$atts->EOF) {
173
            $this->fieldClean($atts->fields['attname']);
174
            $sql .= "    \"{$atts->fields['attname']}\"";
175
            // Dump SERIAL and BIGSERIAL columns correctly
176
            if ($this->phpBool($atts->fields['attisserial']) &&
177
                ($atts->fields['type'] == 'integer' || $atts->fields['type'] == 'bigint')) {
2 ignored issues
show
Coding Style introduced by
Each line in a multi-line IF statement must begin with a boolean operator
Loading history...
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
178
                if ($atts->fields['type'] == 'integer') {
179
                    $sql .= ' SERIAL';
180
                } else {
181
                    $sql .= ' BIGSERIAL';
182
                }
183
            } else {
184
                $sql .= ' ' . $this->formatType($atts->fields['type'], $atts->fields['atttypmod']);
185
186
                // Add NOT NULL if necessary
187
                if ($this->phpBool($atts->fields['attnotnull'])) {
188
                    $sql .= ' NOT NULL';
189
                }
190
191
                // Add default if necessary
192
                if ($atts->fields['adsrc'] !== null) {
193
                    $sql .= " DEFAULT {$atts->fields['adsrc']}";
194
                }
195
            }
196
197
            // Output comma or not
198
            if ($i < $num) {
199
                $sql .= ",\n";
200
            } else {
201
                $sql .= "\n";
202
            }
203
204
            // Does this column have a comment?
205
            if ($atts->fields['comment'] !== null) {
206
                $this->clean($atts->fields['comment']);
207
                $col_comments_sql .= "COMMENT ON COLUMN \"{$t->fields['relname']}\".\"{$atts->fields['attname']}\"  IS '{$atts->fields['comment']}';\n";
208
            }
209
210
            $atts->moveNext();
211
            ++$i;
212
        }
213
        // Output all table constraints
214
        while (!$cons->EOF) {
215
            $this->fieldClean($cons->fields['conname']);
216
            $sql .= "    CONSTRAINT \"{$cons->fields['conname']}\" ";
217
            // Nasty hack to support pre-7.4 PostgreSQL
218
            if ($cons->fields['consrc'] !== null) {
219
                $sql .= $cons->fields['consrc'];
220
            } else {
221
                switch ($cons->fields['contype']) {
222
                    case 'p':
223
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
224
                        $sql .= 'PRIMARY KEY (' . join(',', $keys) . ')';
225
226
                        break;
227
                    case 'u':
228
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
229
                        $sql .= 'UNIQUE (' . join(',', $keys) . ')';
230
231
                        break;
232
                    default:
233
                        // Unrecognised constraint
234
                        $this->rollbackTransaction();
235
236
                        return null;
237
                }
238
            }
239
240
            // Output comma or not
241
            if ($i < $num) {
242
                $sql .= ",\n";
243
            } else {
244
                $sql .= "\n";
245
            }
246
247
            $cons->moveNext();
248
            ++$i;
249
        }
250
251
        $sql .= ')';
252
253
        // @@@@ DUMP CLUSTERING INFORMATION
254
255
        // Inherits
256
        /*
257
         * XXX: This is currently commented out as handling inheritance isn't this simple.
258
         * You also need to make sure you don't dump inherited columns and defaults, as well
259
         * as inherited NOT NULL and CHECK constraints.  So for the time being, we just do
260
         * not claim to support inheritance.
261
        $parents = $this->getTableParents($table);
262
        if ($parents->recordCount() > 0) {
263
        $sql .= " INHERITS (";
264
        while (!$parents->EOF) {
265
        $this->fieldClean($parents->fields['relname']);
266
        // Qualify the parent table if it's in another schema
267
        if ($parents->fields['schemaname'] != $this->_schema) {
268
        $this->fieldClean($parents->fields['schemaname']);
269
        $sql .= "\"{$parents->fields['schemaname']}\".";
270
        }
271
        $sql .= "\"{$parents->fields['relname']}\"";
272
273
        $parents->moveNext();
274
        if (!$parents->EOF) $sql .= ', ';
275
        }
276
        $sql .= ")";
277
        }
278
         */
279
280
        // Handle WITHOUT OIDS
281
        if ($this->hasObjectID($table)) {
282
            $sql .= ' WITH OIDS';
283
        } else {
284
            $sql .= ' WITHOUT OIDS';
285
        }
286
287
        $sql .= ";\n";
288
289
        // Column storage and statistics
290
        $atts->moveFirst();
291
        $first = true;
292
        while (!$atts->EOF) {
293
            $this->fieldClean($atts->fields['attname']);
294
            // Statistics first
295
            if ($atts->fields['attstattarget'] >= 0) {
296
                if ($first) {
297
                    $sql .= "\n";
298
                    $first = false;
299
                }
300
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STATISTICS {$atts->fields['attstattarget']};\n";
301
            }
302
            // Then storage
303
            if ($atts->fields['attstorage'] != $atts->fields['typstorage']) {
304
                switch ($atts->fields['attstorage']) {
305
                    case 'p':
306
                        $storage = 'PLAIN';
307
308
                        break;
309
                    case 'e':
310
                        $storage = 'EXTERNAL';
311
312
                        break;
313
                    case 'm':
314
                        $storage = 'MAIN';
315
316
                        break;
317
                    case 'x':
318
                        $storage = 'EXTENDED';
319
320
                        break;
321
                    default:
322
                        // Unknown storage type
323
                        $this->rollbackTransaction();
324
325
                        return null;
326
                }
327
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STORAGE {$storage};\n";
328
            }
329
330
            $atts->moveNext();
331
        }
332
333
        // Comment
334
        if ($t->fields['relcomment'] !== null) {
335
            $this->clean($t->fields['relcomment']);
336
            $sql .= "\n-- Comment\n\n";
337
            $sql .= "COMMENT ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" IS '{$t->fields['relcomment']}';\n";
338
        }
339
340
        // Add comments on columns, if any
341
        if ($col_comments_sql != '') {
342
            $sql .= $col_comments_sql;
343
        }
344
345
        // Privileges
346
        $privs = $this->getPrivileges($table, 'table');
347
        if (!is_array($privs)) {
348
            $this->rollbackTransaction();
349
350
            return null;
351
        }
352
353
        if (sizeof($privs) > 0) {
354
            $sql .= "\n-- Privileges\n\n";
355
            /*
356
             * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
357
             * wire-in knowledge about the default public privileges for different
358
             * kinds of objects.
359
             */
360
            $sql .= "REVOKE ALL ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" FROM PUBLIC;\n";
361
            foreach ($privs as $v) {
362
                // Get non-GRANT OPTION privs
363
                $nongrant = array_diff($v[2], $v[4]);
364
365
                // Skip empty or owner ACEs
366
                if (sizeof($v[2]) == 0 || ($v[0] == 'user' && $v[1] == $t->fields['relowner'])) {
367
                    continue;
368
                }
369
370
                // Change user if necessary
371
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
372
                    $grantor = $v[3];
373
                    $this->clean($grantor);
374
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
375
                }
376
377
                // Output privileges with no GRANT OPTION
378
                $sql .= 'GRANT ' . join(', ', $nongrant) . " ON TABLE \"{$t->fields['relname']}\" TO ";
379
                switch ($v[0]) {
380
                    case 'public':
381
                        $sql .= "PUBLIC;\n";
382
383
                        break;
384
                    case 'user':
385
                        $this->fieldClean($v[1]);
386
                        $sql .= "\"{$v[1]}\";\n";
387
388
                        break;
389
                    case 'group':
390
                        $this->fieldClean($v[1]);
391
                        $sql .= "GROUP \"{$v[1]}\";\n";
392
393
                        break;
394
                    default:
395
                        // Unknown privilege type - fail
396
                        $this->rollbackTransaction();
397
398
                        return null;
399
                }
400
401
                // Reset user if necessary
402
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
403
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
404
                }
405
406
                // Output privileges with GRANT OPTION
407
408
                // Skip empty or owner ACEs
409
                if (!$this->hasGrantOption() || sizeof($v[4]) == 0) {
410
                    continue;
411
                }
412
413
                // Change user if necessary
414
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
415
                    $grantor = $v[3];
416
                    $this->clean($grantor);
417
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
418
                }
419
420
                $sql .= 'GRANT ' . join(', ', $v[4]) . " ON \"{$t->fields['relname']}\" TO ";
421
                switch ($v[0]) {
422
                    case 'public':
423
                        $sql .= 'PUBLIC';
424
425
                        break;
426
                    case 'user':
427
                        $this->fieldClean($v[1]);
428
                        $sql .= "\"{$v[1]}\"";
429
430
                        break;
431
                    case 'group':
432
                        $this->fieldClean($v[1]);
433
                        $sql .= "GROUP \"{$v[1]}\"";
434
435
                        break;
436
                    default:
437
                        // Unknown privilege type - fail
438
                        return null;
439
                }
440
                $sql .= " WITH GRANT OPTION;\n";
441
442
                // Reset user if necessary
443
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
444
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
445
                }
446
            }
447
        }
448
449
        // Add a newline to separate data that follows (if any)
450
        $sql .= "\n";
451
452
        return $sql;
453
    }
454
455
    /**
456
     * Returns table information.
457
     *
458
     * @param $table The name of the table
459
     *
460
     * @return \PHPPgAdmin\ADORecordSet A recordset
461
     */
462
    public function getTable($table)
463
    {
464
        $c_schema = $this->_schema;
465
        $this->clean($c_schema);
466
        $this->clean($table);
467
468
        $sql = "
469
            SELECT
470
              c.relname, n.nspname, u.usename AS relowner,
471
              pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
472
              (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
473
            FROM pg_catalog.pg_class c
474
                 LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
475
                 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
476
            WHERE c.relkind = 'r'
477
                  AND n.nspname = '{$c_schema}'
478
                  AND n.oid = c.relnamespace
479
                  AND c.relname = '{$table}'";
480
481
        return $this->selectSet($sql);
482
    }
483
484
    /**
485
     * Retrieve the attribute definition of a table.
486
     *
487
     * @param $table The name of the table
488
     * @param $field (optional) The name of a field to return
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\optional was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
489
     *
490
     * @return All attributes in order
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\All was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
491
     */
492
    public function getTableAttributes($table, $field = '')
493
    {
494
        $c_schema = $this->_schema;
495
        $this->clean($c_schema);
496
        $this->clean($table);
497
        $this->clean($field);
498
499
        if ($field == '') {
500
            // This query is made much more complex by the addition of the 'attisserial' field.
501
            // The subquery to get that field checks to see if there is an internally dependent
502
            // sequence on the field.
503
            $sql = "
504
                SELECT
505
                    a.attname, a.attnum,
506
                    pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
507
                    a.atttypmod,
508
                    a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc,
509
                    a.attstattarget, a.attstorage, t.typstorage,
510
                    (
511
                        SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
512
                        WHERE pd.objid=pc.oid
513
                        AND pd.classid=pc.tableoid
514
                        AND pd.refclassid=pc.tableoid
515
                        AND pd.refobjid=a.attrelid
516
                        AND pd.refobjsubid=a.attnum
517
                        AND pd.deptype='i'
518
                        AND pc.relkind='S'
519
                    ) IS NOT NULL AS attisserial,
520
                    pg_catalog.col_description(a.attrelid, a.attnum) AS comment
521
                FROM
522
                    pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
523
                    ON a.attrelid=adef.adrelid
524
                    AND a.attnum=adef.adnum
525
                    LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
526
                WHERE
527
                    a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
528
                        AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
529
                        nspname = '{$c_schema}'))
530
                    AND a.attnum > 0 AND NOT a.attisdropped
531
                ORDER BY a.attnum";
532
        } else {
533
            $sql = "
534
                SELECT
535
                    a.attname, a.attnum,
536
                    pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
537
                    pg_catalog.format_type(a.atttypid, NULL) as base_type,
538
                    a.atttypmod,
539
                    a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc,
540
                    a.attstattarget, a.attstorage, t.typstorage,
541
                    pg_catalog.col_description(a.attrelid, a.attnum) AS comment
542
                FROM
543
                    pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
544
                    ON a.attrelid=adef.adrelid
545
                    AND a.attnum=adef.adnum
546
                    LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
547
                WHERE
548
                    a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
549
                        AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
550
                        nspname = '{$c_schema}'))
551
                    AND a.attname = '{$field}'";
552
        }
553
554
        return $this->selectSet($sql);
555
    }
556
557
    /**
558
     * Returns a list of all constraints on a table.
559
     *
560
     * @param $table The table to find rules for
561
     *
562
     * @return \PHPPgAdmin\ADORecordSet A recordset
563
     */
564
    public function getConstraints($table)
565
    {
566
        $c_schema = $this->_schema;
567
        $this->clean($c_schema);
568
        $this->clean($table);
569
570
        // This SQL is greatly complicated by the need to retrieve
571
        // index clustering information for primary and unique constraints
572
        $sql = "SELECT
573
                pc.conname,
574
                pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc,
575
                pc.contype,
576
                CASE WHEN pc.contype='u' OR pc.contype='p' THEN (
577
                    SELECT
578
                        indisclustered
579
                    FROM
580
                        pg_catalog.pg_depend pd,
581
                        pg_catalog.pg_class pl,
582
                        pg_catalog.pg_index pi
583
                    WHERE
584
                        pd.refclassid=pc.tableoid
585
                        AND pd.refobjid=pc.oid
586
                        AND pd.objid=pl.oid
587
                        AND pl.oid=pi.indexrelid
588
                ) ELSE
589
                    NULL
590
                END AS indisclustered
591
            FROM
592
                pg_catalog.pg_constraint pc
593
            WHERE
594
                pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
595
                    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
596
                    WHERE nspname='{$c_schema}'))
597
            ORDER BY
598
                1
599
        ";
600
601
        return $this->selectSet($sql);
602
    }
603
604
    /**
605
     * Checks to see whether or not a table has a unique id column.
606
     *
607
     * @param $table The table name
608
     *
609
     * @return true if it has a unique id, false otherwise
610
     */
611
    public function hasObjectID($table)
612
    {
613
        $c_schema = $this->_schema;
614
        $this->clean($c_schema);
615
        $this->clean($table);
616
617
        $sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}'
618
            AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')";
619
620
        $rs = $this->selectSet($sql);
621
        if ($rs->recordCount() != 1) {
622
            return null;
623
        }
624
625
        $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']);
0 ignored issues
show
Bug introduced by
It seems like phpBool() 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

625
        /** @scrutinizer ignore-call */ 
626
        $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']);
Loading history...
626
627
        return $rs->fields['relhasoids'];
628
    }
629
630
    /**
631
     * Returns extra table definition information that is most usefully
632
     * dumped after the table contents for speed and efficiency reasons.
633
     *
634
     * @param $table The table to define
635
     *
636
     * @return A string containing the formatted SQL code
637
     */
638
    public function getTableDefSuffix($table)
639
    {
640
        $sql = '';
641
642
        // Indexes
643
        $indexes = $this->getIndexes($table);
644
        if (!is_object($indexes)) {
645
            $this->rollbackTransaction();
646
647
            return null;
648
        }
649
650
        if ($indexes->recordCount() > 0) {
651
            $sql .= "\n-- Indexes\n\n";
652
            while (!$indexes->EOF) {
653
                $sql .= $indexes->fields['inddef'] . ";\n";
654
655
                $indexes->moveNext();
656
            }
657
        }
658
659
        // Triggers
660
        $triggers = $this->getTriggers($table);
661
        if (!is_object($triggers)) {
662
            $this->rollbackTransaction();
663
664
            return null;
665
        }
666
667
        if ($triggers->recordCount() > 0) {
668
            $sql .= "\n-- Triggers\n\n";
669
            while (!$triggers->EOF) {
670
                $sql .= $triggers->fields['tgdef'];
671
                $sql .= ";\n";
672
673
                $triggers->moveNext();
674
            }
675
        }
676
677
        // Rules
678
        $rules = $this->getRules($table);
679
        if (!is_object($rules)) {
680
            $this->rollbackTransaction();
681
682
            return null;
683
        }
684
685
        if ($rules->recordCount() > 0) {
686
            $sql .= "\n-- Rules\n\n";
687
            while (!$rules->EOF) {
688
                $sql .= $rules->fields['definition'] . "\n";
689
690
                $rules->moveNext();
691
            }
692
        }
693
694
        return $sql;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $sql returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\A.
Loading history...
695
    }
696
697
    /**
698
     * Grabs a list of indexes for a table.
699
     *
700
     * @param string                         $table  The name of a table whose indexes to retrieve
701
     * @param bool|\PHPPgAdmin\Database\Only $unique Only get unique/pk indexes
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Only was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
702
     *
703
     * @return \PHPPgAdmin\ADORecordSet A recordset
704
     */
705
    public function getIndexes($table = '', $unique = false)
706
    {
707
        $this->clean($table);
708
709
        $sql = "
710
            SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered,
711
                pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef
712
            FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
713
            WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid)
714
                AND c.oid = i.indrelid AND i.indexrelid = c2.oid
715
        ";
716
        if ($unique) {
717
            $sql .= ' AND i.indisunique ';
718
        }
719
720
        $sql .= ' ORDER BY c2.relname';
721
722
        return $this->selectSet($sql);
723
    }
724
725
    /**
726
     * Grabs a list of triggers on a table.
727
     *
728
     * @param string $table The name of a table whose triggers to retrieve
729
     *
730
     * @return \PHPPgAdmin\ADORecordSet A recordset
731
     */
732
    public function getTriggers($table = '')
733
    {
734
        $c_schema = $this->_schema;
735
        $this->clean($c_schema);
736
        $this->clean($table);
737
738
        $sql = "SELECT
739
                t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef,
740
                CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid,
741
                p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
742
                ns.nspname AS pronamespace
743
            FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
744
            WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
745
                AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}'))
746
                AND ( tgconstraint = 0 OR NOT EXISTS
747
                        (SELECT 1 FROM pg_catalog.pg_depend d    JOIN pg_catalog.pg_constraint c
748
                            ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
749
                        WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
750
                AND p.oid=t.tgfoid
751
                AND p.pronamespace = ns.oid";
752
753
        return $this->selectSet($sql);
754
    }
755
756
    /**
757
     * Returns a list of all rules on a table OR view.
758
     *
759
     * @param $table The table to find rules for
760
     *
761
     * @return \PHPPgAdmin\ADORecordSet A recordset
762
     */
763
    public function getRules($table)
764
    {
765
        $c_schema = $this->_schema;
766
        $this->clean($c_schema);
767
        $this->clean($table);
768
769
        $sql = "
770
            SELECT *
771
            FROM pg_catalog.pg_rules
772
            WHERE
773
                schemaname='{$c_schema}' AND tablename='{$table}'
774
            ORDER BY rulename
775
        ";
776
777
        return $this->selectSet($sql);
778
    }
779
780
    /**
781
     * Creates a new table in the database.
782
     *
783
     * @param $name        The name of the table
784
     * @param $fields      The number of fields
785
     * @param $field       An array of field names
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\An was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
786
     * @param $type        An array of field types
787
     * @param $array       An array of '' or '[]' for each type if it's an array or not
788
     * @param $length      An array of field lengths
789
     * @param $notnull     An array of not null
790
     * @param $default     An array of default values
791
     * @param $withoutoids True if WITHOUT OIDS, false otherwise
792
     * @param $colcomment  An array of comments
793
     * @param $tblcomment
794
     * @param $tablespace  The tablespace name ('' means none/default)
795
     * @param $uniquekey   An Array indicating the fields that are unique (those indexes that are set)
796
     * @param $primarykey  An Array indicating the field used for the primarykey (those indexes that are set)
797
     *
798
     * @return bool|int 0 success
799
     *
800
     * @internal param \PHPPgAdmin\Database\Table $comment comment
801
     */
802
    public function createTable(
803
        $name,
804
        $fields,
805
        $field,
806
        $type,
807
        $array,
808
        $length,
809
        $notnull,
810
        $default,
811
        $withoutoids,
812
        $colcomment,
813
        $tblcomment,
814
        $tablespace,
815
        $uniquekey,
816
        $primarykey
817
    ) {
818
        $f_schema = $this->_schema;
819
        $this->fieldClean($f_schema);
0 ignored issues
show
Bug introduced by
It seems like fieldClean() 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

819
        $this->/** @scrutinizer ignore-call */ 
820
               fieldClean($f_schema);
Loading history...
820
        $this->fieldClean($name);
821
822
        $status = $this->beginTransaction();
0 ignored issues
show
Bug introduced by
It seems like beginTransaction() 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

822
        /** @scrutinizer ignore-call */ 
823
        $status = $this->beginTransaction();
Loading history...
823
        if ($status != 0) {
824
            return -1;
825
        }
826
827
        $found       = false;
828
        $first       = true;
829
        $comment_sql = ''; //Accumulate comments for the columns
830
        $sql         = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (";
831
        for ($i = 0; $i < $fields; ++$i) {
832
            $this->fieldClean($field[$i]);
833
            $this->clean($type[$i]);
834
            $this->clean($length[$i]);
835
            $this->clean($colcomment[$i]);
836
837
            // Skip blank columns - for user convenience
838
            if ($field[$i] == '' || $type[$i] == '') {
839
                continue;
840
            }
841
842
            // If not the first column, add a comma
843
            if (!$first) {
844
                $sql .= ', ';
845
            } else {
846
                $first = false;
847
            }
848
849
            switch ($type[$i]) {
850
                // Have to account for weird placing of length for with/without
851
                // time zone types
852
                case 'timestamp with time zone':
853
                case 'timestamp without time zone':
854
                    $qual = substr($type[$i], 9);
855
                    $sql .= "\"{$field[$i]}\" timestamp";
856
                    if ($length[$i] != '') {
857
                        $sql .= "({$length[$i]})";
858
                    }
859
860
                    $sql .= $qual;
861
862
                    break;
863
                case 'time with time zone':
864
                case 'time without time zone':
865
                    $qual = substr($type[$i], 4);
866
                    $sql .= "\"{$field[$i]}\" time";
867
                    if ($length[$i] != '') {
868
                        $sql .= "({$length[$i]})";
869
                    }
870
871
                    $sql .= $qual;
872
873
                    break;
874
                default:
875
                    $sql .= "\"{$field[$i]}\" {$type[$i]}";
876
                    if ($length[$i] != '') {
877
                        $sql .= "({$length[$i]})";
878
                    }
879
            }
880
            // Add array qualifier if necessary
881
            if ($array[$i] == '[]') {
882
                $sql .= '[]';
883
            }
884
885
            // Add other qualifiers
886
            if (!isset($primarykey[$i])) {
887
                if (isset($uniquekey[$i])) {
888
                    $sql .= ' UNIQUE';
889
                }
890
891
                if (isset($notnull[$i])) {
892
                    $sql .= ' NOT NULL';
893
                }
894
            }
895
            if ($default[$i] != '') {
896
                $sql .= " DEFAULT {$default[$i]}";
897
            }
898
899
            if ($colcomment[$i] != '') {
900
                $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
901
            }
902
903
            $found = true;
904
        }
905
906
        if (!$found) {
0 ignored issues
show
introduced by
The condition $found is always false.
Loading history...
907
            return -1;
908
        }
909
910
        // PRIMARY KEY
911
        $primarykeycolumns = [];
912
        for ($i = 0; $i < $fields; ++$i) {
913
            if (isset($primarykey[$i])) {
914
                $primarykeycolumns[] = "\"{$field[$i]}\"";
915
            }
916
        }
917
        if (count($primarykeycolumns) > 0) {
918
            $sql .= ', PRIMARY KEY (' . implode(', ', $primarykeycolumns) . ')';
919
        }
920
921
        $sql .= ')';
922
923
        // WITHOUT OIDS
924
        if ($withoutoids) {
925
            $sql .= ' WITHOUT OIDS';
926
        } else {
927
            $sql .= ' WITH OIDS';
928
        }
929
930
        // Tablespace
931
        if ($this->hasTablespaces() && $tablespace != '') {
932
            $this->fieldClean($tablespace);
933
            $sql .= " TABLESPACE \"{$tablespace}\"";
934
        }
935
936
        $status = $this->execute($sql);
937
        if ($status) {
938
            $this->rollbackTransaction();
939
940
            return -1;
941
        }
942
943
        if ($tblcomment != '') {
944
            $status = $this->setComment('TABLE', '', $name, $tblcomment, true);
945
            if ($status) {
946
                $this->rollbackTransaction();
947
948
                return -1;
949
            }
950
        }
951
952
        if ($comment_sql != '') {
953
            $status = $this->execute($comment_sql);
954
            if ($status) {
955
                $this->rollbackTransaction();
956
957
                return -1;
958
            }
959
        }
960
961
        return $this->endTransaction();
962
    }
963
964
    /**
965
     * Creates a new table in the database copying attribs and other properties from another table.
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\an was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
966
     *
967
     * @param        $name        The name of the table
968
     * @param        $like        an array giving the schema ans the name of the table from which attribs are copying
969
     *                            from: array(
970
     *                            'table' => table name,
971
     *                            'schema' => the schema name,
972
     *                            )
973
     * @param bool   $defaults    if true, copy the defaults values as well
974
     * @param bool   $constraints if true, copy the constraints as well (CHECK on table & attr)
975
     * @param bool   $idx
976
     * @param string $tablespace  The tablespace name ('' means none/default)
977
     *
978
     * @return bool|int
979
     */
980
    public function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '')
981
    {
982
        $f_schema = $this->_schema;
983
        $this->fieldClean($f_schema);
984
        $this->fieldClean($name);
985
        $this->fieldClean($like['schema']);
986
        $this->fieldClean($like['table']);
987
        $like = "\"{$like['schema']}\".\"{$like['table']}\"";
988
989
        $status = $this->beginTransaction();
990
        if ($status != 0) {
991
            return -1;
992
        }
993
994
        $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (LIKE {$like}";
995
996
        if ($defaults) {
997
            $sql .= ' INCLUDING DEFAULTS';
998
        }
999
1000
        if ($this->hasCreateTableLikeWithConstraints() && $constraints) {
0 ignored issues
show
Bug introduced by
It seems like hasCreateTableLikeWithConstraints() 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

1000
        if ($this->/** @scrutinizer ignore-call */ hasCreateTableLikeWithConstraints() && $constraints) {
Loading history...
1001
            $sql .= ' INCLUDING CONSTRAINTS';
1002
        }
1003
1004
        if ($this->hasCreateTableLikeWithIndexes() && $idx) {
0 ignored issues
show
Bug introduced by
It seems like hasCreateTableLikeWithIndexes() 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

1004
        if ($this->/** @scrutinizer ignore-call */ hasCreateTableLikeWithIndexes() && $idx) {
Loading history...
1005
            $sql .= ' INCLUDING INDEXES';
1006
        }
1007
1008
        $sql .= ')';
1009
1010
        if ($this->hasTablespaces() && $tablespace != '') {
0 ignored issues
show
Bug introduced by
It seems like hasTablespaces() 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

1010
        if ($this->/** @scrutinizer ignore-call */ hasTablespaces() && $tablespace != '') {
Loading history...
1011
            $this->fieldClean($tablespace);
1012
            $sql .= " TABLESPACE \"{$tablespace}\"";
1013
        }
1014
1015
        $status = $this->execute($sql);
0 ignored issues
show
Bug introduced by
It seems like execute() 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

1015
        /** @scrutinizer ignore-call */ 
1016
        $status = $this->execute($sql);
Loading history...
1016
        if ($status) {
1017
            $this->rollbackTransaction();
1018
1019
            return -1;
1020
        }
1021
1022
        return $this->endTransaction();
0 ignored issues
show
Bug introduced by
It seems like endTransaction() 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

1022
        return $this->/** @scrutinizer ignore-call */ endTransaction();
Loading history...
1023
    }
1024
1025
    /**
1026
     * Alter table properties.
1027
     *
1028
     * @param $table      The name of the table
1029
     * @param $name       The new name for the table
1030
     * @param $owner      The new owner for the table
1031
     * @param $schema     The new schema for the table
1032
     * @param $comment    The comment on the table
1033
     * @param $tablespace The new tablespace for the table ('' means leave as is)
1034
     *
1035
     * @return bool|int 0 success
1036
     */
1037
    public function alterTable($table, $name, $owner, $schema, $comment, $tablespace)
1038
    {
1039
        $data = $this->getTable($table);
1040
1041
        if ($data->recordCount() != 1) {
0 ignored issues
show
introduced by
The condition $data->recordCount() != 1 is always true.
Loading history...
1042
            return -2;
1043
        }
1044
1045
        $status = $this->beginTransaction();
1046
        if ($status != 0) {
1047
            $this->rollbackTransaction();
1048
1049
            return -1;
1050
        }
1051
1052
        $status = $this->_alterTable($data, $name, $owner, $schema, $comment, $tablespace);
1053
1054
        if ($status != 0) {
1055
            $this->rollbackTransaction();
1056
1057
            return $status;
1058
        }
1059
1060
        return $this->endTransaction();
1061
    }
1062
1063
    /**
1064
     * Protected method which alter a table
1065
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION.
1066
     *
1067
     * @param $tblrs      The table recordSet returned by getTable()
1068
     * @param $name       The new name for the table
1069
     * @param $owner      The new owner for the table
1070
     * @param $schema     The new schema for the table
1071
     * @param $comment    The comment on the table
1072
     * @param $tablespace The new tablespace for the table ('' means leave as is)
1073
     *
1074
     * @return int 0 success
1075
     */
1076
    protected function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace)
1 ignored issue
show
Coding Style introduced by
Protected method name "TableTrait::_alterTable" must not be prefixed with an underscore
Loading history...
1077
    {
1078
        $this->fieldArrayClean($tblrs->fields);
0 ignored issues
show
Bug introduced by
It seems like fieldArrayClean() 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

1078
        $this->/** @scrutinizer ignore-call */ 
1079
               fieldArrayClean($tblrs->fields);
Loading history...
1079
1080
        // Comment
1081
        $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
0 ignored issues
show
Bug introduced by
It seems like setComment() 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

1081
        /** @scrutinizer ignore-call */ 
1082
        $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
Loading history...
1082
        if ($status != 0) {
1083
            return -4;
1084
        }
1085
1086
        // Owner
1087
        $this->fieldClean($owner);
1088
        $status = $this->alterTableOwner($tblrs, $owner);
1089
        if ($status != 0) {
1090
            return -5;
1091
        }
1092
1093
        // Tablespace
1094
        $this->fieldClean($tablespace);
1095
        $status = $this->alterTableTablespace($tblrs, $tablespace);
1096
        if ($status != 0) {
1097
            return -6;
1098
        }
1099
1100
        // Rename
1101
        $this->fieldClean($name);
1102
        $status = $this->alterTableName($tblrs, $name);
1103
        if ($status != 0) {
1104
            return -3;
1105
        }
1106
1107
        // Schema
1108
        $this->fieldClean($schema);
1109
        $status = $this->alterTableSchema($tblrs, $schema);
1110
        if ($status != 0) {
1111
            return -7;
1112
        }
1113
1114
        return 0;
1115
    }
1116
1117
    /**
1118
     * Alter a table's owner
1119
     * /!\ this function is called from _alterTable which take care of escaping fields.
1120
     *
1121
     * @param      $tblrs The table RecordSet returned by getTable()
1122
     * @param null $owner
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $owner is correct as it would always require null to be passed?
Loading history...
1123
     *
1124
     * @return int 0 if operation was successful
1125
     *
1126
     * @internal param \PHPPgAdmin\Database\The $name new table's owner
1127
     */
1128
    public function alterTableOwner($tblrs, $owner = null)
1129
    {
1130
        /* vars cleaned in _alterTable */
1131
        if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
1132
            $f_schema = $this->_schema;
1133
            $this->fieldClean($f_schema);
1134
            // If owner has been changed, then do the alteration.  We are
1135
            // careful to avoid this generally as changing owner is a
1136
            // superuser only function.
1137
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" OWNER TO \"{$owner}\"";
1138
1139
            return $this->execute($sql);
1140
        }
1141
1142
        return 0;
1143
    }
1144
1145
    /**
1146
     * Alter a table's tablespace
1147
     * /!\ this function is called from _alterTable which take care of escaping fields.
1148
     *
1149
     * @param      $tblrs      The table RecordSet returned by getTable()
1150
     * @param null $tablespace
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $tablespace is correct as it would always require null to be passed?
Loading history...
1151
     *
1152
     * @return int 0 if operation was successful
1153
     *
1154
     * @internal param \PHPPgAdmin\Database\The $name new table's tablespace
1155
     */
1156
    public function alterTableTablespace($tblrs, $tablespace = null)
1157
    {
1158
        /* vars cleaned in _alterTable */
1159
        if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) {
1160
            $f_schema = $this->_schema;
1161
            $this->fieldClean($f_schema);
1162
1163
            // If tablespace has been changed, then do the alteration.  We
1164
            // don't want to do this unnecessarily.
1165
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET TABLESPACE \"{$tablespace}\"";
1166
1167
            return $this->execute($sql);
1168
        }
1169
1170
        return 0;
1171
    }
1172
1173
    /**
1174
     * Alter a table's name
1175
     * /!\ this function is called from _alterTable which take care of escaping fields.
1176
     *
1177
     * @param $tblrs The table RecordSet returned by getTable()
1178
     * @param $name  The new table's name
1179
     *
1180
     * @return int 0 if operation was successful
1181
     */
1182
    public function alterTableName($tblrs, $name = null)
1183
    {
1184
        /* vars cleaned in _alterTable */
1185
        // Rename (only if name has changed)
1186
        if (!empty($name) && ($name != $tblrs->fields['relname'])) {
1187
            $f_schema = $this->_schema;
1188
            $this->fieldClean($f_schema);
1189
1190
            $sql    = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" RENAME TO \"{$name}\"";
1191
            $status = $this->execute($sql);
1192
            if ($status == 0) {
1193
                $tblrs->fields['relname'] = $name;
1194
            } else {
1195
                return $status;
1196
            }
1197
        }
1198
1199
        return 0;
1200
    }
1201
1202
    // Row functions
1203
1204
    /**
1205
     * Alter a table's schema
1206
     * /!\ this function is called from _alterTable which take care of escaping fields.
1207
     *
1208
     * @param      $tblrs  The table RecordSet returned by getTable()
1209
     * @param null $schema
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $schema is correct as it would always require null to be passed?
Loading history...
1210
     *
1211
     * @return int 0 if operation was successful
1212
     *
1213
     * @internal param \PHPPgAdmin\Database\The $name new table's schema
1214
     */
1215
    public function alterTableSchema($tblrs, $schema = null)
1216
    {
1217
        /* vars cleaned in _alterTable */
1218
        if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) {
1219
            $f_schema = $this->_schema;
1220
            $this->fieldClean($f_schema);
1221
            // If tablespace has been changed, then do the alteration.  We
1222
            // don't want to do this unnecessarily.
1223
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET SCHEMA \"{$schema}\"";
1224
1225
            return $this->execute($sql);
1226
        }
1227
1228
        return 0;
1229
    }
1230
1231
    /**
1232
     * Empties a table in the database.
1233
     *
1234
     * @param $table The table to be emptied
1235
     * @param $cascade True to cascade truncate, false to restrict
1236
     *
1237
     * @return int 0 if operation was successful
1238
     */
1239
    public function emptyTable($table, $cascade)
1240
    {
1241
        $f_schema = $this->_schema;
1242
        $this->fieldClean($f_schema);
1243
        $this->fieldClean($table);
1244
1245
        $sql = "TRUNCATE TABLE \"{$f_schema}\".\"{$table}\" ";
1246
        if ($cascade) {
1247
            $sql = $sql . ' CASCADE';
1248
        }
1249
1250
        $status = $this->execute($sql);
1251
1252
        return [$status, $sql];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($status, $sql) returns the type array<integer,mixed|string> which is incompatible with the documented return type integer.
Loading history...
1253
    }
1254
1255
    /**
1256
     * Removes a table from the database.
1257
     *
1258
     * @param $table   The table to drop
1259
     * @param $cascade True to cascade drop, false to restrict
1260
     *
1261
     * @return int 0 if operation was successful
1262
     */
1263
    public function dropTable($table, $cascade)
1264
    {
1265
        $f_schema = $this->_schema;
1266
        $this->fieldClean($f_schema);
1267
        $this->fieldClean($table);
1268
1269
        $sql = "DROP TABLE \"{$f_schema}\".\"{$table}\"";
1270
        if ($cascade) {
1271
            $sql .= ' CASCADE';
1272
        }
1273
1274
        return $this->execute($sql);
1275
    }
1276
1277
    /**
1278
     * Add a new column to a table.
1279
     *
1280
     * @param $table   The table to add to
1281
     * @param $column  The name of the new column
1282
     * @param $type    The type of the column
1283
     * @param $array   True if array type, false otherwise
1284
     * @param $length  The optional size of the column (ie. 30 for varchar(30))
1285
     * @param $notnull True if NOT NULL, false otherwise
1286
     * @param $default The default for the column.  '' for none.
1287
     * @param $comment
1288
     *
1289
     * @return bool|int 0 success
1290
     */
1291
    public function addColumn($table, $column, $type, $array, $length, $notnull, $default, $comment)
1292
    {
1293
        $f_schema = $this->_schema;
1294
        $this->fieldClean($f_schema);
1295
        $this->fieldClean($table);
1296
        $this->fieldClean($column);
1297
        $this->clean($type);
1298
        $this->clean($length);
1299
1300
        if ($length == '') {
1301
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$type}";
1302
        } else {
1303
            switch ($type) {
1304
                // Have to account for weird placing of length for with/without
1305
                // time zone types
1306
                case 'timestamp with time zone':
1307
                case 'timestamp without time zone':
1308
                    $qual = substr($type, 9);
1309
                    $sql  = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" timestamp({$length}){$qual}";
1310
1311
                    break;
1312
                case 'time with time zone':
1313
                case 'time without time zone':
1314
                    $qual = substr($type, 4);
1315
                    $sql  = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" time({$length}){$qual}";
1316
1317
                    break;
1318
                default:
1319
                    $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$type}({$length})";
1320
            }
1321
        }
1322
1323
        // Add array qualifier, if requested
1324
        if ($array) {
1325
            $sql .= '[]';
1326
        }
1327
1328
        // If we have advanced column adding, add the extra qualifiers
1329
        if ($this->hasCreateFieldWithConstraints()) {
0 ignored issues
show
Bug introduced by
It seems like hasCreateFieldWithConstraints() 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

1329
        if ($this->/** @scrutinizer ignore-call */ hasCreateFieldWithConstraints()) {
Loading history...
1330
            // NOT NULL clause
1331
            if ($notnull) {
1332
                $sql .= ' NOT NULL';
1333
            }
1334
1335
            // DEFAULT clause
1336
            if ($default != '') {
1337
                $sql .= ' DEFAULT ' . $default;
1338
            }
1339
        }
1340
1341
        $status = $this->beginTransaction();
1342
        if ($status != 0) {
1343
            return -1;
1344
        }
1345
1346
        $status = $this->execute($sql);
1347
        if ($status != 0) {
1348
            $this->rollbackTransaction();
1349
1350
            return -1;
1351
        }
1352
1353
        $status = $this->setComment('COLUMN', $column, $table, $comment);
1354
        if ($status != 0) {
1355
            $this->rollbackTransaction();
1356
1357
            return -1;
1358
        }
1359
1360
        return $this->endTransaction();
1361
    }
1362
1363
    /**
1364
     * Alters a column in a table.
1365
     *
1366
     * @param $table      The table in which the column resides
1367
     * @param $column     The column to alter
1368
     * @param $name       The new name for the column
1369
     * @param $notnull    (boolean) True if not null, false otherwise
1370
     * @param $oldnotnull (boolean) True if column is already not null, false otherwise
1371
     * @param $default    The new default for the column
1372
     * @param $olddefault The old default for the column
1373
     * @param $type       The new type for the column
1374
     * @param $length     The optional size of the column (ie. 30 for varchar(30))
1375
     * @param $array      True if array type, false otherwise
1376
     * @param $oldtype    The old type for the column
1377
     * @param $comment    Comment for the column
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\Comment was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1378
     *
1379
     * @return array 0 success
1380
     */
1381
    public function alterColumn(
1382
        $table,
1383
        $column,
1384
        $name,
1385
        $notnull,
1386
        $oldnotnull,
1387
        $default,
1388
        $olddefault,
1389
        $type,
1390
        $length,
1391
        $array,
1392
        $oldtype,
1393
        $comment
1394
    ) {
1395
        // Begin transaction
1396
        $status = $this->beginTransaction();
1397
        $sql    = '';
1398
        if ($status != 0) {
1399
            $this->rollbackTransaction();
1400
1401
            return [-6, $sql];
1402
        }
1403
1404
        // Rename the column, if it has been changed
1405
        if ($column != $name) {
1406
            $status = $this->renameColumn($table, $column, $name);
1407
            if ($status != 0) {
1408
                $this->rollbackTransaction();
1409
1410
                return [-4, $sql];
1411
            }
1412
        }
1413
1414
        $f_schema = $this->_schema;
1415
        $this->fieldClean($f_schema);
1416
        $this->fieldClean($name);
1417
        $this->fieldClean($table);
1418
        $this->fieldClean($column);
1419
1420
        $toAlter = [];
1421
        // Create the command for changing nullability
1422
        if ($notnull != $oldnotnull) {
1423
            $toAlter[] = "ALTER COLUMN \"{$name}\" " . ($notnull ? 'SET' : 'DROP') . ' NOT NULL';
1424
        }
1425
1426
        // Add default, if it has changed
1427
        if ($default != $olddefault) {
1428
            if ($default == '') {
1429
                $toAlter[] = "ALTER COLUMN \"{$name}\" DROP DEFAULT";
1430
            } else {
1431
                $toAlter[] = "ALTER COLUMN \"{$name}\" SET DEFAULT {$default}";
1432
            }
1433
        }
1434
1435
        // Add type, if it has changed
1436
        if ($length == '') {
1437
            $ftype = $type;
1438
        } else {
1439
            switch ($type) {
1440
                // Have to account for weird placing of length for with/without
1441
                // time zone types
1442
                case 'timestamp with time zone':
1443
                case 'timestamp without time zone':
1444
                    $qual  = substr($type, 9);
1445
                    $ftype = "timestamp({$length}){$qual}";
1446
1447
                    break;
1448
                case 'time with time zone':
1449
                case 'time without time zone':
1450
                    $qual  = substr($type, 4);
1451
                    $ftype = "time({$length}){$qual}";
1452
1453
                    break;
1454
                default:
1455
                    $ftype = "{$type}({$length})";
1456
            }
1457
        }
1458
1459
        // Add array qualifier, if requested
1460
        if ($array) {
1461
            $ftype .= '[]';
1462
        }
1463
1464
        if ($ftype != $oldtype) {
1465
            $toAlter[] = "ALTER COLUMN \"{$name}\" TYPE {$ftype}";
1466
        }
1467
1468
        // Attempt to process the batch alteration, if anything has been changed
1469
        if (!empty($toAlter)) {
1470
            // Initialise an empty SQL string
1471
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" "
1472
            . implode(',', $toAlter);
1473
1474
            $status = $this->execute($sql);
1475
            if ($status != 0) {
1476
                $this->rollbackTransaction();
1477
1478
                return [-1, $sql];
1479
            }
1480
        }
1481
1482
        // Update the comment on the column
1483
        $status = $this->setComment('COLUMN', $name, $table, $comment);
1484
        if ($status != 0) {
1485
            $this->rollbackTransaction();
1486
1487
            return [-5, $sql];
1488
        }
1489
1490
        return [$this->endTransaction(), $sql];
1491
    }
1492
1493
    /**
1494
     * Renames a column in a table.
1495
     *
1496
     * @param $table   The table containing the column to be renamed
1497
     * @param $column  The column to be renamed
1498
     * @param $newName The new name for the column
1499
     *
1500
     * @return int 0 if operation was successful
1501
     */
1502
    public function renameColumn($table, $column, $newName)
1503
    {
1504
        $f_schema = $this->_schema;
1505
        $this->fieldClean($f_schema);
1506
        $this->fieldClean($table);
1507
        $this->fieldClean($column);
1508
        $this->fieldClean($newName);
1509
1510
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";
1511
1512
        return $this->execute($sql);
1513
    }
1514
1515
    /**
1516
     * Sets default value of a column.
1517
     *
1518
     * @param $table   The table from which to drop
1519
     * @param $column  The column name to set
1520
     * @param $default The new default value
1521
     *
1522
     * @return int 0 if operation was successful
1523
     */
1524
    public function setColumnDefault($table, $column, $default)
1525
    {
1526
        $f_schema = $this->_schema;
1527
        $this->fieldClean($f_schema);
1528
        $this->fieldClean($table);
1529
        $this->fieldClean($column);
1530
1531
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
1532
1533
        return $this->execute($sql);
1534
    }
1535
1536
    /**
1537
     * Sets whether or not a column can contain NULLs.
1538
     *
1539
     * @param $table  The table that contains the column
1540
     * @param $column The column to alter
1541
     * @param $state  True to set null, false to set not null
1542
     *
1543
     * @return int 0 if operation was successful
1544
     */
1545
    public function setColumnNull($table, $column, $state)
1546
    {
1547
        $f_schema = $this->_schema;
1548
        $this->fieldClean($f_schema);
1549
        $this->fieldClean($table);
1550
        $this->fieldClean($column);
1551
1552
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" " . ($state ? 'DROP' : 'SET') . ' NOT NULL';
1553
1554
        return $this->execute($sql);
1555
    }
1556
1557
    /**
1558
     * Drops a column from a table.
1559
     *
1560
     * @param $table   The table from which to drop a column
1561
     * @param $column  The column to be dropped
1562
     * @param $cascade True to cascade drop, false to restrict
1563
     *
1564
     * @return int 0 if operation was successful
1565
     */
1566
    public function dropColumn($table, $column, $cascade)
1567
    {
1568
        $f_schema = $this->_schema;
1569
        $this->fieldClean($f_schema);
1570
        $this->fieldClean($table);
1571
        $this->fieldClean($column);
1572
1573
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" DROP COLUMN \"{$column}\"";
1574
        if ($cascade) {
1575
            $sql .= ' CASCADE';
1576
        }
1577
1578
        return $this->execute($sql);
1579
    }
1580
1581
    /**
1582
     * Drops default value of a column.
1583
     *
1584
     * @param $table  The table from which to drop
1585
     * @param $column The column name to drop default
1586
     *
1587
     * @return int 0 if operation was successful
1588
     */
1589
    public function dropColumnDefault($table, $column)
1590
    {
1591
        $f_schema = $this->_schema;
1592
        $this->fieldClean($f_schema);
1593
        $this->fieldClean($table);
1594
        $this->fieldClean($column);
1595
1596
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";
1597
1598
        return $this->execute($sql);
1599
    }
1600
1601
    /**
1602
     * Sets up the data object for a dump.  eg. Starts the appropriate
1603
     * transaction, sets variables, etc.
1604
     *
1605
     * @return int 0 success
1606
     */
1607
    public function beginDump()
1608
    {
1609
        // Begin serializable transaction (to dump consistent data)
1610
        $status = $this->beginTransaction();
1611
        if ($status != 0) {
1612
            return -1;
1613
        }
1614
1615
        // Set serializable
1616
        $sql    = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE';
1617
        $status = $this->execute($sql);
1618
        if ($status != 0) {
1619
            $this->rollbackTransaction();
1620
1621
            return -1;
1622
        }
1623
1624
        // Set datestyle to ISO
1625
        $sql    = 'SET DATESTYLE = ISO';
1626
        $status = $this->execute($sql);
1627
        if ($status != 0) {
1628
            $this->rollbackTransaction();
1629
1630
            return -1;
1631
        }
1632
1633
        // Set extra_float_digits to 2
1634
        $sql    = 'SET extra_float_digits TO 2';
1635
        $status = $this->execute($sql);
1636
        if ($status != 0) {
1637
            $this->rollbackTransaction();
1638
1639
            return -1;
1640
        }
1641
1642
        return 0;
1643
    }
1644
1645
    /**
1646
     * Ends the data object for a dump.
1647
     *
1648
     * @return bool 0 success
1649
     */
1650
    public function endDump()
1651
    {
1652
        return $this->endTransaction();
1653
    }
1654
1655
    /**
1656
     * Returns a recordset of all columns in a relation.  Used for data export.
1657
     *
1658
     * @@ Note: Really needs to use a cursor
1659
     *
1660
     * @param $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...
1661
     * @param $oids
1662
     *
1663
     * @return \PHPPgAdmin\ADORecordSet A recordset on success
1664
     */
1665
    public function dumpRelation($relation, $oids)
1666
    {
1667
        $this->fieldClean($relation);
1668
1669
        // Actually retrieve the rows
1670
        if ($oids) {
1671
            $oid_str = $this->id . ', ';
1672
        } else {
1673
            $oid_str = '';
1674
        }
1675
1676
        return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
1677
    }
1678
1679
    /**
1680
     * Returns all available autovacuum per table information.
1681
     *
1682
     * @param \PHPPgAdmin\Database\if|string $table if given, return autovacuum info for the given table or return all informations for all table
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\if was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1683
     *
1684
     * @return \PHPPgAdmin\ADORecordSet A recordset
1685
     */
1686
    public function getTableAutovacuum($table = '')
1687
    {
1688
        $sql = '';
1689
1690
        if ($table !== '') {
1691
            $this->clean($table);
1692
            $c_schema = $this->_schema;
1693
            $this->clean($c_schema);
1694
1695
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
1696
                FROM pg_class c
1697
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
1698
                WHERE c.relkind = 'r'::\"char\"
1699
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
1700
                    AND c.reloptions IS NOT NULL
1701
                    AND c.relname = '{$table}' AND n.nspname = '{$c_schema}'
1702
                ORDER BY nspname, relname";
1703
        } else {
1704
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
1705
                FROM pg_class c
1706
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
1707
                WHERE c.relkind = 'r'::\"char\"
1708
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
1709
                    AND c.reloptions IS NOT NULL
1710
                ORDER BY nspname, relname";
1711
        }
1712
1713
        /* tmp var to parse the results */
1714
        $_autovacs = $this->selectSet($sql);
1715
1716
        /* result aray to return as RS */
1717
        $autovacs = [];
1718
        while (!$_autovacs->EOF) {
1719
            $_ = [
1720
                'nspname' => $_autovacs->fields['nspname'],
1721
                'relname' => $_autovacs->fields['relname'],
1722
            ];
1723
1724
            foreach (explode(',', $_autovacs->fields['reloptions']) as $var) {
1725
                list($o, $v) = explode('=', $var);
1726
                $_[$o]       = $v;
1727
            }
1728
1729
            $autovacs[] = $_;
1730
1731
            $_autovacs->moveNext();
1732
        }
1733
1734
        return new \PHPPgAdmin\ArrayRecordSet($autovacs);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new PHPPgAdmin\ArrayRecordSet($autovacs) returns the type PHPPgAdmin\ArrayRecordSet which is incompatible with the documented return type PHPPgAdmin\ADORecordSet.
Loading history...
1735
    }
1736
1737
    /**
1738
     * Get the fields for uniquely identifying a row in a table.
1739
     *
1740
     * @param $table The table for which to retrieve the identifier
1741
     *
1742
     * @return An array mapping attribute number to attribute name, empty for no identifiers
1743
     * @return -1 error
0 ignored issues
show
Coding Style introduced by
Only 1 @return tag is allowed in a function comment
Loading history...
1744
     */
1745
    public function getRowIdentifier($table)
1746
    {
1747
        $oldtable = $table;
1748
        $c_schema = $this->_schema;
1749
        $this->clean($c_schema);
1750
        $this->clean($table);
1751
1752
        $status = $this->beginTransaction();
1753
        if ($status != 0) {
1754
            return -1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -1 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1755
        }
1756
1757
        // Get the first primary or unique index (sorting primary keys first) that
1758
        // is NOT a partial index.
1759
        $sql = "
1760
            SELECT indrelid, indkey
1761
            FROM pg_catalog.pg_index
1762
            WHERE indisunique AND indrelid=(
1763
                SELECT oid FROM pg_catalog.pg_class
1764
                WHERE relname='{$table}' AND relnamespace=(
1765
                    SELECT oid FROM pg_catalog.pg_namespace
1766
                    WHERE nspname='{$c_schema}'
1767
                )
1768
            ) AND indpred IS NULL AND indexprs IS NULL
1769
            ORDER BY indisprimary DESC LIMIT 1";
1770
        $rs = $this->selectSet($sql);
1771
1772
        // If none, check for an OID column.  Even though OIDs can be duplicated, the edit and delete row
1773
        // functions check that they're only modiying a single row.  Otherwise, return empty array.
1774
        if ($rs->recordCount() == 0) {
1775
            // Check for OID column
1776
            $temp = [];
1777
            if ($this->hasObjectID($table)) {
1778
                $temp = ['oid'];
1779
            }
1780
            $this->endTransaction();
1781
1782
            return $temp;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $temp returns the type array<integer,string>|array which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1783
        } // Otherwise find the names of the keys
1784
1785
        $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey']));
0 ignored issues
show
Bug introduced by
It seems like getAttributeNames() 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

1785
        /** @scrutinizer ignore-call */ 
1786
        $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey']));
Loading history...
1786
        if (!is_array($attnames)) {
1787
            $this->rollbackTransaction();
1788
1789
            return -1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -1 returns the type integer which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1790
        }
1791
1792
        $this->endTransaction();
1793
1794
        return $attnames;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $attnames returns the type array which is incompatible with the documented return type PHPPgAdmin\Database\An.
Loading history...
1795
    }
1796
1797
    /**
1798
     * Adds a new row to a table.
1799
     *
1800
     * @param $table  The table in which to insert
1801
     * @param $fields Array of given field in values
1802
     * @param $values Array of new values for the row
1803
     * @param $nulls  An array mapping column => something if it is to be null
1804
     * @param $format An array of the data type (VALUE or EXPRESSION)
1805
     * @param $types  An array of field types
1806
     *
1807
     * @return int 0 if operation was successful
1808
     */
1809
    public function insertRow($table, $fields, $values, $nulls, $format, $types)
1810
    {
1811
        if (!is_array($fields) || !is_array($values) || !is_array($nulls)
1812
            || !is_array($format) || !is_array($types)
1813
            || (count($fields) != count($values))
1814
        ) {
1815
            return -1;
1816
        }
1817
1818
        // Build clause
1819
        if (count($values) > 0) {
1820
            // Escape all field names
1821
            $fields   = array_map(['\PHPPgAdmin\Database\Postgres', 'fieldClean'], $fields);
1822
            $f_schema = $this->_schema;
1823
            $this->fieldClean($table);
1824
            $this->fieldClean($f_schema);
1825
1826
            $sql = '';
1827
            foreach ($values as $i => $value) {
1828
                // Handle NULL values
1829
                if (isset($nulls[$i])) {
1830
                    $sql .= ',NULL';
1831
                } else {
1832
                    $sql .= ',' . $this->formatValue($types[$i], $format[$i], $value);
1833
                }
1834
            }
1835
1836
            $sql = "INSERT INTO \"{$f_schema}\".\"{$table}\" (\"" . implode('","', $fields) . '")
1837
                VALUES (' . substr($sql, 1) . ')';
1838
1839
            return $this->execute($sql);
1840
        }
1841
1842
        return -1;
1843
    }
1844
1845
    /**
1846
     * Formats a value or expression for sql purposes.
1847
     *
1848
     * @param $type   The type of the field
1849
     * @param $format VALUE or EXPRESSION
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1850
     * @param $value  The actual value entered in the field.  Can be NULL
1851
     *
1852
     * @return The suitably quoted and escaped value
1853
     */
1854
    public function formatValue($type, $format, $value)
1855
    {
1856
        switch ($type) {
1857
            case 'bool':
1858
            case 'boolean':
1859
                if ($value == 't') {
1860
                    return 'TRUE';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'TRUE' returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\The.
Loading history...
1861
                }
1862
1863
                if ($value == 'f') {
1864
                    return 'FALSE';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'FALSE' returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\The.
Loading history...
1865
                }
1866
                if ($value == '') {
1867
                    return 'NULL';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'NULL' returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\The.
Loading history...
1868
                }
1869
1870
                return $value;
1871
                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...
1872
            default:
1873
                // Checking variable fields is difficult as there might be a size
1874
                // attribute...
1875
                if (strpos($type, 'time') === 0) {
1876
                    // Assume it's one of the time types...
1877
                    if ($value == '') {
1878
                        return "''";
0 ignored issues
show
Bug Best Practice introduced by
The expression return '''' returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\The.
Loading history...
1879
                    }
1880
1881
                    if (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0
1882
                        || strcasecmp($value, 'CURRENT_TIME') == 0
1883
                        || strcasecmp($value, 'CURRENT_DATE') == 0
1884
                        || strcasecmp($value, 'LOCALTIME') == 0
1885
                        || 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...
1886
                        return $value;
1887
                    }
1888
                    if ($format == 'EXPRESSION') {
1889
                        return $value;
1890
                    }
1891
                    $this->clean($value);
1892
1893
                    return "'{$value}'";
0 ignored issues
show
Bug Best Practice introduced by
The expression return '''.$value.''' returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\The.
Loading history...
1894
                }
1895
                if ($format == 'VALUE') {
1896
                    $this->clean($value);
1897
1898
                    return "'{$value}'";
0 ignored issues
show
Bug Best Practice introduced by
The expression return '''.$value.''' returns the type string which is incompatible with the documented return type PHPPgAdmin\Database\The.
Loading history...
1899
                }
1900
1901
                return $value;
1902
        }
1903
    }
1904
1905
    // View functions
1906
1907
    /**
1908
     * Updates a row in a table.
1909
     *
1910
     * @param $table  The table in which to update
1911
     * @param $vars   An array mapping new values for the row
1912
     * @param $nulls  An array mapping column => something if it is to be null
1913
     * @param $format An array of the data type (VALUE or EXPRESSION)
1914
     * @param $types  An array of field types
1915
     * @param $keyarr An array mapping column => value to update
1916
     *
1917
     * @return bool|int 0 success
1918
     */
1919
    public function editRow($table, $vars, $nulls, $format, $types, $keyarr)
1920
    {
1921
        if (!is_array($vars) || !is_array($nulls) || !is_array($format) || !is_array($types)) {
1922
            return -1;
1923
        }
1924
1925
        $f_schema = $this->_schema;
1926
        $this->fieldClean($f_schema);
1927
        $this->fieldClean($table);
1928
1929
        // Build clause
1930
        if (sizeof($vars) > 0) {
1931
            foreach ($vars as $key => $value) {
1932
                $this->fieldClean($key);
1933
1934
                // Handle NULL values
1935
                if (isset($nulls[$key])) {
1936
                    $tmp = 'NULL';
1937
                } else {
1938
                    $tmp = $this->formatValue($types[$key], $format[$key], $value);
1939
                }
1940
1941
                if (isset($sql)) {
1942
                    $sql .= ", \"{$key}\"={$tmp}";
1943
                } else {
1944
                    $sql = "UPDATE \"{$f_schema}\".\"{$table}\" SET \"{$key}\"={$tmp}";
1945
                }
1946
            }
1947
            $first = true;
1948
            foreach ($keyarr as $k => $v) {
1949
                $this->fieldClean($k);
1950
                $this->clean($v);
1951
                if ($first) {
1952
                    $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...
1953
                    $first = false;
1954
                } else {
1955
                    $sql .= " AND \"{$k}\"='{$v}'";
1956
                }
1957
            }
1958
        }
1959
1960
        // Begin transaction.  We do this so that we can ensure only one row is
1961
        // edited
1962
        $status = $this->beginTransaction();
1963
        if ($status != 0) {
1964
            $this->rollbackTransaction();
1965
1966
            return -1;
1967
        }
1968
1969
        $status = $this->execute($sql);
1970
        if ($status != 0) {
1971
            // update failed
1972
            $this->rollbackTransaction();
1973
1974
            return -1;
1975
        }
1976
1977
        if ($this->conn->Affected_Rows() != 1) {
1978
            // more than one row could be updated
1979
            $this->rollbackTransaction();
1980
1981
            return -2;
1982
        }
1983
1984
        // End transaction
1985
        return $this->endTransaction();
1986
    }
1987
1988
    /**
1989
     * Delete a row from a table.
1990
     *
1991
     * @param      $table  The table from which to delete
1992
     * @param      $key    An array mapping column => value to delete
1993
     * @param bool $schema
1994
     *
1995
     * @return bool|int 0 success
1996
     */
1997
    public function deleteRow($table, $key, $schema = false)
1998
    {
1999
        if (!is_array($key)) {
2000
            return -1;
2001
        }
2002
2003
        // Begin transaction.  We do this so that we can ensure only one row is
2004
        // deleted
2005
        $status = $this->beginTransaction();
2006
        if ($status != 0) {
2007
            $this->rollbackTransaction();
2008
2009
            return -1;
2010
        }
2011
2012
        if ($schema === false) {
2013
            $schema = $this->_schema;
2014
        }
2015
2016
        $status = $this->delete($table, $key, $schema);
0 ignored issues
show
Bug introduced by
The method delete() does not exist on PHPPgAdmin\Database\TableTrait. Did you maybe mean deleteRow()? ( Ignorable by Annotation )

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

2016
        /** @scrutinizer ignore-call */ 
2017
        $status = $this->delete($table, $key, $schema);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2017
        if ($status != 0 || $this->conn->Affected_Rows() != 1) {
2018
            $this->rollbackTransaction();
2019
2020
            return -2;
2021
        }
2022
2023
        // End transaction
2024
        return $this->endTransaction();
2025
    }
2026
2027
}
2028