Fixing code formatting and added detailed column information for PostgreSQL
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / database / editor / MySQLDatabaseEditor.class.php
CommitLineData
11ade432
AE
1<?php
2namespace wcf\system\database\editor;
76d74665 3use wcf\system\database\util\PreparedStatementConditionBuilder;
82a7de7f 4use wcf\system\Regex;
11ade432
AE
5
6/**
a17de04e
MS
7 * Database editor implementation for MySQL4.1 or higher.
8 *
11ade432 9 * @author Marcel Werk
9f959ced 10 * @copyright 2001-2012 WoltLab GmbH
11ade432
AE
11 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
12 * @package com.woltlab.wcf
13 * @subpackage system.database.editor
9f959ced 14 * @category Community Framework
11ade432
AE
15 */
16class MySQLDatabaseEditor extends DatabaseEditor {
17 /**
9f959ced 18 * @see wcf\system\database\editor\DatabaseEditor::getTableNames()
11ade432
AE
19 */
20 public function getTableNames() {
21 $existingTables = array();
22 $sql = "SHOW TABLES FROM `".$this->dbObj->getDatabaseName()."`";
23 $statement = $this->dbObj->prepareStatement($sql);
24 $statement->execute();
25 while ($row = $statement->fetchArray(\PDO::FETCH_NUM)) {
26 $existingTables[] = $row[0];
27 }
28 return $existingTables;
29 }
30
31 /**
9f959ced 32 * @see wcf\system\database\editor\DatabaseEditor::getColumns()
11ade432
AE
33 */
34 public function getColumns($tableName) {
35 $columns = array();
36 $sql = "SHOW COLUMNS FROM ".$tableName;
37 $statement = $this->dbObj->prepareStatement($sql);
38 $statement->execute();
39 while ($row = $statement->fetchArray()) {
4d4a5125
K
40 $typeMatches = Regex::compile('([a-z]+)\(([0-9]+)\)', Regex::CASE_INSENSITIVE)->match($row['Type']);
41
42 $columns[] = array('name' => $row['Field'],
43 'data' => array(
44 'type' => $typeMatches[1],
45 'length' => $typeMatches[2],
46 'notNull' => (($row['Null'] == 'YES') ? true : false),
47 'key' => (($row['Key'] == 'PRI') ? 'PRIMARY' : (($row['Key'] == 'UNI') ? 'UNIQUE' : '')),
48 'default' => $row['Default'],
49 'autoIncrement' => ($row['Extra'] == 'auto_increment' ? true : false)
50 )
51 );
82a7de7f 52 }
bf27dc91 53
9f959ced 54 return $columns;
11ade432
AE
55 }
56
57 /**
9f959ced 58 * @see wcf\system\database\editor\DatabaseEditor::getIndices()
11ade432
AE
59 */
60 public function getIndices($tableName) {
61 $indices = array();
62 $sql = "SHOW INDEX FROM ".$tableName;
63 $statement = $this->dbObj->prepareStatement($sql);
64 $statement->execute();
65 while ($row = $statement->fetchArray()) {
9f959ced
MS
66 $indices[] = $row['Key_name'];
67 }
68
69 return $indices;
11ade432
AE
70 }
71
72 /**
9f959ced 73 * @see wcf\system\database\editor\DatabaseEditor::createTable()
11ade432
AE
74 */
75 public function createTable($tableName, $columns, $indices = array()) {
76 $columnDefinition = $indexDefinition = '';
77
78 // build column definition
79 foreach ($columns as $column) {
80 if (!empty($columnDefinition)) $columnDefinition .= ',';
81 $columnDefinition .= $this->buildColumnDefinition($column['name'], $column['data']);
82 }
83
84 // build index definition
85 $hasFulltextIndex = false;
86 foreach ($indices as $index) {
87 if (!empty($indexDefinition)) $indexDefinition .= ',';
88 $indexDefinition .= $this->buildIndexDefinition($index['name'], $index['data']);
89 if ($index['data']['type'] == 'FULLTEXT') $hasFulltextIndex = true;
90 }
91
92 // create table
93 $sql = "CREATE TABLE ".$tableName." (
94 ".$columnDefinition."
95 ".(!empty($indexDefinition) ? ',' : '')."
96 ".$indexDefinition."
97 ) ENGINE=".($hasFulltextIndex ? 'MyISAM' : 'InnoDB')." DEFAULT CHARSET=utf8";
98 $statement = $this->dbObj->prepareStatement($sql);
99 $statement->execute();
100 }
101
102 /**
9f959ced 103 * @see wcf\system\database\editor\DatabaseEditor::dropTable()
11ade432
AE
104 */
105 public function dropTable($tableName) {
106 $sql = "DROP TABLE IF EXISTS ".$tableName;
107 $statement = $this->dbObj->prepareStatement($sql);
108 $statement->execute();
109 }
110
111 /**
9f959ced 112 * @see wcf\system\database\editor\DatabaseEditor::addColumn()
11ade432
AE
113 */
114 public function addColumn($tableName, $columnName, $columnData) {
115 $sql = "ALTER TABLE ".$tableName." ADD COLUMN ".$this->buildColumnDefinition($columnName, $columnData);
116 $statement = $this->dbObj->prepareStatement($sql);
117 $statement->execute();
118 }
119
120 /**
9f959ced 121 * @see wcf\system\database\editor\DatabaseEditor::alterColumn()
11ade432
AE
122 */
123 public function alterColumn($tableName, $oldColumnName, $newColumnName, $newColumnData) {
124 $sql = "ALTER TABLE ".$tableName." CHANGE COLUMN ".$oldColumnName." ".$this->buildColumnDefinition($newColumnName, $newColumnData);
125 $statement = $this->dbObj->prepareStatement($sql);
126 $statement->execute();
127 }
128
129 /**
9f959ced 130 * @see wcf\system\database\editor\DatabaseEditor::dropColumn()
11ade432
AE
131 */
132 public function dropColumn($tableName, $columnName) {
133 $sql = "ALTER TABLE ".$tableName." DROP COLUMN ".$columnName;
134 $statement = $this->dbObj->prepareStatement($sql);
135 $statement->execute();
136 }
137
138 /**
9f959ced 139 * @see wcf\system\database\editor\DatabaseEditor::addIndex()
11ade432
AE
140 */
141 public function addIndex($tableName, $indexName, $indexData) {
142 $sql = "ALTER TABLE ".$tableName." ADD ".$this->buildIndexDefinition($indexName, $indexData);
143 $statement = $this->dbObj->prepareStatement($sql);
144 $statement->execute();
145 }
146
147 /**
9f959ced 148 * @see wcf\system\database\editor\DatabaseEditor::addIndex()
11ade432
AE
149 */
150 public function addForeignKey($tableName, $indexName, $indexData) {
151 $sql = "ALTER TABLE ".$tableName." ADD";
152
153 // add index name
96017679 154 if (!empty($indexName)) $sql .= " CONSTRAINT `".$indexName."`";
11ade432
AE
155
156 // add columns
157 $sql .= " FOREIGN KEY (".str_replace(',', ',', preg_replace('/\s+/', '', $indexData['columns'])).")";
158
159 // add referenced table name
160 $sql .= " REFERENCES ".$indexData['referencedTable'];
161
162 // add referenced columns
163 $sql .= " (".str_replace(',', ',', preg_replace('/\s+/', '', $indexData['referencedColumns'])).")";
164
165 // add operation and action
166 if (!empty($indexData['operation'])) $sql .= " ON ".$indexData['operation']." ".$indexData['action'];
167
168 $statement = $this->dbObj->prepareStatement($sql);
169 $statement->execute();
170 }
171
172 /**
9f959ced 173 * @see wcf\system\database\editor\DatabaseEditor::dropIndex()
11ade432
AE
174 */
175 public function dropIndex($tableName, $indexName) {
176 $sql = "ALTER TABLE ".$tableName." DROP INDEX ".$indexName;
177 $statement = $this->dbObj->prepareStatement($sql);
178 $statement->execute();
179 }
180
b6ae7d04 181 /**
9f959ced 182 * @see wcf\system\database\editor\DatabaseEditor::dropForeignKey()
b6ae7d04
MW
183 */
184 public function dropForeignKey($tableName, $indexName) {
96017679 185 $sql = "ALTER TABLE ".$tableName." DROP FOREIGN KEY `".$indexName."`";
b6ae7d04
MW
186 $statement = $this->dbObj->prepareStatement($sql);
187 $statement->execute();
188 }
189
11ade432
AE
190 /**
191 * Builds a column definition for execution in a create table or alter table statement.
192 *
193 * @param string $columnName
194 * @param array $columnData
195 * @param string
196 */
197 protected function buildColumnDefinition($columnName, $columnData) {
198 // column name
199 $definition = $columnName;
200 // column type
201 $definition .= " ".$columnData['type'];
202 // column length and decimals
203 if (!empty($columnData['length'])) {
204 $definition .= "(".$columnData['length'].(!empty($columnData['decimals']) ? ",".$columnData['decimals'] : "").")";
205 }
206 // enum / set
207 if ($columnData['type'] == 'enum' && !empty($columnData['values'])) {
208 $definition .= "(".$columnData['values'].")";
209 }
210 // not null / null
211 if (!empty($columnData['notNull'])) $definition .= " NOT NULL";
212 // default
213 if (isset($columnData['default']) && $columnData['default'] !== '') $definition .= " DEFAULT ".$columnData['default'];
214 // auto_increment
215 if (!empty($columnData['autoIncrement'])) $definition .= " AUTO_INCREMENT";
216 // key
217 if (!empty($columnData['key'])) $definition .= " ".$columnData['key']." KEY";
218
219 return $definition;
220 }
221
222 /**
223 * Builds a index definition for execution in a create table or alter table statement.
224 *
225 * @param string $indexName
226 * @param array $indexData
227 * @param string
228 */
229 protected function buildIndexDefinition($indexName, $indexData) {
230 $definition = "";
231 // index type
232 if ($indexData['type'] == 'PRIMARY') $definition = "PRIMARY KEY";
233 else if ($indexData['type'] == 'UNIQUE') $definition = "UNIQUE KEY";
234 else if ($indexData['type'] == 'FULLTEXT') $definition = "FULLTEXT KEY";
235 else $definition = "KEY";
236
237 // index name
238 if (!empty($indexName)) $definition .= " ".$indexName."";
239 // columns
240 $definition .= " (".str_replace(',', ',', preg_replace('/\s+/', '', $indexData['columns'])).")";
241
242 return $definition;
243 }
76d74665
AE
244
245 /**
246 * @see wcf\system\database\editor\DatabaseEditor::dropConflictedTables()
247 */
248 public function dropConflictedTables(array $conflictedTables) {
249 $tables = array();
250 foreach ($conflictedTables as $tableName) {
251 $tables[$tableName] = array();
252 }
253
254 // get current database
255 $sql = "SELECT DATABASE() AS currentDB";
256 $statement = $this->dbObj->prepareStatement($sql);
257 $statement->execute();
258 $row = $statement->fetchArray();
259 $currentDB = $row['currentDB'];
260
261 // get constraints
262 $conditions = new PreparedStatementConditionBuilder();
263 $conditions->add("TABLE_SCHEMA = ?", array($currentDB));
264 $conditions->add("TABLE_NAME IN (?)", array($conflictedTables));
265 $conditions->add("REFERENCED_TABLE_NAME IS NOT NULL");
266 $conditions->add("CONSTRAINT_NAME LIKE ?", array('%_fk'));
267
268 $sql = "SELECT CONSTRAINT_NAME, TABLE_NAME
269 FROM information_schema.KEY_COLUMN_USAGE
270 ".$conditions;
271 $statement = $this->dbObj->prepareStatement($sql);
272 $statement->execute($conditions->getParameters());
273 while ($row = $statement->fetchArray()) {
274 $this->tables[$row['TABLE_NAME']][] = $row['CONSTRAINT_NAME'];
275 }
276
a1517977
AE
277 // handle foreign keys from 3rd party tables
278 $conditions = new PreparedStatementConditionBuilder();
279 $conditions->add("TABLE_SCHEMA = ?", array($currentDB));
280 $conditions->add("REFERENCED_TABLE_NAME IN (?)", array($conflictedTables));
281 $conditions->add("CONSTRAINT_NAME LIKE ?", array('%_fk'));
282
283 $sql = "SELECT CONSTRAINT_NAME, TABLE_NAME
284 FROM information_schema.KEY_COLUMN_USAGE
285 ".$conditions;
286 $statement = $this->dbObj->prepareStatement($sql);
287 $statement->execute($conditions->getParameters());
288 $foreignConstraints = array();
289 while ($row = $statement->fetchArray()) {
290 if (!isset($foreignConstraints[$row['TABLE_NAME']])) {
291 $foreignConstraints[$row['TABLE_NAME']] = array();
292 }
293
294 $foreignConstraints[$row['TABLE_NAME']][] = $row['CONSTRAINT_NAME'];
295 }
296
297 // drop foreign keys from 3rd party tables
298 foreach ($foreignConstraints as $tableName => $foreignKeys) {
299 foreach ($foreignKeys as $fk) {
300 $this->dropForeignKey($tableName, $fk);
301 }
302 }
303
76d74665
AE
304 // drop foreign keys
305 foreach ($this->tables as $tableName => $foreignKeys) {
306 foreach ($foreignKeys as $fk) {
307 $this->dropForeignKey($tableName, $fk);
308 }
309 }
310
311 // drop tables
312 foreach (array_keys($this->tables) as $tableName) {
313 $this->dropTable($tableName);
314 }
315 }
11ade432 316}