/** * Standard modular run function. * * @return tempcode Results */ function run() { $out = new ocp_tempcode(); $tables = $GLOBALS['SITE_DB']->query_select('db_meta', array('DISTINCT m_table')); if (count($GLOBALS['SITE_DB']->connection_write) > 4) { $GLOBALS['SITE_DB']->connection_write = call_user_func_array(array($GLOBALS['SITE_DB']->static_ob, 'db_get_connection'), $GLOBALS['SITE_DB']->connection_write); _general_db_init(); } list($db, $db_name) = $GLOBALS['SITE_DB']->connection_write; mysql_select_db($db_name, $db); foreach ($tables as $table) { if ($table['m_table'] == 'sessions') { continue; } // HEAP, so can't be repaired $table = get_table_prefix() . $table['m_table']; // Check/Repair $result = mysql_query('CHECK TABLE ' . $table . ' FAST', $db); echo mysql_error($db); mysql_data_seek($result, mysql_num_rows($result) - 1); $status_row = mysql_fetch_assoc($result); if ($status_row['Msg_type'] != 'status') { $out->attach(paragraph(do_lang_tempcode('TABLE_ERROR', escape_html($table), escape_html($status_row['Msg_type']), array(escape_html($status_row['Msg_text']))), 'dfsdgdsgfgd')); $result2 = mysql_query('REPAIR TABLE ' . $table, $db); mysql_data_seek($result2, mysql_num_rows($result2) - 1); $status_row_2 = mysql_fetch_assoc($result2); $out->attach(paragraph(do_lang_tempcode('TABLE_FIXED', escape_html($table), escape_html($status_row_2['Msg_type']), array(escape_html($status_row_2['Msg_text']))), 'dfsdfgdst4')); } // Optimise mysql_unbuffered_query('OPTIMIZE TABLE ' . $table, $db); } return $out; }
/** * Change the primary key of a table. * * @param object Link to the real database object * @param ID_TEXT The name of the table to create the index on * @param array A list of fields to put in the new key */ function _helper_change_primary_key($this_ref, $table_name, $new_key) { if (count($this_ref->connection_write) > 4) { $this_ref->connection_write = call_user_func_array(array($this_ref->static_ob, 'db_get_connection'), $this_ref->connection_write); _general_db_init(); } $this_ref->static_ob->db_change_primary_key($this_ref->table_prefix . $table_name, $new_key, $this_ref->connection_write); }
/** * Upgrade shared installs. */ function upgrade_sharedinstall_sites() { global $CURRENT_SHARE_USER, $SITE_INFO, $TABLE_LANG_FIELDS; // Find sites $sites = array(); foreach (array_keys($SITE_INFO) as $key) { $matches = array(); if (preg_match('#^custom_user_(.*)#', $key, $matches) != 0) { $sites[] = $matches[1]; } } disable_php_memory_limit(); foreach ($sites as $i => $site) { if (function_exists('set_time_limit')) { @set_time_limit(0); } // Change active site $CURRENT_SHARE_USER = $site; $TABLE_LANG_FIELDS = array(); _general_db_init(); // Reset DB $GLOBALS['SITE_DB'] = new database_driver(get_db_site(), get_db_site_host(), get_db_site_user(), get_db_site_password(), get_table_prefix()); $GLOBALS['FORUM_DB'] = $GLOBALS['SITE_DB']; // NB: File path will be ok // NB: Other internal caching could need changing in the future, but works at time of writing // Go! automate_upgrade(); echo 'Upgraded ' . htmlentities($site) . '<br />'; flush(); } }
/** * This function is a very basic query executor. It shouldn't usually be used by you, as there are specialised abstracted versions available. * * @param string The complete SQL query * @param ?integer The maximum number of rows to affect (NULL: no limit) * @param ?integer The start row to affect (NULL: no specification) * @param boolean Whether to output an error on failure * @param boolean Whether to get an insert ID * @param ?array Extra language fields to join in for cache-prefilling. You only need to send this if you are doing a JOIN and carefully craft your query so table field names won't conflict (NULL: none) * @param string All the core fields have a prefix of this on them, so when we fiddle with language lookup we need to use this (only consider this if you're setting $lang_fields) * @param boolean Whether we are saving as a 'volatile' file extension (used in the XML DB driver, to mark things as being non-syndicated to subversion) * @return ?mixed The results (NULL: no results) */ function _query($query, $max = NULL, $start = NULL, $fail_ok = false, $get_insert_id = false, $lang_fields = NULL, $field_prefix = '', $save_as_volatile = false) { global $QUERY_COUNT, $NO_QUERY_LIMIT, $QUERY_LOG, $QUERY_LIST, $DEBUG_MODE, $IN_MINIKERNEL_VERSION, $QUERY_FILE_LOG, $UPON_QUERY_HOOKS; if ($QUERY_FILE_LOG !== NULL) { fwrite($QUERY_FILE_LOG, $query . ';' . chr(10) . chr(10)); } if ($DEBUG_MODE) { if (get_forum_type() != 'none' && strpos($query, get_table_prefix() . 'f_') !== false && strpos($query, get_table_prefix() . 'f_') < 100 && strpos($query, 'f_welcome_emails') === false && $this->connection_write === $GLOBALS['SITE_DB']->connection_write && isset($GLOBALS['FORUM_DB']) && $GLOBALS['SITE_DB']->connection_write !== $GLOBALS['FORUM_DB']->connection_write && !$GLOBALS['NO_DB_SCOPE_CHECK']) { /*file_put_contents(get_file_base().'/uploads/downloads/test.txt',var_export(debug_backtrace(),true)); @exit($query); @debug_print_backtrace();*/ fatal_exit('Using OCF queries on the wrong driver'); } } if (!$NO_QUERY_LIMIT) { $QUERY_COUNT++; //@exit('!'); //if ($QUERY_COUNT>10) @ob_end_clean();@print('Query: '.$query.chr(10)); } static $fb = NULL; if ($fb === NULL) { $fb = function_exists('fb'); } if ($fb && !headers_sent() && get_param_integer('keep_firephp_queries', 0) == 1 && function_exists('fb')) { fb('Query: ' . $query); } if ($QUERY_COUNT == 68 && get_param_integer('keep_no_query_limit', 0) == 0 && count($_POST) == 0 && get_page_name() != 'admin_importer' && $IN_MINIKERNEL_VERSION == 0 && get_param('special_page_type', '') != 'query') { $NO_QUERY_LIMIT = true; $log_path = get_custom_file_base() . '/data_custom/big_query_screens.log'; if (is_writable_wrap($log_path)) { $myfile = fopen($log_path, 'at'); fwrite($myfile, get_self_url_easy() . chr(10)); fclose($myfile); } if ($DEBUG_MODE) { $QUERY_COUNT = 0; fatal_exit(do_lang_tempcode('TOO_MANY_QUERIES')); } } $lang_strings_expecting = array(); if (isset($lang_fields[0]) && function_exists('user_lang')) { $lang = user_lang(); // We can we assume this, as we will cache against it -- if subsequently code wants something else it'd be a cache miss which is fine foreach ($lang_fields as $i => $field) { $_i = strval($i); $join = ' LEFT JOIN ' . $this->table_prefix . 'translate t' . $_i . ' ON t' . $_i . '.id=' . $field_prefix . $field . ' AND ' . db_string_equal_to('t' . $_i . '.language', $lang); $_query = strtoupper($query); $from_pos = strpos($_query, ' FROM '); $where_pos = strpos($_query, ' WHERE '); if ($where_pos === false) { $_where_pos = 0; do { $_where_pos = strpos($_query, ' GROUP BY ', $_where_pos + 1); if ($_where_pos !== false) { $where_pos = $_where_pos; } } while ($_where_pos !== false); } if ($where_pos === false) { $_where_pos = 0; do { $_where_pos = strpos($_query, ' ORDER BY ', $_where_pos + 1); if ($_where_pos !== false) { $where_pos = $_where_pos; } } while ($_where_pos !== false); } if ($where_pos !== false) { $query = substr($query, 0, $where_pos) . $join . substr($query, $where_pos); } else { $query .= $join; } $original = 't' . $_i . '.text_original AS t' . $_i . '__text_original'; $parsed = 't' . $_i . '.text_parsed AS t' . $_i . '__text_parsed'; $query = substr($query, 0, $from_pos) . ',' . $original . ',' . $parsed . substr($query, $from_pos); $lang_strings_expecting[] = array($field, 't' . $_i . '__text_original', 't' . $_i . '__text_parsed'); } } if ($start < 0) { $start = 0; } if ($max < 0) { $max = 1; } if ($QUERY_LOG) { $before = microtime(false); } if (substr(strtoupper($query), 0, 7) == 'SELECT ') { $connection =& $this->connection_read; } else { $connection =& $this->connection_write; } if (isset($connection[4])) { $connection = call_user_func_array(array($this->static_ob, 'db_get_connection'), $connection); _general_db_init(); } $ret = $this->static_ob->db_query($query, $connection, $max, $start, $fail_ok, $get_insert_id, false, $save_as_volatile); if ($QUERY_LOG) { $after = microtime(false); $text = !is_null($max) ? $query . ' (' . strval((int) $start) . '-' . strval((int) $start + $max) . ')' : $query; $out = array('time' => microtime_diff($after, $before), 'text' => $text); $QUERY_LIST[] = $out; } // Run hooks, if any exist if ($UPON_QUERY_HOOKS === NULL) { if (!function_exists('find_all_hooks')) { return $ret; } $UPON_QUERY_HOOKS = array(); $hooks = find_all_hooks('systems', 'upon_query'); foreach (array_keys($hooks) as $hook) { require_code('hooks/systems/upon_query/' . filter_naughty($hook)); $UPON_QUERY_HOOKS[$hook] = object_factory('upon_query_' . filter_naughty($hook), true); } } foreach ($UPON_QUERY_HOOKS as $ob) { if ($ob !== NULL) { $ob->run($this, $query, $max, $start, $fail_ok, $get_insert_id, $ret); } } // Copy results to lang cache, but only if not null AND unset to avoid any confusion if ($ret !== NULL) { foreach ($lang_strings_expecting as $bits) { list($field, $original, $parsed) = $bits; foreach ($ret as $row) { $entry = $row[$field]; if ($row[$original] !== NULL && count($this->text_lookup_original_cache) <= 1000) { $this->text_lookup_original_cache[$entry] = $row[$original]; } if ($row[$parsed] !== NULL && count($this->text_lookup_cache) <= 1000) { $this->text_lookup_cache[$entry] = $row[$parsed]; } unset($row[$original]); unset($row[$parsed]); } } } return $ret; }
/** * Script to handle XML DB/MySQL chain synching. */ function xml_dump_script() { // Run checks and set up chain DB if (get_db_type() != 'xml') { warn_exit('It makes no sense to run this script if you are not running the XML database driver.'); } global $SITE_INFO; if (array_key_exists('db_chain_type', $SITE_INFO)) { require_code('database/' . $SITE_INFO['db_chain_type']); $chain_db = new database_driver($SITE_INFO['db_chain'], $SITE_INFO['db_chain_host'], $SITE_INFO['db_chain_user'], $SITE_INFO['db_chain_password'], get_table_prefix(), false, object_factory('Database_Static_' . $SITE_INFO['db_chain_type'])); } else { warn_exit('It makes no sense to run this script if you have not set up the following config options in info.php: db_chain_type, db_chain_host, db_chain_user, db_chain_password, db_chain'); } $chain_connection =& $chain_db->connection_write; if (count($chain_connection) > 4) { $chain_connection = call_user_func_array(array($chain_db->static_ob, 'db_get_connection'), $chain_connection); _general_db_init(); } if (function_exists('set_time_limit')) { @set_time_limit(0); } $GLOBALS['DEBUG_MODE'] = false; $GLOBALS['SEMI_DEBUG_MODE'] = false; @ini_set('ocproducts.xss_detect', '0'); if (strtolower(ocp_srv('REQUEST_METHOD')) == 'get') { $from = get_param('from', NULL); $skip = get_param('skip', NULL); $only = get_param('only', NULL); echo ' <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>XML/MySQL DB syncher</title> </head> <body> '; echo '<p>Select the tables to sync below. Tables have been auto-ticked based on what seems to need re-synching.</p>'; $keep = symbol_tempcode('KEEP', array('1')); echo '<form title="Choose tables" method="post" action="' . escape_html(find_script('xml_db_import') . $keep->evaluate()) . '">'; $tables = array_keys(find_all_tables($GLOBALS['SITE_DB'])); $mysql_status = list_to_map('Name', $chain_db->query('SHOW TABLE STATUS')); $mysql_tables = array_keys($mysql_status); foreach ($tables as $table_name) { $default_selected = (!is_null($from) && $table_name >= $from || !is_null($only) && in_array($table_name, explode(',', $only))) && (!is_null($skip) || !in_array($table_name, explode(',', $skip))); $missing = !in_array(get_table_prefix() . $table_name, $mysql_tables); $count_mismatch = !$missing && $chain_db->query_value($table_name, 'COUNT(*)') != $GLOBALS['SITE_DB']->query_value($table_name, 'COUNT(*)'); $date_mismatch = false; if (!$missing && !$count_mismatch) { $last_m_time = NULL; $path = get_custom_file_base() . '/uploads/website_specific/' . get_db_site() . '/' . get_table_prefix() . $table_name; $dh = @opendir($path); if ($dh !== false) { while (($f = readdir($dh)) !== false) { if (substr($f, -4) == '.dat' || substr($f, -4) == '.xml') { $last_m_time = @max($last_m_time, filemtime($path . '/' . $f)); } // @ because of the 255 read filepath limit on Windows } closedir($dh); } if (!is_null($last_m_time)) { $mysql_time = strtotime($mysql_status[get_table_prefix() . $table_name]['Update_time']); $date_mismatch = $mysql_time < $last_m_time; // We can't do "!=" as last m-time for MySQL could well by the last sync time } } $needs_doing = $count_mismatch || $date_mismatch || $missing || $default_selected; echo ' <div style="width: 500px"> <span style="float: right; font-style: italic"> ' . ($missing ? '[table is missing]' : '') . ' ' . ($count_mismatch ? '[different record-counts]' : '') . ' ' . ($date_mismatch ? '[different last-modified-time]' : '') . ' </span> <input ' . ($needs_doing ? 'checked="checked" ' : '') . 'type="checkbox" name="table_' . htmlentities($table_name) . '" id="table_' . htmlentities($table_name) . '" value="1" /> <label for="table_' . htmlentities($table_name) . '">' . htmlentities($table_name) . '</label> </div> '; } echo '<p><input type="submit" value="Sync" /> [<a href="#" onclick="var form=document.getElementsByTagName(\'form\')[0]; for (var i=0;i<form.elements.length;i++) if (form.elements[i].checked) form.elements[i].checked=false; return false;">un-tick all</a>]</p>'; echo '</form>'; echo ' </body> </html> '; exit; } // Actualiser $from = NULL; $skip = NULL; $only = ''; foreach (array_keys($_POST) as $key) { if (substr($key, 0, 6) == 'table_') { if ($only != '') { $only .= ','; } $only .= substr($key, 6); } } if ($only == '') { $only = NULL; } @header('Content-type: text/plain'); @ob_end_clean(); $sql = get_sql_dump(true, true, $from, is_null($skip) ? array() : explode(',', $skip), is_null($only) ? NULL : explode(',', $only)); $cnt = count($sql); foreach ($sql as $i => $s) { print 'Executing query ' . strval($i + 1) . '/' . strval($cnt) . ' ... ' . $s . "\n\n"; flush(); $fail_ok = substr($s, 0, 5) == 'ALTER'; $chain_db->static_ob->db_query($s, $chain_connection, NULL, NULL, $fail_ok, false); } print '!!Done!!'; }
/** * This function is a very basic query executor. It shouldn't usually be used by you, as there are abstracted versions available. * * @param string The complete SQL query * @param array A DB connection * @param ?integer The maximum number of rows to affect (NULL: no limit) * @param ?integer The start row to affect (NULL: no specification) * @param boolean Whether to not output an error on some kind of run-time failure (parse errors and clear programming errors are always fatal) * @param boolean Whether to get the autoincrement ID created for an insert query * @param boolean Whether to force the query to execute on the XML database driver (won't optimise by using MySQL). Useful for calls happening for multi-part queries from within this DB driver * @param boolean Whether we are saving as a 'volatile' file extension * @return ?mixed The results (NULL: no results), or the insert ID */ function db_query($query, $db, $max = NULL, $start = NULL, $fail_ok = false, $get_insert_id = false, $no_syndicate = false, $save_as_volatile = false) { global $DELIMITERS_FLIPPED, $DELIMITERS, $SYMBOL_DELIMINITER; // LEXING STAGE // ------------ $i = 0; $query .= ' '; // Cheat so that we do not have to handle the end state differently $len = strlen($query); $tokens = array(); $current_token = ''; $doing_symbol_delimiter = true; while ($i < $len) { $next = $query[$i]; if ($next == "'" || $next == '"') { if (trim($current_token) != '') { if (isset($DELIMITERS_FLIPPED[strtoupper($current_token)])) { $tokens[] = strtoupper($current_token); } else { $tokens[] = $current_token; } } $current_token = ''; $i++; while ($i < $len) { $next = $query[$i]; if ($next == '\\') { $i++; $next = $query[$i]; $current_token .= $next; } else { if ($next == "'" || $next == '"') { $tokens[] = "'"; $tokens[] = $current_token; $tokens[] = "'"; break; } else { $current_token .= $next; } } $i++; } $current_token = ''; $doing_symbol_delimiter = true; } else { $symbol_delimiter_coming = isset($SYMBOL_DELIMINITER[$next]) && (isset($DELIMITERS_FLIPPED[$next]) || $i + 1 < $len && isset($DELIMITERS_FLIPPED[$next . $query[$i + 1]])); // (NB: symbol delimiters are a maximum of two in length) if (($symbol_delimiter_coming || $doing_symbol_delimiter) && !$this->is_start_of_delimiter($current_token . $next)) { if (trim($current_token) != '') { if (isset($DELIMITERS_FLIPPED[strtoupper($current_token)])) { $tokens[] = strtoupper($current_token); } else { $tokens[] = $current_token; } } $current_token = $next; $doing_symbol_delimiter = isset($SYMBOL_DELIMINITER[$next]); } else { $current_token .= $next; if ($doing_symbol_delimiter) { $doing_symbol_delimiter = isset($SYMBOL_DELIMINITER[$next]); } } } $i++; } $query = substr($query, 0, $len - 1); // PARSING/EXECUTION STAGE // ----------------------- $random_key = mt_rand(0, min(2147483647, mt_getrandmax())); // Generated later, passed by reference. We will assume we only need one; multi inserts will need to each specify the key in full if (!is_null($GLOBALS['XML_CHAIN_DB']) && !$no_syndicate) { if (substr(strtoupper($query), 0, 7) == 'SELECT ') { $chain_connection =& $GLOBALS['XML_CHAIN_DB']->connection_read; } else { $chain_connection =& $GLOBALS['XML_CHAIN_DB']->connection_write; } if (count($chain_connection) > 4) { $chain_connection = call_user_func_array(array($GLOBALS['XML_CHAIN_DB']->static_ob, 'db_get_connection'), $chain_connection); _general_db_init(); } switch ($tokens[0]) { case 'INSERT': // DB chaining: It's a write query, so needs doing on chained DB too // But because it's an insert we may need to put in an auto-increment also $_inserts = $this->_do_query_insert__parse($tokens, $query, $db, $fail_ok); if (is_null($_inserts)) { return NULL; } list($table_name, $inserts) = $_inserts; $insert_keys = array_keys($inserts[0]); $query_new = 'INSERT INTO ' . $table_name . ' ('; $schema = $this->_read_schema($db, $table_name, $fail_ok); global $TABLE_BASES; foreach ($schema as $key => $val) { if (preg_replace('#[^\\w]#', '', $val) == 'AUTO' && !in_array($key, $insert_keys)) { $insert_keys[] = $key; foreach (array_keys($inserts) as $i) { if ($i != 0) { $random_key = mt_rand(0, min(2147483647, mt_getrandmax())); } $inserts[$i][$key] = isset($TABLE_BASES[$table_name]) ? $TABLE_BASES[$table_name] : $this->db_get_first_id(); // We always want first record as '1', because we often reference it in a hard-coded way while (file_exists($db[0] . '/' . $table_name . '/' . strval($inserts[$i][$key]) . '.xml') || file_exists($db[0] . '/' . $table_name . '/' . $this->_guid($schema, $inserts[$i]) . '.xml') || file_exists($db[0] . '/' . $table_name . '/' . strval($inserts[$i][$key]) . '.xml-volatile') || file_exists($db[0] . '/' . $table_name . '/' . $this->_guid($schema, $inserts[$i]) . '.xml-volatile')) { if ($GLOBALS['IN_MINIKERNEL_VERSION'] == 1) { $inserts[$i][$key]++; $TABLE_BASES[$table_name] = $inserts[$i][$key] + 1; } else { if ($i != 0) { $random_key = mt_rand(0, min(2147483647, mt_getrandmax())); } $inserts[$i][$key] = $random_key; // We don't use auto-increment, we use randomisation. As otherwise when people sync over revision control there'd be conflicts } } } } } foreach ($insert_keys as $i => $key) { if ($i != 0) { $query_new .= ','; } $query_new .= $key; } $query_new .= ')'; foreach ($inserts as $ii => $insert) { if ($ii != 0) { $query_new .= ', ('; } else { $query_new .= ' VALUES ('; } $i = 0; foreach ($insert as $value) { if ($i != 0) { $query_new .= ','; } if (is_integer($value)) { $query_new .= strval($value); } elseif (is_float($value)) { $query_new .= float_to_raw_string($value); } elseif (is_null($value)) { $query_new .= 'NULL'; } else { $query_new .= '\'' . db_escape_string($value) . '\''; } $i++; } $query_new .= ')'; } $GLOBALS['XML_CHAIN_DB']->static_ob->db_query($query_new, $chain_connection, $max, $start, $fail_ok, $get_insert_id); break; case 'UPDATE': case 'DELETE': // DB chaining: It's a write query, so needs doing on chained DB too $GLOBALS['XML_CHAIN_DB']->static_ob->db_query($query, $chain_connection, $max, $start, $fail_ok, $get_insert_id); break; case 'SELECT': return $GLOBALS['XML_CHAIN_DB']->static_ob->db_query($query, $chain_connection, $max, $start, $fail_ok, $get_insert_id); } } switch ($tokens[0]) { case 'ALTER': return $this->_do_query_alter($tokens, $query, $db, $fail_ok); case 'CREATE': return $this->_do_query_create($tokens, $query, $db, $fail_ok); case 'INSERT': return $this->_do_query_insert($tokens, $query, $db, $fail_ok, $get_insert_id, $random_key, $save_as_volatile); case 'UPDATE': return $this->_do_query_update($tokens, $query, $db, $max, $start, $fail_ok); case 'DELETE': return $this->_do_query_delete($tokens, $query, $db, $max, $start, $fail_ok); case 'SELECT': $at = 0; $results = $this->_do_query_select($tokens, $query, $db, $max, $start, $fail_ok, $at); return $results; case 'DROP': return $this->_do_query_drop($tokens, $query, $db, $fail_ok); } return $this->_bad_query($query, $fail_ok, 'Unrecognised query type, ' . $tokens[0]); }