Passed
Branch main (2a1ad3)
by Thierry
06:07 queued 03:57
created

Table::alterTable()   F

Complexity

Conditions 18
Paths 324

Size

Total Lines 41
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 18
eloc 27
nc 324
nop 9
dl 0
loc 41
rs 2.4833
c 1
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A Table::trigger() 0 14 4

How to fix   Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Lagdo\DbAdmin\Driver\Sqlite\Db;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
6
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
7
use Lagdo\DbAdmin\Driver\Entity\IndexEntity;
8
use Lagdo\DbAdmin\Driver\Entity\ForeignKeyEntity;
9
use Lagdo\DbAdmin\Driver\Entity\TriggerEntity;
10
11
use Lagdo\DbAdmin\Driver\Db\ConnectionInterface;
12
13
use Lagdo\DbAdmin\Driver\Db\Table as AbstractTable;
14
15
class Table extends AbstractTable
16
{
17
    /**
18
     * @inheritDoc
19
     */
20
    public function tableHelp(string $name)
21
    {
22
        if ($name == "sqlite_sequence") {
23
            return "fileformat2.html#seqtab";
24
        }
25
        if ($name == "sqlite_master") {
26
            return "fileformat2.html#$name";
27
        }
28
    }
29
30
    /**
31
     * @param string $table
32
     *
33
     * @return array
34
     */
35
    private function queryStatus(string $table = '')
36
    {
37
        $query = "SELECT name AS Name, type AS Engine, 'rowid' AS Oid, '' AS Auto_increment " .
38
            "FROM sqlite_master WHERE type IN ('table', 'view') " .
39
            ($table != "" ? "AND name = " . $this->driver->quote($table) : "ORDER BY name");
40
        return $this->driver->rows($query);
41
    }
42
43
    /**
44
     * @param array $row
45
     *
46
     * @return TableEntity
47
     */
48
    private function makeStatus(array $row)
49
    {
50
        $status = new TableEntity($row['Name']);
51
        $status->engine = $row['Engine'];
52
        $status->oid = $row['Oid'];
53
        // $status->Auto_increment = $row['Auto_increment'];
54
        $query = 'SELECT COUNT(*) FROM ' . $this->driver->escapeId($row['Name']);
55
        $status->rows = $this->connection->result($query);
56
57
        return $status;
58
    }
59
60
    /**
61
     * @inheritDoc
62
     */
63
    public function tableStatus(string $table, bool $fast = false)
64
    {
65
        $rows = $this->queryStatus($table);
66
        if (!($row = reset($rows))) {
67
            return null;
68
        }
69
        return $this->makeStatus($row);
70
    }
71
72
    /**
73
     * @inheritDoc
74
     */
75
    public function tableStatuses(bool $fast = false)
76
    {
77
        $tables = [];
78
        $rows = $this->queryStatus();
79
        foreach ($rows as $row) {
80
            $tables[$row['Name']] = $this->makeStatus($row);
81
        }
82
        return $tables;
83
    }
84
85
    /**
86
     * @inheritDoc
87
     */
88
    public function tableNames()
89
    {
90
        $tables = [];
91
        $rows = $this->queryStatus();
92
        foreach ($rows as $row) {
93
            $tables[] = $row['Name'];
94
        }
95
        return $tables;
96
    }
97
98
    /**
99
     * @inheritDoc
100
     */
101
    public function isView(TableEntity $tableStatus)
102
    {
103
        return $tableStatus->engine == 'view';
104
    }
105
106
    /**
107
     * @inheritDoc
108
     */
109
    public function supportForeignKeys(TableEntity $tableStatus)
110
    {
111
        return !$this->connection->result("SELECT sqlite_compileoption_used('OMIT_FOREIGN_KEY')");
112
    }
113
114
    /**
115
     * @inheritDoc
116
     */
117
    public function fields(string $table)
118
    {
119
        $fields = [];
120
        $primary = "";
121
        foreach ($this->driver->rows("PRAGMA table_info(" . $this->driver->table($table) . ")") as $row) {
122
            $name = $row["name"];
123
            $type = strtolower($row["type"]);
124
            $default = $row["dflt_value"];
125
126
            $field = new TableFieldEntity();
127
128
            $field->name = $name;
129
            $field->type = (preg_match('~int~i', $type) ? "integer" : (preg_match('~char|clob|text~i', $type) ?
130
                "text" : (preg_match('~blob~i', $type) ? "blob" : (preg_match('~real|floa|doub~i', $type) ?
131
                "real" : "numeric"))));
132
            $field->fullType = $type;
133
            $field->default = (preg_match("~'(.*)'~", $default, $match) ? str_replace("''", "'", $match[1]) :
134
                ($default == "NULL" ? null : $default));
135
            $field->null = !$row["notnull"];
136
            $field->privileges = ["select" => 1, "insert" => 1, "update" => 1];
137
            $field->primary = $row["pk"];
138
139
            if ($row["pk"]) {
140
                if ($primary != "") {
141
                    $fields[$primary]->autoIncrement = false;
142
                } elseif (preg_match('~^integer$~i', $type)) {
143
                    $field->autoIncrement = true;
144
                }
145
                $primary = $name;
146
            }
147
148
            $fields[$field->name] = $field;
149
        }
150
        $query = "SELECT sql FROM sqlite_master WHERE type IN ('table', 'view') AND name = " . $this->driver->quote($table);
151
        $result = $this->connection->result($query);
152
        preg_match_all('~(("[^"]*+")+|[a-z0-9_]+)\s+text\s+COLLATE\s+(\'[^\']+\'|\S+)~i',
153
            $result, $matches, PREG_SET_ORDER);
154
        foreach ($matches as $match) {
155
            $name = str_replace('""', '"', preg_replace('~^"|"$~', '', $match[1]));
156
            if (isset($fields[$name])) {
157
                $fields[$name]->collation = trim($match[3], "'");
158
            }
159
        }
160
        return $fields;
161
    }
162
163
    /**
164
     * @inheritDoc
165
     */
166
    public function indexes(string $table, ConnectionInterface $connection = null)
167
    {
168
        if (!is_object($connection)) {
169
            $connection = $this->connection;
170
        }
171
        $indexes = [];
172
        $query = "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = " . $this->driver->quote($table);
173
        $result = $connection->result($query);
174
        if (preg_match('~\bPRIMARY\s+KEY\s*\((([^)"]+|"[^"]*"|`[^`]*`)++)~i', $result, $match)) {
175
            $indexes[""] = new IndexEntity();
176
            $indexes[""]->type = "PRIMARY";
177
            preg_match_all('~((("[^"]*+")+|(?:`[^`]*+`)+)|(\S+))(\s+(ASC|DESC))?(,\s*|$)~i',
178
                $match[1], $matches, PREG_SET_ORDER);
179
            foreach ($matches as $match) {
180
                $indexes[""]->columns[] = $this->driver->unescapeId($match[2]) . $match[4];
181
                $indexes[""]->descs[] = (preg_match('~DESC~i', $match[5]) ? '1' : null);
182
            }
183
        }
184
        if (!$indexes) {
185
            foreach ($this->fields($table) as $name => $field) {
186
                if ($field->primary) {
187
                    if (!isset($indexes[""])) {
188
                        $indexes[""] = new IndexEntity();
189
                    }
190
                    $indexes[""]->type = "PRIMARY";
191
                    $indexes[""]->columns = [$name];
192
                    $indexes[""]->lengths = [];
193
                    $indexes[""]->descs = [null];
194
                }
195
            }
196
        }
197
        $query = "SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = " . $this->driver->quote($table);
198
        $results = $this->driver->keyValues($query, $connection);
199
        foreach ($this->driver->rows("PRAGMA index_list(" . $this->driver->table($table) . ")", $connection) as $row) {
200
            $index = new IndexEntity();
201
202
            $name = $row["name"];
203
            $index->type = $row["unique"] ? "UNIQUE" : "INDEX";
204
            $index->lengths = [];
205
            $index->descs = [];
206
            foreach ($this->driver->rows("PRAGMA index_info(" . $this->driver->escapeId($name) . ")", $connection) as $row1) {
207
                $index->columns[] = $row1["name"];
208
                $index->descs[] = null;
209
            }
210
            if (preg_match('~^CREATE( UNIQUE)? INDEX ' . preg_quote($this->driver->escapeId($name) . ' ON ' .
211
                $this->driver->escapeId($table), '~') . ' \((.*)\)$~i', $results[$name], $regs)) {
212
                preg_match_all('/("[^"]*+")+( DESC)?/', $regs[2], $matches);
213
                foreach ($matches[2] as $key => $val) {
214
                    if ($val) {
215
                        $index->descs[$key] = '1';
216
                    }
217
                }
218
            }
219
            if (!$indexes[""] || $index->type != "UNIQUE" || $index->columns != $indexes[""]->columns ||
220
                $index->descs != $indexes[""]->descs || !preg_match("~^sqlite_~", $name)) {
221
                $indexes[$name] = $index;
222
            }
223
        }
224
        return $indexes;
225
    }
226
227
    /**
228
     * @inheritDoc
229
     */
230
    public function foreignKeys(string $table)
231
    {
232
        $foreignKeys = [];
233
        foreach ($this->driver->rows("PRAGMA foreign_key_list(" . $this->driver->table($table) . ")") as $row) {
234
            $name = $row["id"];
235
            if (!isset($foreignKeys[$name])) {
236
                $foreignKeys[$name] = new ForeignKeyEntity();
237
            }
238
            //! idf_unescape in SQLite2
239
            $foreignKeys[$name]->source[] = $row["from"];
240
            $foreignKeys[$name]->target[] = $row["to"];
241
        }
242
        return $foreignKeys;
243
    }
244
245
    /**
246
     * @inheritDoc
247
     */
248
    public function trigger(string $name, string $table = '')
249
    {
250
        if ($name == "") {
251
            return new TriggerEntity('', '', "BEGIN\n\t;\nEND");
252
        }
253
        $idf = '(?:[^`"\s]+|`[^`]*`|"[^"]*")+';
254
        $options = $this->triggerOptions();
255
        preg_match("~^CREATE\\s+TRIGGER\\s*$idf\\s*(" . implode("|", $options["Timing"]) .
256
            ")\\s+([a-z]+)(?:\\s+OF\\s+($idf))?\\s+ON\\s*$idf\\s*(?:FOR\\s+EACH\\s+ROW\\s)?(.*)~is",
257
            $this->connection->result("SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = " .
258
            $this->driver->quote($name)), $match);
259
        $of = $match[3];
260
        return new TriggerEntity(strtoupper($match[1]), strtoupper($match[2]), $match[4],
261
            ($of[0] == '`' || $of[0] == '"' ? $this->driver->unescapeId($of) : $of), $name);
262
    }
263
264
    /**
265
     * @inheritDoc
266
     */
267
    public function triggers(string $table)
268
    {
269
        $triggers = [];
270
        $options = $this->triggerOptions();
271
        $query = "SELECT * FROM sqlite_master WHERE type = 'trigger' AND tbl_name = " . $this->driver->quote($table);
272
        foreach ($this->driver->rows($query) as $row) {
273
            preg_match('~^CREATE\s+TRIGGER\s*(?:[^`"\s]+|`[^`]*`|"[^"]*")+\s*(' .
274
                implode("|", $options["Timing"]) . ')\s*(.*?)\s+ON\b~i', $row["sql"], $match);
275
            $triggers[$row["name"]] = new TriggerEntity($match[1], $match[2], '', '', $row["name"]);
276
        }
277
        return $triggers;
278
    }
279
280
    /**
281
     * @inheritDoc
282
     */
283
    public function triggerOptions()
284
    {
285
        return [
286
            "Timing" => ["BEFORE", "AFTER", "INSTEAD OF"],
287
            "Event" => ["INSERT", "UPDATE", "UPDATE OF", "DELETE"],
288
            "Type" => ["FOR EACH ROW"],
289
        ];
290
    }
291
}
292