DbExtra-mysql.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <?php
  2. /**
  3. * Simple Machines Forum (SMF)
  4. *
  5. * @package SMF
  6. * @author Simple Machines http://www.simplemachines.org
  7. * @copyright 2011 Simple Machines
  8. * @license http://www.simplemachines.org/about/smf/license.php BSD
  9. *
  10. * @version 2.0
  11. */
  12. if (!defined('SMF'))
  13. die('Hacking attempt...');
  14. /* This file contains rarely used extended database functionality.
  15. void db_extra_init()
  16. - add this file's functions to the $smcFunc array.
  17. resource smf_db_backup_table($table, $backup_table)
  18. - backup $table to $backup_table.
  19. - returns the request handle to the table creation query
  20. string function smf_db_get_version()
  21. - get the version number.
  22. string db_insert_sql(string table_name)
  23. - gets all the necessary INSERTs for the table named table_name.
  24. - goes in 250 row segments.
  25. - returns the query to insert the data back in.
  26. - returns an empty string if the table was empty.
  27. array smf_db_list_tables($db = false, $filter = false)
  28. - lists all tables in the database
  29. - could be filtered according to $filter
  30. - returns an array of table names. (strings)
  31. float smf_db_optimize_table($table)
  32. - optimize a table
  33. - $table - the table to be optimized
  34. - returns how much it was gained
  35. string db_table_sql(string table_name)
  36. - dumps the CREATE for the specified table. (by table_name.)
  37. - returns the CREATE statement.
  38. */
  39. // Add the file functions to the $smcFunc array.
  40. function db_extra_init()
  41. {
  42. global $smcFunc;
  43. if (!isset($smcFunc['db_backup_table']) || $smcFunc['db_backup_table'] != 'smf_db_backup_table')
  44. $smcFunc += array(
  45. 'db_backup_table' => 'smf_db_backup_table',
  46. 'db_optimize_table' => 'smf_db_optimize_table',
  47. 'db_insert_sql' => 'smf_db_insert_sql',
  48. 'db_table_sql' => 'smf_db_table_sql',
  49. 'db_list_tables' => 'smf_db_list_tables',
  50. 'db_get_version' => 'smf_db_get_version',
  51. );
  52. }
  53. // Backup $table to $backup_table.
  54. function smf_db_backup_table($table, $backup_table)
  55. {
  56. global $smcFunc, $db_prefix;
  57. $table = str_replace('{db_prefix}', $db_prefix, $table);
  58. // First, get rid of the old table.
  59. $smcFunc['db_query']('', '
  60. DROP TABLE IF EXISTS {raw:backup_table}',
  61. array(
  62. 'backup_table' => $backup_table,
  63. )
  64. );
  65. // Can we do this the quick way?
  66. $result = $smcFunc['db_query']('', '
  67. CREATE TABLE {raw:backup_table} LIKE {raw:table}',
  68. array(
  69. 'backup_table' => $backup_table,
  70. 'table' => $table
  71. ));
  72. // If this failed, we go old school.
  73. if ($result)
  74. {
  75. $request = $smcFunc['db_query']('', '
  76. INSERT INTO {raw:backup_table}
  77. SELECT *
  78. FROM {raw:table}',
  79. array(
  80. 'backup_table' => $backup_table,
  81. 'table' => $table
  82. ));
  83. // Old school or no school?
  84. if ($request)
  85. return $request;
  86. }
  87. // At this point, the quick method failed.
  88. $result = $smcFunc['db_query']('', '
  89. SHOW CREATE TABLE {raw:table}',
  90. array(
  91. 'table' => $table,
  92. )
  93. );
  94. list (, $create) = $smcFunc['db_fetch_row']($result);
  95. $smcFunc['db_free_result']($result);
  96. $create = preg_split('/[\n\r]/', $create);
  97. $auto_inc = '';
  98. // Default engine type.
  99. $engine = 'MyISAM';
  100. $charset = '';
  101. $collate = '';
  102. foreach ($create as $k => $l)
  103. {
  104. // Get the name of the auto_increment column.
  105. if (strpos($l, 'auto_increment'))
  106. $auto_inc = trim($l);
  107. // For the engine type, see if we can work out what it is.
  108. if (strpos($l, 'ENGINE') !== false || strpos($l, 'TYPE') !== false)
  109. {
  110. // Extract the engine type.
  111. preg_match('~(ENGINE|TYPE)=(\w+)(\sDEFAULT)?(\sCHARSET=(\w+))?(\sCOLLATE=(\w+))?~', $l, $match);
  112. if (!empty($match[1]))
  113. $engine = $match[1];
  114. if (!empty($match[2]))
  115. $engine = $match[2];
  116. if (!empty($match[5]))
  117. $charset = $match[5];
  118. if (!empty($match[7]))
  119. $collate = $match[7];
  120. }
  121. // Skip everything but keys...
  122. if (strpos($l, 'KEY') === false)
  123. unset($create[$k]);
  124. }
  125. if (!empty($create))
  126. $create = '(
  127. ' . implode('
  128. ', $create) . ')';
  129. else
  130. $create = '';
  131. $request = $smcFunc['db_query']('', '
  132. CREATE TABLE {raw:backup_table} {raw:create}
  133. ENGINE={raw:engine}' . (empty($charset) ? '' : ' CHARACTER SET {raw:charset}' . (empty($collate) ? '' : ' COLLATE {raw:collate}')) . '
  134. SELECT *
  135. FROM {raw:table}',
  136. array(
  137. 'backup_table' => $backup_table,
  138. 'table' => $table,
  139. 'create' => $create,
  140. 'engine' => $engine,
  141. 'charset' => empty($charset) ? '' : $charset,
  142. 'collate' => empty($collate) ? '' : $collate,
  143. )
  144. );
  145. if ($auto_inc != '')
  146. {
  147. if (preg_match('~\`(.+?)\`\s~', $auto_inc, $match) != 0 && substr($auto_inc, -1, 1) == ',')
  148. $auto_inc = substr($auto_inc, 0, -1);
  149. $smcFunc['db_query']('', '
  150. ALTER TABLE {raw:backup_table}
  151. CHANGE COLUMN {raw:column_detail} {raw:auto_inc}',
  152. array(
  153. 'backup_table' => $backup_table,
  154. 'column_detail' => $match[1],
  155. 'auto_inc' => $auto_inc,
  156. )
  157. );
  158. }
  159. return $request;
  160. }
  161. // Optimize a table - return data freed!
  162. function smf_db_optimize_table($table)
  163. {
  164. global $smcFunc, $db_name, $db_prefix;
  165. $table = str_replace('{db_prefix}', $db_prefix, $table);
  166. // Get how much overhead there is.
  167. $request = $smcFunc['db_query']('', '
  168. SHOW TABLE STATUS LIKE {string:table_name}',
  169. array(
  170. 'table_name' => str_replace('_', '\_', $table),
  171. )
  172. );
  173. $row = $smcFunc['db_fetch_assoc']($request);
  174. $smcFunc['db_free_result']($request);
  175. $data_before = isset($row['Data_free']) ? $row['Data_free'] : 0;
  176. $request = $smcFunc['db_query']('', '
  177. OPTIMIZE TABLE `{raw:table}`',
  178. array(
  179. 'table' => $table,
  180. )
  181. );
  182. if (!$request)
  183. return -1;
  184. // How much left?
  185. $request = $smcFunc['db_query']('', '
  186. SHOW TABLE STATUS LIKE {string:table}',
  187. array(
  188. 'table' => str_replace('_', '\_', $table),
  189. )
  190. );
  191. $row = $smcFunc['db_fetch_assoc']($request);
  192. $smcFunc['db_free_result']($request);
  193. $total_change = isset($row['Data_free']) && $data_before > $row['Data_free'] ? $data_before / 1024 : 0;
  194. return $total_change;
  195. }
  196. // List all the tables in the database.
  197. function smf_db_list_tables($db = false, $filter = false)
  198. {
  199. global $db_name, $smcFunc;
  200. $db = $db == false ? $db_name : $db;
  201. $db = trim($db);
  202. $filter = $filter == false ? '' : ' LIKE \'' . $filter . '\'';
  203. $request = $smcFunc['db_query']('', '
  204. SHOW TABLES
  205. FROM `{raw:db}`
  206. {raw:filter}',
  207. array(
  208. 'db' => $db[0] == '`' ? strtr($db, array('`' => '')) : $db,
  209. 'filter' => $filter,
  210. )
  211. );
  212. $tables = array();
  213. while ($row = $smcFunc['db_fetch_row']($request))
  214. $tables[] = $row[0];
  215. $smcFunc['db_free_result']($request);
  216. return $tables;
  217. }
  218. // Get the content (INSERTs) for a table.
  219. function smf_db_insert_sql($tableName)
  220. {
  221. global $smcFunc, $db_prefix;
  222. $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
  223. // This will be handy...
  224. $crlf = "\r\n";
  225. // Get everything from the table.
  226. $result = $smcFunc['db_query']('', '
  227. SELECT /*!40001 SQL_NO_CACHE */ *
  228. FROM `{raw:table}`',
  229. array(
  230. 'table' => $tableName,
  231. )
  232. );
  233. // The number of rows, just for record keeping and breaking INSERTs up.
  234. $num_rows = $smcFunc['db_num_rows']($result);
  235. $current_row = 0;
  236. if ($num_rows == 0)
  237. return '';
  238. $fields = array_keys($smcFunc['db_fetch_assoc']($result));
  239. $smcFunc['db_data_seek']($result, 0);
  240. // Start it off with the basic INSERT INTO.
  241. $data = 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES ';
  242. // Loop through each row.
  243. while ($row = $smcFunc['db_fetch_row']($result))
  244. {
  245. $current_row++;
  246. // Get the fields in this row...
  247. $field_list = array();
  248. for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++)
  249. {
  250. // Try to figure out the type of each field. (NULL, number, or 'string'.)
  251. if (!isset($row[$j]))
  252. $field_list[] = 'NULL';
  253. elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j])
  254. $field_list[] = $row[$j];
  255. else
  256. $field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\'';
  257. }
  258. // 'Insert' the data.
  259. $data .= '(' . implode(', ', $field_list) . ')';
  260. // All done!
  261. if ($current_row == $num_rows)
  262. $data .= ';' . $crlf;
  263. // Start a new INSERT statement after every 250....
  264. elseif ($current_row > 249 && $current_row % 250 == 0)
  265. $data .= ';' . $crlf . 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES ';
  266. // Otherwise, go to the next line.
  267. else
  268. $data .= ',' . $crlf . "\t";
  269. }
  270. $smcFunc['db_free_result']($result);
  271. // Return an empty string if there were no rows.
  272. return $num_rows == 0 ? '' : $data;
  273. }
  274. // Get the schema (CREATE) for a table.
  275. function smf_db_table_sql($tableName)
  276. {
  277. global $smcFunc, $db_prefix;
  278. $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
  279. // This will be needed...
  280. $crlf = "\r\n";
  281. // Drop it if it exists.
  282. $schema_create = 'DROP TABLE IF EXISTS `' . $tableName . '`;' . $crlf . $crlf;
  283. // Start the create table...
  284. $schema_create .= 'CREATE TABLE `' . $tableName . '` (' . $crlf;
  285. // Find all the fields.
  286. $result = $smcFunc['db_query']('', '
  287. SHOW FIELDS
  288. FROM `{raw:table}`',
  289. array(
  290. 'table' => $tableName,
  291. )
  292. );
  293. while ($row = $smcFunc['db_fetch_assoc']($result))
  294. {
  295. // Make the CREATE for this column.
  296. $schema_create .= ' `' . $row['Field'] . '` ' . $row['Type'] . ($row['Null'] != 'YES' ? ' NOT NULL' : '');
  297. // Add a default...?
  298. if (!empty($row['Default']) || $row['Null'] !== 'YES')
  299. {
  300. // Make a special case of auto-timestamp.
  301. if ($row['Default'] == 'CURRENT_TIMESTAMP')
  302. $schema_create .= ' /*!40102 NOT NULL default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP */';
  303. // Text shouldn't have a default.
  304. elseif ($row['Default'] !== null)
  305. {
  306. // If this field is numeric the default needs no escaping.
  307. $type = strtolower($row['Type']);
  308. $isNumericColumn = strpos($type, 'int') !== false || strpos($type, 'bool') !== false || strpos($type, 'bit') !== false || strpos($type, 'float') !== false || strpos($type, 'double') !== false || strpos($type, 'decimal') !== false;
  309. $schema_create .= ' default ' . ($isNumericColumn ? $row['Default'] : '\'' . $smcFunc['db_escape_string']($row['Default']) . '\'');
  310. }
  311. }
  312. // And now any extra information. (such as auto_increment.)
  313. $schema_create .= ($row['Extra'] != '' ? ' ' . $row['Extra'] : '') . ',' . $crlf;
  314. }
  315. $smcFunc['db_free_result']($result);
  316. // Take off the last comma.
  317. $schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
  318. // Find the keys.
  319. $result = $smcFunc['db_query']('', '
  320. SHOW KEYS
  321. FROM `{raw:table}`',
  322. array(
  323. 'table' => $tableName,
  324. )
  325. );
  326. $indexes = array();
  327. while ($row = $smcFunc['db_fetch_assoc']($result))
  328. {
  329. // IS this a primary key, unique index, or regular index?
  330. $row['Key_name'] = $row['Key_name'] == 'PRIMARY' ? 'PRIMARY KEY' : (empty($row['Non_unique']) ? 'UNIQUE ' : ($row['Comment'] == 'FULLTEXT' || (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT') ? 'FULLTEXT ' : 'KEY ')) . '`' . $row['Key_name'] . '`';
  331. // Is this the first column in the index?
  332. if (empty($indexes[$row['Key_name']]))
  333. $indexes[$row['Key_name']] = array();
  334. // A sub part, like only indexing 15 characters of a varchar.
  335. if (!empty($row['Sub_part']))
  336. $indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`(' . $row['Sub_part'] . ')';
  337. else
  338. $indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`';
  339. }
  340. $smcFunc['db_free_result']($result);
  341. // Build the CREATEs for the keys.
  342. foreach ($indexes as $keyname => $columns)
  343. {
  344. // Ensure the columns are in proper order.
  345. ksort($columns);
  346. $schema_create .= ',' . $crlf . ' ' . $keyname . ' (' . implode($columns, ', ') . ')';
  347. }
  348. // Now just get the comment and type... (MyISAM, etc.)
  349. $result = $smcFunc['db_query']('', '
  350. SHOW TABLE STATUS
  351. LIKE {string:table}',
  352. array(
  353. 'table' => strtr($tableName, array('_' => '\\_', '%' => '\\%')),
  354. )
  355. );
  356. $row = $smcFunc['db_fetch_assoc']($result);
  357. $smcFunc['db_free_result']($result);
  358. // Probably MyISAM.... and it might have a comment.
  359. $schema_create .= $crlf . ') ENGINE=' . (isset($row['Type']) ? $row['Type'] : $row['Engine']) . ($row['Comment'] != '' ? ' COMMENT="' . $row['Comment'] . '"' : '');
  360. return $schema_create;
  361. }
  362. // Get the version number.
  363. function smf_db_get_version()
  364. {
  365. global $smcFunc;
  366. $request = $smcFunc['db_query']('', '
  367. SELECT VERSION()',
  368. array(
  369. )
  370. );
  371. list ($ver) = $smcFunc['db_fetch_row']($request);
  372. $smcFunc['db_free_result']($request);
  373. return $ver;
  374. }
  375. ?>