1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * This file contains database functionality specifically designed for packages (mods) to utilize. |
||||
5 | * |
||||
6 | * Simple Machines Forum (SMF) |
||||
7 | * |
||||
8 | * @package SMF |
||||
9 | * @author Simple Machines https://www.simplemachines.org |
||||
10 | * @copyright 2020 Simple Machines and individual contributors |
||||
11 | * @license https://www.simplemachines.org/about/smf/license.php BSD |
||||
12 | * |
||||
13 | * @version 2.1 RC2 |
||||
14 | */ |
||||
15 | |||||
16 | if (!defined('SMF')) |
||||
17 | die('No direct access...'); |
||||
18 | |||||
19 | /** |
||||
20 | * Add the file functions to the $smcFunc array. |
||||
21 | */ |
||||
22 | function db_packages_init() |
||||
23 | { |
||||
24 | global $smcFunc, $reservedTables, $db_package_log, $db_prefix; |
||||
25 | |||||
26 | if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table') |
||||
27 | { |
||||
28 | $smcFunc += array( |
||||
29 | 'db_add_column' => 'smf_db_add_column', |
||||
30 | 'db_add_index' => 'smf_db_add_index', |
||||
31 | 'db_calculate_type' => 'smf_db_calculate_type', |
||||
32 | 'db_change_column' => 'smf_db_change_column', |
||||
33 | 'db_create_table' => 'smf_db_create_table', |
||||
34 | 'db_drop_table' => 'smf_db_drop_table', |
||||
35 | 'db_table_structure' => 'smf_db_table_structure', |
||||
36 | 'db_list_columns' => 'smf_db_list_columns', |
||||
37 | 'db_list_indexes' => 'smf_db_list_indexes', |
||||
38 | 'db_remove_column' => 'smf_db_remove_column', |
||||
39 | 'db_remove_index' => 'smf_db_remove_index', |
||||
40 | ); |
||||
41 | $db_package_log = array(); |
||||
42 | } |
||||
43 | |||||
44 | // We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up! |
||||
45 | $reservedTables = array( |
||||
46 | 'admin_info_files', 'approval_queue', 'attachments', |
||||
47 | 'background_tasks', 'ban_groups', 'ban_items', 'board_permissions', |
||||
48 | 'board_permissions_view', 'boards', 'calendar', 'calendar_holidays', |
||||
49 | 'categories', 'custom_fields', 'group_moderators', 'log_actions', |
||||
50 | 'log_activity', 'log_banned', 'log_boards', 'log_comments', |
||||
51 | 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', |
||||
52 | 'log_mark_read', 'log_member_notices', 'log_notify', 'log_online', |
||||
53 | 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments', |
||||
54 | 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', |
||||
55 | 'log_search_subjects', 'log_search_topics', 'log_spider_hits', |
||||
56 | 'log_spider_stats', 'log_subscribed', 'log_topics', 'mail_queue', |
||||
57 | 'member_logins', 'membergroups', 'members', 'mentions', |
||||
58 | 'message_icons', 'messages', 'moderator_groups', 'moderators', |
||||
59 | 'package_servers', 'permission_profiles', 'permissions', |
||||
60 | 'personal_messages', 'pm_labeled_messages', 'pm_labels', |
||||
61 | 'pm_recipients', 'pm_rules', 'poll_choices', 'polls', 'qanda', |
||||
62 | 'scheduled_tasks', 'sessions', 'settings', 'smiley_files', 'smileys', |
||||
63 | 'spiders', 'subscriptions', 'themes', 'topics', 'user_alerts', |
||||
64 | 'user_alerts_prefs', 'user_drafts', 'user_likes', |
||||
65 | ); |
||||
66 | foreach ($reservedTables as $k => $table_name) |
||||
67 | $reservedTables[$k] = strtolower($db_prefix . $table_name); |
||||
68 | |||||
69 | // We in turn may need the extra stuff. |
||||
70 | db_extend('extra'); |
||||
71 | } |
||||
72 | |||||
73 | /** |
||||
74 | * This function can be used to create a table without worrying about schema |
||||
75 | * compatibilities across supported database systems. |
||||
76 | * - If the table exists will, by default, do nothing. |
||||
77 | * - Builds table with columns as passed to it - at least one column must be sent. |
||||
78 | * The columns array should have one sub-array for each column - these sub arrays contain: |
||||
79 | * 'name' = Column name |
||||
80 | * 'type' = Type of column - values from (smallint, mediumint, int, text, varchar, char, tinytext, mediumtext, largetext) |
||||
81 | * 'size' => Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. |
||||
82 | * If not set SMF will pick a size. |
||||
83 | * - 'default' = Default value - do not set if no default required. |
||||
84 | * - 'null' => Can it be null (true or false) - if not set default will be false. |
||||
85 | * - 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set from what |
||||
86 | * it should begin counting. |
||||
87 | * - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are: |
||||
88 | * - 'name' => Index name (If left empty SMF will generate). |
||||
89 | * - 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'. |
||||
90 | * - 'columns' => Array containing columns that form part of key - in the order the index is to be created. |
||||
91 | * - parameters: (None yet) |
||||
92 | * - if_exists values: |
||||
93 | * - 'ignore' will do nothing if the table exists. (And will return true) |
||||
94 | * - 'overwrite' will drop any existing table of the same name. |
||||
95 | * - 'error' will return false if the table already exists. |
||||
96 | * - 'update' will update the table if the table already exists (no change of ai field and only colums with the same name keep the data) |
||||
97 | * |
||||
98 | * @param string $table_name The name of the table to create |
||||
99 | * @param array $columns An array of column info in the specified format |
||||
100 | * @param array $indexes An array of index info in the specified format |
||||
101 | * @param array $parameters Extra parameters. Currently only 'engine', the desired MySQL storage engine, is used. |
||||
102 | * @param string $if_exists What to do if the table exists. |
||||
103 | * @param string $error |
||||
104 | * @return boolean Whether or not the operation was successful |
||||
105 | */ |
||||
106 | function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal') |
||||
107 | { |
||||
108 | global $reservedTables, $smcFunc, $db_package_log, $db_prefix, $db_character_set, $db_name; |
||||
109 | |||||
110 | static $engines = array(); |
||||
111 | |||||
112 | $old_table_exists = false; |
||||
113 | |||||
114 | // Strip out the table name, we might not need it in some cases |
||||
115 | $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; |
||||
116 | |||||
117 | // With or without the database name, the fullname looks like this. |
||||
118 | $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); |
||||
119 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
120 | |||||
121 | // First - no way do we touch SMF tables. |
||||
122 | if (in_array(strtolower($table_name), $reservedTables)) |
||||
123 | return false; |
||||
124 | |||||
125 | // Log that we'll want to remove this on uninstall. |
||||
126 | $db_package_log[] = array('remove_table', $table_name); |
||||
127 | |||||
128 | // Slightly easier on MySQL than the others... |
||||
129 | $tables = $smcFunc['db_list_tables'](); |
||||
130 | if (in_array($full_table_name, $tables)) |
||||
131 | { |
||||
132 | // This is a sad day... drop the table? If not, return false (error) by default. |
||||
133 | if ($if_exists == 'overwrite') |
||||
134 | $smcFunc['db_drop_table']($table_name); |
||||
135 | elseif ($if_exists == 'update') |
||||
136 | { |
||||
137 | $smcFunc['db_transaction']('begin'); |
||||
138 | $db_trans = true; |
||||
0 ignored issues
–
show
Unused Code
introduced
by
![]() |
|||||
139 | $smcFunc['db_drop_table']($table_name . '_old'); |
||||
140 | $smcFunc['db_query']('', ' |
||||
141 | RENAME TABLE ' . $table_name . ' TO ' . $table_name . '_old', |
||||
142 | array( |
||||
143 | 'security_override' => true, |
||||
144 | ) |
||||
145 | ); |
||||
146 | $old_table_exists = true; |
||||
147 | } |
||||
148 | else |
||||
149 | return $if_exists == 'ignore'; |
||||
150 | } |
||||
151 | |||||
152 | // Righty - let's do the damn thing! |
||||
153 | $table_query = 'CREATE TABLE ' . $table_name . "\n" . '('; |
||||
154 | foreach ($columns as $column) |
||||
155 | $table_query .= "\n\t" . smf_db_create_query_column($column) . ','; |
||||
156 | |||||
157 | // Loop through the indexes next... |
||||
158 | foreach ($indexes as $index) |
||||
159 | { |
||||
160 | $columns = implode(',', $index['columns']); |
||||
161 | |||||
162 | // Is it the primary? |
||||
163 | if (isset($index['type']) && $index['type'] == 'primary') |
||||
164 | $table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),'; |
||||
165 | else |
||||
166 | { |
||||
167 | if (empty($index['name'])) |
||||
168 | $index['name'] = implode('_', $index['columns']); |
||||
169 | $table_query .= "\n\t" . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : 'KEY') . ' ' . $index['name'] . ' (' . $columns . '),'; |
||||
170 | } |
||||
171 | } |
||||
172 | |||||
173 | // No trailing commas! |
||||
174 | if (substr($table_query, -1) == ',') |
||||
175 | $table_query = substr($table_query, 0, -1); |
||||
176 | |||||
177 | // Which engine do we want here? |
||||
178 | if (empty($engines)) |
||||
179 | { |
||||
180 | // Figure out which engines we have |
||||
181 | $get_engines = $smcFunc['db_query']('', 'SHOW ENGINES', array()); |
||||
182 | |||||
183 | while ($row = $smcFunc['db_fetch_assoc']($get_engines)) |
||||
184 | { |
||||
185 | if ($row['Support'] == 'YES' || $row['Support'] == 'DEFAULT') |
||||
186 | $engines[] = $row['Engine']; |
||||
187 | } |
||||
188 | |||||
189 | $smcFunc['db_free_result']($get_engines); |
||||
190 | } |
||||
191 | |||||
192 | // If we don't have this engine, or didn't specify one, default to InnoDB or MyISAM |
||||
193 | // depending on which one is available |
||||
194 | if (!isset($parameters['engine']) || !in_array($parameters['engine'], $engines)) |
||||
195 | { |
||||
196 | $parameters['engine'] = in_array('InnoDB', $engines) ? 'InnoDB' : 'MyISAM'; |
||||
197 | } |
||||
198 | |||||
199 | $table_query .= ') ENGINE=' . $parameters['engine']; |
||||
200 | if (!empty($db_character_set) && $db_character_set == 'utf8') |
||||
201 | $table_query .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci'; |
||||
202 | |||||
203 | // Create the table! |
||||
204 | $smcFunc['db_query']('', $table_query, |
||||
205 | array( |
||||
206 | 'security_override' => true, |
||||
207 | ) |
||||
208 | ); |
||||
209 | |||||
210 | // Fill the old data |
||||
211 | if ($old_table_exists) |
||||
212 | { |
||||
213 | $same_col = array(); |
||||
214 | |||||
215 | $request = $smcFunc['db_query']('', ' |
||||
216 | SELECT count(*), column_name |
||||
217 | FROM information_schema.columns |
||||
218 | WHERE table_name in ({string:table1},{string:table2}) AND table_schema = {string:schema} |
||||
219 | GROUP BY column_name |
||||
220 | HAVING count(*) > 1', |
||||
221 | array( |
||||
222 | 'table1' => $table_name, |
||||
223 | 'table2' => $table_name . '_old', |
||||
224 | 'schema' => $db_name, |
||||
225 | ) |
||||
226 | ); |
||||
227 | |||||
228 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
229 | { |
||||
230 | $same_col[] = $row['column_name']; |
||||
231 | } |
||||
232 | |||||
233 | $smcFunc['db_query']('', ' |
||||
234 | INSERT INTO ' . $table_name . '(' |
||||
235 | . implode($same_col, ',') . |
||||
0 ignored issues
–
show
The call to
implode() has too many arguments starting with ',' .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
236 | ') |
||||
237 | SELECT ' . implode($same_col, ',') . ' |
||||
238 | FROM ' . $table_name . '_old', |
||||
239 | array() |
||||
240 | ); |
||||
241 | |||||
242 | $smcFunc['db_drop_table']($table_name . '_old'); |
||||
243 | } |
||||
244 | |||||
245 | return true; |
||||
246 | } |
||||
247 | |||||
248 | /** |
||||
249 | * Drop a table. |
||||
250 | * |
||||
251 | * @param string $table_name The name of the table to drop |
||||
252 | * @param array $parameters Not used at the moment |
||||
253 | * @param string $error |
||||
254 | * @return boolean Whether or not the operation was successful |
||||
255 | */ |
||||
256 | function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal') |
||||
257 | { |
||||
258 | global $reservedTables, $smcFunc, $db_prefix; |
||||
259 | |||||
260 | // After stripping away the database name, this is what's left. |
||||
261 | $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; |
||||
262 | |||||
263 | // Get some aliases. |
||||
264 | $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); |
||||
265 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
266 | |||||
267 | // God no - dropping one of these = bad. |
||||
268 | if (in_array(strtolower($table_name), $reservedTables)) |
||||
269 | return false; |
||||
270 | |||||
271 | // Does it exist? |
||||
272 | if (in_array($full_table_name, $smcFunc['db_list_tables']())) |
||||
273 | { |
||||
274 | $query = 'DROP TABLE ' . $table_name; |
||||
275 | $smcFunc['db_query']('', |
||||
276 | $query, |
||||
277 | array( |
||||
278 | 'security_override' => true, |
||||
279 | ) |
||||
280 | ); |
||||
281 | |||||
282 | return true; |
||||
283 | } |
||||
284 | |||||
285 | // Otherwise do 'nout. |
||||
286 | return false; |
||||
287 | } |
||||
288 | |||||
289 | /** |
||||
290 | * This function adds a column. |
||||
291 | * |
||||
292 | * @param string $table_name The name of the table to add the column to |
||||
293 | * @param array $column_info An array of column info ({@see smf_db_create_table}) |
||||
294 | * @param array $parameters Not used? |
||||
295 | * @param string $if_exists What to do if the column exists. If 'update', column is updated. |
||||
296 | * @param string $error |
||||
297 | * @return boolean Whether or not the operation was successful |
||||
298 | */ |
||||
299 | function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') |
||||
300 | { |
||||
301 | global $smcFunc, $db_package_log, $db_prefix; |
||||
302 | |||||
303 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
304 | |||||
305 | // Log that we will want to uninstall this! |
||||
306 | $db_package_log[] = array('remove_column', $table_name, $column_info['name']); |
||||
307 | |||||
308 | // Does it exist - if so don't add it again! |
||||
309 | $columns = $smcFunc['db_list_columns']($table_name, false); |
||||
310 | foreach ($columns as $column) |
||||
311 | if ($column == $column_info['name']) |
||||
312 | { |
||||
313 | // If we're going to overwrite then use change column. |
||||
314 | if ($if_exists == 'update') |
||||
315 | return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info); |
||||
316 | else |
||||
317 | return false; |
||||
318 | } |
||||
319 | |||||
320 | // Get the specifics... |
||||
321 | $column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null; |
||||
322 | |||||
323 | // Now add the thing! |
||||
324 | $query = ' |
||||
325 | ALTER TABLE ' . $table_name . ' |
||||
326 | ADD ' . smf_db_create_query_column($column_info) . (empty($column_info['auto']) ? '' : ' primary key' |
||||
327 | ); |
||||
328 | $smcFunc['db_query']('', $query, |
||||
329 | array( |
||||
330 | 'security_override' => true, |
||||
331 | ) |
||||
332 | ); |
||||
333 | |||||
334 | return true; |
||||
335 | } |
||||
336 | |||||
337 | /** |
||||
338 | * Removes a column. |
||||
339 | * |
||||
340 | * @param string $table_name The name of the table to drop the column from |
||||
341 | * @param string $column_name The name of the column to drop |
||||
342 | * @param array $parameters Not used? |
||||
343 | * @param string $error |
||||
344 | * @return boolean Whether or not the operation was successful |
||||
345 | */ |
||||
346 | function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal') |
||||
347 | { |
||||
348 | global $smcFunc, $db_prefix; |
||||
349 | |||||
350 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
351 | |||||
352 | // Does it exist? |
||||
353 | $columns = $smcFunc['db_list_columns']($table_name, true); |
||||
354 | foreach ($columns as $column) |
||||
355 | if ($column['name'] == $column_name) |
||||
356 | { |
||||
357 | $smcFunc['db_query']('', ' |
||||
358 | ALTER TABLE ' . $table_name . ' |
||||
359 | DROP COLUMN ' . $column_name, |
||||
360 | array( |
||||
361 | 'security_override' => true, |
||||
362 | ) |
||||
363 | ); |
||||
364 | |||||
365 | return true; |
||||
366 | } |
||||
367 | |||||
368 | // If here we didn't have to work - joy! |
||||
369 | return false; |
||||
370 | } |
||||
371 | |||||
372 | /** |
||||
373 | * Change a column. |
||||
374 | * |
||||
375 | * @param string $table_name The name of the table this column is in |
||||
376 | * @param string $old_column The name of the column we want to change |
||||
377 | * @param array $column_info An array of info about the "new" column definition (see {@link smf_db_create_table()}) |
||||
378 | * @return bool |
||||
379 | */ |
||||
380 | function smf_db_change_column($table_name, $old_column, $column_info) |
||||
381 | { |
||||
382 | global $smcFunc, $db_prefix; |
||||
383 | |||||
384 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
385 | |||||
386 | // Check it does exist! |
||||
387 | $columns = $smcFunc['db_list_columns']($table_name, true); |
||||
388 | $old_info = null; |
||||
389 | foreach ($columns as $column) |
||||
390 | if ($column['name'] == $old_column) |
||||
391 | $old_info = $column; |
||||
392 | |||||
393 | // Nothing? |
||||
394 | if ($old_info == null) |
||||
395 | return false; |
||||
396 | |||||
397 | // Get the right bits. |
||||
398 | if (!isset($column_info['name'])) |
||||
399 | $column_info['name'] = $old_column; |
||||
400 | if (!isset($column_info['default'])) |
||||
401 | $column_info['default'] = $old_info['default']; |
||||
402 | if (!isset($column_info['null'])) |
||||
403 | $column_info['null'] = $old_info['null']; |
||||
404 | if (!isset($column_info['auto'])) |
||||
405 | $column_info['auto'] = $old_info['auto']; |
||||
406 | if (!isset($column_info['type'])) |
||||
407 | $column_info['type'] = $old_info['type']; |
||||
408 | if (!isset($column_info['size']) || !is_numeric($column_info['size'])) |
||||
409 | $column_info['size'] = $old_info['size']; |
||||
410 | if (!isset($column_info['unsigned']) || !in_array($column_info['type'], array('int', 'tinyint', 'smallint', 'mediumint', 'bigint'))) |
||||
411 | $column_info['unsigned'] = ''; |
||||
412 | |||||
413 | list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']); |
||||
414 | |||||
415 | // Allow for unsigned integers (mysql only) |
||||
416 | $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column_info['unsigned']) ? 'unsigned ' : ''; |
||||
417 | |||||
418 | if ($size !== null) |
||||
419 | $type = $type . '(' . $size . ')'; |
||||
420 | |||||
421 | $smcFunc['db_query']('', ' |
||||
422 | ALTER TABLE ' . $table_name . ' |
||||
423 | CHANGE COLUMN `' . $old_column . '` `' . $column_info['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (empty($column_info['null']) ? 'NOT NULL' : '') . ' ' . |
||||
424 | (!isset($column_info['default']) ? '' : 'default \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'') . ' ' . |
||||
425 | (empty($column_info['auto']) ? '' : 'auto_increment') . ' ', |
||||
426 | array( |
||||
427 | 'security_override' => true, |
||||
428 | ) |
||||
429 | ); |
||||
430 | } |
||||
431 | |||||
432 | /** |
||||
433 | * Add an index. |
||||
434 | * |
||||
435 | * @param string $table_name The name of the table to add the index to |
||||
436 | * @param array $index_info An array of index info (see {@link smf_db_create_table()}) |
||||
437 | * @param array $parameters Not used? |
||||
438 | * @param string $if_exists What to do if the index exists. If 'update', the definition will be updated. |
||||
439 | * @param string $error |
||||
440 | * @return boolean Whether or not the operation was successful |
||||
441 | */ |
||||
442 | function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') |
||||
443 | { |
||||
444 | global $smcFunc, $db_package_log, $db_prefix; |
||||
445 | |||||
446 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
447 | |||||
448 | // No columns = no index. |
||||
449 | if (empty($index_info['columns'])) |
||||
450 | return false; |
||||
451 | $columns = implode(',', $index_info['columns']); |
||||
452 | |||||
453 | // No name - make it up! |
||||
454 | if (empty($index_info['name'])) |
||||
455 | { |
||||
456 | // No need for primary. |
||||
457 | if (isset($index_info['type']) && $index_info['type'] == 'primary') |
||||
458 | $index_info['name'] = ''; |
||||
459 | else |
||||
460 | $index_info['name'] = implode('_', $index_info['columns']); |
||||
461 | } |
||||
462 | |||||
463 | // Log that we are going to want to remove this! |
||||
464 | $db_package_log[] = array('remove_index', $table_name, $index_info['name']); |
||||
465 | |||||
466 | // Let's get all our indexes. |
||||
467 | $indexes = $smcFunc['db_list_indexes']($table_name, true); |
||||
468 | // Do we already have it? |
||||
469 | foreach ($indexes as $index) |
||||
470 | { |
||||
471 | if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary')) |
||||
472 | { |
||||
473 | // If we want to overwrite simply remove the current one then continue. |
||||
474 | if ($if_exists != 'update' || $index['type'] == 'primary') |
||||
475 | return false; |
||||
476 | else |
||||
477 | $smcFunc['db_remove_index']($table_name, $index_info['name']); |
||||
478 | } |
||||
479 | } |
||||
480 | |||||
481 | // If we're here we know we don't have the index - so just add it. |
||||
482 | if (!empty($index_info['type']) && $index_info['type'] == 'primary') |
||||
483 | { |
||||
484 | $smcFunc['db_query']('', ' |
||||
485 | ALTER TABLE ' . $table_name . ' |
||||
486 | ADD PRIMARY KEY (' . $columns . ')', |
||||
487 | array( |
||||
488 | 'security_override' => true, |
||||
489 | ) |
||||
490 | ); |
||||
491 | } |
||||
492 | else |
||||
493 | { |
||||
494 | $smcFunc['db_query']('', ' |
||||
495 | ALTER TABLE ' . $table_name . ' |
||||
496 | ADD ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : 'INDEX') . ' ' . $index_info['name'] . ' (' . $columns . ')', |
||||
497 | array( |
||||
498 | 'security_override' => true, |
||||
499 | ) |
||||
500 | ); |
||||
501 | } |
||||
502 | } |
||||
503 | |||||
504 | /** |
||||
505 | * Remove an index. |
||||
506 | * |
||||
507 | * @param string $table_name The name of the table to remove the index from |
||||
508 | * @param string $index_name The name of the index to remove |
||||
509 | * @param array $parameters Not used? |
||||
510 | * @param string $error |
||||
511 | * @return boolean Whether or not the operation was successful |
||||
512 | */ |
||||
513 | function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal') |
||||
514 | { |
||||
515 | global $smcFunc, $db_prefix; |
||||
516 | |||||
517 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
518 | |||||
519 | // Better exist! |
||||
520 | $indexes = $smcFunc['db_list_indexes']($table_name, true); |
||||
521 | |||||
522 | foreach ($indexes as $index) |
||||
523 | { |
||||
524 | // If the name is primary we want the primary key! |
||||
525 | if ($index['type'] == 'primary' && $index_name == 'primary') |
||||
526 | { |
||||
527 | // Dropping primary key? |
||||
528 | $smcFunc['db_query']('', ' |
||||
529 | ALTER TABLE ' . $table_name . ' |
||||
530 | DROP PRIMARY KEY', |
||||
531 | array( |
||||
532 | 'security_override' => true, |
||||
533 | ) |
||||
534 | ); |
||||
535 | |||||
536 | return true; |
||||
537 | } |
||||
538 | if ($index['name'] == $index_name) |
||||
539 | { |
||||
540 | // Drop the bugger... |
||||
541 | $smcFunc['db_query']('', ' |
||||
542 | ALTER TABLE ' . $table_name . ' |
||||
543 | DROP INDEX ' . $index_name, |
||||
544 | array( |
||||
545 | 'security_override' => true, |
||||
546 | ) |
||||
547 | ); |
||||
548 | |||||
549 | return true; |
||||
550 | } |
||||
551 | } |
||||
552 | |||||
553 | // Not to be found ;( |
||||
554 | return false; |
||||
555 | } |
||||
556 | |||||
557 | /** |
||||
558 | * Get the schema formatted name for a type. |
||||
559 | * |
||||
560 | * @param string $type_name The data type (int, varchar, smallint, etc.) |
||||
561 | * @param int $type_size The size (8, 255, etc.) |
||||
562 | * @param boolean $reverse |
||||
563 | * @return array An array containing the appropriate type and size for this DB type |
||||
564 | */ |
||||
565 | function smf_db_calculate_type($type_name, $type_size = null, $reverse = false) |
||||
566 | { |
||||
567 | // MySQL is actually the generic baseline. |
||||
568 | |||||
569 | $type_name = strtolower($type_name); |
||||
570 | // Generic => Specific. |
||||
571 | if (!$reverse) |
||||
572 | { |
||||
573 | $types = array( |
||||
574 | 'inet' => 'varbinary', |
||||
575 | ); |
||||
576 | } |
||||
577 | else |
||||
578 | { |
||||
579 | $types = array( |
||||
580 | 'varbinary' => 'inet', |
||||
581 | ); |
||||
582 | } |
||||
583 | |||||
584 | // Got it? Change it! |
||||
585 | if (isset($types[$type_name])) |
||||
586 | { |
||||
587 | if ($type_name == 'inet' && !$reverse) |
||||
588 | { |
||||
589 | $type_size = 16; |
||||
590 | $type_name = 'varbinary'; |
||||
591 | } |
||||
592 | elseif ($type_name == 'varbinary' && $reverse && $type_size == 16) |
||||
593 | { |
||||
594 | $type_name = 'inet'; |
||||
595 | $type_size = null; |
||||
596 | } |
||||
597 | elseif ($type_name == 'varbinary') |
||||
598 | $type_name = 'varbinary'; |
||||
599 | else |
||||
600 | $type_name = $types[$type_name]; |
||||
601 | } |
||||
602 | elseif ($type_name == 'boolean') |
||||
603 | $type_size = null; |
||||
604 | |||||
605 | return array($type_name, $type_size); |
||||
606 | } |
||||
607 | |||||
608 | /** |
||||
609 | * Get table structure. |
||||
610 | * |
||||
611 | * @param string $table_name The name of the table |
||||
612 | * @return array An array of table structure - the name, the column info from {@link smf_db_list_columns()} and the index info from {@link smf_db_list_indexes()} |
||||
613 | */ |
||||
614 | function smf_db_table_structure($table_name) |
||||
615 | { |
||||
616 | global $smcFunc, $db_prefix; |
||||
617 | |||||
618 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
619 | |||||
620 | // Find the table engine and add that to the info as well |
||||
621 | $table_status = $smcFunc['db_query']('', ' |
||||
622 | SHOW TABLE STATUS |
||||
623 | LIKE {string:table}', |
||||
624 | array( |
||||
625 | 'table' => strtr($table_name, array('_' => '\\_', '%' => '\\%')) |
||||
626 | ) |
||||
627 | ); |
||||
628 | |||||
629 | // Only one row, so no need for a loop... |
||||
630 | $row = $smcFunc['db_fetch_assoc']($table_status); |
||||
631 | |||||
632 | $smcFunc['db_free_result']($table_status); |
||||
633 | |||||
634 | return array( |
||||
635 | 'name' => $table_name, |
||||
636 | 'columns' => $smcFunc['db_list_columns']($table_name, true), |
||||
637 | 'indexes' => $smcFunc['db_list_indexes']($table_name, true), |
||||
638 | 'engine' => $row['Engine'], |
||||
639 | ); |
||||
640 | } |
||||
641 | |||||
642 | /** |
||||
643 | * Return column information for a table. |
||||
644 | * |
||||
645 | * @param string $table_name The name of the table to get column info for |
||||
646 | * @param bool $detail Whether or not to return detailed info. If true, returns the column info. If false, just returns the column names. |
||||
647 | * @param array $parameters Not used? |
||||
648 | * @return array An array of column names or detailed column info, depending on $detail |
||||
649 | */ |
||||
650 | function smf_db_list_columns($table_name, $detail = false, $parameters = array()) |
||||
651 | { |
||||
652 | global $smcFunc, $db_prefix, $db_name; |
||||
653 | |||||
654 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
655 | |||||
656 | $result = $smcFunc['db_query']('', ' |
||||
657 | SELECT column_name "Field", COLUMN_TYPE "Type", is_nullable "Null", COLUMN_KEY "Key" , column_default "Default", extra "Extra" |
||||
658 | FROM information_schema.columns |
||||
659 | WHERE table_name = {string:table_name} |
||||
660 | AND table_schema = {string:db_name} |
||||
661 | ORDER BY ordinal_position', |
||||
662 | array( |
||||
663 | 'table_name' => $table_name, |
||||
664 | 'db_name' => $db_name, |
||||
665 | ) |
||||
666 | ); |
||||
667 | $columns = array(); |
||||
668 | while ($row = $smcFunc['db_fetch_assoc']($result)) |
||||
669 | { |
||||
670 | if (!$detail) |
||||
671 | { |
||||
672 | $columns[] = $row['Field']; |
||||
673 | } |
||||
674 | else |
||||
675 | { |
||||
676 | // Is there an auto_increment? |
||||
677 | $auto = strpos($row['Extra'], 'auto_increment') !== false ? true : false; |
||||
678 | |||||
679 | // Can we split out the size? |
||||
680 | if (preg_match('~(.+?)\s*\((\d+)\)(?:(?:\s*)?(unsigned))?~i', $row['Type'], $matches) === 1) |
||||
681 | { |
||||
682 | $type = $matches[1]; |
||||
683 | $size = $matches[2]; |
||||
684 | if (!empty($matches[3]) && $matches[3] == 'unsigned') |
||||
685 | $unsigned = true; |
||||
686 | } |
||||
687 | else |
||||
688 | { |
||||
689 | $type = $row['Type']; |
||||
690 | $size = null; |
||||
691 | } |
||||
692 | |||||
693 | $columns[$row['Field']] = array( |
||||
694 | 'name' => $row['Field'], |
||||
695 | 'null' => $row['Null'] != 'YES' ? false : true, |
||||
696 | 'default' => isset($row['Default']) ? $row['Default'] : null, |
||||
697 | 'type' => $type, |
||||
698 | 'size' => $size, |
||||
699 | 'auto' => $auto, |
||||
700 | ); |
||||
701 | |||||
702 | if (isset($unsigned)) |
||||
703 | { |
||||
704 | $columns[$row['Field']]['unsigned'] = $unsigned; |
||||
705 | unset($unsigned); |
||||
706 | } |
||||
707 | } |
||||
708 | } |
||||
709 | $smcFunc['db_free_result']($result); |
||||
710 | |||||
711 | return $columns; |
||||
712 | } |
||||
713 | |||||
714 | /** |
||||
715 | * Get index information. |
||||
716 | * |
||||
717 | * @param string $table_name The name of the table to get indexes for |
||||
718 | * @param bool $detail Whether or not to return detailed info. |
||||
719 | * @param array $parameters Not used? |
||||
720 | * @return array An array of index names or a detailed array of index info, depending on $detail |
||||
721 | */ |
||||
722 | function smf_db_list_indexes($table_name, $detail = false, $parameters = array()) |
||||
723 | { |
||||
724 | global $smcFunc, $db_prefix; |
||||
725 | |||||
726 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||||
727 | |||||
728 | $result = $smcFunc['db_query']('', ' |
||||
729 | SHOW KEYS |
||||
730 | FROM {raw:table_name}', |
||||
731 | array( |
||||
732 | 'table_name' => substr($table_name, 0, 1) == '`' ? $table_name : '`' . $table_name . '`', |
||||
733 | ) |
||||
734 | ); |
||||
735 | $indexes = array(); |
||||
736 | while ($row = $smcFunc['db_fetch_assoc']($result)) |
||||
737 | { |
||||
738 | if (!$detail) |
||||
739 | $indexes[] = $row['Key_name']; |
||||
740 | else |
||||
741 | { |
||||
742 | // What is the type? |
||||
743 | if ($row['Key_name'] == 'PRIMARY') |
||||
744 | $type = 'primary'; |
||||
745 | elseif (empty($row['Non_unique'])) |
||||
746 | $type = 'unique'; |
||||
747 | elseif (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT') |
||||
748 | $type = 'fulltext'; |
||||
749 | else |
||||
750 | $type = 'index'; |
||||
751 | |||||
752 | // This is the first column we've seen? |
||||
753 | if (empty($indexes[$row['Key_name']])) |
||||
754 | { |
||||
755 | $indexes[$row['Key_name']] = array( |
||||
756 | 'name' => $row['Key_name'], |
||||
757 | 'type' => $type, |
||||
758 | 'columns' => array(), |
||||
759 | ); |
||||
760 | } |
||||
761 | |||||
762 | // Is it a partial index? |
||||
763 | if (!empty($row['Sub_part'])) |
||||
764 | $indexes[$row['Key_name']]['columns'][] = $row['Column_name'] . '(' . $row['Sub_part'] . ')'; |
||||
765 | else |
||||
766 | $indexes[$row['Key_name']]['columns'][] = $row['Column_name']; |
||||
767 | } |
||||
768 | } |
||||
769 | $smcFunc['db_free_result']($result); |
||||
770 | |||||
771 | return $indexes; |
||||
772 | } |
||||
773 | |||||
774 | /** |
||||
775 | * Creates a query for a column |
||||
776 | * |
||||
777 | * @param array $column An array of column info |
||||
778 | * @return string The column definition |
||||
779 | */ |
||||
780 | function smf_db_create_query_column($column) |
||||
781 | { |
||||
782 | global $smcFunc; |
||||
783 | |||||
784 | // Auto increment is easy here! |
||||
785 | if (!empty($column['auto'])) |
||||
786 | { |
||||
787 | $default = 'auto_increment'; |
||||
788 | } |
||||
789 | elseif (isset($column['default']) && $column['default'] !== null) |
||||
790 | $default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\''; |
||||
791 | else |
||||
792 | $default = ''; |
||||
793 | |||||
794 | // Sort out the size... and stuff... |
||||
795 | $column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null; |
||||
796 | list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']); |
||||
797 | |||||
798 | // Allow unsigned integers (mysql only) |
||||
799 | $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column['unsigned']) ? 'unsigned ' : ''; |
||||
800 | |||||
801 | if ($size !== null) |
||||
802 | $type = $type . '(' . $size . ')'; |
||||
803 | |||||
804 | // Now just put it together! |
||||
805 | return '`' . $column['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default; |
||||
806 | } |
||||
807 | |||||
808 | ?> |