Failed Conditions
Pull Request — master (#2378)
by Sergey
05:54 queued 01:03
created

_getPortableTriggerDefinition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Schema;
21
22
use Doctrine\DBAL\Exception\DriverException;
23
use Doctrine\DBAL\Types\Type;
24
25
/**
26
 * PostgreSQL Schema Manager.
27
 *
28
 * @author Konsta Vesterinen <[email protected]>
29
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
30
 * @author Benjamin Eberlei <[email protected]>
31
 * @since  2.0
32
 */
33
class PostgreSqlSchemaManager extends AbstractSchemaManager
34
{
35
    /**
36
     * @var array
37
     */
38
    private $existingSchemaPaths;
39
40
    /**
41
     * Gets all the existing schema names.
42
     *
43
     * @return array
44
     */
45
    public function getSchemaNames()
46
    {
47
        $rows = $this->_conn->fetchAll("SELECT nspname as schema_name FROM pg_namespace WHERE nspname !~ '^pg_.*' and nspname != 'information_schema'");
48
49
        return array_map(function ($v) { return $v['schema_name']; }, $rows);
50
    }
51
52
    /**
53
     * Returns an array of schema search paths.
54
     *
55
     * This is a PostgreSQL only function.
56
     *
57
     * @return array
58
     */
59
    public function getSchemaSearchPaths()
60
    {
61
        $params = $this->_conn->getParams();
62
        $schema = explode(",", $this->_conn->fetchColumn('SHOW search_path'));
63
64
        if (isset($params['user'])) {
65
            $schema = str_replace('"$user"', $params['user'], $schema);
66
        }
67
68
        return array_map('trim', $schema);
69
    }
70
71
    /**
72
     * Gets names of all existing schemas in the current users search path.
73
     *
74
     * This is a PostgreSQL only function.
75
     *
76
     * @return array
77
     */
78
    public function getExistingSchemaSearchPaths()
79
    {
80
        if ($this->existingSchemaPaths === null) {
81
            $this->determineExistingSchemaSearchPaths();
82
        }
83
84
        return $this->existingSchemaPaths;
85
    }
86
87
    /**
88
     * Sets or resets the order of the existing schemas in the current search path of the user.
89
     *
90
     * This is a PostgreSQL only function.
91
     *
92
     * @return void
93
     */
94
    public function determineExistingSchemaSearchPaths()
95
    {
96
        $names = $this->getSchemaNames();
97
        $paths = $this->getSchemaSearchPaths();
98
99
        $this->existingSchemaPaths = array_filter($paths, function ($v) use ($names) {
100
            return in_array($v, $names);
101
        });
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function dropDatabase($database)
108
    {
109
        try {
110
            parent::dropDatabase($database);
111
        } catch (DriverException $exception) {
112
            // If we have a SQLSTATE 55006, the drop database operation failed
113
            // because of active connections on the database.
114
            // To force dropping the database, we first have to close all active connections
115
            // on that database and issue the drop database operation again.
116
            if ($exception->getSQLState() !== '55006') {
117
                throw $exception;
118
            }
119
120
            $this->_execSql(
121
                array(
122
                    $this->_platform->getDisallowDatabaseConnectionsSQL($database),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Doctrine\DBAL\Platforms\AbstractPlatform as the method getDisallowDatabaseConnectionsSQL() does only exist in the following sub-classes of Doctrine\DBAL\Platforms\AbstractPlatform: Doctrine\DBAL\Platforms\PostgreSQL91Platform, Doctrine\DBAL\Platforms\PostgreSQL92Platform, Doctrine\DBAL\Platforms\PostgreSQL94Platform, Doctrine\DBAL\Platforms\PostgreSqlPlatform. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
123
                    $this->_platform->getCloseActiveDatabaseConnectionsSQL($database),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Doctrine\DBAL\Platforms\AbstractPlatform as the method getCloseActiveDatabaseConnectionsSQL() does only exist in the following sub-classes of Doctrine\DBAL\Platforms\AbstractPlatform: Doctrine\DBAL\Platforms\PostgreSQL91Platform, Doctrine\DBAL\Platforms\PostgreSQL92Platform, Doctrine\DBAL\Platforms\PostgreSQL94Platform, Doctrine\DBAL\Platforms\PostgreSqlPlatform. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
124
                )
125
            );
126
127
            parent::dropDatabase($database);
128
        }
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
135
    {
136
        $onUpdate = null;
137
        $onDelete = null;
138
139
        if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) {
140
            $onUpdate = $match[1];
141
        }
142
        if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) {
143
            $onDelete = $match[1];
144
        }
145
146
        if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) {
147
            // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get
148
            // the idea to trim them here.
149
            $localColumns = array_map('trim', explode(",", $values[1]));
150
            $foreignColumns = array_map('trim', explode(",", $values[3]));
151
            $foreignTable = $values[2];
152
        }
153
154
        return new ForeignKeyConstraint(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \Doctrine\DBA...Delete' => $onDelete)); (Doctrine\DBAL\Schema\ForeignKeyConstraint) is incompatible with the return type of the parent method Doctrine\DBAL\Schema\Abs...bleForeignKeyDefinition of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
155
            $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'],
0 ignored issues
show
Bug introduced by
The variable $localColumns does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $foreignTable does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $foreignColumns does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
156
            array('onUpdate' => $onUpdate, 'onDelete' => $onDelete)
157
        );
158
    }
159
160
    /**
161
     * {@inheritdoc}
162
     */
163
    protected function _getPortableTriggerDefinition($trigger)
164
    {
165
        return $trigger['trigger_name'];
166
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171
    protected function _getPortableViewDefinition($view)
172
    {
173
        return new View($view['schemaname'].'.'.$view['viewname'], $view['definition']);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \Doctrine\DBA..., $view['definition']); (Doctrine\DBAL\Schema\View) is incompatible with the return type of the parent method Doctrine\DBAL\Schema\Abs...tPortableViewDefinition of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    protected function _getPortableUserDefinition($user)
180
    {
181
        return array(
182
            'user' => $user['usename'],
183
            'password' => $user['passwd']
184
        );
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190
    protected function _getPortableTableDefinition($table)
191
    {
192
        $schemas = $this->getExistingSchemaSearchPaths();
193
        $firstSchema = array_shift($schemas);
194
195
        if ($table['schema_name'] == $firstSchema) {
196
            return $table['table_name'];
197
        } else {
198
            return $table['schema_name'] . "." . $table['table_name'];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $table['schema_na.... $table['table_name']; (string) is incompatible with the return type of the parent method Doctrine\DBAL\Schema\Abs...PortableTableDefinition of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
199
        }
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     *
205
     * @license New BSD License
206
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
207
     */
208
    protected function _getPortableTableIndexesList($tableIndexes, $tableName=null)
209
    {
210
        $buffer = array();
211
        foreach ($tableIndexes as $row) {
212
            $colNumbers = explode(' ', $row['indkey']);
213
            $colNumbersSql = 'IN (' . join(' ,', $colNumbers) . ' )';
214
            $columnNameSql = "SELECT attnum, attname FROM pg_attribute
215
                WHERE attrelid={$row['indrelid']} AND attnum $colNumbersSql ORDER BY attnum ASC;";
216
217
            $stmt = $this->_conn->executeQuery($columnNameSql);
218
            $indexColumns = $stmt->fetchAll();
219
220
            // required for getting the order of the columns right.
221
            foreach ($colNumbers as $colNum) {
222
                foreach ($indexColumns as $colRow) {
223 View Code Duplication
                    if ($colNum === '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...
224
                        $buffer[] = array(
225
                            'key_name' => $row['relname'],
226
                            'column_name' => $row['relname'],
227
                            'non_unique' => !$row['indisunique'],
228
                            'primary' => $row['indisprimary'],
229
                            'where' => $row['where'],
230
                        );
231
                    }
232
233 View Code Duplication
                    if ($colNum == $colRow['attnum']) {
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...
234
                        $buffer[] = array(
235
                            'key_name' => $row['relname'],
236
                            'column_name' => trim($colRow['attname']),
237
                            'non_unique' => !$row['indisunique'],
238
                            'primary' => $row['indisprimary'],
239
                            'where' => $row['where'],
240
                        );
241
                    }
242
                }
243
            }
244
        }
245
246
        return parent::_getPortableTableIndexesList($buffer, $tableName);
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    protected function _getPortableDatabaseDefinition($database)
253
    {
254
        return $database['datname'];
255
    }
256
257
    /**
258
     * {@inheritdoc}
259
     */
260 1
    protected function _getPortableSequencesList($sequences)
261
    {
262 1
        $sequenceDefinitions = array();
263
264 1
        foreach ($sequences as $sequence) {
265 1 View Code Duplication
            if ($sequence['schemaname'] != 'public') {
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...
266 1
                $sequenceName = $sequence['schemaname'] . "." . $sequence['relname'];
267
            } else {
268
                $sequenceName = $sequence['relname'];
269
            }
270
271 1
            $sequenceDefinitions[$sequenceName] = $sequence;
272
        }
273
274 1
        $list = array();
275
276 1
        foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) {
277 1
            $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]);
278
        }
279
280 1
        return $list;
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286
    protected function getPortableNamespaceDefinition(array $namespace)
287
    {
288
        return $namespace['nspname'];
289
    }
290
291
    /**
292
     * {@inheritdoc}
293
     */
294 1
    protected function _getPortableSequenceDefinition($sequence)
295
    {
296 1 View Code Duplication
        if ($sequence['schemaname'] != 'public') {
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...
297 1
            $sequenceName = $sequence['schemaname'] . "." . $sequence['relname'];
298
        } else {
299
            $sequenceName = $sequence['relname'];
300
        }
301
302 1
        $data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName));
303
304 1
        return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']);
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    protected function _getPortableTableColumnDefinition($tableColumn)
311
    {
312
        $tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
313
314
        if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') {
315
            // get length from varchar definition
316
            $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']);
317
            $tableColumn['length'] = $length;
318
        }
319
320
        $matches = array();
321
322
        $autoincrement = false;
323
        if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) {
324
            $tableColumn['sequence'] = $matches[1];
325
            $tableColumn['default'] = null;
326
            $autoincrement = true;
327
        }
328
329
        if (preg_match("/^['(](.*)[')]::.*$/", $tableColumn['default'], $matches)) {
330
            $tableColumn['default'] = $matches[1];
331
        }
332
333
        if (stripos($tableColumn['default'], 'NULL') === 0) {
334
            $tableColumn['default'] = null;
335
        }
336
337
        $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null;
338
        if ($length == '-1' && isset($tableColumn['atttypmod'])) {
339
            $length = $tableColumn['atttypmod'] - 4;
340
        }
341
        if ((int) $length <= 0) {
342
            $length = null;
343
        }
344
        $fixed = null;
345
346
        if (!isset($tableColumn['name'])) {
347
            $tableColumn['name'] = '';
348
        }
349
350
        $precision = null;
351
        $scale = null;
352
        $jsonb = null;
353
354
        $dbType = strtolower($tableColumn['type']);
355
        if (strlen($tableColumn['domain_type']) && !$this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) {
356
            $dbType = strtolower($tableColumn['domain_type']);
357
            $tableColumn['complete_type'] = $tableColumn['domain_complete_type'];
358
        }
359
360
        $type = $this->_platform->getDoctrineTypeMapping($dbType);
361
        $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
362
        $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
363
364
        switch ($dbType) {
365
            case 'smallint':
366 View Code Duplication
            case 'int2':
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...
367
                $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
368
                $length = null;
369
                break;
370
            case 'int':
371
            case 'int4':
372 View Code Duplication
            case 'integer':
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...
373
                $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
374
                $length = null;
375
                break;
376
            case 'bigint':
377 View Code Duplication
            case 'int8':
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...
378
                $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
379
                $length = null;
380
                break;
381
            case 'bool':
382
            case 'boolean':
383
                if ($tableColumn['default'] === 'true') {
384
                    $tableColumn['default'] = true;
385
                }
386
387
                if ($tableColumn['default'] === 'false') {
388
                    $tableColumn['default'] = false;
389
                }
390
391
                $length = null;
392
                break;
393
            case 'text':
394
                $fixed = false;
395
                break;
396
            case 'varchar':
397
            case 'interval':
398
            case '_varchar':
399
                $fixed = false;
400
                break;
401
            case 'char':
402
            case 'bpchar':
403
                $fixed = true;
404
                break;
405
            case 'float':
406
            case 'float4':
407
            case 'float8':
408
            case 'double':
409
            case 'double precision':
410
            case 'real':
411
            case 'decimal':
412
            case 'money':
413
            case 'numeric':
414
                $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
415
416 View Code Duplication
                if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) {
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...
417
                    $precision = $match[1];
418
                    $scale = $match[2];
419
                    $length = null;
420
                }
421
                break;
422
            case 'year':
423
                $length = null;
424
                break;
425
426
            // PostgreSQL 9.4+ only
427
            case 'jsonb':
428
                $jsonb = true;
429
                break;
430
        }
431
432
        if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) {
433
            $tableColumn['default'] = $match[1];
434
        }
