SQLiteColumn::sqlStatement()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 15
ccs 3
cts 3
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of Cycle ORM package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Database\Driver\SQLite\Schema;
13
14
use Cycle\Database\Driver\DriverInterface;
15
use Cycle\Database\Schema\AbstractColumn;
0 ignored issues
show
Bug introduced by
The type Cycle\Database\Schema\AbstractColumn was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
16
use Cycle\Database\Schema\Attribute\ColumnAttribute;
17
18
class SQLiteColumn extends AbstractColumn
19
{
20
    /**
21
     * Default timestamp expression (driver specific).
22
     */
23
    public const DATETIME_NOW = 'CURRENT_TIMESTAMP';
24
25
    /**
26
     * Private state related values.
27
     */
28
    public const EXCLUDE_FROM_COMPARE = [
29
        'userType',
30
        'timezone',
31
        'size',
32
        'attributes',
33
    ];
34
35
    protected array $mapping = [
36
        //Primary sequences
37
        'primary'     => [
38
            'type'       => 'integer',
39
            'primaryKey' => true,
40
            'nullable'   => false,
41
        ],
42
        'bigPrimary'  => [
43
            'type'       => 'integer',
44
            'primaryKey' => true,
45
            'nullable'   => false,
46
        ],
47
48
        //Enum type (mapped via method)
49
        'enum'        => 'enum',
50
51
        //Logical types
52
        'boolean'     => ['type' => 'tinyint', 'size' => 1],
53
54
        //Integer types (size can always be changed with size method), longInteger has method alias
55
        //bigInteger
56
        'integer'     => 'integer',
57
        'tinyInteger' => 'tinyint',
58
        'smallInteger' => 'smallint',
59
        'bigInteger'  => 'bigint',
60
61
        //String with specified length (mapped via method)
62
        'string'      => ['type' => 'text', 'size' => 255],
63
64
        //Generic types
65
        'text'        => 'text',
66
        'tinyText'    => 'text',
67
        'mediumText'  => 'text',
68
        'longText'    => 'text',
69
70
        //Real types
71
        'double'      => 'double',
72
        'float'       => 'real',
73
74
        //Decimal type (mapped via method)
75
        'decimal'     => 'numeric',
76
77
        //Date and Time types
78
        'datetime'    => 'datetime',
79
        'date'        => 'date',
80
        'time'        => 'time',
81
        'timestamp'   => 'timestamp',
82
83
        //Binary types
84
        'binary'      => 'blob',
85
        'tinyBinary'  => 'blob',
86
        'longBinary'  => 'blob',
87
88
        //Additional types
89
        'json'        => 'text',
90
        'uuid'        => ['type' => 'varchar', 'size' => 36],
91
    ];
92
    protected array $reverseMapping = [
93
        'primary'     => [['type' => 'integer', 'primaryKey' => true]],
94
        'enum'        => ['enum'],
95
        'boolean'     => ['boolean', ['type' => 'tinyint', 'size' => 1]],
96
        'integer'     => ['int', 'integer', 'mediumint'],
97
        'tinyInteger' => ['tinyint'],
98
        'smallInteger' => ['smallint'],
99
        'bigInteger'  => ['bigint'],
100
        'text'        => ['text', 'string'],
101
        'double'      => ['double'],
102
        'float'       => ['real'],
103
        'decimal'     => ['numeric'],
104
        'datetime'    => ['datetime'],
105
        'date'        => ['date'],
106
        'time'        => ['time'],
107
        'timestamp'   => ['timestamp'],
108
        'binary'      => ['blob'],
109
        'string'      => ['varchar'],
110
    ];
111
112
    /**
113
     * Indication that column is primary key.
114
     */
115
    protected bool $primaryKey = false;
116
117
    #[ColumnAttribute(['numeric'])]
118
    protected int $precision = 0;
119 476
120
    #[ColumnAttribute(['numeric'])]
121 476
    protected int $scale = 0;
122 332
123
    /**
124
     * @psalm-param non-empty-string $table
125 460
     */
126
    public static function createInstance(
127
        string $table,
128
        array $schema,
129
        ?\DateTimeZone $timezone = null,
130
    ): self {
131 476
        $column = new self($table, $schema['name'], $timezone);
132
133 476
        $column->nullable = !$schema['notnull'];
0 ignored issues
show
Bug Best Practice introduced by
The property nullable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
134 476
        $column->type = \strtolower($schema['type']);
0 ignored issues
show
Bug Best Practice introduced by
The property type does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
135 468
136
        if ((bool) $schema['pk'] && $column->type === 'integer') {
137
            $column->primaryKey = true;
138 152
        }
139 152
140 152
        /*
141
         * Normalizing default value.
142
         */
143 152
        $column->defaultValue = $schema['dflt_value'];
0 ignored issues
show
Bug Best Practice introduced by
The property defaultValue does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
144
145 152
        if (
146
            \is_string($column->defaultValue)
147
            && \preg_match('/^[\'""].*?[\'"]$/', $column->defaultValue)
148
        ) {
149
            $column->defaultValue = \substr($column->defaultValue, 1, -1);
150
        }
151 472
152
        if (
153
            !\preg_match(
154
                '/^(?P<type>[a-z]+) *(?:\((?P<options>[^\)]+)\))?/',
155
                $schema['type'],
156 472
                $matches,
157
            )
158 472
        ) {
159 472
            //No type definition included
160
            return $column;
161 472
        }
162 328
163
        // reformatted type value
164
        $column->type = $matches['type'];
165
166
        //Fetching size options
167
        if (!empty($matches['options'])) {
168 472
            $options = \explode(',', $matches['options']);
169
170
            if (\count($options) > 1) {
171 472
                $column->precision = (int) $options[0];
172 472
                $column->scale = (int) $options[1];
173
            } else {
174 178
                $column->size = (int) $options[0];
0 ignored issues
show
Bug Best Practice introduced by
The property size does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
175
            }
176
        }
177
178 472
        if ($column->type === 'enum') {
179 472
            //Quoted column name
180 472
            $quoted = $schema['identifier'];
181
182
            foreach ($schema['table'] as $columnSchema) {
183
                //Looking for enum values in column definition code
184
                if (
185
                    \preg_match(
186
                        "/{$quoted} +enum.*?CHECK *\\({$quoted} in \\((.*?)\\)\\)/i",
187
                        \trim($columnSchema),
188
                        $matches,
189 472
                    )
190
                ) {
191
                    $enumValues = \explode(',', $matches[1]);
192 472
                    foreach ($enumValues as &$value) {
193 262
                        //Trimming values
194
                        if (\preg_match("/^'?(.*?)'?$/", \trim($value), $matches)) {
195 262
                            //In database: 'value'
196 14
                            $value = $matches[1];
197 14
                        }
198
199 252
                        unset($value);
200
                    }
201
                    unset($value);
202
203 472
                    $column->enumValues = $enumValues;
0 ignored issues
show
Bug Best Practice introduced by
The property enumValues does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
204
                }
205 152
            }
206
        }
207 152
208
        return $column;
209
    }
210 152
211 152
    /**
212 152
     * DBMS specific reverse mapping must map database specific type into limited set of abstract
213
     * types.
214
     *
215
     * @psalm-return non-empty-string
216 152
     */
217 152
    public function getAbstractType(): string
218
    {
219 152
        if ($this->primaryKey && $this->type === 'integer') {
220
            return 'primary';
221 152
        }
222
223
        return parent::getAbstractType();
224 152
    }
225
226 152
    /**
227
     * @psalm-return non-empty-string
228 152
     */
229
    public function sqlStatement(DriverInterface $driver): string
230
    {
231
        $statement = parent::sqlStatement($driver);
232
        if ($this->getAbstractType() !== 'enum') {
233 472
            return $statement;
234
        }
235
236 152
        $enumValues = [];
237
        foreach ($this->enumValues as $value) {
238 152
            $enumValues[] = $driver->quote($value);
239
        }
240
241
        $quoted = $driver->identifier($this->name);
0 ignored issues
show
Bug introduced by
The method identifier() does not exist on Cycle\Database\Driver\DriverInterface. It seems like you code against a sub-type of Cycle\Database\Driver\DriverInterface such as Cycle\Database\Driver\Driver. ( Ignorable by Annotation )

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

241
        /** @scrutinizer ignore-call */ 
242
        $quoted = $driver->identifier($this->name);
Loading history...
242
243
        return "$statement CHECK ({$quoted} IN (" . \implode(', ', $enumValues) . '))';
244
    }
245
246
    protected static function isEnum(AbstractColumn $column): bool
247
    {
248
        return false;
249
    }
250
251
    protected static function isJson(AbstractColumn $column): ?bool
252
    {
253
        // In SQLite, we cannot determine if a column has a JSON type.
254
        return $column->getAbstractType() === 'text' ? null : false;
255
    }
256
257
    protected function quoteEnum(DriverInterface $driver): string
258
    {
259
        return '';
260
    }
261
}
262