Passed
Pull Request — develop (#3248)
by Sergei
23:58
created

SQLServer2012Platform::getReservedKeywordsClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\DBAL\Schema\Sequence;
6
use const PREG_OFFSET_CAPTURE;
7
use function preg_match;
8
use function preg_match_all;
9
use function sprintf;
10
use function substr_count;
11
12
/**
13
 * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2012 version.
14
 *
15
 * Differences to SQL Server 2008 and before are that sequences are introduced,
16
 * and support for the new OFFSET... FETCH syntax for result pagination has been added.
17
 *
18
 * @author Steve Müller <[email protected]>
19
 */
20
class SQLServer2012Platform extends SQLServerPlatform
21
{
22
    /**
23
     * {@inheritdoc}
24
     */
25 16
    public function getAlterSequenceSQL(Sequence $sequence)
26
    {
27 16
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
28 16
               ' INCREMENT BY ' . $sequence->getAllocationSize();
29
    }
30
31
    /**
32
     * {@inheritdoc}
33
     */
34 26
    public function getCreateSequenceSQL(Sequence $sequence)
35
    {
36 26
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
37 26
               ' START WITH ' . $sequence->getInitialValue() .
38 26
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
39 26
               ' MINVALUE ' . $sequence->getInitialValue();
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 20
    public function getDropSequenceSQL($sequence)
46
    {
47 20
        if ($sequence instanceof Sequence) {
48
            $sequence = $sequence->getQuotedName($this);
49
        }
50
51 20
        return 'DROP SEQUENCE ' . $sequence;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57 12
    public function getListSequencesSQL($database)
58
    {
59 12
        return 'SELECT seq.name,
60
                       CAST(
61
                           seq.increment AS VARCHAR(MAX)
62
                       ) AS increment, -- CAST avoids driver error for sql_variant type
63
                       CAST(
64
                           seq.start_value AS VARCHAR(MAX)
65
                       ) AS start_value -- CAST avoids driver error for sql_variant type
66
                FROM   sys.sequences AS seq';
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72 18
    public function getSequenceNextValSQL($sequenceName)
73
    {
74 18
        return 'SELECT NEXT VALUE FOR ' . $sequenceName;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80 30
    public function supportsSequences()
81
    {
82 30
        return true;
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     *
88
     * Returns Microsoft SQL Server 2012 specific keywords class
89
     */
90 1007
    protected function getReservedKeywordsClass()
91
    {
92 1007
        return Keywords\SQLServer2012Keywords::class;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98 432
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
99
    {
100 432
        if ($limit === null && $offset <= 0) {
101 18
            return $query;
102
        }
103
104
        // Queries using OFFSET... FETCH MUST have an ORDER BY clause
105
        // Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement
106
        // but can be in a newline
107 414
        $matches = [];
108 414
        $matchesCount = preg_match_all("/[\\s]+order\\s+by\\s/im", $query, $matches, PREG_OFFSET_CAPTURE);
109 414
        $orderByPos = false;
110 414
        if ($matchesCount > 0) {
111 284
            $orderByPos = $matches[0][($matchesCount - 1)][1];
112
        }
113
114 414
        if ($orderByPos === false
115 414
            || substr_count($query, "(", $orderByPos) - substr_count($query, ")", $orderByPos)
116
        ) {
117 146
            if (preg_match('/^SELECT\s+DISTINCT/im', $query)) {
118
                // SQL Server won't let us order by a non-selected column in a DISTINCT query,
119
                // so we have to do this madness. This says, order by the first column in the
120
                // result. SQL Server's docs say that a nonordered query's result order is non-
121
                // deterministic anyway, so this won't do anything that a bunch of update and
122
                // deletes to the table wouldn't do anyway.
123 32
                $query .= " ORDER BY 1";
124
            } else {
125
                // In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you
126
                // use constant expressions in the order by list.
127 114
                $query .= " ORDER BY (SELECT 0)";
128
            }
129
        }
130
131
        // This looks somewhat like MYSQL, but limit/offset are in inverse positions
132
        // Supposedly SQL:2008 core standard.
133
        // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS.
134 414
        $query .= sprintf(' OFFSET %d ROWS', $offset);
135
136 414
        if ($limit !== null) {
137 414
            $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit);
138
        }
139
140 414
        return $query;
141
    }
142
}
143