435
436
        $options = array(
437
            'length'        => $length,
438
            'notnull'       => (bool) $tableColumn['isnotnull'],
439
            'default'       => $tableColumn['default'],
440
            'primary'       => (bool) ($tableColumn['pri'] == 't'),
441
            'precision'     => $precision,
442
            'scale'         => $scale,
443
            'fixed'         => $fixed,
444
            'unsigned'      => false,
445
            'autoincrement' => $autoincrement,
446
            'comment'       => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
447
                ? $tableColumn['comment']
448
                : null,
449
        );
450
451
        $column = new Column($tableColumn['field'], Type::getType($type), $options);
452
453 View Code Duplication
        if (isset($tableColumn['collation']) && !empty($tableColumn['collation'])) {
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...
454
            $column->setPlatformOption('collation', $tableColumn['collation']);
455
        }
456
457
        if ($column->getType()->getName() === 'json_array') {
458
            $column->setPlatformOption('jsonb', $jsonb);
459
        }
460
461
        return $column;
462
    }
463
464
    /**
465
     * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually.
466
     *
467
     * @param mixed $defaultValue
468
     *
469
     * @return mixed
470
     */
471
    private function fixVersion94NegativeNumericDefaultValue($defaultValue)
472
    {
473
        if (strpos($defaultValue, '(') === 0) {
474
            return trim($defaultValue, '()');
475
        }
476
477
        return $defaultValue;
478
    }
479
}
480