Completed
Pull Request — master (#1326)
by
unknown
01:56
created

OracleAdapter::getForeignKeys()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 12

Duplication

Lines 23
Ratio 92 %

Importance

Changes 0
Metric Value
dl 23
loc 25
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 12
nc 2
nop 2
1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
namespace Phinx\Db\Adapter;
30
31
use Phinx\Db\Table;
32
use Phinx\Db\Table\Column;
33
use Phinx\Db\Table\ForeignKey;
34
use Phinx\Db\Table\Index;
35
use Phinx\Migration\MigrationInterface;
36
37
/**
38
 * Phinx Oracle Adapter.
39
 *
40
 * @author Felipe Maia <[email protected]>
41
 */
42
class OracleAdapter extends PdoAdapter implements AdapterInterface
43
{
44
    protected $schema = 'dbo';
45
46
    protected $signedColumnTypes = ['integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true];
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function connect()
52
    {
53
        if ($this->connection === null) {
54
            if (!extension_loaded('pdo_oci')) {
55
                // @codeCoverageIgnoreStart
56
                throw new \RuntimeException('You need to enable the PDO_OCI extension for Phinx to run properly.');
57
                // @codeCoverageIgnoreEnd
58
            }
59
60
            $options = $this->getOptions();
61
62
            // if port is specified use it, otherwise use the Oracle default
63 View Code Duplication
            if (empty($options['port'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
64
                $dsn = "oci:dbname=//".$options['host']."/".$options['sid']."";
65
            } else {
66
                $dsn = "oci:dbname=//".$options['host'].":".$options['port']."/".$options['sid']."";
67
            }
68
69
            $driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION];
70
71
            try {
72
                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
73
            } catch (\PDOException $exception) {
74
                throw new \InvalidArgumentException(sprintf(
75
                    'There was a problem connecting to the database: %s',
76
                    $exception->getMessage()
77
                ));
78
            }
79
            $this->setConnection($db);
80
        }
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86
    public function disconnect()
87
    {
88
        $this->connection = null;
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94
    public function hasTransactions()
95
    {
96
        return true;
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function beginTransaction()
103
    {
104
//        $this->execute('BEGIN TRANSACTION');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function commitTransaction()
111
    {
112
//        $this->execute('COMMIT TRANSACTION');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function rollbackTransaction()
119
    {
120
//        $this->execute('ROLLBACK TRANSACTION');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function quoteTableName($tableName)
127
    {
128
        return str_replace('.', '].[', $this->quoteColumnName($tableName));
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function quoteColumnName($columnName)
135
    {
136
        return '"' . str_replace(']', '"', $columnName) . '"';
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function hasTable($tableName)
143
    {
144
        $result = $this->fetchRow(
145
            sprintf(
146
                'SELECT count(*) as count FROM ALL_TABLES WHERE table_name = \'%s\'',
147
                $tableName
148
            )
149
        );
150
        return $result['COUNT'] > 0;
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function createTable(Table $table)
157
    {
158
        $options = $table->getOptions();
159
160
        // Add the default primary key
161
        $columns = $table->getPendingColumns();
162
163
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
164
            $column = new Column();
165
            $column->setName('id')
166
                ->setType('integer')
167
                ->setIdentity(true);
168
169
            array_unshift($columns, $column);
170
            $options['primary_key'] = 'id';
171
        } elseif (isset($options['id']) && is_string($options['id'])) {
172
            // Handle id => "field_name" to support AUTO_INCREMENT
173
            $column = new Column();
174
            $column->setName($options['id'])
175
                ->setType('integer')
176
                ->setIdentity(true);
177
178
            array_unshift($columns, $column);
179
            $options['primary_key'] = $options['id'];
180
        }
181
182
        $sql = 'CREATE TABLE ';
183
        $sql .= $this->quoteTableName($table->getName()) . ' (';
184
        $sqlBuffer = [];
185
        $columnsWithComments = [];
186
187 View Code Duplication
        foreach ($columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
188
            $sqlBuffer[] = $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column);
189
190
            // set column comments, if needed
191
            if ($column->getComment()) {
192
                $columnsWithComments[] = $column;
193
            }
194
        }
195
196
        // set the primary key(s)
197 View Code Duplication
        if (isset($options['primary_key'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
198
            $pkSql = sprintf('CONSTRAINT PK_%s PRIMARY KEY (', substr($table->getName(), 0, 28));
199
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
200
                $pkSql .= $this->quoteColumnName($options['primary_key']);
201
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
202
                $pkSql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
203
            }
204
            $pkSql .= ')';
205
            $sqlBuffer[] = $pkSql;
206
        }
207
208
        // set the foreign keys
209
        $foreignKeys = $table->getForeignKeys();
210
        foreach ($foreignKeys as $key => $foreignKey) {
211
            $sqlBuffer[] = $this->getForeignKeySqlDefinition($foreignKey, $table->getName(), $key);
0 ignored issues
show
Unused Code introduced by
The call to OracleAdapter::getForeignKeySqlDefinition() has too many arguments starting with $key.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
212
        }
213
214
        $sql .= implode(', ', $sqlBuffer);
215
        $sql .= ')';
216
217
        $this->execute($sql);
218
        // process column comments
219
        foreach ($columnsWithComments as $key => $column) {
220
            $sql = $this->getColumnCommentSqlDefinition($column, $table->getName());
221
            $this->execute($sql);
222
        }
223
        // set the indexes
224
        $indexes = $table->getIndexes();
225
226
        if (!empty($indexes)) {
227
            foreach ($indexes as $index) {
228
                $sql = $this->getIndexSqlDefinition($index, $table->getName());
229
                $this->execute($sql);
230
            }
231
        }
232
233
        if (!$this->hasSequence($table->getName())) {
234
            $sql = "CREATE SEQUENCE SQ_".$table->getName()." MINVALUE 1 MAXVALUE 99999999999999999 INCREMENT BY 1";
235
            $this->execute($sql);
236
        }
237
    }
238
239
    /**
240
     * Verify if the table has a Sequence for primary Key
241
     *
242
     * @param string $tableName Table name
243
     *
244
     * @return boolean
245
     */
246
    public function hasSequence($tableName)
247
    {
248
        $sql = sprintf(
249
            "SELECT COUNT(*) as COUNT FROM user_sequences WHERE sequence_name = '%s'",
250
            strtoupper("SQ_".$tableName)
251
        );
252
        $result = $this->fetchRow($sql);
253
        return $result['COUNT'] > 0;
254
    }
255
256
    /**
257
     * Gets the Oracle Column Comment Defininition for a column object.
258
     *
259
     * @param \Phinx\Db\Table\Column $column    Column
260
     * @param string $tableName Table name
261
     *
262
     * @return string
263
     */
264
    protected function getColumnCommentSqlDefinition(Column $column, $tableName)
265
    {
266
        $comment = (strcasecmp($column->getComment(), 'NULL') !== 0) ? $column->getComment() : '';
267
268
        return sprintf(
269
            "COMMENT ON COLUMN \"%s\".\"%s\" IS '%s'",
270
            $tableName,
271
            $column->getName(),
272
            str_replace("'", "", $comment)
273
        );
274
    }
275
276
    /**
277
     * {@inheritdoc}
278
     */
279
    public function renameTable($tableName, $newTableName)
280
    {
281
        $this->execute(sprintf('alter table "%s" rename to "%s"', $tableName, $newTableName));
282
283
        if (!$this->hasSequence("SQ_" . strtoupper($newTableName))) {
284
            $this->renameSequence("SQ_" . strtoupper($tableName), "SQ_" . strtoupper($newTableName));
285
        }
286
    }
287
288
    public function renameSequence($sequenceName, $newSequenceName)
289
    {
290
        $this->execute(sprintf('rename "%s" to "%s"', $sequenceName, $newSequenceName));
291
    }
292
293
    /**
294
     * {@inheritdoc}
295
     */
296
    public function dropTable($tableName)
297
    {
298
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
299
        $this->execute(sprintf('DROP SEQUENCE %s', $this->quoteTableName(strtoupper("SQ_" . $tableName))));
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     */
305
    public function truncateTable($tableName)
306
    {
307
        $sql = sprintf(
308
            'TRUNCATE TABLE %s',
309
            $this->quoteTableName($tableName)
310
        );
311
312
        $this->execute($sql);
313
    }
314
315 View Code Duplication
    public function getColumnComment($tableName, $columnName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
316
    {
317
        $sql = sprintf(
318
            "select COMMENTS from ALL_COL_COMMENTS WHERE COLUMN_NAME = '%s' and TABLE_NAME = '%s'",
319
            $columnName,
320
            $tableName
321
        );
322
        $row = $this->fetchRow($sql);
323
324
        if ($row['COMMENTS'] != 'NULL') {
325
            return $row['COMMENTS'];
326
        }
327
328
        return false;
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334
    public function getColumns($tableName)
335
    {
336
        $columns = [];
337
338
        $sql = sprintf(
339
            "select TABLE_NAME \"TABLE_NAME\", COLUMN_NAME \"NAME\", DATA_TYPE \"TYPE\", NULLABLE \"NULL\", 
340
            DATA_DEFAULT \"DEFAULT\", DATA_LENGTH \"CHAR_LENGTH\", DATA_PRECISION \"PRECISION\", DATA_SCALE \"SCALE\", 
341
            COLUMN_ID \"ORDINAL_POSITION\" FROM ALL_TAB_COLUMNS WHERE table_name = '%s'",
342
            $tableName
343
        );
344
345
        $rows = $this->fetchAll($sql);
346
347
        foreach ($rows as $columnInfo) {
348
            $default = null;
349
            if (trim($columnInfo['DEFAULT']) != 'NULL') {
350
                $default = trim($columnInfo['DEFAULT']);
351
            }
352
353
            $column = new Column();
354
            $column->setName($columnInfo['NAME'])
355
                ->setType($this->getPhinxType($columnInfo['TYPE'], $columnInfo['PRECISION']))
356
                ->setNull($columnInfo['NULL'] !== 'N')
357
                ->setDefault($default)
358
//                TODO VERIFICAR SE � PRIMARY KEY
359
//                ->setIdentity($columnInfo['identity'] === '1')
360
                ->setComment($this->getColumnComment($columnInfo['TABLE_NAME'], $columnInfo['NAME']));
361
362
            if (!empty($columnInfo['CHAR_LENGTH'])) {
363
                $column->setLimit($columnInfo['CHAR_LENGTH']);
364
            }
365
366
            $columns[$columnInfo['NAME']] = $column;
367
        }
368
369
        return $columns;
370
    }
371
372 View Code Duplication
    protected function parseDefault($default)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
373
    {
374
        $default = preg_replace(["/\('(.*)'\)/", "/\(\((.*)\)\)/", "/\((.*)\)/"], '$1', $default);
375
376
        if (strtoupper($default) === 'NULL') {
377
            $default = null;
378
        } elseif (is_numeric($default)) {
379
            $default = (int)$default;
380
        }
381
382
        return $default;
383
    }
384
385
    /**
386
     * {@inheritdoc}
387
     */
388 View Code Duplication
    public function hasColumn($tableName, $columnName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
389
    {
390
        $sql = sprintf(
391
            "select count(*) as count from ALL_TAB_COLUMNS 
392
            where table_name = '%s' and column_name = '%s'",
393
            $tableName,
394
            $columnName
395
        );
396
397
        $result = $this->fetchRow($sql);
398
        return $result['COUNT'] > 0;
399
    }
400
401
    /**
402
     * {@inheritdoc}
403
     */
404 View Code Duplication
    public function addColumn(Table $table, Column $column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
405
    {
406
        $sql = sprintf(
407
            'ALTER TABLE %s ADD %s %s',
408
            $this->quoteTableName($table->getName()),
409
            $this->quoteColumnName($column->getName()),
410
            $this->getColumnSqlDefinition($column)
411
        );
412
413
        $this->execute($sql);
414
    }
415
416
    /**
417
     * {@inheritdoc}
418
     */
419
    public function renameColumn($tableName, $columnName, $newColumnName)
420
    {
421
        if (!$this->hasColumn($tableName, $columnName)) {
422
            throw new \InvalidArgumentException("The specified column does not exist: $columnName");
423
        }
424
//        $this->renameDefault($tableName, $columnName, $newColumnName);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
425
426
        $this->execute(
427
            sprintf(
428
                "alter table \"%s\" rename column \"%s\" TO \"%s\"",
429
                $tableName,
430
                $columnName,
431
                $newColumnName
432
            )
433
        );
434
    }
435
436 View Code Duplication
    protected function renameDefault($tableName, $columnName, $newColumnName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
437
    {
438
        $oldConstraintName = "DF_{$tableName}_{$columnName}";
439
        $newConstraintName = "DF_{$tableName}_{$newColumnName}";
440
        $sql = <<<SQL
441
IF (OBJECT_ID('$oldConstraintName', 'D') IS NOT NULL)
442
BEGIN
443
     EXECUTE sp_rename N'%s', N'%s', N'OBJECT'
444
END
445
SQL;
446
        $this->execute(sprintf(
447
            $sql,
448
            $oldConstraintName,
449
            $newConstraintName
450
        ));
451
    }
452
453
    /**
454
     * {@inheritdoc}
455
     */
456
    public function changeColumn($tableName, $columnName, Column $newColumn)
457
    {
458
        $columns = $this->getColumns($tableName);
459
460
        if ($columnName !== $newColumn->getName()) {
461
            $this->renameColumn($tableName, $columnName, $newColumn->getName());
462
        }
463
464
        $setNullSql = ($newColumn->isNull() == $columns[$columnName]->isNull() ? false : true);
465
466
        $this->execute(
467
            sprintf(
468
                'ALTER TABLE %s MODIFY(%s %s)',
469
                $this->quoteTableName($tableName),
470
                $this->quoteColumnName($newColumn->getName()),
471
                $this->getColumnSqlDefinition($newColumn, false, $setNullSql)
472
            )
473
        );
474
        // change column comment if needed
475
        if ($newColumn->getComment()) {
476
            $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName);
477
            $this->execute($sql);
478
        }
479
    }
480
481
    /**
482
     * {@inheritdoc}
483
     */
484
    public function dropColumn($tableName, $columnName)
485
    {
486
        $this->execute(
487
            sprintf(
488
                'ALTER TABLE %s DROP COLUMN %s',
489
                $this->quoteTableName($tableName),
490
                $this->quoteColumnName($columnName)
491
            )
492
        );
493
    }
494
495
    /**
496
     * Get an array of indexes from a particular table.
497
     *
498
     * @param string $tableName Table Name
499
     * @return array
500
     */
501 View Code Duplication
    public function getIndexes($tableName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
502
    {
503
        $indexes = [];
504
        $sql = "SELECT index_owner as owner,index_name,column_name FROM ALL_IND_COLUMNS 
505
                WHERE TABLE_NAME = '$tableName'";
506
507
        $rows = $this->fetchAll($sql);
508
        foreach ($rows as $row) {
509
            if (!isset($indexes[$row['INDEX_NAME']])) {
510
                $indexes[$row['INDEX_NAME']] = ['columns' => []];
511
            }
512
            $indexes[$row['INDEX_NAME']]['columns'][] = strtoupper($row['COLUMN_NAME']);
513
        }
514
515
        return $indexes;
516
    }
517
518
    /**
519
     * {@inheritdoc}
520
     */
521 View Code Duplication
    public function hasIndex($tableName, $columns)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
522
    {
523
524
        if (is_string($columns)) {
525
            $columns = [$columns]; // str to array
526
        }
527
528
        $indexes = $this->getIndexes($tableName);
529
        foreach ($indexes as $index) {
530
            $a = array_diff($columns, $index['columns']);
531
532
            if (empty($a)) {
533
                return true;
534
            }
535
        }
536
537
        return false;
538
    }
539
540
    /**
541
     * {@inheritdoc}
542
     */
543 View Code Duplication
    public function hasIndexByName($tableName, $indexName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
544
    {
545
        $indexes = $this->getIndexes($tableName);
546
547
        foreach ($indexes as $name => $index) {
548
            if ($name === $indexName) {
549
                return true;
550
            }
551
        }
552
553
        return false;
554
    }
555
556
    /**
557
     * {@inheritdoc}
558
     */
559
    public function addIndex(Table $table, Index $index)
560
    {
561
        $sql = $this->getIndexSqlDefinition($index, $table->getName());
562
        $this->execute($sql);
563
    }
564
565
    /**
566
     * {@inheritdoc}
567
     */
568 View Code Duplication
    public function dropIndex($tableName, $columns)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
569
    {
570
        if (is_string($columns)) {
571
            $columns = [$columns]; // str to array
572
        }
573
574
        $indexes = $this->getIndexes($tableName);
575
        $columns = array_map('strtoupper', $columns);
576
577
        foreach ($indexes as $indexName => $index) {
578
            $a = array_diff($columns, $index['columns']);
579
            if (empty($a)) {
580
                $this->execute(
581
                    sprintf(
582
                        'DROP INDEX %s',
583
                        $this->quoteColumnName($indexName)
584
                    )
585
                );
586
587
                return;
588
            }
589
        }
590
    }
591
592
    /**
593
     * {@inheritdoc}
594
     */
595 View Code Duplication
    public function dropIndexByName($tableName, $indexName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
596
    {
597
        $indexes = $this->getIndexes($tableName);
598
599
        foreach ($indexes as $name => $index) {
600
            if ($name === $indexName) {
601
                $this->execute(
602
                    sprintf(
603
                        'DROP INDEX %s',
604
                        $this->quoteColumnName($indexName),
605
                        $this->quoteTableName($tableName)
606
                    )
607
                );
608
609
                return;
610
            }
611
        }
612
    }
613
614
    /**
615
     * {@inheritdoc}
616
     */
617 View Code Duplication
    public function hasForeignKey($tableName, $columns, $constraint = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
618
    {
619
        if (is_string($columns)) {
620
            $columns = [$columns]; // str to array
621
        }
622
        $foreignKeys = $this->getForeignKeys($tableName);
623
624
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
625
            if (isset($foreignKeys[$constraint])) {
626
                return !empty($foreignKeys[$constraint]);
627
            }
628
629
            return false;
630
        } else {
631
            foreach ($foreignKeys as $key) {
632
                $a = array_diff($columns, $key['COLUMNS']);
633
                if (empty($a)) {
634
                    return true;
635
                }
636
            }
637
638
            return false;
639
        }
640
    }
641
642
    /**
643
     * Get an array of foreign keys from a particular table.
644
     *
645
     * @param string $tableName Table Name
646
     * @return array
647
     */
648 View Code Duplication
    protected function getForeignKeys($tableName, $type = 'R')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
649
    {
650
        $foreignKeys = [];
651
        $rows = $this->fetchAll(sprintf(
652
            "SELECT a.CONSTRAINT_NAME, a.TABLE_NAME, b.COLUMN_NAME, 
653
                    (SELECT c.TABLE_NAME from ALL_CONS_COLUMNS c 
654
                    WHERE c.CONSTRAINT_NAME = a.R_CONSTRAINT_NAME) referenced_table_name,
655
                    (SELECT c.COLUMN_NAME from ALL_CONS_COLUMNS c 
656
                    WHERE c.CONSTRAINT_NAME = a.R_CONSTRAINT_NAME) referenced_column_name
657
                    FROM all_constraints a JOIN ALL_CONS_COLUMNS b ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME
658
                    WHERE a.table_name = '%s'
659
                    AND CONSTRAINT_TYPE = '%s'",
660
            $tableName,
661
            $type
662
        ));
663
664
        foreach ($rows as $row) {
665
            $foreignKeys[$row['CONSTRAINT_NAME']]['TABLE'] = $row['TABLE_NAME'];
666
            $foreignKeys[$row['CONSTRAINT_NAME']]['COLUMNS'][] = $row['COLUMN_NAME'];
667
            $foreignKeys[$row['CONSTRAINT_NAME']]['REFERENCED_TABLE'] = $row['REFERENCED_TABLE_NAME'];
668
            $foreignKeys[$row['CONSTRAINT_NAME']]['REFERENCED_COLUMNS'][] = $row['REFERENCED_COLUMN_NAME'];
669
        }
670
671
        return $foreignKeys;
672
    }
673
674
    /**
675
     * {@inheritdoc}
676
     */
677
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
678
    {
679
680
        $this->execute(
681
            sprintf(
682
                'ALTER TABLE %s ADD %s',
683
                $this->quoteTableName($table->getName()),
684
                $this->getForeignKeySqlDefinition($foreignKey, $table->getName())
685
            )
686
        );
687
    }
688
689
    /**
690
     * {@inheritdoc}
691
     */
692 View Code Duplication
    public function dropForeignKey($tableName, $columns, $constraint = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
693
    {
694
        if (is_string($columns)) {
695
            $columns = [$columns]; // str to array
696
        }
697
698
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
699
            $this->execute(
700
                sprintf(
701
                    'ALTER TABLE %s DROP CONSTRAINT %s',
702
                    $this->quoteTableName($tableName),
703
                    $constraint
704
                )
705
            );
706
707
            return;
708
        } else {
709
            foreach ($columns as $column) {
710
                $rows = $this->fetchAll(sprintf(
711
                    "SELECT a.CONSTRAINT_NAME, a.TABLE_NAME, b.COLUMN_NAME, 
712
                    (SELECT c.TABLE_NAME from ALL_CONS_COLUMNS c 
713
                    WHERE c.CONSTRAINT_NAME = a.R_CONSTRAINT_NAME) referenced_table_name,
714
                    (SELECT c.COLUMN_NAME from ALL_CONS_COLUMNS c 
715
                    WHERE c.CONSTRAINT_NAME = a.R_CONSTRAINT_NAME) referenced_column_name
716
                    FROM all_constraints a JOIN ALL_CONS_COLUMNS b ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME
717
                    WHERE a.table_name = '%s'
718
                    AND CONSTRAINT_TYPE = 'R'
719
                    AND COLUMN_NAME = '%s'",
720
                    $tableName,
721
                    $column
722
                ));
723
                foreach ($rows as $row) {
724
                    $this->dropForeignKey($tableName, $columns, $row['CONSTRAINT_NAME']);
725
                }
726
            }
727
        }
728
    }
729
730
    /**
731
     * {@inheritdoc}
732
     */
733
    public function getSqlType($type, $limit = null)
734
    {
735
//      reference: https://docs.oracle.com/cd/B19306_01/gateways.102/b14270/apa.htm
736
        switch ($type) {
737
            case static::PHINX_TYPE_STRING:
738
                return ['name' => 'VARCHAR2', 'limit' => 255];
739
            case static::PHINX_TYPE_CHAR:
740
                return ['name' => 'CHAR', 'limit' => 255];
741
            case static::PHINX_TYPE_TEXT:
742
                return ['name' => 'LONG'];
743
            case static::PHINX_TYPE_INTEGER:
744
                return ['name' => 'NUMBER', 'precision' => 10];
745
            case static::PHINX_TYPE_BIG_INTEGER:
746
                return ['name' => 'NUMBER', 'precision' => 19];
747
            case static::PHINX_TYPE_FLOAT:
748
                return ['name' => 'FLOAT', 'precision' => 49];
749
            case static::PHINX_TYPE_DECIMAL:
750
                return ['name' => 'NUMBER'];
751
            case static::PHINX_TYPE_DATETIME:
752
                return ['name' => 'DATE'];
753
            case static::PHINX_TYPE_TIMESTAMP:
754
                return ['name' => 'TIMESTAMP'];
755
            case static::PHINX_TYPE_TIME:
756
                return ['name' => 'time'];
757
            case static::PHINX_TYPE_DATE:
758
                return ['name' => 'DATE'];
759
            case static::PHINX_TYPE_BLOB:
760
                return ['name' => 'BLOB'];
761
            case 'CLOB':
762
                return ['name' => 'CLOB'];
763
            case static::PHINX_TYPE_BINARY:
764
                return ['name' => 'RAW', 'limit' => 2000];
765
            case static::PHINX_TYPE_BOOLEAN:
766
                return ['name' => 'NUMBER', 'precision' => 1];
767
            case static::PHINX_TYPE_UUID:
768
                return ['name' => 'RAW', 'precision' => 16, 'default' => 'SYS_GUID()', 'limit' => 2000];
769
            case static::PHINX_TYPE_FILESTREAM:
770
                return ['name' => 'varbinary', 'limit' => 'max'];
771
            // Geospatial database types
772
            case static::PHINX_TYPE_GEOMETRY:
773
            case static::PHINX_TYPE_POINT:
774
            case static::PHINX_TYPE_LINESTRING:
775
            case static::PHINX_TYPE_POLYGON:
776
                // SQL Server stores all spatial data using a single data type.
777
                // Specific types (point, polygon, etc) are set at insert time.
778
                return ['name' => 'geography'];
779
            default:
780
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
781
        }
782
    }
783
784
    /**
785
     * Returns Phinx type by SQL type
786
     *
787
     * @param string $sqlType SQL Type definition
788
     * @throws \RuntimeException
789
     * @internal param string $sqlType SQL type
790
     * @returns string Phinx type
791
     */
792
    public function getPhinxType($sqlType, $precision = null)
793
    {
794
        if ($sqlType === 'VARCHAR2') {
795
            return static::PHINX_TYPE_STRING;
796
        } elseif ($sqlType === 'CHAR') {
797
            return static::PHINX_TYPE_CHAR;
798
        } elseif ($sqlType == 'LONG') {
799
            return static::PHINX_TYPE_TEXT;
800
        } elseif ($sqlType === 'NUMBER' and $precision === 10) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
801
            return static::PHINX_TYPE_INTEGER;
802
        } elseif ($sqlType === 'NUMBER' and $precision === 19) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
803
            return static::PHINX_TYPE_BIG_INTEGER;
804
        } elseif ($sqlType === 'FLOAT') {
805
            return static::PHINX_TYPE_FLOAT;
806
        } elseif ($sqlType === 'TIMESTAMP(6)') {
807
            return static::PHINX_TYPE_TIMESTAMP;
808
        } elseif ($sqlType === 'TIME') {
809
            return static::PHINX_TYPE_TIME;
810
        } elseif ($sqlType === 'DATE') {
811
            return static::PHINX_TYPE_DATE;
812
        } elseif ($sqlType === 'BLOB') {
813
            return static::PHINX_TYPE_BLOB;
814
        } elseif ($sqlType === 'CLOB') {
815
            return 'CLOB';
816
        } elseif ($sqlType === 'RAW' and $precision === 16) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
817
            return static::PHINX_TYPE_UUID;
818
        } elseif ($sqlType === 'RAW') {
819
            return static::PHINX_TYPE_BLOB;
820
        } elseif ($sqlType === 'NUMBER' and $precision === 1) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
821
            return static::PHINX_TYPE_BOOLEAN;
822
        } elseif ($sqlType === 'NUMBER') {
823
            return static::PHINX_TYPE_DECIMAL;
824
        } else {
825
            throw new \RuntimeException('The Oracle type: "' . $sqlType . '" is not supported');
826
        }
827
        //TODO Geospatial database types and Filestream type
828
    }
829
830
    /**
831
     * {@inheritdoc}
832
     */
833
    //    TODO
834 View Code Duplication
    public function createDatabase($name, $options = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
835
    {
836
        if (isset($options['collation'])) {
837
            $this->execute(sprintf('CREATE DATABASE [%s] COLLATE [%s]', $name, $options['collation']));
838
        } else {
839
            $this->execute(sprintf('CREATE DATABASE [%s]', $name));
840
        }
841
        $this->execute(sprintf('USE [%s]', $name));
842
    }
843
844
    /**
845
     * {@inheritdoc}
846
     */
847
    public function hasDatabase($name)
848
    {
849
        $result = $this->fetchRow(
850
            sprintf(
851
                'SELECT count(*) as [count] FROM master.dbo.sysdatabases WHERE [name] = \'%s\'',
852
                $name
853
            )
854
        );
855
856
        return $result['count'] > 0;
857
    }
858
859
    /**
860
     * {@inheritdoc}
861
     */
862
//    TODO
863
    public function dropDatabase($name)
864
    {
865
        $sql = <<<SQL
866
USE master;
867
IF EXISTS(select * from sys.databases where name=N'$name')
868
ALTER DATABASE [$name] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
869
DROP DATABASE [$name];
870
SQL;
871
        $this->execute($sql);
872
    }
873
874
    /**
875
     * Get the defintion for a `DEFAULT` statement.
876
     *
877
     * @param  mixed $default
878
     * @return string
879
     */
880
    protected function getDefaultValueDefinition($default)
881
    {
882
        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default && 'SYSDATE' !== $default) {
883
            $default = $this->getConnection()->quote($default);
884
        } elseif (is_bool($default)) {
885
            $default = $this->castToBool($default);
886
        }
887
888
        return isset($default) ? ' DEFAULT ' . $default : 'DEFAULT NULL';
889
    }
890
891
    /**
892
     * Gets the Oracle Column Definition for a Column object.
893
     *
894
     * @param \Phinx\Db\Table\Column $column Column
895
     * @return string
896
     */
897
    protected function getColumnSqlDefinition(Column $column, $create = true, $setNullSql = true)
0 ignored issues
show
Unused Code introduced by
The parameter $create is not used and could be removed.

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

Loading history...
898
    {
899
        $buffer = [];
900
901
        $sqlType = $this->getSqlType($column->getType());
902
903
        $buffer[] = strtoupper($sqlType['name']);
904
        // integers cant have limits in Oracle
905
        $noLimits = [
906
            static::PHINX_TYPE_INTEGER,
907
            static::PHINX_TYPE_BIG_INTEGER,
908
            static::PHINX_TYPE_FLOAT,
909
            static::PHINX_TYPE_UUID,
910
            static::PHINX_TYPE_BOOLEAN
911
        ];
912 View Code Duplication
        if (!in_array($column->getType(), $noLimits) && ($column->getLimit() || isset($sqlType['limit']))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
913
            $buffer[] = sprintf('(%s)', $column->getLimit() ?: $sqlType['limit']);
914
        }
915 View Code Duplication
        if ($column->getPrecision() && $column->getScale()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
916
            $buffer[] = '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
917
        }
918
919 View Code Duplication
        if ($column->getDefault() === null && $column->isNull()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
920
            $buffer[] = ' DEFAULT NULL';
921
        } else {
922
            $buffer[] = $this->getDefaultValueDefinition($column->getDefault());
923
        }
924
925
        if ($setNullSql) {
926
            $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL';
927
        }
928
929
        return implode(' ', $buffer);
930
    }
931
932
    /**
933
     * Gets the Oracle Index Definition for an Index object.
934
     *
935
     * @param \Phinx\Db\Table\Index $index Index
936
     * @return string
937
     */
938 View Code Duplication
    protected function getIndexSqlDefinition(Index $index, $tableName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
939
    {
940
        if (is_string($index->getName())) {
941
            $indexName = $index->getName();
942
        } else {
943
            $columnNames = $index->getColumns();
944
            if (is_string($columnNames)) {
945
                $columnNames = [$columnNames];
946
            }
947
            $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames));
948
        }
949
        $def = sprintf(
950
            "CREATE %s INDEX %s ON %s (%s)",
951
            ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''),
952
            $indexName,
953
            $this->quoteTableName($tableName),
954
            '"' . implode('","', $index->getColumns()) . '"'
955
        );
956
957
        return $def;
958
    }
959
960
    /**
961
     * Gets the Oracle Foreign Key Definition for an ForeignKey object.
962
     *
963
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
964
     * @return string
965
     */
966
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, $tableName)
967
    {
968
        $constraintName = $foreignKey->getConstraint() ?: $tableName . '_' . implode('_', $foreignKey->getColumns());
969
        $def = ' CONSTRAINT ' . $this->quoteColumnName(substr($constraintName, 0, 27));
970
        $def .= ' FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")';
971
        $def .= " REFERENCES {$this->quoteTableName($foreignKey->getReferencedTable()->getName())} 
972
        (\"" . implode('", "', $foreignKey->getReferencedColumns()) . '")';
973
        if ($foreignKey->getOnDelete() && $foreignKey->getOnDelete() != "NO ACTION") {
974
            $def .= " ON DELETE {$foreignKey->getOnDelete()}";
975
        }
976
        if ($foreignKey->getOnUpdate()) {
977
            $def .= " ON UPDATE {$foreignKey->getOnUpdate()}";
978
        }
979
980
        return $def;
981
    }
982
983
    /**
984
     * {@inheritdoc}
985
     */
986
    public function getColumnTypes()
987
    {
988
        return array_merge(parent::getColumnTypes(), ['filestream']);
989
    }
990
991
    /**
992
     * Records a migration being run.
993
     *
994
     * @param \Phinx\Migration\MigrationInterface $migration Migration
995
     * @param string $direction Direction
996
     * @param int $startTime Start Time
997
     * @param int $endTime End Time
998
     * @return \Phinx\Db\Adapter\AdapterInterface
999
     */
1000
    public function migrated(\Phinx\Migration\MigrationInterface $migration, $direction, $startTime, $endTime)
1001
    {
1002
        $startTime = "TO_TIMESTAMP('$startTime', 'YYYY-MM-DD HH24:MI:SS')";
1003
        $endTime = "TO_TIMESTAMP('$endTime', 'YYYY-MM-DD HH24:MI:SS')";
1004
1005 View Code Duplication
        if (strcasecmp($direction, MigrationInterface::UP) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1006
            // up
1007
            $sql = sprintf(
1008
                "INSERT INTO \"%s\" (%s, %s, %s, %s, %s) VALUES ('%s', '%s', %s, %s, %s)",
1009
                $this->getSchemaTableName(),
1010
                $this->quoteColumnName('version'),
1011
                $this->quoteColumnName('migration_name'),
1012
                $this->quoteColumnName('start_time'),
1013
                $this->quoteColumnName('end_time'),
1014
                $this->quoteColumnName('breakpoint'),
1015
                $migration->getVersion(),
1016
                substr($migration->getName(), 0, 100),
1017
                $startTime,
1018
                $endTime,
1019
                $this->castToBool(false)
1020
            );
1021
1022
            $this->execute($sql);
1023
        } else {
1024
            // down
1025
            $sql = sprintf(
1026
                "DELETE FROM \"%s\" WHERE %s = '%s'",
1027
                $this->getSchemaTableName(),
1028
                $this->quoteColumnName('version'),
1029
                $migration->getVersion()
1030
            );
1031
1032
            $this->execute($sql);
1033
        }
1034
1035
        return $this;
1036
    }
1037
1038
    /**
1039
     * {@inheritdoc}
1040
     */
1041
    public function bulkinsert(Table $table, $rows)
1042
    {
1043
        $sql = "INSERT ALL ";
1044
1045
        $vals = [];
1046
        $tableName = $table->getName();
1047
        $primaryKeyColumn = current($this->getForeignKeys($tableName, 'P'));
1048
        $sequenceNextVal = $this->getNextValSequence('SQ_' . $tableName);
1049
//        buscar sequence e primary key padr�o para incrementar PK com a SEQUENCE.NEXTVAL
1050
1051
        foreach ($rows as $key => $row) {
1052
            $pk = ($sequenceNextVal + $key);
1053
            $row[$primaryKeyColumn['COLUMNS'][0]] = (int) $pk;
1054
1055
            $sql .= sprintf(
1056
                "INTO %s ",
1057
                $this->quoteTableName($tableName)
1058
            );
1059
1060
            $keys = array_keys($row);
1061
            $sql .= "(" . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ") VALUES";
1062
1063
            foreach ($row as $v) {
1064
                $vals[] = $v;
1065
            }
1066
1067
            $count_keys = count($keys);
1068
            $query = " (" . implode(', ', array_fill(0, $count_keys, '?')) . ") ";
1069
1070
            $queries = array_fill(0, 1, $query);
1071
            $sql .= implode(',', $queries);
1072
        }
1073
        $sql .= "SELECT 1 FROM DUAL";
1074
        $stmt = $this->getConnection()->prepare($sql);
1075
        $stmt->execute($vals);
1076
    }
1077
1078
    protected function getNextValSequence($sequence)
1079
    {
1080
        $sql = "SELECT %s.NEXTVAL FROM DUAL";
1081
        $rows = $this->fetchAll(sprintf($sql, $sequence));
1082
        return $rows[0]['NEXTVAL'];
1083
    }
1084
    
1085
    /**
1086
     * {@inheritdoc}
1087
     */
1088 View Code Duplication
    public function getVersionLog()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1089
    {
1090
        $result = [];
1091
1092
        switch ($this->options['version_order']) {
1093
            case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME:
1094
                $orderBy = '"version" ASC';
1095
                break;
1096
            case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME:
1097
                $orderBy = '"start_time" ASC, "version" ASC';
1098
                break;
1099
            default:
1100
                throw new \RuntimeException('Invalid version_order configuration option');
1101
        }
1102
1103
        $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->quoteColumnName(
1104
            $this->getSchemaTableName()
1105
        ), $orderBy));
1106
        foreach ($rows as $version) {
1107
            $result[$version['version']] = $version;
1108
        }
1109
1110
        return $result;
1111
    }
1112
}
1113