Completed
Push — master ( 07fa0a...a5b00e )
by Edgard
10:14
created

QueryBuilder::buildSelect()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 14
cts 14
cp 1
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 11
nc 2
nop 4
crap 6
1
<?php
2
3
/**
4
 * @link http://www.yiiframework.com/
5
 * @copyright Copyright (c) 2008 Yii Software LLC
6
 * @license http://www.yiiframework.com/license/
7
 */
8
9
namespace edgardmessias\db\firebird;
10
11
use yii\base\InvalidParamException;
12
use yii\db\Expression;
13
use yii\db\Query;
14
15
/**
16
 *
17
 * @author Edgard Lorraine Messias <[email protected]>
18
 * @since 2.0
19
 */
20
class QueryBuilder extends \yii\db\QueryBuilder
21
{
22
23
    /**
24
     * @var array mapping from abstract column types (keys) to physical column types (values).
25
     */
26
    public $typeMap = [
27
        Schema::TYPE_PK        => 'integer NOT NULL PRIMARY KEY',
28
        Schema::TYPE_UPK       => 'integer NOT NULL PRIMARY KEY',
29
        Schema::TYPE_BIGPK     => 'bigint NOT NULL PRIMARY KEY',
30
        Schema::TYPE_UBIGPK    => 'bigint NOT NULL PRIMARY KEY',
31
        Schema::TYPE_CHAR      => 'char(1)',
32
        Schema::TYPE_STRING    => 'varchar(255)',
33
        Schema::TYPE_TEXT      => 'blob sub_type text',
34
        Schema::TYPE_SMALLINT  => 'smallint',
35
        Schema::TYPE_INTEGER   => 'integer',
36
        Schema::TYPE_BIGINT    => 'bigint',
37
        Schema::TYPE_FLOAT     => 'float',
38
        Schema::TYPE_DOUBLE    => 'double precision',
39
        Schema::TYPE_DECIMAL   => 'numeric(10,0)',
40
        Schema::TYPE_DATETIME  => 'timestamp',
41
        Schema::TYPE_TIMESTAMP => 'timestamp',
42
        Schema::TYPE_TIME      => 'time',
43
        Schema::TYPE_DATE      => 'date',
44
        Schema::TYPE_BINARY    => 'blob',
45
        Schema::TYPE_BOOLEAN   => 'smallint',
46
        Schema::TYPE_MONEY     => 'numeric(18,4)',
47
    ];
48
49
    /**
50
     * Generates a SELECT SQL statement from a [[Query]] object.
51
     * @param Query $query the [[Query]] object from which the SQL statement will be generated.
52
     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will
53
     * be included in the result with the additional parameters generated during the query building process.
54
     * @return array the generated SQL statement (the first array element) and the corresponding
55
     * parameters to be bound to the SQL statement (the second array element). The parameters returned
56
     * include those provided in `$params`.
57
     */
58 176
    public function build($query, $params = [])
59
    {
60 176
        $query = $query->prepare($this);
61
62 176
        $params = empty($params) ? $query->params : array_merge($params, $query->params);
63
64
        $clauses = [
65 176
            $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
66 176
            $this->buildFrom($query->from, $params),
67 176
            $this->buildJoin($query->join, $params),
68 176
            $this->buildWhere($query->where, $params),
69 176
            $this->buildGroupBy($query->groupBy),
70 176
            $this->buildHaving($query->having, $params),
71 176
        ];
72
73 176
        $sql = implode($this->separator, array_filter($clauses));
74 176
        $sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset);
75
76 176
        if (!empty($query->orderBy)) {
77 33
            foreach ($query->orderBy as $expression) {
78 33
                if ($expression instanceof Expression) {
79 1
                    $params = array_merge($params, $expression->params);
80 1
                }
81 33
            }
82 33
        }
83 176
        if (!empty($query->groupBy)) {
84 2
            foreach ($query->groupBy as $expression) {
85 2
                if ($expression instanceof Expression) {
86 1
                    $params = array_merge($params, $expression->params);
87 1
                }
88 2
            }
89 2
        }
90
91 176
        $union = $this->buildUnion($query->union, $params);
92 176
        if ($union !== '') {
93 2
            $sql = "$sql{$this->separator}$union";
94 2
        }
95
96 176
        return [$sql, $params];
97
    }
98
99
    /**
100
     * @inheritdoc
101
     */
102 176
    public function buildSelect($columns, &$params, $distinct = false, $selectOption = null)
