Test Failed
Pull Request — develop (#380)
by Felipe
03:39
created

IndexTrait::createIndex()   B

Complexity

Conditions 8
Paths 64

Size

Total Lines 54
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 33
nc 64
nop 8
dl 0
loc 54
rs 8.1475
c 0
b 0
f 0

How to fix   Long Method    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 6.1.3
5
 */
6
7
namespace PHPPgAdmin\Database\Traits;
8
9
use PHPPgAdmin\ADORecordSet;
10
11
/**
12
 * Common trait for indexes and constraints manipulation.
13
 */
14
trait IndexTrait
15
{
16
    /**
17
     * Test if a table has been clustered on an index.
18
     *
19
     * @param string $table The table to test
20
     *
21
     * @return bool true if the table has been already clustered
22
     */
23
    public function alreadyClustered($table)
24
    {
25
        $c_schema = $this->_schema;
26
        $this->clean($c_schema);
27
        $this->clean($table);
28
29
        $sql = \sprintf(
30
            'SELECT i.indisclustered
31
			FROM pg_catalog.pg_class c, pg_catalog.pg_index i
32
			WHERE c.relname = \'%s\'
33
				AND c.oid = i.indrelid AND i.indisclustered
34
				AND c.relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
35
					WHERE nspname=\'%s\')
36
				',
37
            $table,
38
            $c_schema
39
        );
40
41
        $v = $this->selectSet($sql);
42
43
        return !(0 === $v->recordCount());
44
    }
45
46
    /**
47
     * Creates an index.
48
     *
49
     * @param string       $name         The index name (can be blank)
50
     * @param string       $table        The table on which to add the index
51
     * @param array|string $columns      An array of columns that form the index  or a string expression for a functional index
52
     * @param string       $type         The index type
53
     * @param bool         $unique       True if unique, false otherwise
54
     * @param string       $where        Index predicate ('' for none)
55
     * @param string       $tablespace   The tablespaces ('' means none/default)
56
     * @param bool         $concurrently true to create index concurrently
57
     *
58
     * @return array status (0 if operation was successful) and sql sentence
59
     */
60
    public function createIndex($name, $table, $columns, $type, $unique, $where, $tablespace, $concurrently)
61
    {
62
        $f_schema = $this->_schema;
63
        $this->fieldClean($f_schema);
64
        $this->fieldClean($name);
65
        $this->fieldClean($table);
66
67
        $sql = 'CREATE ';
68
69
        $sql .= $unique ? ' UNIQUE ' : '';
70
71
        $sql .= ' INDEX ';
72
73
        $sql .= $concurrently ? ' CONCURRENTLY ' : '';
74
75
        $sql .= $name ? \sprintf(
76
            '  "%s" ',
77
            $name
78
        ) : '';
79
80
        $sql .= \sprintf(
81
            ' ON "%s"."%s" USING %s ',
82
            $f_schema,
83
            $table,
84
            $type
85
        );
86
87
        if (\is_array($columns)) {
88
            $this->arrayClean($columns);
89
            $sql .= '("' . \implode('","', $columns) . '")';
90
        } else {
91
            $sql .= '(' . $columns . ')';
92
        }
93
94
        // Tablespace
95
        if ($this->hasTablespaces() && '' !== $tablespace) {
96
            $this->fieldClean($tablespace);
97
            $sql .= \sprintf(
98
                ' TABLESPACE "%s"',
99
                $tablespace
100
            );
101
        }
102
103
        // Predicate
104
        if ('' !== \trim($where)) {
105
            $sql .= \sprintf(
106
                ' WHERE (%s)',
107
                $where
108
            );
109
        }
110
111
        $status = $this->execute($sql);
112
113
        return [$status, $sql];
114
    }
115
116
    /**
117
     * Removes an index from the database.
118
     *
119
     * @param string $index   The index to drop
120
     * @param bool   $cascade True to cascade drop, false to restrict
121
     *
122
     * @return array<integer,mixed|string> 0 if operation was successful
123
     */
124
    public function dropIndex($index, $cascade)
125
    {
126
        $f_schema = $this->_schema;
127
        $this->fieldClean($f_schema);
128
        $this->fieldClean($index);
129
130
        $sql = \sprintf(
131
            'DROP INDEX "%s"."%s"',
132
            $f_schema,
133
            $index
134
        );
135
136
        if ($cascade) {
137
            $sql .= ' CASCADE';
138
        }
139
140
        $status = $this->execute($sql);
141
142
        return [$status, $sql];
143
    }
144
145
    /**
146
     * Rebuild indexes.
147
     *
148
     * @param string $type  'DATABASE' or 'TABLE' or 'INDEX'
149
     * @param string $name  The name of the specific database, table, or index to be reindexed
150
     * @param bool   $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in
151
     *                      7.2-7.3, ignored in >=7.4
152
     *
153
     * @return ADORecordSet|int
154
     */
155
    public function reindex($type, $name, $force = false)
156
    {
157
        $f_schema = $this->_schema;
158
        $this->fieldClean($f_schema);
159
        $this->fieldClean($name);
160
161
        switch ($type) {
162
            case 'DATABASE':
163
                $sql = \sprintf(
164
                    'REINDEX %s "%s"',
165
                    $type,
166
                    $name
167
                );
168
169
                if ($force) {
170
                    $sql .= ' FORCE';
171
                }
172
173
                break;
174
            case 'TABLE':
175
            case 'INDEX':
176
                $sql = \sprintf(
177
                    'REINDEX %s "%s"."%s"',
178
                    $type,
179
                    $f_schema,
180
                    $name
181
                );
182
183
                if ($force) {
184
                    $sql .= ' FORCE';
185
                }
186
187
                break;
188
189
            default:
190
                return -1;
191
        }
192
193
        return $this->execute($sql);
194
    }
195
196
    /**
197
     * Clusters an index.
198
     *
199
     * @param string $table The table the index is on
200
     * @param string $index The name of the index
201
     *
202
     * @return array<integer,mixed|string> 0 if operation was successful
203
     */
204
    public function clusterIndex($table = '', $index = '')
205
    {
206
        $sql = 'CLUSTER';
207
208
        // We don't bother with a transaction here, as there's no point rolling
209
        // back an expensive cluster if a cheap analyze fails for whatever reason
210
211
        if (!empty($table)) {
212
            $f_schema = $this->_schema;
213
            $this->fieldClean($f_schema);
214
            $this->fieldClean($table);
215
            $sql .= \sprintf(
216
                ' "%s"."%s"',
217
                $f_schema,
218
                $table
219
            );
220
221
            if (!empty($index)) {
222
                $this->fieldClean($index);
223
                $sql .= \sprintf(
224
                    ' USING "%s"',
225
                    $index
226
                );
227
            }
228
        }
229
230
        $status = $this->execute($sql);
231
232
        return [$status, $sql];
233
    }
234
235
    /**
236
     * Returns a list of all constraints on a table,
237
     * including constraint name, definition, related col and referenced namespace,
238
     * table and col if needed.
239
     *
240
     * @param string $table the table where we are looking for fk
241
     *
242
     * @return ADORecordSet|int
243
     */
244
    public function getConstraintsWithFields($table)
245
    {
246
        $c_schema = $this->_schema;
247
        $this->clean($c_schema);
248
        $this->clean($table);
249
250
        // get the max number of col used in a constraint for the table
251
        $sql = \sprintf(
252
            'SELECT DISTINCT
253
			max(SUBSTRING(array_dims(c.conkey) FROM  $patern$^\[.*:(.*)\]$$patern$)) as nb
254
		FROM pg_catalog.pg_constraint AS c
255
			JOIN pg_catalog.pg_class AS r ON (c.conrelid=r.oid)
256
			JOIN pg_catalog.pg_namespace AS ns ON (r.relnamespace=ns.oid)
257
		WHERE
258
			r.relname = \'%s\' AND ns.nspname=\'%s\'',
259
            $table,
260
            $c_schema
261
        );
262
263
        $rs = $this->selectSet($sql);
264
265
        if ($rs->EOF) {
266
            $max_col = 0;
267
        } else {
268
            $max_col = $rs->fields['nb'];
269
        }
270
271
        $sql = '
272
			SELECT
273
				c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc,
274
				ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
275
				r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum, f2.attname as f_field,
276
				f2.attnum AS f_attnum, pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment,
277
				c.conrelid, c.confrelid
278
			FROM
279
				pg_catalog.pg_constraint AS c
280
				JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
281
				JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]';
282
283
        for ($i = 2; $i <= $rs->fields['nb']; ++$i) {
284
            $sql .= \sprintf(
285
                ' OR f1.attnum=c.conkey[%s]',
286
                $i
287
            );
288
        }
289
        $sql .= '))
290
				JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid
291
				LEFT JOIN (
292
					pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
293
				) ON (c.confrelid=r2.oid)
294
				LEFT JOIN pg_catalog.pg_attribute AS f2 ON
295
					(f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)';
296
297
        for ($i = 2; $i <= $rs->fields['nb']; ++$i) {
298
            $sql .= \sprintf(
299
                ' OR (c.confkey[%s]=f2.attnum AND c.conkey[%s]=f1.attnum)',
300
                $i,
301
                $i
302
            );
303
        }
304
305
        $sql .= \sprintf("))
306
			WHERE
307
				r1.relname = '%s' AND ns1.nspname='%s'
308
			ORDER BY 1", $table, $c_schema);
309
310
        return $this->selectSet($sql);
311
    }
312
313
    /**
314
     * Adds a primary key constraint to a table.
315
     *
316
     * @param string $table      The table to which to add the primery key
317
     * @param array  $fields     (array) An array of fields over which to add the primary key
318
     * @param string $name       (optional) The name to give the key, otherwise default name is assigned
319
     * @param string $tablespace (optional) The tablespace for the schema, '' indicates default
320
     *
321
     * @return ADORecordSet|int
322
     */
323
    public function addPrimaryKey($table, $fields, $name = '', $tablespace = '')
324
    {
325
        if (!\is_array($fields) || 0 === \count($fields)) {
326
            return -1;
327
        }
328
329
        $f_schema = $this->_schema;
330
        $this->fieldClean($f_schema);
331
        $this->fieldClean($table);
332
        $this->fieldArrayClean($fields);
333
        $this->fieldClean($name);
334
        $this->fieldClean($tablespace);
335
336
        $sql = \sprintf(
337
            'ALTER TABLE "%s"."%s" ADD ',
338
            $f_schema,
339
            $table
340
        );
341
342
        if ('' !== $name) {
343
            $sql .= \sprintf(
344
                'CONSTRAINT "%s" ',
345
                $name
346
            );
347
        }
348
349
        $sql .= 'PRIMARY KEY ("' . \implode('","', $fields) . '")';
350
351
        if ('' !== $tablespace && $this->hasTablespaces()) {
352
            $sql .= \sprintf(
353
                ' USING INDEX TABLESPACE "%s"',
354
                $tablespace
355
            );
356
        }
357
358
        return $this->execute($sql);
359
    }
360
361
    /**
362
     * Adds a unique constraint to a table.
363
     *
364
     * @param string      $table      The table to which to add the unique key
365
     * @param array|mixed $fields     (array) An array of fields over which to add the unique key
366
     * @param string      $name       (optional) The name to give the key, otherwise default name is assigned
367
     * @param string      $tablespace (optional) The tablespace for the schema, '' indicates default
368
     *
369
     * @return ADORecordSet|int
370
     */
371
    public function addUniqueKey($table, $fields, $name = '', $tablespace = '')
372
    {
373
        if (!\is_array($fields) || 0 === \count($fields)) {
374
            return -1;
375
        }
376
377
        $f_schema = $this->_schema;
378
        $this->fieldClean($f_schema);
379
        $this->fieldClean($table);
380
        $this->fieldArrayClean($fields);
381
        $this->fieldClean($name);
382
        $this->fieldClean($tablespace);
383
384
        $sql = \sprintf(
385
            'ALTER TABLE "%s"."%s" ADD ',
386
            $f_schema,
387
            $table
388
        );
389
390
        if ('' !== $name) {
391
            $sql .= \sprintf(
392
                'CONSTRAINT "%s" ',
393
                $name
394
            );
395
        }
396
397
        $sql .= 'UNIQUE ("' . \implode('","', $fields) . '")';
398
399
        if ('' !== $tablespace && $this->hasTablespaces()) {
400
            $sql .= \sprintf(
401
                ' USING INDEX TABLESPACE "%s"',
402
                $tablespace
403
            );
404
        }
405
406
        return $this->execute($sql);
407
    }
408
409
    // Function functions
410
411
    /**
412
     * Adds a check constraint to a table.
413
     *
414
     * @param string $table      The table to which to add the check
415
     * @param string $definition The definition of the check
416
     * @param string $name       (optional) The name to give the check, otherwise default name is assigned
417
     *
418
     * @return ADORecordSet|int
419
     */
420
    public function addCheckConstraint($table, $definition, $name = '')
421
    {
422
        $f_schema = $this->_schema;
423
        $this->fieldClean($f_schema);
424
        $this->fieldClean($table);
425
        $this->fieldClean($name);
426
        // @@ How the heck do you clean a definition???
427
428
        $sql = \sprintf(
429
            'ALTER TABLE "%s"."%s" ADD ',
430
            $f_schema,
431
            $table
432
        );
433
434
        if ('' !== $name) {
435
            $sql .= \sprintf(
436
                'CONSTRAINT "%s" ',
437
                $name
438
            );
439
        }
440
441
        $sql .= \sprintf(
442
            'CHECK (%s)',
443
            $definition
444
        );
445
446
        return $this->execute($sql);
447
    }
448
449
    /**
450
     * Drops a check constraint from a table.
451
     *
452
     * @param string $table The table from which to drop the check
453
     * @param string $name  The name of the check to be dropped
454
     *
455
     * @return bool|int 0 success
456
     */
457
    public function dropCheckConstraint($table, $name)
458
    {
459
        $f_schema = $this->_schema;
460
        $this->fieldClean($f_schema);
461
        $c_schema = $this->_schema;
462
        $this->clean($c_schema);
463
        $c_table = $table;
464
        $this->fieldClean($table);
465
        $this->clean($c_table);
466
        $this->clean($name);
467
468
        // Begin transaction
469
        $status = $this->beginTransaction();
470
471
        if (0 !== $status) {
472
            return -2;
473
        }
474
475
        // Properly lock the table
476
        $sql = \sprintf(
477
            'LOCK TABLE "%s"."%s" IN ACCESS EXCLUSIVE MODE',
478
            $f_schema,
479
            $table
480
        );
481
        $status = $this->execute($sql);
482
483
        if (0 !== $status) {
484
            $this->rollbackTransaction();
485
486
            return -3;
487
        }
488
489
        // Delete the check constraint
490
        $sql = \sprintf(
491
            'DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname=\'%s\'
492
			AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
493
			nspname = \'%s\')) AND rcname=\'%s\'',
494
            $c_table,
495
            $c_schema,
496
            $name
497
        );
498
        $status = $this->execute($sql);
499
500
        if (0 !== $status) {
501
            $this->rollbackTransaction();
502
503
            return -4;
504
        }
505
506
        // Update the pg_class catalog to reflect the new number of checks
507
        $sql = \sprintf(
508
            'UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE
509
					rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname=\'%s\'
510
						AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
511
						nspname = \'%s\')))
512
					WHERE relname=\'%s\'',
513
            $c_table,
514
            $c_schema,
515
            $c_table
516
        );
517
        $status = $this->execute($sql);
518
519
        if (0 !== $status) {
520
            $this->rollbackTransaction();
521
522
            return -4;
523
        }
524
525
        // Otherwise, close the transaction
526
        return $this->endTransaction();
527
    }
528
529
    /**
530
     * Adds a foreign key constraint to a table.
531
     *
532
     * @param string $table      The table on which to add an FK
533
     * @param string $targschema The schema that houses the target table to which to add the foreign key
534
     * @param string $targtable  The table to which to add the foreign key
535
     * @param array  $sfields    (array) An array of source fields over which to add the foreign key
536
     * @param array  $tfields    (array) An array of target fields over which to add the foreign key
537
     * @param string $upd_action The action for updates (eg. RESTRICT)
538
     * @param string $del_action The action for deletes (eg. RESTRICT)
539
     * @param string $match      The match type (eg. MATCH FULL)
540
     * @param string $deferrable The deferrability (eg. NOT DEFERRABLE)
541
     * @param string $initially  The initially parameter for the FK (eg. INITIALLY IMMEDIATE)
542
     * @param string $name       [optional] The name to give the key, otherwise default name is assigned
543
     *
544
     * @return ADORecordSet|int
545
     *
546
     * @internal param \PHPPgAdmin\Database\The $target table that contains the target columns
547
     * @internal param \PHPPgAdmin\Database\The $intially initial deferrability (eg. INITIALLY IMMEDIATE)
548
     */
549
    public function addForeignKey(
550
        $table,
551
        $targschema,
552
        $targtable,
553
        $sfields,
554
        $tfields,
555
        $upd_action,
556
        $del_action,
557
        $match,
558
        $deferrable,
559
        $initially,
560
        $name = ''
561
    ) {
562
        if (!\is_array($sfields) || 0 === \count($sfields) ||
563
            !\is_array($tfields) || 0 === \count($tfields)) {
564
            return -1;
565
        }
566
567
        $f_schema = $this->_schema;
568
        $this->fieldClean($f_schema);
569
        $this->fieldClean($table);
570
        $this->fieldClean($targschema);
571
        $this->fieldClean($targtable);
572
        $this->fieldArrayClean($sfields);
573
        $this->fieldArrayClean($tfields);
574
        $this->fieldClean($name);
575
576
        $sql = \sprintf(
577
            'ALTER TABLE "%s"."%s" ADD ',
578
            $f_schema,
579
            $table
580
        );
581
582
        if ('' !== $name) {
583
            $sql .= \sprintf(
584
                'CONSTRAINT "%s" ',
585
                $name
586
            );
587
        }
588
589
        $sql .= 'FOREIGN KEY ("' . \implode('","', $sfields) . '") ';
590
        // Target table needs to be fully qualified
591
        $sql .= \sprintf(
592
            'REFERENCES "%s"."%s"("',
593
            $targschema,
594
            $targtable
595
        ) . \implode('","', $tfields) . '") ';
596
597
        if ($match !== $this->fkmatches[0]) {
598
            $sql .= \sprintf(
599
                ' %s',
600
                $match
601
            );
602
        }
603
604
        if ($upd_action !== $this->fkactions[0]) {
605
            $sql .= \sprintf(
606
                ' ON UPDATE %s',
607
                $upd_action
608
            );
609
        }
610
611
        if ($del_action !== $this->fkactions[0]) {
612
            $sql .= \sprintf(
613
                ' ON DELETE %s',
614
                $del_action
615
            );
616
        }
617
618
        if ($deferrable !== $this->fkdeferrable[0]) {
619
            $sql .= \sprintf(
620
                ' %s',
621
                $deferrable
622
            );
623
        }
624
625
        if ($initially !== $this->fkinitial[0]) {
626
            $sql .= \sprintf(
627
                ' %s',
628
                $initially
629
            );
630
        }
631
632
        return $this->execute($sql);
633
    }
634
635
    /**
636
     * Removes a constraint from a relation.
637
     *
638
     * @param string $constraint The constraint to drop
639
     * @param string $relation   The relation from which to drop
640
     * @param string $type       The type of constraint (c, f, u or p)
641
     * @param bool   $cascade    True to cascade drop, false to restrict
642
     *
643
     * @return ADORecordSet|int
644
     */
645
    public function dropConstraint($constraint, $relation, $type, $cascade)
646
    {
647
        $f_schema = $this->_schema;
648
        $this->fieldClean($f_schema);
649
        $this->fieldClean($constraint);
650
        $this->fieldClean($relation);
651
652
        $sql = \sprintf(
653
            'ALTER TABLE "%s"."%s" DROP CONSTRAINT "%s"',
654
            $f_schema,
655
            $relation,
656
            $constraint
657
        );
658
659
        if ($cascade) {
660
            $sql .= ' CASCADE';
661
        }
662
663
        return $this->execute($sql);
664
    }
665
666
    /**
667
     * A function for getting all columns linked by foreign keys given a group of tables.
668
     *
669
     * @param array $tables multi dimensional assoc array that holds schema and table name
670
     *
671
     * @return ADORecordSet|int recordset of linked tables and columns or -1 if $tables isn't an array
672
     */
673
    public function getLinkingKeys($tables)
674
    {
675
        if (!\is_array($tables)) {
676
            return -1;
677
        }
678
679
        $this->clean($tables[0]['tablename']);
680
        $this->clean($tables[0]['schemaname']);
681
        $tables_list = \sprintf(
682
            '\'%s\'',
683
            $tables[0]['tablename']
684
        );
685
        $schema_list = \sprintf(
686
            '\'%s\'',
687
            $tables[0]['schemaname']
688
        );
689
        $schema_tables_list = \sprintf(
690
            '\'%s.%s\'',
691
            $tables[0]['schemaname'],
692
            $tables[0]['tablename']
693
        );
694
        $tablescount = \count($tables);
695
696
        for ($i = 1; $i < $tablescount; ++$i) {
697
            $this->clean($tables[$i]['tablename']);
698
            $this->clean($tables[$i]['schemaname']);
699
            $tables_list .= \sprintf(
700
                ', \'%s\'',
701
                $tables[$i]['tablename']
702
            );
703
            $schema_list .= \sprintf(
704
                ', \'%s\'',
705
                $tables[$i]['schemaname']
706
            );
707
            $schema_tables_list .= \sprintf(
708
                ', \'%s.%s\'',
709
                $tables[$i]['schemaname'],
710
                $tables[$i]['tablename']
711
            );
712
        }
713
714
        $maxDimension = 1;
715
716
        $sql = \sprintf(
717
            '
718
			SELECT DISTINCT
719
				array_dims(pc.conkey) AS arr_dim,
720
				pgc1.relname AS p_table
721
			FROM
722
				pg_catalog.pg_constraint AS pc,
723
				pg_catalog.pg_class AS pgc1
724
			WHERE
725
				pc.contype = \'f\'
726
				AND (pc.conrelid = pgc1.relfilenode OR pc.confrelid = pgc1.relfilenode)
727
				AND pgc1.relname IN (%s)
728
			',
729
            $tables_list
730
        );
731
732
        //parse our output to find the highest dimension of foreign keys since pc.conkey is stored in an array
733
        $rs = $this->selectSet($sql);
734
735
        while (!$rs->EOF) {
736
            $arrData = \explode(':', $rs->fields['arr_dim']);
737
            $strdimension = \trim(\mb_substr($arrData[1], 0, \mb_strlen($arrData[1]) - 1));
738
            $tmpDimension = (int) $strdimension;
739
            $maxDimension = $tmpDimension > $maxDimension ? $tmpDimension : $maxDimension;
740
            $rs->MoveNext();
741
        }
742
743
        //we know the highest index for foreign keys that conkey goes up to, expand for us in an IN query
744
        $cons_str = '( (pfield.attnum = conkey[1] AND cfield.attnum = confkey[1]) ';
745
746
        for ($i = 2; $i <= $maxDimension; ++$i) {
747
            $cons_str .= \sprintf(
748
                'OR (pfield.attnum = conkey[%s] AND cfield.attnum = confkey[%s]) ',
749
                $i,
750
                $i
751
            );
752
        }
753
        $cons_str .= ') ';
754
755
        $sql = \sprintf(
756
            '
757
			SELECT
758
				pgc1.relname AS p_table,
759
				pgc2.relname AS f_table,
760
				pfield.attname AS p_field,
761
				cfield.attname AS f_field,
762
				pgns1.nspname AS p_schema,
763
				pgns2.nspname AS f_schema
764
			FROM
765
				pg_catalog.pg_constraint AS pc,
766
				pg_catalog.pg_class AS pgc1,
767
				pg_catalog.pg_class AS pgc2,
768
				pg_catalog.pg_attribute AS pfield,
769
				pg_catalog.pg_attribute AS cfield,
770
				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN (%s) ) AS pgns1,
771
 				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN (%s) ) AS pgns2
772
			WHERE
773
				pc.contype = \'f\'
774
				AND pgc1.relnamespace = pgns1.ns_id
775
 				AND pgc2.relnamespace = pgns2.ns_id
776
				AND pc.conrelid = pgc1.relfilenode
777
				AND pc.confrelid = pgc2.relfilenode
778
				AND pfield.attrelid = pc.conrelid
779
				AND cfield.attrelid = pc.confrelid
780
				AND %s
781
				AND pgns1.nspname || \'.\' || pgc1.relname IN (%s)
782
				AND pgns2.nspname || \'.\' || pgc2.relname IN (%s)
783
		',
784
            $schema_list,
785
            $schema_list,
786
            $cons_str,
787
            $schema_tables_list,
788
            $schema_tables_list
789
        );
790
791
        return $this->selectSet($sql);
792
    }
