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

IndexTrait   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 666
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 64
dl 0
loc 666
rs 3.0278
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A dropConstraint() 0 13 2
B getLinkingKeys() 0 81 6
B reindex() 0 26 6
B addPrimaryKey() 0 25 6
A addCheckConstraint() 0 16 2
A clusterIndex() 0 22 3
B addUniqueKey() 0 25 6
B dropCheckConstraint() 0 52 5
C createIndex() 0 44 8
A dropIndex() 0 14 2
C addForeignKey() 0 56 11
A getConstraintsWithFields() 0 54 4
A alreadyClustered() 0 17 1
B getReferrers() 0 33 2

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.43
5
 */
6
7
namespace PHPPgAdmin\Traits;
8
9
/**
10
 * Common trait for indexes and constraints manipulation.
11
 */
12
trait IndexTrait
13
{
14
    /**
15
     * test if a table has been clustered on an index.
1 ignored issue
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
16
     *
17
     * @param string $table The table to test
18
     *
19
     * @return bool true if the table has been already clustered
20
     */
21
    public function alreadyClustered($table)
22
    {
23
        $c_schema = $this->_schema;
24
        $this->clean($c_schema);
25
        $this->clean($table);
26
27
        $sql = "SELECT i.indisclustered
28
			FROM pg_catalog.pg_class c, pg_catalog.pg_index i
29
			WHERE c.relname = '{$table}'
30
				AND c.oid = i.indrelid AND i.indisclustered
31
				AND c.relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
32
					WHERE nspname='{$c_schema}')
33
				";
34
35
        $v = $this->selectSet($sql);
36
37
        return !($v->recordCount() == 0);
38
    }
39
40
    /**
41
     * Creates an index.
42
     *
43
     * @param string $name         The index name (can be blank)
44
     * @param string $table        The table on which to add the index
45
     * @param array  $columns      An array of columns that form the index  or a string expression for a functional index
46
     * @param string $type         The index type
47
     * @param bool   $unique       True if unique, false otherwise
48
     * @param string $where        Index predicate ('' for none)
49
     * @param string $tablespace   The tablespaces ('' means none/default)
50
     * @param bool   $concurrently true to create index concurrently
51
     *
52
     * @return array status (0 if operation was successful) and sql sentence
53
     */
54
    public function createIndex($name, $table, $columns, $type, $unique, $where, $tablespace, $concurrently)
55
    {
56
        $f_schema = $this->_schema;
57
        $this->fieldClean($f_schema);
58
        $this->fieldClean($name);
59
        $this->fieldClean($table);
60
61
        $sql = 'CREATE ';
62
        if ($unique) {
63
            $sql .= ' UNIQUE ';
64
        }
65
66
        $sql .= ' INDEX ';
67
        if ($concurrently) {
68
            $sql .= ' CONCURRENTLY ';
69
        }
70
71
        if ($name) {
72
            $sql .= "  \"{$name}\" ";
73
        }
74
75
        $sql .= " ON \"{$f_schema}\".\"{$table}\" USING {$type} ";
76
77
        if (is_array($columns)) {
0 ignored issues
show
introduced by
The condition is_array($columns) is always true.
Loading history...
78
            $this->arrayClean($columns);
0 ignored issues
show
Bug introduced by
It seems like arrayClean() 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

78
            $this->/** @scrutinizer ignore-call */ 
79
                   arrayClean($columns);
Loading history...
79
            $sql .= '("' . implode('","', $columns) . '")';
80
        } else {
81
            $sql .= '(' . $columns . ')';
82
        }
83
84
        // Tablespace
85
        if ($this->hasTablespaces() && $tablespace != '') {
86
            $this->fieldClean($tablespace);
87
            $sql .= " TABLESPACE \"{$tablespace}\"";
88
        }
89
90
        // Predicate
91
        if (trim($where) != '') {
92
            $sql .= " WHERE ({$where})";
93
        }
94
95
        $status = $this->execute($sql);
96
97
        return [$status, $sql];
98
    }
99
100
    /**
101
     * Removes an index from the database.
102
     *
103
     * @param string $index   The index to drop
104
     * @param bool   $cascade True to cascade drop, false to restrict
105
     *
106
     * @return array<integer,mixed|string> 0 if operation was successful
107
     */
