• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dg / dibi / 23992436981

05 Apr 2026 02:21AM UTC coverage: 77.838% (+0.3%) from 77.523%
23992436981

push

github

dg
drivers: escape*() methods moved to Engine [WIP]

101 of 113 new or added lines in 8 files covered. (89.38%)

160 existing lines in 9 files now uncovered.

1721 of 2211 relevant lines covered (77.84%)

0.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.71
/src/Dibi/Drivers/Engines/SQLServerEngine.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * This file is part of the Dibi, smart database abstraction layer (https://dibi.nette.org)
5
 * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Dibi\Drivers\Engines;
9

10
use Dibi;
11
use Dibi\Drivers\Connection;
12
use Dibi\Drivers\Engine;
13

14

15
/**
16
 * The reflector for Microsoft SQL Server and SQL Azure databases.
17
 */
18
class SQLServerEngine implements Engine
19
{
20
        public function __construct(
1✔
21
                private readonly Connection $driver,
22
        ) {
23
        }
1✔
24

25

26
        public function escapeIdentifier(string $value): string
1✔
27
        {
28
                // @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
29
                return '[' . str_replace(']', ']]', $value) . ']';
1✔
30
        }
31

32

33
        public function escapeBool(bool $value): string
1✔
34
        {
35
                return $value ? '1' : '0';
1✔
36
        }
37

38

39
        public function escapeDate(\DateTimeInterface $value): string
1✔
40
        {
41
                return $value->format("'Y-m-d'");
1✔
42
        }
43

44

45
        public function escapeDateTime(\DateTimeInterface $value): string
1✔
46
        {
47
                return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')';
1✔
48
        }
49

50

51
        public function escapeDateInterval(\DateInterval $value): string
52
        {
NEW
53
                throw new Dibi\NotImplementedException;
×
54
        }
55

56

57
        /**
58
         * Encodes string for use in a LIKE statement.
59
         */
60
        public function escapeLike(string $value, int $pos): string
1✔
61
        {
62
                $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
1✔
63
                return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
1✔
64
        }
65

66

67
        /**
68
         * Injects LIMIT/OFFSET to the SQL query.
69
         */
70
        public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
1✔
71
        {
72
                if ($limit < 0 || $offset < 0) {
1✔
73
                        throw new Dibi\NotSupportedException('Negative offset or limit.');
1✔
74

75
                } elseif ($limit !== null) {
1✔
76
                        // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
77
                        $sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
1✔
78
                } elseif ($offset) {
1✔
79
                        // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
80
                        $sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
1✔
81
                }
82
        }
1✔
83

84

85
        /**
86
         * Returns list of tables.
87
         */
88
        public function getTables(): array
89
        {
90
                $res = $this->driver->query("SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE [TABLE_SCHEMA] = 'dbo'")
1✔
UNCOV
91
                        ?? throw new \LogicException('Unexpected null result.');
×
92
                $tables = [];
1✔
93
                while ($row = $res->fetch(false)) {
1✔
94
                        $tables[] = [
1✔
95
                                'name' => $row[0],
1✔
96
                                'view' => isset($row[1]) && $row[1] === 'VIEW',
1✔
97
                        ];
98
                }
99

100
                return $tables;
1✔
101
        }
102

103

104
        /**
105
         * Returns metadata for all columns in a table.
106
         */
107
        public function getColumns(string $table): array
1✔
108
        {
109
                $res = $this->driver->query("
1✔
110
                        SELECT c.name as COLUMN_NAME, c.is_identity AS AUTO_INCREMENT
111
                        FROM sys.columns c
112
                        INNER JOIN sys.tables t ON c.object_id = t.object_id
113
                        WHERE t.name = {$this->driver->escapeText($table)}
1✔
UNCOV
114
                ") ?? throw new \LogicException('Unexpected null result.');
×
115

116
                $autoIncrements = [];
1✔
117
                while ($row = $res->fetch(true)) {
1✔
118
                        $autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT'];
1✔
119
                }
120

121
                $res = $this->driver->query("
1✔
122
                        SELECT C.COLUMN_NAME, C.DATA_TYPE, C.CHARACTER_MAXIMUM_LENGTH , C.COLUMN_DEFAULT  , C.NUMERIC_PRECISION, C.NUMERIC_SCALE , C.IS_NULLABLE, Case When Z.CONSTRAINT_NAME Is Null Then 0 Else 1 End As IsPartOfPrimaryKey
123
                        FROM INFORMATION_SCHEMA.COLUMNS As C
124
                        Outer Apply (
125
                                SELECT CCU.CONSTRAINT_NAME
126
                                FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS As TC
127
                                Join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE As CCU
128
                                        On CCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
129
                                WHERE TC.TABLE_SCHEMA = C.TABLE_SCHEMA
130
                                        And TC.TABLE_NAME = C.TABLE_NAME
131
                                        And TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
132
                                        And CCU.COLUMN_NAME = C.COLUMN_NAME
133
                        ) As Z
134
                        WHERE C.TABLE_NAME = {$this->driver->escapeText($table)}
1✔
UNCOV
135
                ") ?? throw new \LogicException('Unexpected null result.');
×
136
                $columns = [];
1✔
137
                while ($row = $res->fetch(true)) {
1✔
138
                        $columns[] = [
1✔
139
                                'name' => $row['COLUMN_NAME'],
1✔
140
                                'table' => $table,
1✔
141
                                'nativetype' => strtoupper($row['DATA_TYPE']),
1✔
142
                                'size' => $row['CHARACTER_MAXIMUM_LENGTH'],
1✔
143
                                'nullable' => $row['IS_NULLABLE'] === 'YES',
1✔
144
                                'default' => $row['COLUMN_DEFAULT'],
1✔
145
                                'autoincrement' => $autoIncrements[$row['COLUMN_NAME']],
1✔
146
                                'vendor' => $row,
1✔
147
                        ];
148
                }
149

150
                return $columns;
1✔
151
        }
152

153

154
        /**
155
         * Returns metadata for all indexes in a table.
156
         */
157
        public function getIndexes(string $table): array
1✔
158
        {
159
                $keyUsagesRes = $this->driver->query(sprintf('EXEC [sys].[sp_helpindex] @objname = %s', $this->driver->escapeText($table)))
1✔
UNCOV
160
                        ?? throw new \LogicException('Unexpected null result.');
×
161
                $keyUsages = [];
1✔
162
                while ($row = $keyUsagesRes->fetch(true)) {
1✔
163
                        $keyUsages[$row['index_name']] = explode(',', $row['index_keys']);
1✔
164
                }
165

166
                $res = $this->driver->query("SELECT [i].* FROM [sys].[indexes] [i] INNER JOIN [sys].[tables] [t] ON [i].[object_id] = [t].[object_id] WHERE [t].[name] = {$this->driver->escapeText($table)}")
1✔
UNCOV
167
                        ?? throw new \LogicException('Unexpected null result.');
×
168
                $indexes = [];
1✔
169
                while ($row = $res->fetch(true)) {
1✔
170
                        $indexes[$row['name']]['name'] = $row['name'];
1✔
171
                        $indexes[$row['name']]['unique'] = $row['is_unique'] === 1;
1✔
172
                        $indexes[$row['name']]['primary'] = $row['is_primary_key'] === 1;
1✔
173
                        $indexes[$row['name']]['columns'] = $keyUsages[$row['name']] ?? [];
1✔
174
                }
175

176
                return array_values($indexes);
1✔
177
        }
178

179

180
        /**
181
         * Returns metadata for all foreign keys in a table.
182
         */
183
        public function getForeignKeys(string $table): array
184
        {
UNCOV
185
                throw new Dibi\NotImplementedException;
×
186
        }
187
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc