Passed
Branch main (217991)
by Thierry
02:18
created

Database::countTables()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
243
                $row['ROUTINE_TYPE'], $row['DTD_IDENTIFIER']);
244
        }, $rows);
245
    }
246
247
    /**
248
     * @inheritDoc
249
     */
250
    public function routineId(string $name, array $row)
251
    {
252
        return $this->driver->escapeId($name);
253
    }
254
}
255