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

Database::dropTables()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
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\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
        $use_all_fields = ($table == "" || !empty($foreign));
24
        foreach ($fields as $field) {
25
            if ($field[0] != "" || !$field[1] || $field[2]) {
26
                $use_all_fields = true;
27
                break;
28
            }
29
        }
30
        $alter = [];
31
        $originals = [];
32
        foreach ($fields as $field) {
33
            if ($field[1]) {
34
                $alter[] = ($use_all_fields ? $field[1] : "ADD " . implode($field[1]));
35
                if ($field[0] != "") {
36
                    $originals[$field[0]] = $field[1][0];
37
                }
38
            }
39
        }
40
        if (!$use_all_fields) {
41
            foreach ($alter as $val) {
42
                if (!$this->driver->execute("ALTER TABLE " . $this->driver->table($table) . " $val")) {
43
                    return false;
44
                }
45
            }
46
            if ($table != $name && !$this->driver->execute("ALTER TABLE " .
47
                $this->driver->table($table) . " RENAME TO " . $this->driver->table($name))) {
48
                return false;
49
            }
50
        } elseif (!$this->recreateTable($table, $name, $alter, $originals, $foreign, $autoIncrement)) {
51
            return false;
52
        }
53
        if ($autoIncrement) {
54
            $this->driver->execute("BEGIN");
55
            $this->driver->execute("UPDATE sqlite_sequence SET seq = $autoIncrement WHERE name = " .
56
                $this->driver->quote($name)); // ignores error
57
            if (!$this->driver->affectedRows()) {
58
                $this->driver->execute("INSERT INTO sqlite_sequence (name, seq) VALUES (" .
59
                    $this->driver->quote($name) . ", $autoIncrement)");
60
            }
61
            $this->driver->execute("COMMIT");
62
        }
63
        return true;
64
    }
65
66
    /**
67
     * Recreate a table
68
     *
69
     * @param string $table
70
     * @param string $name
71
     * @param array $fields
72
     * @param array $originals
73
     * @param array $foreign
74
     * @param integer $autoIncrement
75
     * @param array $indexes
76
     *
77
     * @return bool
78
     */
79
    protected function recreateTable(string $table, string $name, array $fields, array $originals,
80
        array $foreign, int $autoIncrement, array $indexes = [])
81
    {
82
        if ($table != "") {
83
            if (empty($fields)) {
84
                foreach ($this->driver->fields($table) as $key => $field) {
85
                    if (!empty($indexes)) {
86
                        $field->autoIncrement = 0;
87
                    }
88
                    $fields[] = $this->util->processField($field, $field);
89
                    $originals[$key] = $this->driver->escapeId($key);
90
                }
91
            }
92
            $primary_key = false;
93
            foreach ($fields as $field) {
94
                if ($field[6]) {
95
                    $primary_key = true;
96
                }
97
            }
98
            $drop_indexes = [];
99
            foreach ($indexes as $key => $val) {
100
                if ($val[2] == "DROP") {
101
                    $drop_indexes[$val[1]] = true;
102
                    unset($indexes[$key]);
103
                }
104
            }
105
            foreach ($this->driver->indexes($table) as $key_name => $index) {
106
                $columns = [];
107
                foreach ($index->columns as $key => $column) {
108
                    if (!$originals[$column]) {
109
                        continue 2;
110
                    }
111
                    $columns[] = $originals[$column] . ($index->descs[$key] ? " DESC" : "");
112
                }
113
                if (!$drop_indexes[$key_name]) {
114
                    if ($index->type != "PRIMARY" || !$primary_key) {
115
                        $indexes[] = [$index->type, $key_name, $columns];
116
                    }
117
                }
118
            }
119
            foreach ($indexes as $key => $val) {
120
                if ($val[0] == "PRIMARY") {
121
                    unset($indexes[$key]);
122
                    $foreign[] = "  PRIMARY KEY (" . implode(", ", $val[2]) . ")";
123
                }
124
            }
125
            foreach ($this->driver->foreignKeys($table) as $key_name => $foreignKey) {
126
                foreach ($foreignKey->source as $key => $column) {
127
                    if (!$originals[$column]) {
128
                        continue 2;
129
                    }
130
                    $foreignKey->source[$key] = $this->driver->unescapeId($originals[$column]);
131
                }
132
                if (!isset($foreign[" $key_name"])) {
133
                    $foreign[] = " " . $this->driver->formatForeignKey($foreignKey);
134
                }
135
            }
136
            $this->driver->execute("BEGIN");
137
        }
138
        foreach ($fields as $key => $field) {
139
            $fields[$key] = "  " . implode($field);
140
        }
141
        $fields = array_merge($fields, array_filter($foreign));
142
        $tempName = ($table == $name ? "adminer_$name" : $name);
143
        if (!$this->driver->execute("CREATE TABLE " . $this->driver->table($tempName) .
144
            " (\n" . implode(",\n", $fields) . "\n)")) {
145
            // implicit ROLLBACK to not overwrite $this->driver->error()
146
            return false;
147
        }
148
        if ($table != "") {
149
            if ($originals && !$this->driver->execute("INSERT INTO " . $this->driver->table($tempName) .
150
                " (" . implode(", ", $originals) . ") SELECT " . implode(
151
                    ", ",
152
                    array_map(function ($key) {
153
                   return $this->driver->escapeId($key);
154
               }, array_keys($originals))
155
                ) . " FROM " . $this->driver->table($table))) {
156
                return false;
157
            }
158
            $triggers = [];
159
            foreach ($this->driver->triggers($table) as $trigger_name => $timing_event) {
160
                $trigger = $this->trigger($trigger_name);
0 ignored issues
show
Bug introduced by
The method trigger() does not exist on Lagdo\DbAdmin\Driver\Sqlite\Db\Database. ( Ignorable by Annotation )

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

160
                /** @scrutinizer ignore-call */ 
161
                $trigger = $this->trigger($trigger_name);

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...
161
                $triggers[] = "CREATE TRIGGER " . $this->driver->escapeId($trigger_name) . " " .
162
                    implode(" ", $timing_event) . " ON " . $this->driver->table($name) . "\n$trigger[Statement]";
163
            }
164
            $autoIncrement = $autoIncrement ? 0 :
165
                $this->connection->result("SELECT seq FROM sqlite_sequence WHERE name = " .
166
                $this->driver->quote($table)); // if $autoIncrement is set then it will be updated later
167
            // drop before creating indexes and triggers to allow using old names
168
            if (!$this->driver->execute("DROP TABLE " . $this->driver->table($table)) ||
169
                ($table == $name && !$this->driver->execute("ALTER TABLE " . $this->driver->table($tempName) .
170
                " RENAME TO " . $this->driver->table($name))) || !$this->alterIndexes($name, $indexes)
171
            ) {
172
                return false;
173
            }
174
            if ($autoIncrement) {
175
                $this->driver->execute("UPDATE sqlite_sequence SET seq = $autoIncrement WHERE name = " . $this->driver->quote($name)); // ignores error
176
            }
177
            foreach ($triggers as $trigger) {
178
                if (!$this->driver->execute($trigger)) {
179
                    return false;
180
                }
181
            }
182
            $this->driver->execute("COMMIT");
183
        }
184
        return true;
185
    }