103
    {
104 176
        if (is_array($columns)) {
105 48
            foreach ($columns as $i => $column) {
106 48
                if (!is_string($column)) {
107 3
                    continue;
108
                }
109 47
                $matches = [];
110 47
                if (preg_match("/^(COUNT|SUM|AVG|MIN|MAX)\([\{\[]{0,2}(\w+|\*)[\}\]]{0,2}\)$/i", $column, $matches)) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /^(COUNT|SUM|AVG|MIN|MAX...\w+|\*)[\}\]]{0,2}\)$/i does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
111 17
                    $function = $matches[1];
112 17
                    $alias = $matches[2] != '*' ? $matches[2] : 'ALL';
113
114 17
                    $columns[$i] = "{$column} AS {$function}_{$alias}";
115 17
                }
116 48
            }
117 48
        }
118
119 176
        return parent::buildSelect($columns, $params, $distinct, $selectOption);
120
    }
121
122
    /**
123
     * @inheritdoc
124
     */
125 5
    protected function buildCompositeInCondition($operator, $columns, $values, &$params)
126
    {
127 5
        $quotedColumns = [];
128 5
        foreach ($columns as $i => $column) {
129 5
            $quotedColumns[$i] = strpos($column, '(') === false ? $this->db->quoteColumnName($column) : $column;
130 5
        }
131 5
        $vss = [];
132 5
        foreach ($values as $value) {
133 5
            $vs = [];
134 5
            foreach ($columns as $i => $column) {
135 5
                if (isset($value[$column])) {
136 5
                    $phName = self::PARAM_PREFIX . count($params);
137 5
                    $params[$phName] = $value[$column];
138 5
                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' = ' : ' != ') . $phName;
139 5
                } else {
140
                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' IS' : ' IS NOT') . ' NULL';
141
                }
142 5
            }
143 5
            $vss[] = '(' . implode($operator === 'IN' ? ' AND ' : ' OR ', $vs) . ')';
144 5
        }
145 5
        return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';
146
    }
147
148
    /**
149
     * @inheritdoc
150
     */
151 176
    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
152
    {
153
154 176
        $orderBy = $this->buildOrderBy($orderBy);
155 176
        if ($orderBy !== '') {
156 33
            $sql .= $this->separator . $orderBy;
157 33
        }
158
159 176
        $limit = $limit !== null ? intval($limit) : -1;
160 176
        $offset = $offset !== null ? intval($offset) : -1;
161
        // If ignoring both params then do nothing
162 176
        if ($offset < 0 && $limit < 0) {
163 175
            return $sql;
164
        }
165
        // If we are ignoring limit then return full result set starting
166
        // from $offset. In Firebird this can only be done with SKIP
167 3
        if ($offset >= 0 && $limit < 0) {
168 2
            $count = 1; //Only do it once
169 2
            $sql = preg_replace('/^SELECT /i', 'SELECT SKIP ' . (int) $offset . ' ', $sql, $count);
170 2
            return $sql;
171
        }
172
        // If we are ignoring $offset then return $limit rows.
173
        // ie, return the first $limit rows in the set.
174 2
        if ($offset < 0 && $limit >= 0) {
175 2
            $count = 1; //Only do it once
176 2
            $sql = preg_replace('/^SELECT /i', 'SELECT FIRST ' . (int) $limit . ' ', $sql, $count);
177 2
            return $sql;
178
        }
179
        // Otherwise apply the params and return the amended sql.
180 1
        if ($offset >= 0 && $limit >= 0) {
181 1
            $count = 1; //Only do it once
182 1
            $sql = preg_replace('/^SELECT /i', 'SELECT FIRST ' . (int) $limit . ' SKIP ' . (int) $offset, $sql, $count);
183 1
            return $sql;
184
        }
185
        // If we have fallen through the cracks then just pass
186
        // the sql back.
187
        return $sql;
188
    }
189
190
    /**
191
     * @param array $unions
192
     * @param array $params the binding parameters to be populated
193
     * @return string the UNION clause built from [[Query::$union]].
194
     */
195 176
    public function buildUnion($unions, &$params)
