Passed
Push — 2.x ( 18bce1...b13a23 )
by Maxim
17:24
created

SQLiteCompiler   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Test Coverage

Coverage 95.56%

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 106
ccs 43
cts 45
cp 0.9556
rs 10
c 0
b 0
f 0
wmc 16

4 Methods

Rating   Name   Duplication   Size   Complexity  
B insertQuery() 0 57 9
A limit() 0 19 5
A compileJsonOrderBy() 0 3 1
A selectQuery() 0 6 1
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;
13
14
use Cycle\Database\Driver\CachingCompilerInterface;
15
use Cycle\Database\Driver\Compiler;
16
use Cycle\Database\Driver\Quoter;
17
use Cycle\Database\Driver\SQLite\Injection\CompileJson;
18
use Cycle\Database\Exception\CompilerException;
19
use Cycle\Database\Injection\FragmentInterface;
20
use Cycle\Database\Injection\Parameter;
21
use Cycle\Database\Injection\ParameterInterface;
22
use Cycle\Database\Query\QueryParameters;
23
24
class SQLiteCompiler extends Compiler implements CachingCompilerInterface
25
{
26
    /**
27
     * {@inheritdoc}
28
     *
29 326
     * @link http://stackoverflow.com/questions/10491492/sqllite-with-skip-offset-only-not-limit
30
     */
31 326
    protected function limit(QueryParameters $params, Quoter $q, int $limit = null, int $offset = null): string
32 310
    {
33
        if ($limit === null && $offset === null) {
34
            return '';
35 16
        }
36 4
37
        if ($limit === null) {
38 12
            $statement = 'LIMIT -1 ';
39 12
        } else {
40
            $statement = 'LIMIT ? ';
41
            $params->push(new Parameter($limit));
42 16
        }
43 10
44 10
        if ($offset !== null) {
45
            $statement .= 'OFFSET ?';
46
            $params->push(new Parameter($offset));
47 16
        }
48
49
        return trim($statement);
50
    }
51
52
    /**
53 326
     * @inheritDoc
54
     */
55
    protected function selectQuery(QueryParameters $params, Quoter $q, array $tokens): string
56 326
    {
57
        // FOR UPDATE is not available
58 326
        $tokens['forUpdate'] = false;
59
60
        return parent::selectQuery($params, $q, $tokens);
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     *
66 48
     * @see http://stackoverflow.com/questions/1609637/is-it-possible-to-insert-multiple-rows-at-a-time-in-an-sqlite-database
67
     */
68 48
    protected function insertQuery(QueryParameters $params, Quoter $q, array $tokens): string
69 4
    {
70 4
        if ($tokens['columns'] === []) {
71 4
            return sprintf(
72
                'INSERT INTO %s DEFAULT VALUES',
73
                $this->name($params, $q, $tokens['table'], true)
74
            );
75
        }
76 44
77 16
        // @todo possibly different statement for versions higher than 3.7.11
78
        if (count($tokens['values']) === 1) {
79
            return parent::insertQuery($params, $q, $tokens);
80
        }
81 28
82 28
        // SQLite uses alternative syntax
83 28
        $statement = [];
84 28
        $statement[] = sprintf(
85 28
            'INSERT INTO %s (%s)',
86
            $this->name($params, $q, $tokens['table'], true),
87
            $this->columns($params, $q, $tokens['columns'])
88 28
        );
89 28
90
        foreach ($tokens['values'] as $rowset) {
91
            if (count($statement) !== 1) {
92 28
                // It is critically important to use UNION ALL, UNION will try to merge values together
93 28
                // which will cause non predictable insert order
94 28
                $statement[] = sprintf(
95
                    'UNION ALL SELECT %s',
96 28
                    trim($this->value($params, $q, $rowset), '()')
97
                );
98
                continue;
99 28
            }
100
101 28
            $selectColumns = [];
102 28
103
            if ($rowset instanceof ParameterInterface && $rowset->isArray()) {
104
                $rowset = $rowset->getValue();
105 28
            }
106
107
            if (!is_array($rowset)) {
108
                throw new CompilerException(
109
                    'Insert parameter expected to be parametric array'
110
                );
111 28
            }
112 28
113 28
            foreach ($tokens['columns'] as $index => $column) {
114 28
                $selectColumns[] = sprintf(
115 28
                    '%s AS %s',
116
                    $this->value($params, $q, $rowset[$index]),
117
                    $this->name($params, $q, $column)
118
                );
119 28
            }
120
121
            $statement[] = 'SELECT ' . implode(', ', $selectColumns);
122 28
        }
123
124
        return implode("\n", $statement);
125
    }
126
127
    protected function compileJsonOrderBy(string $path): FragmentInterface
128
    {
129
        return new CompileJson($path);
130
    }
131
}
132