108
    public function dropIndex($index, $cascade)
109
    {
110
        $f_schema = $this->_schema;
111
        $this->fieldClean($f_schema);
112
        $this->fieldClean($index);
113
114
        $sql = "DROP INDEX \"{$f_schema}\".\"{$index}\"";
115
        if ($cascade) {
116
            $sql .= ' CASCADE';
117
        }
118
119
        $status = $this->execute($sql);
120
121
        return [$status, $sql];
122
    }
123
124
    /**
125
     * Rebuild indexes.
126
     *
127
     * @param string $type  'DATABASE' or 'TABLE' or 'INDEX'
128
     * @param string $name  The name of the specific database, table, or index to be reindexed
129
     * @param bool   $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in
130
     *                      7.2-7.3, ignored in >=7.4
131
     *
132
     * @return int 0 if operation was successful
133
     */
134
    public function reindex($type, $name, $force = false)
135
    {
136
        $f_schema = $this->_schema;
137
        $this->fieldClean($f_schema);
138
        $this->fieldClean($name);
139
        switch ($type) {
140
            case 'DATABASE':
141
                $sql = "REINDEX {$type} \"{$name}\"";
142
                if ($force) {
143
                    $sql .= ' FORCE';
144
                }
145
146
                break;
147
            case 'TABLE':
148
            case 'INDEX':
149
                $sql = "REINDEX {$type} \"{$f_schema}\".\"{$name}\"";
150
                if ($force) {
151
                    $sql .= ' FORCE';
152
                }
153
154
                break;
155
            default:
156
                return -1;
157
        }
158
159
        return $this->execute($sql);
160
    }
161
162
    /**
163
     * Clusters an index.
164
     *
165
     * @param string $table The table the index is on
166
     * @param string $index The name of the index
167
     *
168
     * @return array<integer,mixed|string> 0 if operation was successful
169
     */
170
    public function clusterIndex($table = '', $index = '')
171
    {
172
        $sql = 'CLUSTER';
173
174
        // We don't bother with a transaction here, as there's no point rolling
175
        // back an expensive cluster if a cheap analyze fails for whatever reason
176
177
        if (!empty($table)) {
178
            $f_schema = $this->_schema;
179
            $this->fieldClean($f_schema);
180
            $this->fieldClean($table);
181
            $sql .= " \"{$f_schema}\".\"{$table}\"";
182
183
            if (!empty($index)) {
184
                $this->fieldClean($index);
185
                $sql .= " USING \"{$index}\"";
186
            }
187
        }
188
189
        $status = $this->execute($sql);
190
191
        return [$status, $sql];
192
    }
193
194
    /**
195
     * Returns a list of all constraints on a table,
196
     * including constraint name, definition, related col and referenced namespace,
197
     * table and col if needed.
198
     *
199
     * @param string $table the table where we are looking for fk
200
     *
201
     * @return \PHPPgAdmin\ADORecordSet A recordset
202
     */
203
    public function getConstraintsWithFields($table)
204
    {
205
        $c_schema = $this->_schema;
206
        $this->clean($c_schema);
207
        $this->clean($table);
208
209
        // get the max number of col used in a constraint for the table
210
        $sql = "SELECT DISTINCT
211
			max(SUBSTRING(array_dims(c.conkey) FROM  \$patern\$^\\[.*:(.*)\\]$\$patern\$)) as nb
212
		FROM pg_catalog.pg_constraint AS c
213
			JOIN pg_catalog.pg_class AS r ON (c.conrelid=r.oid)
214
			JOIN pg_catalog.pg_namespace AS ns ON (r.relnamespace=ns.oid)
215
		WHERE
216
			r.relname = '{$table}' AND ns.nspname='{$c_schema}'";
217
218
        $rs = $this->selectSet($sql);
219
220
        if ($rs->EOF) {
221
            $max_col = 0;
1 ignored issue
show
Unused Code introduced by
The assignment to $max_col is dead and can be removed.
Loading history...
222
        } else {
223
            $max_col = $rs->fields['nb'];
224
        }
225
226
        $sql = '
227
			SELECT
228
				c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc,
229
				ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
230
				r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum, f2.attname as f_field,
231
				f2.attnum AS f_attnum, pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment,
232
				c.conrelid, c.confrelid
233
			FROM
234
				pg_catalog.pg_constraint AS c
235
				JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
236
				JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]';
