Passed
Push — main ( 0df457...604840 )
by Thierry
01:35
created

Database::alterTable()   A

Complexity

Conditions 6
Paths 16

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 14
c 1
b 0
f 0
nc 16
nop 2
dl 0
loc 22
rs 9.2222
1
<?php
2
3
namespace Lagdo\DbAdmin\Driver\MySql\Db;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
6
use Lagdo\DbAdmin\Driver\Entity\RoutineEntity;
7
8
use Lagdo\DbAdmin\Driver\Db\Database as AbstractDatabase;
9
10
class Database extends AbstractDatabase
11
{
12
    /**
13
     * @param TableEntity $tableAttrs
14
     *
15
     * @return string
16
     */
17
    private function _tableStatus(TableEntity $tableAttrs)
18
    {
19
        return 'COMMENT=' . $this->driver->quote($tableAttrs->comment) .
0 ignored issues
show
Bug introduced by
The method quote() does not exist on Lagdo\DbAdmin\Driver\DriverInterface. Did you maybe mean quoteBinary()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

19
        return 'COMMENT=' . $this->driver->/** @scrutinizer ignore-call */ quote($tableAttrs->comment) .

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
20
            ($tableAttrs->engine ? ' ENGINE=' . $this->driver->quote($tableAttrs->engine) : '') .
21
            ($tableAttrs->collation ? ' COLLATE ' . $this->driver->quote($tableAttrs->collation) : '') .
22
            ($tableAttrs->autoIncrement !== 0 ? " AUTO_INCREMENT=$tableAttrs->autoIncrement" : '');
23
    }
24
25
    /**
26
     * @inheritDoc
27
     */
28
    public function createTable(TableEntity $tableAttrs)
29
    {
30
        $clauses = [];
31
        foreach ($tableAttrs->fields as $field) {
32
            $clauses[] = implode($field[1]);
33
        }
34
35
        $clauses = array_merge($clauses, $tableAttrs->foreign);
36
        $status = $this->_tableStatus($tableAttrs);
37
38
        $result = $this->driver->execute('CREATE TABLE ' . $this->driver->table($tableAttrs->name) .
39
            ' (' . implode(', ', $clauses) . ") $status $tableAttrs->partitioning");
40
        return $result !== false;
41
    }
42
43
    /**
44
     * @inheritDoc
45
     */
46
    public function alterTable(string $table, TableEntity $tableAttrs)
47
    {
48
        $clauses = [];
49
        foreach ($tableAttrs->fields as $field) {
50
            $clauses[] = 'ADD ' . implode($field[1]) . $field[2];
51
        }
52
        foreach ($tableAttrs->edited as $field) {
53
            $clauses[] = 'CHANGE ' . $this->driver->escapeId($field[0]) . ' ' . implode($field[1]) . $field[2];
54
        }
55
        foreach ($tableAttrs->dropped as $column) {
56
            $clauses[] = 'DROP ' . $this->driver->escapeId($column);
57
        }
58
59
        $clauses = array_merge($clauses, $tableAttrs->foreign);
60
        if ($tableAttrs->name !== '' && $table !== $tableAttrs->name) {
61
            $clauses[] = 'RENAME TO ' . $this->driver->table($tableAttrs->name);
62
        }
63
        $clauses[] = $this->_tableStatus($tableAttrs);
64
65
        $result = $this->driver->execute('ALTER TABLE ' . $this->driver->table($table) . ' ' .
66
            implode(', ', $clauses) . ' ' . $tableAttrs->partitioning);
67
        return $result !== false;
68
    }
69
70
    /**
71
     * @inheritDoc
72
     */
73
    public function alterIndexes(string $table, array $alter, array $drop)
74
    {
75
        $clauses = [];
76
        foreach ($drop as $index) {
77
            $clauses[] = 'DROP INDEX ' . $this->driver->escapeId($index->name);
78
        }
79
        foreach ($alter as $index) {
80
            $clauses[] = 'ADD ' . ($index->type == 'PRIMARY' ? 'PRIMARY KEY ' :  $index->type . ' ') .
81
                ($index->name != '' ? $this->driver->escapeId($index->name) . ' ' : '') .
82
                '(' . implode(', ', $index->columns) . ')';
83
        }
84
        $result = $this->driver->execute('ALTER TABLE ' . $this->driver->table($table) . ' ' . implode(', ', $clauses));
85
        return $result !== false;
86
    }
87
88
    /**
89
     * @inheritDoc
90
     */
91
    public function tables()
92
    {
93
        return $this->driver->keyValues($this->driver->minVersion(5) ?
94
            'SELECT TABLE_NAME, TABLE_TYPE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME' :
95
            'SHOW TABLES');
96
    }
97
98
    /**
99
     * @inheritDoc
100
     */
101
    public function countTables(array $databases)
102
    {
103
        $counts = [];
104
        foreach ($databases as $database) {
105
            $counts[$database] = count($this->driver->values('SHOW TABLES IN ' . $this->driver->escapeId($database)));
106
        }
107
        return $counts;
108
    }
109
110
    /**
111
     * @inheritDoc
112
     */
113
    public function dropViews(array $views)
114
    {
115
        $this->driver->execute('DROP VIEW ' . implode(', ', array_map(function ($view) {
116
            return $this->driver->table($view);
117
        }, $views)));
118
        return true;
119
    }
120
121
    /**
122
     * @inheritDoc
123
     */
124
    public function dropTables(array $tables)
125
    {
126
        $this->driver->execute('DROP TABLE ' . implode(', ', array_map(function ($table) {
127
            return $this->driver->table($table);
128
        }, $tables)));
129
        return true;
130
    }
131
132
    /**
133
     * @inheritDoc
134
     */
135
    public function truncateTables(array $tables)
136
    {
137
        return $this->driver->applyQueries('TRUNCATE TABLE', $tables);
138
    }
139
140
    /**
141
     * @inheritDoc
142
     */
143
    public function moveTables(array $tables, array $views, string $target)
144
    {
145
        $rename = [];
146
        foreach ($tables as $table) {
147
            $rename[] = $this->driver->table($table) . ' TO ' . $this->driver->escapeId($target) . '.' . $this->driver->table($table);
148
        }
149
        if (!$rename || $this->driver->execute('RENAME TABLE ' . implode(', ', $rename))) {
150
            $definitions = [];
151
            foreach ($views as $table) {
152
                $definitions[$this->driver->table($table)] = $this->driver->view($table);
153
            }
154
            $this->connection->open($target);
155
            $database = $this->driver->escapeId($this->driver->database());
156
            foreach ($definitions as $name => $view) {
157
                if (!$this->driver->execute("CREATE VIEW $name AS " . str_replace(" $database.", ' ', $view['select'])) ||
158
                    !$this->driver->execute("DROP VIEW $database.$name")) {
159
                    return false;
160
                }
161
            }
162
            return true;
163
        }
164
        //! move triggers
165
        return false;
166
    }
167
168
    /**
169
     * @inheritDoc
170
     */
171
    public function copyTables(array $tables, array $views, string $target)
172
    {
173
        $this->driver->execute("SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'");
174
        $overwrite = $this->util->input()->getOverwrite();
175
        foreach ($tables as $table) {
176
            $name = ($target == $this->driver->database() ? $this->driver->table("copy_$table") : $this->driver->escapeId($target) . '.' . $this->driver->table($table));
177
            if (($overwrite && !$this->driver->execute("\nDROP TABLE IF EXISTS $name"))
178
                || !$this->driver->execute("CREATE TABLE $name LIKE " . $this->driver->table($table))
179
                || !$this->driver->execute("INSERT INTO $name SELECT * FROM " . $this->driver->table($table))
180
            ) {
181
                return false;
182
            }
183
            foreach ($this->driver->rows('SHOW TRIGGERS LIKE ' . $this->driver->quote(addcslashes($table, "%_\\"))) as $row) {
184
                $trigger = $row['Trigger'];
185
                if (!$this->driver->execute('CREATE TRIGGER ' .
186
                    ($target == $this->driver->database() ? $this->driver->escapeId("copy_$trigger") :
187
                    $this->driver->escapeId($target) . '.' . $this->driver->escapeId($trigger)) .
188
                    " $row[Timing] $row[Event] ON $name FOR EACH ROW\n$row[Statement];")) {
189
                    return false;
190
                }
191
            }
192
        }
193
        foreach ($views as $table) {
194
            $name = ($target == $this->driver->database() ? $this->driver->table("copy_$table") :
195
                $this->driver->escapeId($target) . '.' . $this->driver->table($table));
196
            $view = $this->driver->view($table);
197
            if (($overwrite && !$this->driver->execute("DROP VIEW IF EXISTS $name"))
198
                || !$this->driver->execute("CREATE VIEW $name AS $view[select]")) { //! USE to avoid db.table
199
                return false;
200
            }
201
        }
202
        return true;
203
    }
204
205
    /**
206
     * @inheritDoc
207
     */
208
    public function events()
209
    {
210
        return $this->driver->rows('SHOW EVENTS');
211
    }
212
213
    /**
214
     * @inheritDoc
215
     */
216
    public function routine(string $name, string $type)
217
    {
218
        $enumLength = $this->driver->enumLength();
219
        $aliases = ['bool', 'boolean', 'integer', 'double precision', 'real', 'dec',
220
            'numeric', 'fixed', 'national char', 'national varchar'];
221
        $space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
222
        $type_pattern = '((' . implode('|', array_merge(array_keys($this->driver->types()), $aliases)) .
223
            ")\\b(?:\\s*\\(((?:[^'\")]|$enumLength)++)\\))?\\s*(zerofill\\s*)?(unsigned" .
224
            "(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?";
225
        $pattern = "$space*(" . ($type == 'FUNCTION' ? '' : $this->driver->inout()) .
226
            ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
227
        $create = $this->connection->result("SHOW CREATE $type " . $this->driver->escapeId($name), 2);
228
        preg_match("~\\(((?:$pattern\\s*,?)*)\\)\\s*" .
229
            ($type == "FUNCTION" ? "RETURNS\\s+$type_pattern\\s+" : '') . "(.*)~is", $create, $match);
230
        $fields = [];
231
        preg_match_all("~$pattern\\s*,?~is", $match[1], $matches, PREG_SET_ORDER);
232
        foreach ($matches as $param) {
233
            $fields[] = [
234
                'field' => str_replace('``', '`', $param[2]) . $param[3],
235
                'type' => strtolower($param[5]),
236
                'length' => preg_replace_callback("~$enumLength~s", 'normalize_enum', $param[6]),
237
                'unsigned' => strtolower(preg_replace('~\s+~', ' ', trim("$param[8] $param[7]"))),
238
                'null' => 1,
239
                'full_type' => $param[4],
240
                'inout' => strtoupper($param[1]),
241
                'collation' => strtolower($param[9]),
242
            ];
243
        }
244
        if ($type != 'FUNCTION') {
245
            return ['fields' => $fields, 'definition' => $match[11]];
246
        }
247
        return [
248
            'fields' => $fields,
249
            'returns' => ['type' => $match[12], 'length' => $match[13], 'unsigned' => $match[15], 'collation' => $match[16]],
250
            'definition' => $match[17],
251
            'language' => 'SQL', // available in information_schema.ROUTINES.PARAMETER_STYLE
252
        ];
253
    }
254
255
    /**
256
     * @inheritDoc
257
     */
258
    public function routines()
259
    {
260
        $rows = $this->driver->rows('SELECT ROUTINE_NAME, ROUTINE_TYPE, DTD_IDENTIFIER ' .
261
            'FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ' .
262
            $this->driver->quote($this->driver->database()));
263
        return array_map(function($row) {
264
            return new RoutineEntity($row['ROUTINE_NAME'], $row['ROUTINE_NAME'],
265
                $row['ROUTINE_TYPE'], $row['DTD_IDENTIFIER']);
266
        }, $rows);
267
    }
268
269
    /**
270
     * @inheritDoc
271
     */
272
    public function routineId(string $name, array $row)
273
    {
274
        return $this->driver->escapeId($name);
275
    }
276
}
277