/svntrunk/bp-forums/bbpress/bb-includes/backpress/class.bp-sql-schema-parser.php

https://bitbucket.org/simplemediacode/bptrunk · PHP · 615 lines · 477 code · 61 blank · 77 comment · 94 complexity · c78ed33563b8af9319a744d61be0fafa MD5 · raw file

  1. <?php
  2. /**
  3. * Parses SQL schema statements for comparison to real table structures
  4. *
  5. * @package BackPress
  6. **/
  7. class BP_SQL_Schema_Parser
  8. {
  9. /**
  10. * Builds a column definition as used in CREATE TABLE statements from
  11. * an array such as those returned by DESCRIBE `foo` statements
  12. */
  13. function get_column_definition( $column_data )
  14. {
  15. if ( !is_array( $column_data ) ) {
  16. return $column_data;
  17. }
  18. $null = '';
  19. if ( $column_data['Null'] != 'YES' ) {
  20. $null = 'NOT NULL';
  21. }
  22. $default = '';
  23. // Defaults aren't allowed at all on certain column types
  24. if ( !in_array(
  25. strtolower( $column_data['Type'] ),
  26. array( 'tinytext', 'text', 'mediumtext', 'longtext', 'blob', 'mediumblob', 'longblob' )
  27. ) ) {
  28. if ( $column_data['Null'] == 'YES' && $column_data['Default'] === null ) {
  29. $default = 'default NULL';
  30. } elseif ( preg_match( '@^\d+$@', $column_data['Default'] ) ) {
  31. $default = 'default ' . $column_data['Default'];
  32. } elseif ( is_string( $column_data['Default'] ) || is_float( $column_data['Default'] ) ) {
  33. $default = 'default \'' . $column_data['Default'] . '\'';
  34. }
  35. }
  36. $column_definition = '`' . $column_data['Field'] . '` ' . $column_data['Type'] . ' ' . $null . ' ' . $column_data['Extra'] . ' ' . $default;
  37. return preg_replace( '@\s+@', ' ', trim( $column_definition ) );
  38. }
  39. /**
  40. * Builds an index definition as used in CREATE TABLE statements from
  41. * an array similar to those returned by SHOW INDEX FROM `foo` statements
  42. */
  43. function get_index_definition( $index_data )
  44. {
  45. if ( !is_array( $index_data ) ) {
  46. return $index_data;
  47. }
  48. if ( !count( $index_data ) ) {
  49. return $index_data;
  50. }
  51. $_name = '`' . $index_data[0]['Key_name'] . '`';
  52. if ( $index_data[0]['Index_type'] == 'BTREE' && $index_data[0]['Key_name'] == 'PRIMARY' ) {
  53. $_type = 'PRIMARY KEY';
  54. $_name = '';
  55. } elseif ( $index_data[0]['Index_type'] == 'BTREE' && !$index_data[0]['Non_unique'] ) {
  56. $_type = 'UNIQUE KEY';
  57. } elseif ( $index_data[0]['Index_type'] == 'FULLTEXT' ) {
  58. $_type = 'FULLTEXT KEY';
  59. } else {
  60. $_type = 'KEY';
  61. }
  62. $_columns = array();
  63. foreach ( $index_data as $_index ) {
  64. if ( $_index['Sub_part'] ) {
  65. $_columns[] = '`' . $_index['Column_name'] . '`(' . $_index['Sub_part'] . ')';
  66. } else {
  67. $_columns[] = '`' . $_index['Column_name'] . '`';
  68. }
  69. }
  70. $_columns = join( ', ', $_columns );
  71. $index_definition = $_type . ' ' . $_name . ' (' . $_columns . ')';
  72. return preg_replace( '@\s+@', ' ', $index_definition );
  73. }
  74. /**
  75. * Returns a table structure from a raw sql query of the form "CREATE TABLE foo" etc.
  76. * The resulting array contains the original query, the columns as would be returned by DESCRIBE `foo`
  77. * and the indices as would be returned by SHOW INDEX FROM `foo` on a real table
  78. */
  79. function describe_table( $query )
  80. {
  81. // Retrieve the table structure from the query
  82. if ( !preg_match( '@^CREATE\s+TABLE(\s+IF\s+NOT\s+EXISTS)?\s+`?([^\s|`]+)`?\s+\((.*)\)\s*([^\)|;]*)\s*;?@ims', $query, $_matches ) ) {
  83. return $query;
  84. }
  85. $_if_not_exists = $_matches[1];
  86. // Tidy up the table name
  87. $_table_name = trim( $_matches[2] );
  88. // Tidy up the table columns/indices
  89. $_columns_indices = trim( $_matches[3], " \t\n\r\0\x0B," );
  90. // Split by commas not followed by a closing parenthesis ")", using fancy lookaheads
  91. $_columns_indices = preg_split( '@,(?!(?:[^\(]+\)))@ms', $_columns_indices );
  92. $_columns_indices = array_map( 'trim', $_columns_indices );
  93. // Tidy the table attributes
  94. $_attributes = preg_replace( '@\s+@', ' ', trim( $_matches[4] ) );
  95. unset( $_matches );
  96. // Initialise some temporary arrays
  97. $_columns = array();
  98. $_indices = array();
  99. // Loop over the columns/indices
  100. foreach ( $_columns_indices as $_column_index ) {
  101. if ( preg_match( '@^(PRIMARY\s+KEY|UNIQUE\s+(?:KEY|INDEX)|FULLTEXT\s+(?:KEY|INDEX)|KEY|INDEX)\s+(?:`?(\w+)`?\s+)*\((.+?)\)$@im', $_column_index, $_matches ) ) {
  102. // It's an index
  103. // Tidy the type
  104. $_index_type = strtoupper( preg_replace( '@\s+@', ' ', trim( $_matches[1] ) ) );
  105. $_index_type = str_replace( 'INDEX', 'KEY', $_index_type );
  106. // Set the index name
  107. $_index_name = ( 'PRIMARY KEY' == $_matches[1] ) ? 'PRIMARY' : $_matches[2];
  108. // Split into columns
  109. $_index_columns = array_map( 'trim', explode( ',', $_matches[3] ) );
  110. foreach ( $_index_columns as $_index_columns_index => $_index_column ) {
  111. preg_match( '@`?(\w+)`?(?:\s*\(\s*(\d+)\s*\))?@i', $_index_column, $_matches_column );
  112. $_indices[$_index_name][] = array(
  113. 'Table' => $_table_name,
  114. 'Non_unique' => ( 'UNIQUE KEY' == $_index_type || 'PRIMARY' == $_index_name ) ? '0' : '1',
  115. 'Key_name' => $_index_name,
  116. 'Seq_in_index' => (string) ( $_index_columns_index + 1 ),
  117. 'Column_name' => $_matches_column[1],
  118. 'Sub_part' => ( isset( $_matches_column[2] ) && $_matches_column[2] ) ? $_matches_column[2] : null,
  119. 'Index_type' => ( 'FULLTEXT KEY' == $_index_type ) ? 'FULLTEXT' : 'BTREE'
  120. );
  121. }
  122. unset( $_index_type, $_index_name, $_index_columns, $_index_columns_index, $_index_column, $_matches_column );
  123. } elseif ( preg_match( "@^`?(\w+)`?\s+(?:(\w+)(?:\s*\(\s*(\d+)\s*\))?(?:\s+(unsigned)){0,1})(?:\s+(NOT\s+NULL))?(?:\s+(auto_increment))?(?:\s+(default)\s+(?:(NULL|'[^']*'|\d+)))?@im", $_column_index, $_matches ) ) {
  124. // It's a column
  125. // Tidy the NOT NULL
  126. $_matches[5] = isset( $_matches[5] ) ? strtoupper( preg_replace( '@\s+@', ' ', trim( $_matches[5] ) ) ) : '';
  127. $_columns[$_matches[1]] = array(
  128. 'Field' => $_matches[1],
  129. 'Type' => ( is_numeric( $_matches[3] ) ) ? $_matches[2] . '(' . $_matches[3] . ')' . ( ( isset( $_matches[4] ) && strtolower( $_matches[4] ) == 'unsigned' ) ? ' unsigned' : '' ) : $_matches[2],
  130. 'Null' => ( 'NOT NULL' == strtoupper( $_matches[5] ) ) ? 'NO' : 'YES',
  131. 'Default' => ( isset( $_matches[7] ) && 'default' == strtolower( $_matches[7] ) && 'NULL' !== strtoupper( $_matches[8] ) ) ? trim( $_matches[8], "'" ) : null,
  132. 'Extra' => ( isset( $_matches[6] ) && 'auto_increment' == strtolower( $_matches[6] ) ) ? 'auto_increment' : ''
  133. );
  134. }
  135. }
  136. unset( $_matches, $_columns_indices, $_column_index );
  137. // Tidy up the original query
  138. $_tidy_query = 'CREATE TABLE';
  139. if ( $_if_not_exists ) {
  140. $_tidy_query .= ' IF NOT EXISTS';
  141. }
  142. $_tidy_query .= ' `' . $_table_name . '` (' . "\n";
  143. foreach ( $_columns as $_column ) {
  144. $_tidy_query .= "\t" . BP_SQL_Schema_Parser::get_column_definition( $_column ) . ",\n";
  145. }
  146. unset( $_column );
  147. foreach ( $_indices as $_index ) {
  148. $_tidy_query .= "\t" . BP_SQL_Schema_Parser::get_index_definition( $_index ) . ",\n";
  149. }
  150. $_tidy_query = substr( $_tidy_query, 0, -2 ) . "\n" . ') ' . $_attributes . ';';
  151. // Add to the query array using the table name as the index
  152. $description = array(
  153. 'query_original' => $query,
  154. 'query_tidy' => $_tidy_query,
  155. 'columns' => $_columns,
  156. 'indices' => $_indices
  157. );
  158. unset( $_table_name, $_columns, $_indices, $_tidy_query );
  159. return $description;
  160. }
  161. /**
  162. * Helper function to flatten arrays
  163. */
  164. function _flatten_array( $array, $cut_branch = 0, $keep_child_array_keys = true )
  165. {
  166. if ( !is_array( $array ) ) {
  167. return $array;
  168. }
  169. if ( empty( $array ) ) {
  170. return null;
  171. }
  172. $temp = array();
  173. foreach ( $array as $k => $v ) {
  174. if ( $cut_branch && $k == $cut_branch )
  175. continue;
  176. if ( is_array( $v ) ) {
  177. if ( $keep_child_array_keys ) {
  178. $temp[$k] = true;
  179. }
  180. $temp += BP_SQL_Schema_Parser::_flatten_array( $v, $cut_branch, $keep_child_array_keys );
  181. } else {
  182. $temp[$k] = $v;
  183. }
  184. }
  185. return $temp;
  186. }
  187. /**
  188. * Splits grouped SQL statements into queries within a highly structured array
  189. * Only supports CREATE TABLE, INSERT and UPDATE
  190. */
  191. function parse( $sql )
  192. {
  193. // Only accept strings or arrays
  194. if ( is_string( $sql ) ) {
  195. // Just pop strings into an array to start with
  196. $queries = array( $sql );
  197. } elseif ( is_array( $sql ) ) {
  198. // Flatten the array
  199. $queries = BP_SQL_Schema_Parser::_flatten_array( $sql, 0, false );
  200. // Remove empty nodes
  201. $queries = array_filter( $queries );
  202. } else {
  203. return false;
  204. }
  205. // Clean up the queries
  206. $_clean_queries = array();
  207. foreach ( $queries as $_query ) {
  208. // Trim space and semi-colons
  209. $_query = trim( $_query, "; \t\n\r\0\x0B" );
  210. // If it exists and isn't a number
  211. if ( $_query && !is_numeric( $_query ) ) {
  212. // Is it more than one query?
  213. if ( strpos( ';', $_query ) !== false ) {
  214. // Explode by semi-colon
  215. foreach ( explode( ';', $_query ) as $_part ) {
  216. $_part = trim( $_part );
  217. if ( $_part && !is_numeric( $_part ) ) {
  218. // Pull out any commented code
  219. // Can't properly deal with /*!4321 FOO `bar` */ version specific inclusion, just includes it regardless of version
  220. $_part = preg_replace( '@/\*![0-9]*([^\*]*)\*/@', '$1', $_part );
  221. $_part = preg_replace( '@/\*[^\*]*\*/@', '', $_part );
  222. $_part = preg_replace( '@[\-\-|#].*$@m', '', $_part );
  223. $_clean_queries[] = trim( $_part ) . ';';
  224. }
  225. }
  226. unset( $_part );
  227. } else {
  228. $_clean_queries[] = $_query . ';';
  229. }
  230. }
  231. }
  232. unset( $_query );
  233. if ( !count( $_clean_queries ) ) {
  234. return false;
  235. }
  236. $queries = $_clean_queries;
  237. unset( $_clean_queries );
  238. $_queries = array();
  239. foreach ( $queries as $_query ) {
  240. // Only process table creation, inserts and updates, capture the table/database name while we are at it
  241. if ( !preg_match( '@^(CREATE\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?|INSERT\s+INTO|UPDATE)\s+`?([^\s|`]+)`?@im', $_query, $_matches ) ) {
  242. continue;
  243. }
  244. // Tidy up the type so we can switch it
  245. $_type = strtoupper( preg_replace( '@\s+@', ' ', trim( $_matches[1] ) ) );
  246. $_table_name = trim( $_matches[2] );
  247. unset( $_matches );
  248. switch ( $_type ) {
  249. case 'CREATE TABLE':
  250. case 'CREATE TABLE IF NOT EXISTS':
  251. $_description = BP_SQL_Schema_Parser::describe_table( $_query );
  252. if ( is_array( $_description ) ) {
  253. $_queries['tables'][$_table_name] = $_description;
  254. }
  255. break;
  256. case 'INSERT INTO':
  257. // Just add the query as is for now
  258. $_queries['insert'][$_table_name][] = $_query;
  259. break;
  260. case 'UPDATE':
  261. // Just add the query as is for now
  262. $_queries['update'][$_table_name][] = $_query;
  263. break;
  264. }
  265. unset( $_type, $_table_name );
  266. }
  267. unset( $_query );
  268. if ( !count( $_queries ) ) {
  269. return false;
  270. }
  271. return $_queries;
  272. }
  273. /**
  274. * Evaluates the difference between a given set of SQL queries and real database structure
  275. */
  276. function delta( $db_object, $queries, $ignore = false, $execute = true )
  277. {
  278. if ( !$db_object || !is_object( $db_object ) || !( is_a( $db_object, 'BPDB' ) || is_a( $db_object, 'BPDB_Multi' ) || is_a( $db_object, 'BPDB_Hyper' ) ) ) {
  279. return __( 'Passed variable is not a BackPress database object.' );
  280. }
  281. if ( !$_queries = BP_SQL_Schema_Parser::parse( $queries ) ) {
  282. return __( 'No schema available.' );
  283. }
  284. // Set up default elements to ignore
  285. $ignore_defaults = array(
  286. 'tables' => array(), // Just a list of tablenames, including prefix. Does not affect INSERT and UPDATE queries.
  287. 'columns' => array(), // Arrays of column names, keyed with the table names, including prefix.
  288. 'indices' => array() // Arrays of index names, keyed with the table names, including prefix.
  289. );
  290. // Add the elements to ignore that were passed to the function
  291. if ( !$ignore || !is_array( $ignore ) ) {
  292. $ignore = $ignore_defaults;
  293. } else {
  294. if ( isset( $ignore['tables'] ) && is_array( $ignore['tables'] ) ) {
  295. $ignore['tables'] = array_merge( $ignore_defaults['tables'], $ignore['tables'] );
  296. }
  297. if ( isset( $ignore['columns'] ) && is_array( $ignore['columns'] ) ) {
  298. $ignore['columns'] = array_merge( $ignore_defaults['columns'], $ignore['columns'] );
  299. }
  300. if ( isset( $ignore['indices'] ) && is_array( $ignore['indices'] ) ) {
  301. $ignore['indices'] = array_merge( $ignore_defaults['indices'], $ignore['indices'] );
  302. }
  303. }
  304. // Build an array of $db_object registered tables and their database identifiers
  305. $_tables = $db_object->tables;
  306. $db_object_tables = array();
  307. foreach ( $_tables as $_table_id => $_table_name ) {
  308. if ( is_array( $_table_name ) && isset( $db_object->db_servers['dbh_' . $_table_name[0]] ) ) {
  309. $db_object_tables[$db_object->$_table_id] = 'dbh_' . $_table_name[0];
  310. } else {
  311. $db_object_tables[$db_object->$_table_id] = 'dbh_global';
  312. }
  313. }
  314. unset( $_tables, $_table_id, $_table_name );
  315. $alterations = array();
  316. // Loop through table queries
  317. if ( isset( $_queries['tables'] ) ) {
  318. foreach ( $_queries['tables'] as $_new_table_name => $_new_table_data ) {
  319. if ( in_array( $_new_table_name, $ignore['tables'] ) ) {
  320. continue;
  321. }
  322. // See if the table is custom and registered in $db_object under a custom database
  323. if (
  324. isset( $db_object_tables[$_new_table_name] ) &&
  325. $db_object_tables[$_new_table_name] != 'dbh_global' &&
  326. isset( $db_object->db_servers[$db_object_tables[$_new_table_name]]['ds'] )
  327. ) {
  328. // Force the database connection
  329. $_dbhname = $db_object->db_servers[$db_object_tables[$_new_table_name]]['ds'];
  330. $db_object->_force_dbhname = $_dbhname;
  331. } else {
  332. $_dbhname = 'dbh_global';
  333. }
  334. // Fetch the existing table column structure from the database
  335. $db_object->suppress_errors();
  336. if ( !$_existing_table_columns = $db_object->get_results( 'DESCRIBE `' . $_new_table_name . '`;', ARRAY_A ) ) {
  337. $db_object->suppress_errors( false );
  338. // The table doesn't exist, add it and then continue to the next table
  339. $alterations[$_dbhname][$_new_table_name][] = array(
  340. 'action' => 'create_table',
  341. 'message' => __( 'Creating table' ),
  342. 'query' => $_new_table_data['query_tidy']
  343. );
  344. continue;
  345. }
  346. $db_object->suppress_errors( false );
  347. // Add an index to the existing columns array
  348. $__existing_table_columns = array();
  349. foreach ( $_existing_table_columns as $_existing_table_column ) {
  350. // Remove 'Key' from returned column structure
  351. unset( $_existing_table_column['Key'] );
  352. $__existing_table_columns[$_existing_table_column['Field']] = $_existing_table_column;
  353. }
  354. $_existing_table_columns = $__existing_table_columns;
  355. unset( $__existing_table_columns );
  356. // Loop over the columns in this table and look for differences
  357. foreach ( $_new_table_data['columns'] as $_new_column_name => $_new_column_data ) {
  358. if ( isset( $ignore['columns'][$_new_table_name] ) && in_array( $_new_column_name, $ignore['columns'][$_new_table_name] ) ) {
  359. continue;
  360. }
  361. if ( !in_array( $_new_column_data, $_existing_table_columns ) ) {
  362. // There is a difference
  363. if ( !isset( $_existing_table_columns[$_new_column_name] ) ) {
  364. // The column doesn't exist, so add it
  365. $alterations[$_dbhname][$_new_table_name][] = array(
  366. 'action' => 'add_column',
  367. 'message' => sprintf( __( 'Adding column: %s' ), $_new_column_name ),
  368. 'column' => $_new_column_name,
  369. 'query' => 'ALTER TABLE `' . $_new_table_name . '` ADD COLUMN ' . BP_SQL_Schema_Parser::get_column_definition( $_new_column_data ) . ';'
  370. );
  371. continue;
  372. }
  373. // Adjust defaults on columns that allow defaults
  374. if (
  375. $_new_column_data['Default'] !== $_existing_table_columns[$_new_column_name]['Default'] &&
  376. !in_array(
  377. strtolower( $_new_column_data['Type'] ),
  378. array( 'tinytext', 'text', 'mediumtext', 'longtext', 'blob', 'mediumblob', 'longblob' )
  379. )
  380. ) {
  381. // Change the default value for the column
  382. $alterations[$_dbhname][$_new_table_name][] = array(
  383. 'action' => 'set_default',
  384. 'message' => sprintf( __( 'Setting default on column: %s' ), $_new_column_name ),
  385. 'column' => $_new_column_name,
  386. 'query' => 'ALTER TABLE `' . $_new_table_name . '` ALTER COLUMN `' . $_new_column_name . '` SET DEFAULT \'' . $_new_column_data['Default'] . '\';'
  387. );
  388. // Don't continue, overwrite this if the next conditional is met
  389. }
  390. if (
  391. $_new_column_data['Type'] !== $_existing_table_columns[$_new_column_name]['Type'] ||
  392. ( 'YES' === $_new_column_data['Null'] xor 'YES' === $_existing_table_columns[$_new_column_name]['Null'] ) ||
  393. $_new_column_data['Extra'] !== $_existing_table_columns[$_new_column_name]['Extra']
  394. ) {
  395. // Change the structure for the column
  396. $alterations[$_dbhname][$_new_table_name][] = array(
  397. 'action' => 'change_column',
  398. 'message' => sprintf( __( 'Changing column: %s' ), $_new_column_name ),
  399. 'column' => $_new_column_name,
  400. 'query' => 'ALTER TABLE `' . $_new_table_name . '` CHANGE COLUMN `' . $_new_column_name . '` ' . BP_SQL_Schema_Parser::get_column_definition( $_new_column_data ) . ';'
  401. );
  402. }
  403. }
  404. }
  405. unset( $_existing_table_columns, $_new_column_name, $_new_column_data );
  406. // Fetch the table index structure from the database
  407. if ( !$_existing_table_indices = $db_object->get_results( 'SHOW INDEX FROM `' . $_new_table_name . '`;', ARRAY_A ) ) {
  408. continue;
  409. }
  410. // Add an index to the existing columns array and organise by index name
  411. $__existing_table_indices = array();
  412. foreach ( $_existing_table_indices as $_existing_table_index ) {
  413. // Remove unused parts from returned index structure
  414. unset(
  415. $_existing_table_index['Collation'],
  416. $_existing_table_index['Cardinality'],
  417. $_existing_table_index['Packed'],
  418. $_existing_table_index['Null'],
  419. $_existing_table_index['Comment']
  420. );
  421. $__existing_table_indices[$_existing_table_index['Key_name']][] = $_existing_table_index;
  422. }
  423. $_existing_table_indices = $__existing_table_indices;
  424. unset( $__existing_table_indices );
  425. // Loop over the indices in this table and look for differences
  426. foreach ( $_new_table_data['indices'] as $_new_index_name => $_new_index_data ) {
  427. if ( isset( $ignore['indices'][$_new_table_name] ) && in_array( $_new_index_name, $ignore['indices'][$_new_table_name] ) ) {
  428. continue;
  429. }
  430. if ( !in_array( $_new_index_data, $_existing_table_indices ) ) {
  431. // There is a difference
  432. if ( !isset( $_existing_table_indices[$_new_index_name] ) ) {
  433. // The index doesn't exist, so add it
  434. $alterations[$_dbhname][$_new_table_name][] = array(
  435. 'action' => 'add_index',
  436. 'message' => sprintf( __( 'Adding index: %s' ), $_new_index_name ),
  437. 'index' => $_new_index_name,
  438. 'query' => 'ALTER TABLE `' . $_new_table_name . '` ADD ' . BP_SQL_Schema_Parser::get_index_definition( $_new_index_data ) . ';'
  439. );
  440. continue;
  441. }
  442. if ( $_new_index_data !== $_existing_table_indices[$_new_index_name] ) {
  443. // The index is incorrect, so drop it and add the new one
  444. if ( $_new_index_name == 'PRIMARY' ) {
  445. $_drop_index_name = 'PRIMARY KEY';
  446. } else {
  447. $_drop_index_name = 'INDEX `' . $_new_index_name . '`';
  448. }
  449. $alterations[$_dbhname][$_new_table_name][] = array(
  450. 'action' => 'drop_index',
  451. 'message' => sprintf( __( 'Dropping index: %s' ), $_new_index_name ),
  452. 'index' => $_new_index_name,
  453. 'query' => 'ALTER TABLE `' . $_new_table_name . '` DROP ' . $_drop_index_name . ';'
  454. );
  455. unset( $_drop_index_name );
  456. $alterations[$_dbhname][$_new_table_name][] = array(
  457. 'action' => 'add_index',
  458. 'message' => sprintf( __( 'Adding index: %s' ), $_new_index_name ),
  459. 'index' => $_new_index_name,
  460. 'query' => 'ALTER TABLE `' . $_new_table_name . '` ADD ' . BP_SQL_Schema_Parser::get_index_definition( $_new_index_data ) . ';'
  461. );
  462. }
  463. }
  464. }
  465. unset( $_new_index_name, $_new_index_data );
  466. // Go back to the default database connection
  467. $db_object->_force_dbhname = false;
  468. }
  469. unset( $_new_table_name, $_new_table_data, $_dbhname );
  470. }
  471. // Now deal with the sundry INSERT and UPDATE statements (if any)
  472. if ( isset( $_queries['insert'] ) && is_array( $_queries['insert'] ) && count( $_queries['insert'] ) ) {
  473. foreach ( $_queries['insert'] as $_table_name => $_inserts ) {
  474. foreach ( $_inserts as $_insert ) {
  475. $alterations['dbh_global'][$_table_name][] = array(
  476. 'action' => 'insert',
  477. 'message' => __( 'Inserting data' ),
  478. 'query' => $_insert
  479. );
  480. }
  481. unset( $_insert );
  482. }
  483. unset( $_table_name, $_inserts );
  484. }
  485. if ( isset( $_queries['update'] ) && is_array( $_queries['update'] ) && count( $_queries['update'] ) ) {
  486. foreach ( $_queries['update'] as $_table_name => $_updates ) {
  487. foreach ( $_updates as $_update ) {
  488. $alterations['dbh_global'][$_table_name][] = array(
  489. 'action' => 'update',
  490. 'message' => __( 'Updating data' ),
  491. 'query' => $_update
  492. );
  493. }
  494. unset( $_update );
  495. }
  496. unset( $_table_name, $_updates );
  497. }
  498. // Initialise an array to hold the output messages
  499. $messages = array();
  500. $errors = array();
  501. foreach ( $alterations as $_dbhname => $_tables ) {
  502. // Force the database connection (this was already checked to be valid in the previous loop)
  503. $db_object->_force_dbhname = $_dbhname;
  504. // Note the database in the return messages
  505. $messages[] = '>>> ' . sprintf( __( 'Modifying database: %s (%s)' ), $db_object->db_servers[$_dbhname]['name'], $db_object->db_servers[$_dbhname]['host'] );
  506. foreach ( $_tables as $_table_name => $_alterations ) {
  507. // Note the table in the return messages
  508. $messages[] = '>>>>>> ' . sprintf( __( 'Table: %s' ), $_table_name );
  509. foreach ( $_alterations as $_alteration ) {
  510. // If there is no query, then skip
  511. if ( !$_alteration['query'] ) {
  512. continue;
  513. }
  514. // Note the action in the return messages
  515. $messages[] = '>>>>>>>>> ' . $_alteration['message'];
  516. if ( !$execute ) {
  517. $messages[] = '>>>>>>>>>>>> ' . __( 'Skipped' );
  518. continue;
  519. }
  520. // Run the query
  521. $_result = $db_object->query( $_alteration['query'] );
  522. $_result_error = $db_object->get_error();
  523. if ( $_result_error ) {
  524. // There was an error
  525. $_result =& $_result_error;
  526. unset( $_result_error );
  527. $messages[] = '>>>>>>>>>>>> ' . __( 'SQL ERROR! See the error log for more detail' );
  528. $errors[] = __( 'SQL ERROR!' );
  529. $errors[] = '>>> ' . sprintf( __( 'Database: %s (%s)' ), $db_object->db_servers[$_dbhname]['name'], $db_object->db_servers[$_dbhname]['host'] );
  530. $errors[] = '>>>>>> ' . $_result->error_data['db_query']['query'];
  531. $errors[] = '>>>>>> ' . $_result->error_data['db_query']['error'];
  532. } else {
  533. $messages[] = '>>>>>>>>>>>> ' . __( 'Done' );
  534. }
  535. unset( $_result );
  536. }
  537. unset( $_alteration );
  538. }
  539. unset( $_table_name, $_alterations );
  540. }
  541. unset( $_dbhname, $_tables );
  542. // Reset the database connection
  543. $db_object->_force_dbhname = false;
  544. return array( 'messages' => $messages, 'errors' => $errors );
  545. }
  546. } // END class BP_SQL_Schema_Parser