diff --git a/src/aphront/storage/connection/mysql/AphrontMySQLDatabaseConnection.php b/src/aphront/storage/connection/mysql/AphrontMySQLDatabaseConnection.php index 7371863..40a2c6c 100644 --- a/src/aphront/storage/connection/mysql/AphrontMySQLDatabaseConnection.php +++ b/src/aphront/storage/connection/mysql/AphrontMySQLDatabaseConnection.php @@ -1,233 +1,233 @@ validateUTF8String($string); return $this->escapeBinaryString($string); } public function escapeBinaryString($string) { return mysql_real_escape_string($string, $this->requireConnection()); } public function getInsertID() { return mysql_insert_id($this->requireConnection()); } public function getAffectedRows() { return mysql_affected_rows($this->requireConnection()); } protected function closeConnection() { mysql_close($this->requireConnection()); } protected function connect() { if (!function_exists('mysql_connect')) { // We have to '@' the actual call since it can spew all sorts of silly // noise, but it will also silence fatals caused by not having MySQL // installed, which has bitten me on three separate occasions. Make sure // such failures are explicit and loud. throw new Exception( pht( 'About to call %s, but the PHP MySQL extension is not available!', 'mysql_connect()')); } $user = $this->getConfiguration('user'); $host = $this->getConfiguration('host'); $port = $this->getConfiguration('port'); if ($port) { $host .= ':'.$port; } $database = $this->getConfiguration('database'); $pass = $this->getConfiguration('pass'); if ($pass instanceof PhutilOpaqueEnvelope) { $pass = $pass->openEnvelope(); } $timeout = $this->getConfiguration('timeout'); $timeout_ini = 'mysql.connect_timeout'; if ($timeout) { $old_timeout = ini_get($timeout_ini); ini_set($timeout_ini, $timeout); } try { $conn = @mysql_connect( $host, $user, $pass, $new_link = true, $flags = 0); } catch (Exception $ex) { if ($timeout) { ini_set($timeout_ini, $old_timeout); } throw $ex; } if ($timeout) { ini_set($timeout_ini, $old_timeout); } if (!$conn) { $errno = mysql_errno(); $error = mysql_error(); $this->throwConnectionException($errno, $error, $user, $host); } if ($database !== null) { $ret = @mysql_select_db($database, $conn); if (!$ret) { $this->throwQueryException($conn); } } $ok = @mysql_set_charset('utf8mb4', $conn); if (!$ok) { - mysql_set_charset('utf8', $conn); + mysql_set_charset('binary', $conn); } return $conn; } protected function rawQuery($raw_query) { return @mysql_query($raw_query, $this->requireConnection()); } /** * @phutil-external-symbol function mysql_multi_query * @phutil-external-symbol function mysql_fetch_result * @phutil-external-symbol function mysql_more_results * @phutil-external-symbol function mysql_next_result */ protected function rawQueries(array $raw_queries) { $conn = $this->requireConnection(); $results = array(); if (!function_exists('mysql_multi_query')) { foreach ($raw_queries as $key => $raw_query) { $results[$key] = $this->processResult($this->rawQuery($raw_query)); } return $results; } if (!mysql_multi_query(implode("\n;\n\n", $raw_queries), $conn)) { $ex = $this->processResult(false); return array_fill_keys(array_keys($raw_queries), $ex); } $processed_all = false; foreach ($raw_queries as $key => $raw_query) { $results[$key] = $this->processResult(@mysql_fetch_result($conn)); if (!mysql_more_results($conn)) { $processed_all = true; break; } mysql_next_result($conn); } if (!$processed_all) { throw new Exception( pht('There are some results left in the result set.')); } return $results; } protected function freeResult($result) { mysql_free_result($result); } public function supportsParallelQueries() { // fb_parallel_query() doesn't support results with different columns. return false; } /** * @phutil-external-symbol function fb_parallel_query */ public function executeParallelQueries( array $queries, array $conns = array()) { assert_instances_of($conns, __CLASS__); $map = array(); $is_write = false; foreach ($queries as $id => $query) { $is_write = $is_write || $this->checkWrite($query); $conn = idx($conns, $id, $this); $host = $conn->getConfiguration('host'); $port = 0; $match = null; if (preg_match('/(.+):(.+)/', $host, $match)) { list(, $host, $port) = $match; } $pass = $conn->getConfiguration('pass'); if ($pass instanceof PhutilOpaqueEnvelope) { $pass = $pass->openEnvelope(); } $map[$id] = array( 'sql' => $query, 'ip' => $host, 'port' => $port, 'username' => $conn->getConfiguration('user'), 'password' => $pass, 'db' => $conn->getConfiguration('database'), ); } $profiler = PhutilServiceProfiler::getInstance(); $call_id = $profiler->beginServiceCall( array( 'type' => 'multi-query', 'queries' => $queries, 'write' => $is_write, )); $map = fb_parallel_query($map); $profiler->endServiceCall($call_id, array()); $results = array(); $pos = 0; $err_pos = 0; foreach ($queries as $id => $query) { $errno = idx(idx($map, 'errno', array()), $err_pos); $err_pos++; if ($errno) { try { $this->throwQueryCodeException($errno, $map['error'][$id]); } catch (Exception $ex) { $results[$id] = $ex; } continue; } $results[$id] = $map['result'][$pos]; $pos++; } return $results; } protected function fetchAssoc($result) { return mysql_fetch_assoc($result); } protected function getErrorCode($connection) { return mysql_errno($connection); } protected function getErrorDescription($connection) { return mysql_error($connection); } } diff --git a/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php b/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php index f3c5d88..240dff4 100644 --- a/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php +++ b/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php @@ -1,199 +1,199 @@ validateUTF8String($string); return $this->escapeBinaryString($string); } public function escapeBinaryString($string) { return $this->requireConnection()->escape_string($string); } public function getInsertID() { return $this->requireConnection()->insert_id; } public function getAffectedRows() { return $this->requireConnection()->affected_rows; } protected function closeConnection() { $this->requireConnection()->close(); } protected function connect() { if (!class_exists('mysqli', false)) { throw new Exception(pht( 'About to call new %s, but the PHP MySQLi extension is not available!', 'mysqli()')); } $user = $this->getConfiguration('user'); $host = $this->getConfiguration('host'); $port = $this->getConfiguration('port'); $database = $this->getConfiguration('database'); $pass = $this->getConfiguration('pass'); if ($pass instanceof PhutilOpaqueEnvelope) { $pass = $pass->openEnvelope(); } // If the host is "localhost", the port is ignored and mysqli attempts to // connect over a socket. if ($port) { if ($host === 'localhost' || $host === null) { $host = '127.0.0.1'; } } $conn = mysqli_init(); $timeout = $this->getConfiguration('timeout'); if ($timeout) { $conn->options(MYSQLI_OPT_CONNECT_TIMEOUT, $timeout); } @$conn->real_connect( $host, $user, $pass, $database, $port); $errno = $conn->connect_errno; if ($errno) { $error = $conn->connect_error; $this->throwConnectionException($errno, $error, $user, $host); } $ok = @$conn->set_charset('utf8mb4'); if (!$ok) { - $ok = $conn->set_charset('utf8'); + $ok = $conn->set_charset('binary'); } return $conn; } protected function rawQuery($raw_query) { $conn = $this->requireConnection(); $time_limit = $this->getQueryTimeout(); // If we have a query time limit, run this query synchronously but use // the async API. This allows us to kill queries which take too long // without requiring any configuration on the server side. if ($time_limit && $this->supportsAsyncQueries()) { $conn->query($raw_query, MYSQLI_ASYNC); $read = array($conn); $error = array($conn); $reject = array($conn); $result = mysqli::poll($read, $error, $reject, $time_limit); if ($result === false) { $this->closeConnection(); throw new Exception( pht('Failed to poll mysqli connection!')); } else if ($result === 0) { $this->closeConnection(); throw new AphrontQueryTimeoutQueryException( pht( 'Query timed out after %s second(s)!', new PhutilNumber($time_limit))); } return @$conn->reap_async_query(); } return @$conn->query($raw_query); } protected function rawQueries(array $raw_queries) { $conn = $this->requireConnection(); $have_result = false; $results = array(); foreach ($raw_queries as $key => $raw_query) { if (!$have_result) { // End line in front of semicolon to allow single line comments at the // end of queries. $have_result = $conn->multi_query(implode("\n;\n\n", $raw_queries)); } else { $have_result = $conn->next_result(); } array_shift($raw_queries); $result = $conn->store_result(); if (!$result && !$this->getErrorCode($conn)) { $result = true; } $results[$key] = $this->processResult($result); } if ($conn->more_results()) { throw new Exception( pht('There are some results left in the result set.')); } return $results; } protected function freeResult($result) { $result->free_result(); } protected function fetchAssoc($result) { return $result->fetch_assoc(); } protected function getErrorCode($connection) { return $connection->errno; } protected function getErrorDescription($connection) { return $connection->error; } public function supportsAsyncQueries() { return defined('MYSQLI_ASYNC'); } public function asyncQuery($raw_query) { $this->checkWrite($raw_query); $async = $this->beginAsyncConnection(); $async->query($raw_query, MYSQLI_ASYNC); return $async; } public static function resolveAsyncQueries(array $conns, array $asyncs) { assert_instances_of($conns, __CLASS__); assert_instances_of($asyncs, 'mysqli'); $read = $error = $reject = array(); foreach ($asyncs as $async) { $read[] = $error[] = $reject[] = $async; } if (!mysqli::poll($read, $error, $reject, 0)) { return array(); } $results = array(); foreach ($read as $async) { $key = array_search($async, $asyncs, $strict = true); $conn = $conns[$key]; $conn->endAsyncConnection($async); $results[$key] = $conn->processResult($async->reap_async_query()); } return $results; } }