196
    {
197 176
        if (empty($unions)) {
198 176
            return '';
199
        }
200
201 2
        $result = '';
202
203 2
        foreach ($unions as $i => $union) {
204 2
            $query = $union['query'];
205 2
            if ($query instanceof Query) {
206 2
                list($unions[$i]['query'], $params) = $this->build($query, $params);
207 2
            }
208
209 2
            $result .= 'UNION ' . ($union['all'] ? 'ALL ' : '') . $unions[$i]['query'] . ' ';
210 2
        }
211
212 2
        return trim($result);
213
    }
214
215
    /**
216
     *
217
     * @param Expression $value
218
     * @return Expression
219
     */
220 3
    protected function convertExpression($value)
221
    {
222 3
        if (!($value instanceof Expression)) {
223
            return $value;
224
        }
225
        
226
        $expressionMap = [
227
            "strftime('%Y')" => "EXTRACT(YEAR FROM TIMESTAMP 'now')"
228 3
        ];
229
        
230 3
        if (isset($expressionMap[$value->expression])) {
231
            return new Expression($expressionMap[$value->expression]);
232
        }
233 3
        return $value;
234
    }
235
236
    /**
237
     * @inheritdoc
238
     */
239 22
    public function insert($table, $columns, &$params)
240
    {
241 22
        $schema = $this->db->getSchema();
242 22
        if (($tableSchema = $schema->getTableSchema($table)) !== null) {
243 22
            $columnSchemas = $tableSchema->columns;
244 22
        } else {
245
            $columnSchemas = [];
246
        }
247
        
248
        //Empty insert
249 22
        if(empty($columns) && !empty($columnSchemas)){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
250 1
            $columns = [];
251 1
            foreach ($columnSchemas as $columnSchema) {
252 1
                if(!$columnSchema->autoIncrement){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
253 1
                    $columns[$columnSchema->name] = $columnSchema->defaultValue;
254 1
                }
255 1
            }
256 1
        }
257
258 22
        foreach ($columns as $name => $value) {
259 22
            if ($value instanceof Expression) {
260 1
                $columns[$name] = $this->convertExpression($value);
261 22
            } elseif (isset($columnSchemas[$name]) && in_array($columnSchemas[$name]->type, [Schema::TYPE_TEXT, Schema::TYPE_BINARY])) {
262
                $columns[$name] = [$value, 'blob'];
263
            }
264 22
        }
265
266 22
        return parent::insert($table, $columns, $params);
267
    }
268
269
    /**
270
     * @inheritdoc
271
     */
272 13
    public function update($table, $columns, $condition, &$params)
273
    {
274 13
        $schema = $this->db->getSchema();
275 13
        if (($tableSchema = $schema->getTableSchema($table)) !== null) {
276 13
            $columnSchemas = $tableSchema->columns;
277 13
        } else {
278
            $columnSchemas = [];
279
        }
280 13
        foreach ($columns as $name => $value) {
281 13
            if ($value instanceof Expression) {
282 2
                $columns[$name] = $this->convertExpression($value);
283 13
            } elseif (isset($columnSchemas[$name]) && in_array($columnSchemas[$name]->type, [Schema::TYPE_TEXT, Schema::TYPE_BINARY])) {
284
                $columns[$name] = [$value, 'blob'];
285
            }
286 13
        }
287 13
        return parent::update($table, $columns, $condition, $params);
288
    }
289
290
    /**
291
     * @inheritdoc
292
     */
293 1
    public function batchInsert($table, $columns, $rows)
294
    {
295 1
        if (empty($rows)) {
296 1
            return '';
297
        }
298
299 1
        $schema = $this->db->getSchema();
300 1
        if (($tableSchema = $schema->getTableSchema($table)) !== null) {
301 1
            $columnSchemas = $tableSchema->columns;
302 1
        } else {
303
            $columnSchemas = [];
304
        }
305
306 1
        $values = [];
307 1
        foreach ($rows as $row) {
308 1
            $vs = [];
309 1
            foreach ($row as $i => $value) {
310 1
                if (isset($columns[$i], $columnSchemas[$columns[$i]]) && !is_array($value)) {
311 1
                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);
312 1
                }
313 1
                if (is_string($value)) {
314 1
                    $value = $schema->quoteValue($value);
315 1
                } elseif ($value === false) {
316
                    $value = 0;
317 1
                } elseif ($value === null) {
318 1
                    $value = 'NULL';
319 1
                }
320 1
                $vs[] = $value;
321 1
            }
322 1
            $values[] = 'INSERT INTO ' . $schema->quoteTableName($table)
323 1
                    . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $vs) . ');';
324 1
        }
