cache_db)) { if ($this->last_select_db[1] !== $db_name) { mysqli_select_db($this->cache_db[$x], $db_name); $this->last_select_db = array($this->cache_db[$x], $db_name); } return array($this->cache_db[$x], $db_name); } mysqli_report(MYSQLI_REPORT_OFF); // Connect $db_port = 3306; if (strpos($db_host, ':') !== false) { list($db_host, $_db_port) = explode(':', $db_host); $db_port = intval($_db_port); } $db = @mysqli_connect(($persistent ? 'p:' : '') . $db_host, $db_user, $db_password, '', $db_port); if ($db === false) { $error = 'Could not connect to database-server (when authenticating) (' . mysqli_connect_error() . ')'; if ($fail_ok) { echo ((running_script('install')) && (get_param_string('type', '') == 'ajax_db_details')) ? strip_html($error) : $error; return null; } critical_error('PASSON', $error); //warn_exit(do_lang_tempcode('CONNECT_DB_ERROR')); } if (!mysqli_select_db($db, $db_name)) { if ($db_user == 'root') { @mysqli_query($db, 'CREATE DATABASE IF NOT EXISTS ' . $db_name); } if (!mysqli_select_db($db, $db_name)) { $error = 'Could not connect to database (' . mysqli_error($db) . ')'; if ($fail_ok) { echo $error . "\n"; return null; } critical_error('PASSON', $error); //warn_exit(do_lang_tempcode('CONNECT_ERROR')); } } $this->last_select_db = array($db, $db_name); $this->cache_db[$x] = $db; global $SITE_INFO; if (empty($SITE_INFO['database_charset'])) { $SITE_INFO['database_charset'] = (get_charset() == 'utf-8') ? 'utf8mb4' : 'latin1'; } if (function_exists('mysqli_set_charset')) { $test = @mysqli_set_charset($db, $SITE_INFO['database_charset']); if ((!$test) && ($SITE_INFO['database_charset'] == 'utf8mb4')) { // Conflict between compiled-in MySQL client library and what the server supports $test = @mysqli_set_charset($db, 'utf8'); @mysqli_query($db, 'SET NAMES "' . addslashes('utf8mb4') . '"'); } } else { @mysqli_query($db, 'SET NAMES "' . addslashes($SITE_INFO['database_charset']) . '"'); } if (!empty($SITE_INFO['database_collation'])) { @mysqli_query($db, 'SET collation_connection=' . $SITE_INFO['database_collation']); } @mysqli_query($db, 'SET wait_timeout=28800'); @mysqli_query($db, 'SET sql_big_selects=1'); if ((get_forum_type() == 'cns') && (!$GLOBALS['IN_MINIKERNEL_VERSION'])) { @mysqli_query($db, 'SET sql_mode=\'STRICT_ALL_TABLES\''); } else { $test = @mysqli_query($db, 'SET sql_mode=\'MYSQL40\''); // We may be in some legacy context, such as backup restoration, upgrader, or another forum driver if ($test === false) { // Won't work on MySQL 8 for example @mysqli_query($db, 'SET sql_mode=\'STRICT_ALL_TABLES\''); } } // NB: Can add ,ONLY_FULL_GROUP_BY for testing on what other DBs will do, but can_arbitrary_groupby() would need to be made to return false return array($db, $db_name); } /** * Find whether full-text-search is present * * @param array $db A DB connection * @return boolean Whether it is */ public function db_has_full_text($db) { return true; } /** * Find whether subquery support is present * * @param array $db A DB connection * @return boolean Whether it is */ public function db_has_subqueries($db) { return true; } /** * Find whether collate support is present * * @param array $db A DB connection * @return boolean Whether it is */ public function db_has_collate_settings($db) { return true; } /** * Find whether full-text-boolean-search is present * * @return boolean Whether it is */ public function db_has_full_text_boolean() { return true; } /** * Escape a string so it may be inserted into a query. If SQL statements are being built up and passed using db_query then it is essential that this is used for security reasons. Otherwise, the abstraction layer deals with the situation. * * @param string $string The string * @return string The escaped string */ public function db_escape_string($string) { if (function_exists('ctype_alnum')) { if (ctype_alnum($string)) { return $string; // No non-trivial characters } } if (preg_match('#[^a-zA-Z0-9\.]#', $string) === 0) { return $string; // No non-trivial characters } $string = fix_bad_unicode($string); if ($this->last_select_db === null) { return addslashes($string); } return mysqli_real_escape_string($this->last_select_db[0], $string); } /** * This function is a very basic query executor. It shouldn't usually be used by you, as there are abstracted versions available. * * @param string $query The complete SQL query * @param array $db_parts A DB connection * @param ?integer $max The maximum number of rows to affect (null: no limit) * @param ?integer $start The start row to affect (null: no specification) * @param boolean $fail_ok Whether to output an error on failure * @param boolean $get_insert_id Whether to get the autoincrement ID created for an insert query * @return ?mixed The results (null: no results), or the insert ID */ public function db_query($query, $db_parts, $max = null, $start = null, $fail_ok = false, $get_insert_id = false) { list($db, $db_name) = $db_parts; if (isset($query[500000])) { // Let's hope we can fail on this, because it's a huge query. We can only allow it if MySQL can. $test_result = $this->db_query('SHOW VARIABLES LIKE \'max_allowed_packet\'', $db_parts, null, null, true); if (!is_array($test_result)) { return null; } if (intval($test_result[0]['Value']) < intval(strlen($query) * 1.2)) { if ($get_insert_id) { fatal_exit(do_lang_tempcode('QUERY_FAILED_TOO_BIG', escape_html($query), escape_html(integer_format(strlen($query))), escape_html(integer_format(intval($test_result[0]['Value']))))); } else { attach_message(do_lang_tempcode('QUERY_FAILED_TOO_BIG', escape_html(substr($query, 0, 300)) . '...', escape_html(integer_format(strlen($query))), escape_html(integer_format(intval($test_result[0]['Value'])))), 'warn'); } return null; } } if ($this->last_select_db[1] !== $db_name) { mysqli_select_db($db, $db_name); $this->last_select_db = array($db, $db_name); } static $version = null; if ($version === null) { $version = mysqli_get_server_version($db); } if ($version >= 80000) { $query = $this->fix_mysql8_query($query); } $this->apply_sql_limit_clause($query, $max, $start); $results = @mysqli_query($db, $query); if (($results === false) && ((!$fail_ok) || (strpos(mysqli_error($db), 'is marked as crashed and should be repaired') !== false))) { $err = mysqli_error($db); if ((function_exists('mysqli_ping')) && ($err == 'MySQL server has gone away') && (!$this->reconnected_once)) { safe_ini_set('mysqli.reconnect', '1'); $this->reconnected_once = true; mysqli_ping($db); $ret = $this->db_query($query, $db_parts, null/*already encoded*/, null/*already encoded*/, $fail_ok, $get_insert_id); $this->reconnected_once = false; return $ret; } if (function_exists('ocp_mark_as_escaped')) { ocp_mark_as_escaped($err); } if ((!running_script('upgrader')) && ((!get_mass_import_mode()) || (get_param_integer('keep_fatalistic', 0) == 1)) && (strpos($err, 'Duplicate entry') === false)) { $matches = array(); if (preg_match('#/(\w+)\' is marked as crashed and should be repaired#U', $err, $matches) !== 0) { $this->db_query('REPAIR TABLE ' . $matches[1], $db_parts); } if (!function_exists('do_lang') || is_null(do_lang('QUERY_FAILED', null, null, null, null, false))) { fatal_exit(htmlentities('Query failed: ' . $query . ' : ' . $err)); } fatal_exit(do_lang_tempcode('QUERY_FAILED', escape_html($query), ($err))); } else { echo htmlentities('Database query failed: ' . $query . ' [') . ($err) . htmlentities(']') . "
\n"; return null; } } $sub = substr(ltrim($query), 0, 4); if (($results !== true) && (($sub === '(SEL') || ($sub === 'SELE') || ($sub === 'sele') || ($sub === 'CHEC') || ($sub === 'EXPL') || ($sub === 'REPA') || ($sub === 'DESC') || ($sub === 'SHOW')) && ($results !== false)) { return $this->db_get_query_rows($results, $query, $start); } if ($get_insert_id) { if (strtoupper(substr($query, 0, 7)) === 'UPDATE ') { return mysqli_affected_rows($db); } $ins = mysqli_insert_id($db); if ($ins === 0) { $table = substr($query, 12, strpos($query, ' ', 12) - 12); $rows = $this->db_query('SELECT MAX(id) AS x FROM ' . $table, $db_parts, 1, 0, false, false); return $rows[0]['x']; } return $ins; } return null; } /** * Get the rows returned from a SELECT query. * * @param resource $results The query result pointer * @param string $query The complete SQL query (useful for debugging) * @param ?integer $start Whether to start reading from (null: irrelevant) * @return array A list of row maps */ public function db_get_query_rows($results, $query, $start = null) { $names = array(); $types = array(); $fields = mysqli_fetch_fields($results); foreach ($fields as $x => $field) { $names[$x] = $field->name; $types[$x] = $field->type; } $out = array(); $newrow = array(); while (($row = mysqli_fetch_row($results)) !== null) { $j = 0; foreach ($row as $v) { $name = $names[$j]; $type = $types[$j]; if (($type === 1) || ($type === 2) || ($type === 3) || ($type === 8) || ($type === 9)) { // Integer field of some kind if ($v === null) { $newrow[$name] = null; } else { $newrow[$name] = intval($v); } } elseif (($type === 4) || ($type === 5) || ($type === 246)) { // Decimal field of some kind if ($v === null) { $newrow[$name] = null; } else { $newrow[$name] = floatval($v); } } elseif ($type === 16) { // Bit field if ((strlen($v) === 1) && (ord($v[0]) <= 1)) { $newrow[$name] = ord($v); // 0/1 char format } else { $newrow[$name] = intval($v); // Int-as-string format } } else { $newrow[$name] = $v; } $j++; } $out[] = $newrow; } mysqli_free_result($results); return $out; } }