These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Some static helper functions that SMW uses for setting up |
||
5 | * SQL databases. |
||
6 | * |
||
7 | * @author Markus Krötzsch |
||
8 | * @author Marcel Gsteiger |
||
9 | * @author Jeroen De Dauw |
||
10 | * |
||
11 | * @ingroup SMWStore |
||
12 | */ |
||
13 | class SMWSQLHelpers { |
||
14 | |||
15 | /** |
||
16 | * Database backends often have different types that need to be used |
||
17 | * repeatedly in (Semantic) MediaWiki. This function provides the |
||
18 | * preferred type (as a string) for various common kinds of columns. |
||
19 | * The input is one of the following strings: 'id' (page id numbers or |
||
20 | * similar), 'title' (title strings or similar), 'namespace' (namespace |
||
21 | * numbers), 'blob' (longer text blobs), 'iw' (interwiki prefixes). |
||
22 | * |
||
23 | * @param string $input |
||
24 | * @return string|false SQL type declaration |
||
25 | */ |
||
26 | 3 | static public function getStandardDBType( $input ) { |
|
27 | 3 | global $wgDBtype; |
|
28 | |||
29 | switch ( $input ) { |
||
30 | 3 | case 'id': |
|
31 | 3 | return $wgDBtype == 'postgres' ? 'SERIAL' : ($wgDBtype == 'sqlite' ? 'INTEGER' :'INT(8) UNSIGNED'); // like page_id in MW page table |
|
32 | 3 | case 'namespace': |
|
33 | 3 | return $wgDBtype == 'postgres' ? 'BIGINT' : 'INT(11)'; // like page_namespace in MW page table |
|
34 | 3 | case 'title': |
|
35 | 3 | return $wgDBtype == 'postgres' ? 'TEXT' : 'VARBINARY(255)'; // like page_title in MW page table |
|
36 | 3 | case 'iw': |
|
37 | 3 | return ($wgDBtype == 'postgres' || $wgDBtype == 'sqlite') ? 'TEXT' : 'VARBINARY(32)'; // like iw_prefix in MW interwiki table |
|
38 | 3 | case 'blob': |
|
39 | 3 | return $wgDBtype == 'postgres' ? 'BYTEA' : 'MEDIUMBLOB'; // larger blobs of character data, usually not subject to SELECT conditions |
|
40 | } |
||
41 | |||
42 | return false; |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * Generic creation and updating function for database tables. Ideally, it |
||
47 | * would be able to modify a table's signature in arbitrary ways, but it will |
||
48 | * fail for some changes. Its string-based interface is somewhat too |
||
49 | * impoverished for a permanent solution. It would be possible to go for update |
||
50 | * scripts (specific to each change) in the style of MediaWiki instead. |
||
51 | * |
||
52 | * Make sure the table of the given name has the given fields, provided |
||
53 | * as an array with entries fieldname => typeparams. typeparams should be |
||
54 | * in a normalised form and order to match to existing values. |
||
55 | * |
||
56 | * The function returns an array that includes all columns that have been |
||
57 | * changed. For each such column, the array contains an entry |
||
58 | * columnname => action, where action is one of 'up', 'new', or 'del' |
||
59 | * |
||
60 | * If progress reports during this operation are desired, then the parameter $reportTo should |
||
61 | * be given an object that has a method reportProgress(string) for doing so. |
||
62 | * |
||
63 | * @note The function partly ignores the order in which fields are set up. |
||
64 | * Only if the type of some field changes will its order be adjusted explicitly. |
||
65 | * |
||
66 | * @param string $tableName The table name. Does not need to have been passed to DatabaseBase->tableName yet. |
||
67 | * @param array $columns The fields and their types the table should have. |
||
68 | * @param DatabaseBase or Database $db |
||
69 | * @param object $reportTo Object to report back to. |
||
70 | */ |
||
71 | 3 | public static function setupTable( $rawTableName, array $fields, $db, $reportTo = null ) { |
|
72 | 3 | $tableName = $db->tableName( $rawTableName ); |
|
73 | |||
74 | 3 | self::reportProgress( "Checking table $tableName ...\n", $reportTo ); |
|
75 | |||
76 | 3 | if ( $db->tableExists( $rawTableName ) === false ) { // create new table |
|
77 | 2 | self::reportProgress( " Table not found, now creating...\n", $reportTo ); |
|
78 | 2 | self::createTable( $tableName, $fields, $db ); |
|
79 | 2 | self::reportProgress( " ... done.\n", $reportTo ); |
|
0 ignored issues
–
show
|
|||
80 | } else { |
||
81 | 2 | self::reportProgress( " Table already exists, checking structure ...\n", $reportTo ); |
|
82 | 2 | self::updateTable( $tableName, $fields, $db, $reportTo ); |
|
83 | 2 | self::reportProgress( " ... done.\n", $reportTo ); |
|
84 | } |
||
85 | 3 | } |
|
86 | |||
87 | /** |
||
88 | * Creates a new database table with the specified fields. |
||
89 | * |
||
90 | * @param string $tableName The table name. |
||
91 | * @param array $columns The fields and their types the table should have. |
||
92 | * @param DatabaseBase|Database $db |
||
93 | */ |
||
94 | 2 | private static function createTable( $tableName, array $fields, $db ) { |
|
95 | 2 | global $wgDBtype, $wgDBname; |
|
96 | |||
97 | 2 | $sql = 'CREATE TABLE ' . ( ( $wgDBtype == 'postgres' || $wgDBtype == 'sqlite' ) ? '' : "`$wgDBname`." ) . $tableName . ' ('; |
|
98 | |||
99 | 2 | $fieldSql = array(); |
|
100 | |||
101 | 2 | foreach ( $fields as $fieldName => $fieldType ) { |
|
102 | 2 | $fieldSql[] = "$fieldName $fieldType"; |
|
103 | } |
||
104 | |||
105 | 2 | $sql .= implode( ',', $fieldSql ) . ') '; |
|
106 | |||
107 | 2 | if ( $wgDBtype != 'postgres' && $wgDBtype != 'sqlite' ) { |
|
108 | // This replacement is needed for compatibility, see http://bugs.mysql.com/bug.php?id=17501 |
||
109 | 2 | $sql .= str_replace( 'TYPE', 'ENGINE', $GLOBALS['wgDBTableOptions'] ); |
|
110 | } |
||
111 | |||
112 | 2 | $db->query( $sql, __METHOD__ ); |
|
113 | 2 | } |
|
114 | |||
115 | /** |
||
116 | * Update a table given an array of field names and field types. |
||
117 | * |
||
118 | * @param string $tableName The table name. |
||
119 | * @param array $columns The fields and their types the table should have. |
||
120 | * @param DatabaseBase|Database $db |
||
121 | * @param object $reportTo Object to report back to. |
||
122 | */ |
||
123 | 2 | private static function updateTable( $tableName, array $fields, $db, $reportTo ) { |
|
124 | 2 | global $wgDBtype; |
|
125 | |||
126 | 2 | $currentFields = self::getFields( $tableName, $db, $reportTo ); |
|
127 | |||
128 | 2 | $isPostgres = $wgDBtype == 'postgres'; |
|
129 | |||
130 | 2 | if ( !$isPostgres ) { |
|
131 | 2 | $position = 'FIRST'; |
|
132 | } |
||
133 | |||
134 | // Loop through all the field definitions, and handle each definition for either postgres or MySQL. |
||
135 | 2 | foreach ( $fields as $fieldName => $fieldType ) { |
|
136 | 2 | if ( $isPostgres ) { |
|
137 | self::updatePostgresField( $tableName, $fieldName, $fieldType, $currentFields, $db, $reportTo ); |
||
138 | } else { |
||
139 | 2 | self::updateMySqlField( $tableName, $fieldName, $fieldType, $currentFields, $db, $reportTo, $position ); |
|
140 | 2 | $position = "AFTER $fieldName"; |
|
141 | } |
||
142 | |||
143 | 2 | $currentFields[$fieldName] = false; |
|
144 | } |
||
145 | |||
146 | // The updated fields have their value set to false, so if a field has a value |
||
147 | // that differs from false, it's an obsolete one that should be removed. |
||
148 | 2 | foreach ( $currentFields as $fieldName => $value ) { |
|
149 | 2 | if ( $value !== false ) { |
|
150 | self::reportProgress( " ... deleting obsolete field $fieldName ... ", $reportTo ); |
||
151 | |||
152 | if ( $isPostgres ) { |
||
153 | $db->query( 'ALTER TABLE "' . $tableName . '" DROP COLUMN "' . $fieldName . '"', __METHOD__ ); |
||
154 | } elseif ( $wgDBtype == 'sqlite' ) { |
||
155 | // DROP COLUMN not supported in Sqlite3 |
||
156 | self::reportProgress( " ... deleting obsolete field $fieldName not possible in SQLLite ... you could delete and reinitialize the tables to remove obsolete data, or just keep it ... ", $reportTo ); |
||
157 | } else { |
||
158 | $db->query( "ALTER TABLE $tableName DROP COLUMN `$fieldName`", __METHOD__ ); |
||
159 | } |
||
160 | |||
161 | 2 | self::reportProgress( "done.\n", $reportTo ); |
|
162 | } |
||
163 | } |
||
164 | 2 | } |
|
165 | |||
166 | /** |
||
167 | * Returns an array of fields (as keys) and their types (as values). |
||
168 | * |
||
169 | * @param string $tableName The table name. |
||
170 | * @param DatabaseBase|Database $db |
||
171 | * @param object $reportTo to report back to. |
||
172 | * |
||
173 | * @return array |
||
174 | */ |
||
175 | 2 | private static function getFields( $tableName, $db, $reportTo ) { |
|
176 | 2 | global $wgDBtype; |
|
177 | |||
178 | 2 | if ( $wgDBtype == 'postgres' ) { |
|
179 | $tableName = str_replace( '"', '', $tableName ); |
||
180 | // Use the data dictionary in postgresql to get an output comparable to DESCRIBE. |
||
181 | $sql = <<<EOT |
||
182 | SELECT |
||
183 | a.attname as "Field", |
||
184 | upper(pg_catalog.format_type(a.atttypid, a.atttypmod)) as "Type", |
||
185 | (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128) |
||
186 | FROM pg_catalog.pg_attrdef d |
||
187 | WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) as "Extra", |
||
188 | case when a.attnotnull THEN 'NO'::text else 'YES'::text END as "Null", a.attnum |
||
189 | FROM pg_catalog.pg_attribute a |
||
190 | WHERE a.attrelid = ( |
||
191 | SELECT c.oid |
||
192 | FROM pg_catalog.pg_class c |
||
193 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace |
||
194 | WHERE c.relname ~ '^($tableName)$' |
||
195 | AND pg_catalog.pg_table_is_visible(c.oid) |
||
196 | LIMIT 1 |
||
197 | ) AND a.attnum > 0 AND NOT a.attisdropped |
||
198 | ORDER BY a.attnum |
||
199 | EOT; |
||
200 | 2 | } elseif ( $wgDBtype == 'sqlite' ) { // SQLite |
|
201 | $sql = 'PRAGMA table_info(' . $tableName . ')'; |
||
202 | } else { // MySQL |
||
203 | 2 | $sql = 'DESCRIBE ' . $tableName; |
|
204 | } |
||
205 | |||
206 | 2 | $res = $db->query( $sql, __METHOD__ ); |
|
207 | 2 | $curfields = array(); |
|
208 | |||
209 | 2 | foreach ( $res as $row ) { |
|
210 | 2 | if ( $wgDBtype == 'postgres' ) { // postgresql |
|
211 | $type = strtoupper( $row->Type ); |
||
212 | |||
213 | if ( preg_match( '/^nextval\\(.+\\)/i', $row->Extra ) ) { |
||
214 | $type = 'SERIAL NOT NULL'; |
||
215 | } elseif ( $row->Null != 'YES' ) { |
||
216 | $type .= ' NOT NULL'; |
||
217 | } |
||
218 | 2 | } elseif ( $wgDBtype == 'sqlite' ) { // SQLite |
|
219 | $row->Field = $row->name; |
||
220 | $row->Type = $row->type; |
||
221 | $type = $row->type; |
||
222 | if ( $row->notnull == '1' ) { |
||
223 | $type .= ' NOT NULL'; |
||
224 | } |
||
225 | if ( $row->pk == '1' ) { |
||
226 | $type .= ' PRIMARY KEY AUTOINCREMENT'; |
||
227 | } |
||
228 | } else { // mysql |
||
229 | 2 | $type = strtoupper( $row->Type ); |
|
230 | |||
231 | 2 | if ( substr( $type, 0, 8 ) == 'VARCHAR(' ) { |
|
232 | $type .= ' binary'; // just assume this to be the case for VARCHAR, though DESCRIBE will not tell us |
||
233 | } |
||
234 | |||
235 | 2 | if ( $row->Null != 'YES' ) { |
|
236 | 2 | $type .= ' NOT NULL'; |
|
237 | } |
||
238 | |||
239 | 2 | if ( $row->Key == 'PRI' ) { /// FIXME: updating "KEY" is not possible, the below query will fail in this case. |
|
240 | 2 | $type .= ' KEY'; |
|
241 | } |
||
242 | |||
243 | 2 | if ( $row->Extra == 'auto_increment' ) { |
|
244 | 2 | $type .= ' AUTO_INCREMENT'; |
|
245 | } |
||
246 | } |
||
247 | |||
248 | 2 | $curfields[$row->Field] = $type; |
|
249 | } |
||
250 | |||
251 | 2 | return $curfields; |
|
252 | } |
||
253 | |||
254 | /** |
||
255 | * Update a single field given it's name and type and an array of |
||
256 | * current fields. Postgres version. |
||
257 | * |
||
258 | * @param string $tableName The table name. |
||
259 | * @param string $name The field name. |
||
260 | * @param string $type The field type and attributes. |
||
261 | * @param array $currentFields List of fields as they have been found in the database. |
||
262 | * @param DatabaseBase|Database $db |
||
263 | * @param object $reportTo Object to report back to. |
||
264 | */ |
||
265 | private static function updatePostgresField( $tableName, $name, $type, array $currentFields, $db, $reportTo ) { |
||
266 | $keypos = strpos( $type, ' PRIMARY KEY' ); |
||
267 | |||
268 | if ( $keypos > 0 ) { |
||
269 | $type = substr( $type, 0, $keypos ); |
||
270 | } |
||
271 | |||
272 | $type = strtoupper( $type ); |
||
273 | |||
274 | if ( !array_key_exists( $name, $currentFields ) ) { |
||
275 | self::reportProgress( " ... creating field $name ... ", $reportTo ); |
||
276 | |||
277 | $db->query( "ALTER TABLE $tableName ADD \"" . $name . "\" $type", __METHOD__ ); |
||
278 | |||
279 | self::reportProgress( "done.\n", $reportTo ); |
||
280 | } elseif ( $currentFields[$name] != $type ) { |
||
281 | self::reportProgress( " ... changing type of field $name from '$currentFields[$name]' to '$type' ... ", $reportTo ); |
||
282 | |||
283 | $notnullposnew = strpos( $type, ' NOT NULL' ); |
||
284 | if ( $notnullposnew > 0 ) { |
||
285 | $type = substr( $type, 0, $notnullposnew ); |
||
286 | } |
||
287 | |||
288 | $notnullposold = strpos( $currentFields[$name], ' NOT NULL' ); |
||
289 | $typeold = strtoupper( ( $notnullposold > 0 ) ? substr( $currentFields[$name], 0, $notnullposold ) : $currentFields[$name] ); |
||
290 | |||
291 | if ( $typeold != $type ) { |
||
292 | $sql = "ALTER TABLE " . $tableName . " ALTER COLUMN \"" . $name . "\" TYPE " . $type; |
||
293 | $db->query( $sql, __METHOD__ ); |
||
294 | } |
||
295 | |||
296 | if ( $notnullposold != $notnullposnew ) { |
||
297 | $sql = "ALTER TABLE " . $tableName . " ALTER COLUMN \"" . $name . "\" " . ( $notnullposnew > 0 ? 'SET' : 'DROP' ) . " NOT NULL"; |
||
298 | $db->query( $sql, __METHOD__ ); |
||
299 | } |
||
300 | |||
301 | self::reportProgress( "done.\n", $reportTo ); |
||
302 | } else { |
||
303 | self::reportProgress( " ... field $name is fine.\n", $reportTo ); |
||
304 | } |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * Update a single field given it's name and type and an array of |
||
309 | * current fields. MySQL version. |
||
310 | * |
||
311 | * @param string $tableName The table name. |
||
312 | * @param string $name The field name. |
||
313 | * @param string $type The field type and attributes. |
||
314 | * @param array $currentFields List of fields as they have been found in the database. |
||
315 | * @param DatabaseBase|Database $db |
||
316 | * @param object $reportTo Object to report back to. |
||
317 | * @param string $position |
||
318 | */ |
||
319 | 2 | private static function updateMySqlField( $tableName, $name, $type, array $currentFields, $db, $reportTo, $position ) { |
|
320 | 2 | if ( !array_key_exists( $name, $currentFields ) ) { |
|
321 | self::reportProgress( " ... creating field $name ... ", $reportTo ); |
||
322 | |||
323 | $db->query( "ALTER TABLE $tableName ADD `$name` $type $position", __METHOD__ ); |
||
324 | $result[$name] = 'new'; |
||
325 | |||
326 | self::reportProgress( "done.\n", $reportTo ); |
||
327 | 2 | } elseif ( $currentFields[$name] != $type ) { |
|
328 | self::reportProgress( " ... changing type of field $name from '$currentFields[$name]' to '$type' ... ", $reportTo ); |
||
329 | |||
330 | // To avoid Error: 1068 Multiple primary key defined when a PRIMARY is involved |
||
331 | if ( strpos( $type, 'AUTO_INCREMENT' ) !== false ) { |
||
332 | $db->query( "ALTER TABLE $tableName DROP PRIMARY KEY", __METHOD__ ); |
||
333 | } |
||
334 | |||
335 | $db->query( "ALTER TABLE $tableName CHANGE `$name` `$name` $type $position", __METHOD__ ); |
||
336 | $result[$name] = 'up'; |
||
337 | self::reportProgress( "done.\n", $reportTo ); |
||
338 | } else { |
||
339 | 2 | self::reportProgress( " ... field $name is fine.\n", $reportTo ); |
|
340 | } |
||
341 | 2 | } |
|
342 | |||
343 | /** |
||
344 | * Make sure that each of the column descriptions in the given array is |
||
345 | * indexed by *one* index in the given DB table. |
||
346 | * |
||
347 | * @param string $tableName table name. Does not need to have been passed to DatabaseBase->tableName yet. |
||
348 | * @param array $indexes array of strings, each a comma separated list with column names to index |
||
349 | * @param DatabaseBase|Database $db DatabaseBase or Database |
||
350 | * @param object $reportTo object to report messages to; since 1.8 |
||
351 | */ |
||
352 | 3 | public static function setupIndex( $rawTableName, array $indexes, $db, $reportTo = null ) { |
|
353 | 3 | global $wgDBtype; |
|
354 | |||
355 | 3 | $tableName = $wgDBtype == 'postgres' ? $db->tableName( $rawTableName, 'raw' ) : $db->tableName( $rawTableName ); |
|
356 | |||
357 | 3 | self::reportProgress( "Checking index structures for table $tableName ...\n", $reportTo ); |
|
358 | |||
359 | // First remove obsolete indexes. |
||
360 | 3 | $oldIndexes = self::getIndexInfo( $db, $tableName ); |
|
361 | 3 | if ( $wgDBtype == 'sqlite' ) { // SQLite |
|
362 | /// TODO We do not currently get the right column definitions in |
||
363 | /// SQLLite; hence we can only drop all indexes. Wasteful. |
||
364 | foreach ( $oldIndexes as $key => $index ) { |
||
365 | self::dropIndex( $db, $key, $tableName, $key, $reportTo ); |
||
366 | } |
||
367 | } else { |
||
368 | 3 | foreach ( $oldIndexes as $key => $indexColumn ) { |
|
369 | 3 | $id = array_search( $indexColumn, $indexes ); |
|
370 | 3 | if ( $id !== false || $key == 'PRIMARY' ) { |
|
371 | 3 | self::reportProgress( " ... index $indexColumn is fine.\n", $reportTo ); |
|
372 | 3 | unset( $indexes[$id] ); |
|
373 | } else { // Duplicate or unrequired index. |
||
374 | 3 | self::dropIndex( $db, $key, $tableName, $indexColumn, $reportTo ); |
|
375 | } |
||
376 | } |
||
377 | } |
||
378 | |||
379 | // Add new indexes. |
||
380 | 3 | foreach ( $indexes as $key => $index ) { |
|
381 | // If the index is an array, it contains the column |
||
382 | // name as first element, and index type as second one. |
||
383 | 3 | if ( is_array( $index ) ) { |
|
384 | 3 | $columns = $index[0]; |
|
385 | 3 | $type = count( $index ) > 1 ? $index[1] : 'INDEX'; |
|
386 | } else { |
||
387 | 2 | $columns = $index; |
|
388 | 2 | $type = 'INDEX'; |
|
389 | } |
||
390 | |||
391 | 3 | self::createIndex( $db, $type, "{$tableName}_index{$key}", $tableName, $columns, $reportTo ); |
|
392 | } |
||
393 | |||
394 | 3 | self::reportProgress( " ... done.\n", $reportTo ); |
|
395 | |||
396 | 3 | return true; |
|
397 | } |
||
398 | |||
399 | /** |
||
400 | * Get the information about all indexes of a table. The result is an |
||
401 | * array of format indexname => indexcolumns. The latter is a comma |
||
402 | * separated list. |
||
403 | * |
||
404 | * @since 1.8 |
||
405 | * @param DatabaseBase|Database $db database handler |
||
406 | * @param string $tableName name of table |
||
407 | * @return array indexname => columns |
||
408 | */ |
||
409 | 3 | private static function getIndexInfo( $db, $tableName ) { |
|
410 | 3 | global $wgDBtype; |
|
411 | |||
412 | 3 | $indexes = array(); |
|
413 | 3 | if ( $wgDBtype == 'postgres' ) { // postgresql |
|
414 | $sql = "SELECT i.relname AS indexname," |
||
415 | . " pg_get_indexdef(i.oid) AS indexdef, " |
||
416 | . " replace(substring(pg_get_indexdef(i.oid) from E'\\\\((.*)\\\\)'), ' ' , '') AS indexcolumns" |
||
417 | . " FROM pg_index x" |
||
418 | . " JOIN pg_class c ON c.oid = x.indrelid" |
||
419 | . " JOIN pg_class i ON i.oid = x.indexrelid" |
||
420 | . " LEFT JOIN pg_namespace n ON n.oid = c.relnamespace" |
||
421 | . " LEFT JOIN pg_tablespace t ON t.oid = i.reltablespace" |
||
422 | . " WHERE c.relkind = 'r'::\"char\" AND i.relkind = 'i'::\"char\"" |
||
423 | . " AND c.relname = '" . $tableName . "'" |
||
424 | . " AND NOT pg_get_indexdef(i.oid) ~ '^CREATE UNIQUE INDEX'"; |
||
425 | $res = $db->query( $sql, __METHOD__ ); |
||
426 | |||
427 | if ( !$res ) { |
||
428 | return false; |
||
429 | } |
||
430 | |||
431 | foreach ( $res as $row ) { |
||
432 | $indexes[$row->indexname] = $row->indexcolumns; |
||
433 | } |
||
434 | 3 | } elseif ( $wgDBtype == 'sqlite' ) { // SQLite |
|
435 | $res = $db->query( 'PRAGMA index_list(' . $tableName . ')', __METHOD__ ); |
||
436 | |||
437 | if ( !$res ) { |
||
438 | return false; |
||
439 | } |
||
440 | |||
441 | foreach ( $res as $row ) { |
||
442 | /// FIXME The value should not be $row->name below?! |
||
443 | if ( !array_key_exists( $row->name, $indexes ) ) { |
||
444 | $indexes[$row->name] = $row->name; |
||
445 | } else { |
||
446 | $indexes[$row->name] .= ',' . $row->name; |
||
447 | } |
||
448 | } |
||
449 | } else { // MySQL and default |
||
450 | 3 | $res = $db->query( 'SHOW INDEX FROM ' . $tableName, __METHOD__ ); |
|
451 | |||
452 | 3 | if ( !$res ) { |
|
453 | return false; |
||
454 | } |
||
455 | |||
456 | 3 | foreach ( $res as $row ) { |
|
457 | 3 | if ( !array_key_exists( $row->Key_name, $indexes ) ) { |
|
458 | 3 | $indexes[$row->Key_name] = $row->Column_name; |
|
459 | } else { |
||
460 | 3 | $indexes[$row->Key_name] .= ',' . $row->Column_name; |
|
461 | } |
||
462 | } |
||
463 | } |
||
464 | |||
465 | 3 | return $indexes; |
|
466 | } |
||
467 | |||
468 | /** |
||
469 | * Drop an index using the suitable SQL for various RDBMS. |
||
470 | * |
||
471 | * @since 1.8 |
||
472 | * @param DatabaseBase|Database $db database handler |
||
473 | * @param string $indexName name fo the index as in DB |
||
474 | * @param string $tableName name of the table (not relevant in all DBMSs) |
||
475 | * @param string $columns list of column names to index, comma |
||
476 | * separated; only for reporting |
||
477 | * @param object $reportTo to report messages to |
||
478 | */ |
||
479 | 2 | private static function dropIndex( $db, $indexName, $tableName, $columns, $reportTo = null ) { |
|
480 | 2 | global $wgDBtype; |
|
481 | |||
482 | 2 | self::reportProgress( " ... removing index $columns ...", $reportTo ); |
|
483 | 2 | if ( $wgDBtype == 'postgres' ) { // postgresql |
|
484 | $db->query( 'DROP INDEX IF EXISTS ' . $indexName, __METHOD__ ); |
||
485 | 2 | } elseif ( $wgDBtype == 'sqlite' ) { // SQLite |
|
486 | $db->query( 'DROP INDEX ' . $indexName, __METHOD__ ); |
||
487 | } else { // MySQL and default |
||
488 | 2 | $db->query( 'DROP INDEX ' . $indexName . ' ON ' . $tableName, __METHOD__ ); |
|
489 | } |
||
490 | 2 | self::reportProgress( "done.\n", $reportTo ); |
|
491 | 2 | } |
|
492 | |||
493 | /** |
||
494 | * Create an index using the suitable SQL for various RDBMS. |
||
495 | * |
||
496 | * @since 1.8 |
||
497 | * @param DatabaseBase|Database $db Database handler |
||
498 | * @param string $type "INDEX", "UNIQUE" or similar |
||
499 | * @param string $indexName name fo the index as in DB |
||
500 | * @param string $tableName name of the table |
||
501 | * @param array $columns list of column names to index, comma separated |
||
502 | * @param object $reportTo object to report messages to |
||
503 | */ |
||
504 | 3 | private static function createIndex( $db, $type, $indexName, $tableName, $columns, $reportTo = null ) { |
|
505 | 3 | global $wgDBtype; |
|
506 | |||
507 | 3 | self::reportProgress( " ... creating new index $columns ...", $reportTo ); |
|
508 | 3 | if ( $wgDBtype == 'postgres' ) { // postgresql |
|
509 | if ( $db->indexInfo( $tableName, $indexName ) === false ) { |
||
510 | $db->query( "CREATE $type $indexName ON $tableName ($columns)", __METHOD__ ); |
||
511 | } |
||
512 | 3 | } elseif ( $wgDBtype == 'sqlite' ) { // SQLite |
|
513 | $db->query( "CREATE $type $indexName ON $tableName ($columns)", __METHOD__ ); |
||
514 | } else { // MySQL and default |
||
515 | 3 | $db->query( "ALTER TABLE $tableName ADD $type ($columns)", __METHOD__ ); |
|
516 | } |
||
517 | 3 | self::reportProgress( "done.\n", $reportTo ); |
|
518 | 3 | } |
|
519 | |||
520 | /** |
||
521 | * Reports the given message to the reportProgress method of the |
||
522 | * $receiver. |
||
523 | * |
||
524 | * @param string $msg |
||
525 | * @param object $receiver |
||
526 | */ |
||
527 | 3 | private static function reportProgress( $msg, $receiver ) { |
|
528 | 3 | if ( !is_null( $receiver ) ) { |
|
529 | $receiver->reportProgress( $msg ); |
||
530 | } |
||
531 | 3 | } |
|
532 | |||
533 | } |
||
534 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.