325
326 1
        foreach ($columns as $i => $name) {
327 1
            $columns[$i] = $schema->quoteColumnName($name);
328 1
        }
329
330 1
        return 'EXECUTE block AS BEGIN ' . implode(' ', $values) . ' END;';
331
    }
332
    
333
    /**
334
     * @inheritdoc
335
     */
336 1
    public function renameTable($oldName, $newName)
337
    {
338 1
        throw new \yii\base\NotSupportedException($this->db->getDriverName() . ' does not support rename table.');
339
    }
340
    
341
    /**
342
     * @inheritdoc
343
     */
344 2
    public function truncateTable($table)
345
    {
346 2
        return "DELETE FROM " . $this->db->quoteTableName($table);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal DELETE FROM does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
347
    }
348
    
349
    /**
350
     * @inheritdoc
351
     */
352 1
    public function dropColumn($table, $column)
353
    {
354 1
        return "ALTER TABLE " . $this->db->quoteTableName($table)
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal ALTER TABLE does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
355 1
            . " DROP " . $this->db->quoteColumnName($column);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal DROP does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
356
    }
357
    
358
    /**
359
     * @inheritdoc
360
     */
361 1
    public function renameColumn($table, $oldName, $newName)
362
    {
363 1
        return "ALTER TABLE " . $this->db->quoteTableName($table)
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal ALTER TABLE does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
364 1
            . " ALTER " . $this->db->quoteColumnName($oldName)
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal ALTER does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
365 1
            . " TO " . $this->db->quoteColumnName($newName);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal TO does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
366
    }
367
    
368
    /**
369
     * @inheritdoc
370
     */
371 3
    public function alterColumn($table, $column, $type)
372
    {
373 3
        $schema = $this->db->getSchema();
374 3
        $tableSchema = $schema->getTableSchema($table);
375 3
        $columnSchema = $tableSchema->getColumn($column);
376
        
377 3
        $allowNullNewType = !preg_match("/not +null/i", $type);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /not +null/i does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
378
        
379 3
        $type = preg_replace("/ +(not)? *null/i", "", $type);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal / +(not)? *null/i does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
380
        
381 3
        $hasType = false;
382
        
383 3
        $matches = [];
384 3
        if (isset($this->typeMap[$type])) {
385 2
            $hasType = true;
386 3
        } elseif (preg_match('/^(\w+)[\( ]/', $type, $matches)) {
387 2
            if (isset($this->typeMap[$matches[1]])) {
388 2
                $hasType = true;
389 2
            }
390 2
        }
391
        
392 3
        $baseSql = 'ALTER TABLE ' . $this->db->quoteTableName($table)
393 3
        . ' ALTER ' . $this->db->quoteColumnName($column)
394 3
        . (($hasType) ? ' TYPE ' : ' ') .  $this->getColumnType($type);
395
        
396 3
        if ($columnSchema->allowNull == $allowNullNewType) {
397 2
            return $baseSql;
398
        } else {
399
            $sql = 'EXECUTE BLOCK AS BEGIN'
400 2
                . ' EXECUTE STATEMENT ' . $this->db->quoteValue($baseSql) . ';'
401 2
                . ' UPDATE RDB$RELATION_FIELDS SET RDB$NULL_FLAG = ' . ($allowNullNewType ? 'NULL' : '1')
402 2
                . ' WHERE UPPER(RDB$FIELD_NAME) = UPPER(\'' . $column . '\') AND UPPER(RDB$RELATION_NAME) = UPPER(\'' . $table . '\');';
403
            /**
404
             * In any case (whichever option you choose), make sure that the column doesn't have any NULLs.
405
             * Firebird will not check it for you. Later when you backup the database, everything is fine,
406
             * but restore will fail as the NOT NULL column has NULLs in it. To be safe, each time you change from NULL to NOT NULL.
407
             */
408 2
            if (!$allowNullNewType) {
409 2
                $sql .= ' UPDATE ' . $this->db->quoteTableName($table) . ' SET ' . $this->db->quoteColumnName($column) . ' = 0'
410 2
                    . ' WHERE ' . $this->db->quoteColumnName($column) . ' IS NULL;';
411 2
            }
412 2
            $sql .= ' END';
413 2
            return $sql;
414
        }
415
    }
416
    
417
    /**
418
     * @inheritdoc
419
     */
420 1
    public function dropIndex($name, $table)
421
    {
422 1
        return 'DROP INDEX ' . $this->db->quoteTableName($name);
423
    }