237
        for ($i = 2; $i <= $rs->fields['nb']; ++$i) {
238
            $sql .= " OR f1.attnum=c.conkey[${i}]";
239
        }
240
        $sql .= '))
241
				JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid
242
				LEFT JOIN (
243
					pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
244
				) ON (c.confrelid=r2.oid)
245
				LEFT JOIN pg_catalog.pg_attribute AS f2 ON
246
					(f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)';
247
        for ($i = 2; $i <= $rs->fields['nb']; ++$i) {
248
            $sql .= " OR (c.confkey[${i}]=f2.attnum AND c.conkey[${i}]=f1.attnum)";
249
        }
250
251
        $sql .= sprintf("))
1 ignored issue
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
252
			WHERE
253
				r1.relname = '%s' AND ns1.nspname='%s'
254
			ORDER BY 1", $table, $c_schema);
1 ignored issue
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
255
256
        return $this->selectSet($sql);
257
    }
258
259
    /**
260
     * Adds a primary key constraint to a table.
261
     *
262
     * @param string $table      The table to which to add the primery key
263
     * @param array  $fields     (array) An array of fields over which to add the primary key
264
     * @param string $name       (optional) The name to give the key, otherwise default name is assigned
265
     * @param string $tablespace (optional) The tablespace for the schema, '' indicates default
266
     *
267
     * @return int 0 if operation was successful
268
     */
269
    public function addPrimaryKey($table, $fields, $name = '', $tablespace = '')
270
    {
271
        if (!is_array($fields) || sizeof($fields) == 0) {
1 ignored issue
show
introduced by
The condition is_array($fields) is always true.
Loading history...
272
            return -1;
273
        }
274
275
        $f_schema = $this->_schema;
276
        $this->fieldClean($f_schema);
277
        $this->fieldClean($table);
278
        $this->fieldArrayClean($fields);
279
        $this->fieldClean($name);
280
        $this->fieldClean($tablespace);
281
282
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
283
        if ($name != '') {
284
            $sql .= "CONSTRAINT \"{$name}\" ";
285
        }
286
287
        $sql .= 'PRIMARY KEY ("' . join('","', $fields) . '")';
288
289
        if ($tablespace != '' && $this->hasTablespaces()) {
290
            $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
291
        }
292
293
        return $this->execute($sql);
294
    }
295
296
    /**
297
     * Adds a unique constraint to a table.
298
     *
299
     * @param        $table      The table to which to add the unique key
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Traits\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...
300
     * @param        $fields     (array) An array of fields over which to add the unique key
301
     * @param string $name       (optional) The name to give the key, otherwise default name is assigned
302
     * @param string $tablespace (optional) The tablespace for the schema, '' indicates default
303
     *
304
     * @return int 0 if operation was successful
305
     */
306
    public function addUniqueKey($table, $fields, $name = '', $tablespace = '')
307
    {
308
        if (!is_array($fields) || sizeof($fields) == 0) {
309
            return -1;
310
        }
311
312
        $f_schema = $this->_schema;
313
        $this->fieldClean($f_schema);
314
        $this->fieldClean($table);
315
        $this->fieldArrayClean($fields);
316
        $this->fieldClean($name);
317
        $this->fieldClean($tablespace);
318
319
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
320
        if ($name != '') {
321
            $sql .= "CONSTRAINT \"{$name}\" ";
322
        }
323
324
        $sql .= 'UNIQUE ("' . join('","', $fields) . '")';
325
326
        if ($tablespace != '' && $this->hasTablespaces()) {
327
            $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
328
        }
329
330
        return $this->execute($sql);
331
    }
332
333
    // Function functions
334
335
    /**
336
     * Adds a check constraint to a table.
337
     *
338
     * @param string $table      The table to which to add the check
339
     * @param string $definition The definition of the check
340
     * @param string $name       (optional) The name to give the check, otherwise default name is assigned
341
     *
342
     * @return int 0 if operation was successful
343
     */
344
    public function addCheckConstraint($table, $definition, $name = '')