793
794
    /**
795
     * Finds the foreign keys that refer to the specified table.
796
     *
797
     * @param string $table The table to find referrers for
798
     *
799
     * @return ADORecordSet|int A recordset or -1 in case of error
800
     */
801
    public function getReferrers($table)
802
    {
803
        $this->clean($table);
804
805
        $status = $this->beginTransaction();
806
807
        if (0 !== $status) {
808
            return -1;
809
        }
810
811
        $c_schema = $this->_schema;
812
        $this->clean($c_schema);
813
814
        $sql = \sprintf(
815
            '
816
			SELECT
817
				pn.nspname,
818
				pl.relname,
819
				pc.conname,
820
				pg_catalog.pg_get_constraintdef(pc.oid) AS consrc
821
			FROM
822
				pg_catalog.pg_constraint pc,
823
				pg_catalog.pg_namespace pn,
824
				pg_catalog.pg_class pl
825
			WHERE
826
				pc.connamespace = pn.oid
827
				AND pc.conrelid = pl.oid
828
				AND pc.contype = \'f\'
829
				AND confrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=\'%s\'
830
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
831
					WHERE nspname=\'%s\'))
832
			ORDER BY 1,2,3
833
		',
834
            $table,
835
            $c_schema
836
        );
837
838
        return $this->selectSet($sql);
839
    }
840
841
    abstract public function fieldClean(&$str);
842
843
    abstract public function beginTransaction();
844
845
    abstract public function rollbackTransaction();
846
847
    abstract public function endTransaction();
848
849
    abstract public function execute($sql);
850
851
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
852
853
    abstract public function selectSet($sql);
854
855
    abstract public function clean(&$str);
856
857
    abstract public function hasTablespaces();
858
859
    abstract public function arrayClean(&$arr);
860
861
    abstract public function fieldArrayClean(&$arr);
862
}
863