424
    
425
    /**
426
     * @inheritdoc
427
     */
428 1
    public function resetSequence($table, $value = null)
429
    {
430 1
        $tableSchema = $this->db->getTableSchema($table);
431 1
        if ($tableSchema === null) {
432
            throw new InvalidParamException("Table not found: $table");
433
        }
434 1
        if ($tableSchema->sequenceName === null) {
435
            throw new InvalidParamException("There is not sequence associated with table '$table'.");
436
        }
437
438 1
        if ($value !== null) {
439 1
            $value = (int) $value;
440 1
        } else {
441
            // use master connection to get the biggest PK value
442 1
            $value = $this->db->useMaster(function(Connection $db) use ($tableSchema) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
443 1
                $key = false;
444 1
                foreach ($tableSchema->primaryKey as $name) {
445 1
                    if ($tableSchema->columns[$name]->autoIncrement) {
446 1
                        $key = $name;
447 1
                        break;
448
                    }
449 1
                }
450 1
                if ($key === false) {
451
                    return 0;
452
                }
453 1
                return $db->createCommand("SELECT MAX({$this->db->quoteColumnName($key)}) FROM {$this->db->quoteTableName($tableSchema->name)}")->queryScalar();
454 1
            }) + 1;
455
        }
456
457 1
        return "ALTER SEQUENCE {$this->db->quoteColumnName($tableSchema->sequenceName)} RESTART WITH $value";
458
    }
459
    
460
    /**
461
     * @inheritdoc
462
     */
463 5
    public function createTable($table, $columns, $options = null)
464
    {
465 5
        $sql = parent::createTable($table, $columns, $options);
466
        
467 5
        foreach ($columns as $name => $type) {
468 5
            if (!is_string($name)) {
469
                continue;
470
            }
471
            
472 5
            if (strpos($type, Schema::TYPE_PK) === 0 || strpos($type, Schema::TYPE_BIGPK) === 0) {
473
                $sqlTrigger = <<<SQLTRIGGER
474 4
CREATE TRIGGER tr_{$table}_{$name} FOR {$this->db->quoteTableName($table)}
475
ACTIVE BEFORE INSERT POSITION 0
476
AS
477
BEGIN
478 4
    if (NEW.{$this->db->quoteColumnName($name)} is NULL) then NEW.{$this->db->quoteColumnName($name)} = NEXT VALUE FOR seq_{$table}_{$name};
479 4
END
480 4
SQLTRIGGER;
481
                
482
                $sqlBlock = <<<SQL
483
EXECUTE block AS
484
BEGIN
485 4
    EXECUTE STATEMENT {$this->db->quoteValue($sql)};
486 4
    EXECUTE STATEMENT {$this->db->quoteValue("CREATE SEQUENCE seq_{$table}_{$name}")};
487 4
    EXECUTE STATEMENT {$this->db->quoteValue($sqlTrigger)};
488 4
END;
489 4
SQL;
490
491 4
                return $sqlBlock;
492
            }
493 1
        }
494
        
495 1
        return $sql;
496
    }
497
    
498
    /**
499
     * @inheritdoc
500
     */
501 3
    public function dropTable($table)
502
    {
503 3
        $sql = parent::dropTable($table);
504
        
505 3
        $tableSchema = $this->db->getTableSchema($table);
506 3
        if ($tableSchema === null || $tableSchema->sequenceName === null) {
507 1
            return $sql;
508
        }
509
        
510
        $sqlBlock = <<<SQL
511
EXECUTE block AS
512
BEGIN
513 2
    EXECUTE STATEMENT {$this->db->quoteValue($sql)};
514 2
    EXECUTE STATEMENT {$this->db->quoteValue("DROP SEQUENCE {$tableSchema->sequenceName}")};
515 2
END;
516 2
SQL;
517 2
                return $sqlBlock;
518
        
519
    }
0 ignored issues
show
Coding Style introduced by
Function closing brace must go on the next line following the body; found 1 blank lines before brace
Loading history...
520
521
    /**
522
     * Creates a SELECT EXISTS() SQL statement.
523
     * @param string $rawSql the subquery in a raw form to select from.
524
     * @return string the SELECT EXISTS() SQL statement.
525
     *
526
     * @since 2.0.8
527
     */
528 3
    public function selectExists($rawSql)
529
    {
530 3
        return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END FROM RDB$DATABASE';
531
    }
532
}
533