345
    {
346
        $f_schema = $this->_schema;
347
        $this->fieldClean($f_schema);
348
        $this->fieldClean($table);
349
        $this->fieldClean($name);
350
        // @@ How the heck do you clean a definition???
351
352
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
353
        if ($name != '') {
354
            $sql .= "CONSTRAINT \"{$name}\" ";
355
        }
356
357
        $sql .= "CHECK ({$definition})";
358
359
        return $this->execute($sql);
360
    }
361
362
    /**
363
     * Drops a check constraint from a table.
364
     *
365
     * @param string $table The table from which to drop the check
366
     * @param string $name  The name of the check to be dropped
367
     *
368
     * @return bool|int 0 success
369
     */
370
    public function dropCheckConstraint($table, $name)
371
    {
372
        $f_schema = $this->_schema;
373
        $this->fieldClean($f_schema);
374
        $c_schema = $this->_schema;
375
        $this->clean($c_schema);
376
        $c_table = $table;
377
        $this->fieldClean($table);
378
        $this->clean($c_table);
379
        $this->clean($name);
380
381
        // Begin transaction
382
        $status = $this->beginTransaction();
383
        if ($status != 0) {
384
            return -2;
385
        }
386
387
        // Properly lock the table
388
        $sql    = "LOCK TABLE \"{$f_schema}\".\"{$table}\" IN ACCESS EXCLUSIVE MODE";
389
        $status = $this->execute($sql);
390
        if ($status != 0) {
391
            $this->rollbackTransaction();
392
393
            return -3;
394
        }
395
396
        // Delete the check constraint
397
        $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$c_table}'
398
			AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
399
			nspname = '{$c_schema}')) AND rcname='{$name}'";
400
        $status = $this->execute($sql);
401
        if ($status != 0) {
402
            $this->rollbackTransaction();
403
404
            return -4;
405
        }
406
407
        // Update the pg_class catalog to reflect the new number of checks
408
        $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE
409
					rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$c_table}'
410
						AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
411
						nspname = '{$c_schema}')))
412
					WHERE relname='{$c_table}'";
413
        $status = $this->execute($sql);
414
        if ($status != 0) {
415
            $this->rollbackTransaction();
416
417
            return -4;
418
        }
419
420
        // Otherwise, close the transaction
421
        return $this->endTransaction();
422
    }
423
424
    /**
425
     * Adds a foreign key constraint to a table.
426
     *
427
     * @param string $table      The table on which to add an FK
428
     * @param string $targschema The schema that houses the target table to which to add the foreign key
429
     * @param string $targtable  The table to which to add the foreign key
430
     * @param array  $sfields    (array) An array of source fields over which to add the foreign key
431
     * @param array  $tfields    (array) An array of target fields over which to add the foreign key
432
     * @param string $upd_action The action for updates (eg. RESTRICT)
433
     * @param string $del_action The action for deletes (eg. RESTRICT)
434
     * @param string $match      The match type (eg. MATCH FULL)
435
     * @param string $deferrable The deferrability (eg. NOT DEFERRABLE)
436
     * @param string $initially  The initially parameter for the FK (eg. INITIALLY IMMEDIATE)
437
     * @param string $name       [optional] The name to give the key, otherwise default name is assigned
438
     *
439
     * @return int 0 if operation was successful
440
     *
441
     * @internal param \PHPPgAdmin\Database\The $target table that contains the target columns
442
     * @internal param \PHPPgAdmin\Database\The $intially initial deferrability (eg. INITIALLY IMMEDIATE)
443
     */