186
187
    /**
188
     * @inheritDoc
189
     */
190
    public function alterIndexes(string $table, array $alter)
191
    {
192
        foreach ($alter as $primary) {
193
            if ($primary[0] == "PRIMARY") {
194
                return $this->recreateTable($table, $table, [], [], [], 0, $alter);
195
            }
196
        }
197
        foreach (array_reverse($alter) as $val) {
198
            if (!$this->driver->execute($val[2] == "DROP" ?
199
                "DROP INDEX " . $this->driver->escapeId($val[1]) :
200
                $this->driver->sqlForCreateIndex($table, $val[0], $val[1], "(" . implode(", ", $val[2]) . ")")
201
            )) {
202
                return false;
203
            }
204
        }
205
        return true;
206
    }
207
208
    /**
209
     * @inheritDoc
210
     */
211
    public function tables()
212
    {
213
        return $this->driver->keyValues("SELECT name, type FROM sqlite_master " .
214
            "WHERE type IN ('table', 'view') ORDER BY (name = 'sqlite_sequence'), name");
215
    }
216
217
    /**
218
     * @inheritDoc
219
     */
220
    public function countTables(array $databases)
221
    {
222
        $connection = $this->driver->createConnection(); // New connection
223
        $counts = [];
224
        $query = "SELECT count(*) FROM sqlite_master WHERE type IN ('table', 'view')";
225
        foreach ($databases as $database) {
226
            $counts[$database] = 0;
227
            $connection->open($database);
228
            $statement = $connection->query($query);
229
            if (is_object($statement) && ($row = $statement->fetchRow())) {
230
                $counts[$database] = intval($row[0]);
231
            }
232
        }
233
        return $counts;
234
    }
235
236
    /**
237
     * @inheritDoc
238
     */
239
    public function dropViews(array $views)
240
    {
241
        return $this->driver->applyQueries("DROP VIEW", $views);
242
    }
243
244
    /**
245
     * @inheritDoc
246
     */
247
    public function dropTables(array $tables)
248
    {
249
        return $this->driver->applyQueries("DROP TABLE", $tables);
250
    }
251
252
    /**
253
     * @inheritDoc
254
     */
255
    public function moveTables(array $tables, array $views, string $target)
256
    {
257
        return false;
258
    }
259
260
    /**
261
     * @inheritDoc
262
     */
263
    public function truncateTables(array $tables)
264
    {
265
        return $this->driver->applyQueries("DELETE FROM", $tables);
266
    }
267
}
268