444
    public function addForeignKey(
445
        $table,
446
        $targschema,
447
        $targtable,
448
        $sfields,
449
        $tfields,
450
        $upd_action,
451
        $del_action,
452
        $match,
453
        $deferrable,
454
        $initially,
455
        $name = ''
456
    ) {
457
        if (!is_array($sfields) || sizeof($sfields) == 0 ||
1 ignored issue
show
introduced by
The condition is_array($sfields) is always true.
Loading history...
458
            !is_array($tfields) || sizeof($tfields) == 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...
459
            return -1;
460
        }
461
462
        $f_schema = $this->_schema;
463
        $this->fieldClean($f_schema);
464
        $this->fieldClean($table);
465
        $this->fieldClean($targschema);
466
        $this->fieldClean($targtable);
467
        $this->fieldArrayClean($sfields);
468
        $this->fieldArrayClean($tfields);
469
        $this->fieldClean($name);
470
471
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
472
        if ($name != '') {
473
            $sql .= "CONSTRAINT \"{$name}\" ";
474
        }
475
476
        $sql .= 'FOREIGN KEY ("' . join('","', $sfields) . '") ';
477
        // Target table needs to be fully qualified
478
        $sql .= "REFERENCES \"{$targschema}\".\"{$targtable}\"(\"" . join('","', $tfields) . '") ';
479
        if ($match != $this->fkmatches[0]) {
480
            $sql .= " {$match}";
481
        }
482
483
        if ($upd_action != $this->fkactions[0]) {
484
            $sql .= " ON UPDATE {$upd_action}";
485
        }
486
487
        if ($del_action != $this->fkactions[0]) {
488
            $sql .= " ON DELETE {$del_action}";
489
        }
490
491
        if ($deferrable != $this->fkdeferrable[0]) {
492
            $sql .= " {$deferrable}";
493
        }
494
495
        if ($initially != $this->fkinitial[0]) {
496
            $sql .= " {$initially}";
497
        }
498
499
        return $this->execute($sql);
500
    }
501
502
    /**
503
     * Removes a constraint from a relation.
504
     *
505
     * @param string $constraint The constraint to drop
506
     * @param string $relation   The relation from which to drop
507
     * @param string $type       The type of constraint (c, f, u or p)
508
     * @param bool   $cascade    True to cascade drop, false to restrict
509
     *
510
     * @return int 0 if operation was successful
511
     */
512
    public function dropConstraint($constraint, $relation, $type, $cascade)
1 ignored issue
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

512
    public function dropConstraint($constraint, $relation, /** @scrutinizer ignore-unused */ $type, $cascade)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
513
    {
514
        $f_schema = $this->_schema;
515
        $this->fieldClean($f_schema);
516
        $this->fieldClean($constraint);
517
        $this->fieldClean($relation);
518
519
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$relation}\" DROP CONSTRAINT \"{$constraint}\"";
520
        if ($cascade) {
521
            $sql .= ' CASCADE';
522
        }
523
524
        return $this->execute($sql);
525
    }
526
527
    /**
528
     * A function for getting all columns linked by foreign keys given a group of tables.
529
     *
530
     * @param array $tables multi dimensional assoc array that holds schema and table name
531
     *
532
     * @return int|\PHPPgAdmin\ADORecordSet recordset of linked tables and columns or -1 if $tables isn't an array
533
     */
534
    public function getLinkingKeys($tables)
535
    {
536
        if (!is_array($tables)) {
1 ignored issue
show
introduced by
The condition is_array($tables) is always true.
Loading history...
537
            return -1;
538
        }
539
540
        $this->clean($tables[0]['tablename']);
541
        $this->clean($tables[0]['schemaname']);
542
        $tables_list        = "'{$tables[0]['tablename']}'";
543
        $schema_list        = "'{$tables[0]['schemaname']}'";
544
        $schema_tables_list = "'{$tables[0]['schemaname']}.{$tables[0]['tablename']}'";
545
546
        for ($i = 1; $i < sizeof($tables); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
547
            $this->clean($tables[$i]['tablename']);
548
            $this->clean($tables[$i]['schemaname']);
549
            $tables_list .= ", '{$tables[$i]['tablename']}'";
550
            $schema_list .= ", '{$tables[$i]['schemaname']}'";
551
            $schema_tables_list .= ", '{$tables[$i]['schemaname']}.{$tables[$i]['tablename']}'";
552
        }
553
554
        $maxDimension = 1;
555
556
        $sql = "
557
			SELECT DISTINCT
558
				array_dims(pc.conkey) AS arr_dim,
559
				pgc1.relname AS p_table
560
			FROM
561
				pg_catalog.pg_constraint AS pc,
562
				pg_catalog.pg_class AS pgc1
563
			WHERE
564
				pc.contype = 'f'
565
				AND (pc.conrelid = pgc1.relfilenode OR pc.confrelid = pgc1.relfilenode)
566
				AND pgc1.relname IN (${tables_list})
567
			";
568
569
        //parse our output to find the highest dimension of foreign keys since pc.conkey is stored in an array
570
        $rs = $this->selectSet($sql);
571
        while (!$rs->EOF) {
572
            $arrData      = explode(':', $rs->fields['arr_dim']);
573
            $tmpDimension = (int) (substr($arrData[1], 0, strlen($arrData[1] - 1)));
574
            $maxDimension = $tmpDimension > $maxDimension ? $tmpDimension : $maxDimension;
575
            $rs->MoveNext();
576
        }
577
578
        //we know the highest index for foreign keys that conkey goes up to, expand for us in an IN query
579
        $cons_str = '( (pfield.attnum = conkey[1] AND cfield.attnum = confkey[1]) ';
580
        for ($i = 2; $i <= $maxDimension; ++$i) {
581
            $cons_str .= "OR (pfield.attnum = conkey[{$i}] AND cfield.attnum = confkey[{$i}]) ";
582
        }
583
        $cons_str .= ') ';
584
585
        $sql = "
586
			SELECT
587
				pgc1.relname AS p_table,
588
				pgc2.relname AS f_table,
589
				pfield.attname AS p_field,
590
				cfield.attname AS f_field,
591
				pgns1.nspname AS p_schema,
592
				pgns2.nspname AS f_schema
593
			FROM
594
				pg_catalog.pg_constraint AS pc,
595
				pg_catalog.pg_class AS pgc1,
596
				pg_catalog.pg_class AS pgc2,
597
				pg_catalog.pg_attribute AS pfield,
598
				pg_catalog.pg_attribute AS cfield,
599
				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN (${schema_list}) ) AS pgns1,
600
 				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN (${schema_list}) ) AS pgns2
601
			WHERE
602
				pc.contype = 'f'
603
				AND pgc1.relnamespace = pgns1.ns_id
604
 				AND pgc2.relnamespace = pgns2.ns_id
605
				AND pc.conrelid = pgc1.relfilenode
606
				AND pc.confrelid = pgc2.relfilenode
607
				AND pfield.attrelid = pc.conrelid
608
				AND cfield.attrelid = pc.confrelid
609
				AND ${cons_str}
610
				AND pgns1.nspname || '.' || pgc1.relname IN (${schema_tables_list})
611
				AND pgns2.nspname || '.' || pgc2.relname IN (${schema_tables_list})
612
		";
613
614
        return $this->selectSet($sql);
615
    }
616
617
    /**
618
     * Finds the foreign keys that refer to the specified table.
619
     *
620
     * @param string $table The table to find referrers for
621
     *
622
     * @return \PHPPgAdmin\ADORecordSet A recordset
623
     */
624
    public function getReferrers($table)
625
    {
626
        $this->clean($table);
627
628
        $status = $this->beginTransaction();
629
        if ($status != 0) {
630
            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\ADORecordSet.
Loading history...
631
        }
632
633
        $c_schema = $this->_schema;
634
        $this->clean($c_schema);
635
636
        $sql = "
637
			SELECT
638
				pn.nspname,
639
				pl.relname,
640
				pc.conname,
641
				pg_catalog.pg_get_constraintdef(pc.oid) AS consrc
642
			FROM
643
				pg_catalog.pg_constraint pc,
644
				pg_catalog.pg_namespace pn,
645
				pg_catalog.pg_class pl
646
			WHERE
647
				pc.connamespace = pn.oid
648
				AND pc.conrelid = pl.oid
649
				AND pc.contype = 'f'
650
				AND confrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
651
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
652
					WHERE nspname='{$c_schema}'))
653
			ORDER BY 1,2,3
654
		";
655
656
        return $this->selectSet($sql);
657
    }
658
659
    abstract public function fieldClean(&$str);
660
661
    abstract public function beginTransaction();
662
663
    abstract public function rollbackTransaction();
664
665
    abstract public function endTransaction();
666
667
    abstract public function execute($sql);
668
669
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
670
671
    abstract public function selectSet($sql);
672
673
    abstract public function clean(&$str);
674
675
    abstract public function hasTablespaces();
676
677
    abstract public function fieldArrayClean(&$arr);
678
}
679