Page MenuHomePhorge

No OneTemporary

diff --git a/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php b/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
index 4cce4ed84e..6a9b7639c9 100644
--- a/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
+++ b/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
@@ -1,278 +1,278 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group storage
*/
class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
private $config;
private $connection;
private static $connectionCache = array();
public function __construct(array $configuration) {
$this->configuration = $configuration;
}
public function escapeString($string) {
$this->requireConnection();
return mysql_real_escape_string($string, $this->connection);
}
public function escapeColumnName($name) {
return '`'.str_replace('`', '\\`', $name).'`';
}
public function escapeMultilineComment($comment) {
// These can either terminate a comment, confuse the hell out of the parser,
// make MySQL execute the comment as a query, or, in the case of semicolon,
// are quasi-dangerous because the semicolon could turn a broken query into
// a working query plus an ignored query.
static $map = array(
'--' => '(DOUBLEDASH)',
'*/' => '(STARSLASH)',
'//' => '(SLASHSLASH)',
'#' => '(HASH)',
'!' => '(BANG)',
';' => '(SEMICOLON)',
);
$comment = str_replace(
array_keys($map),
array_values($map),
$comment);
// For good measure, kill anything else that isn't a nice printable
// character.
$comment = preg_replace('/[^\x20-\x7F]+/', ' ', $comment);
return '/* '.$comment.' */';
}
public function escapeStringForLikeClause($value) {
$value = $this->escapeString($value);
// Ideally the query shouldn't be modified after safely escaping it,
// but we need to escape _ and % within LIKE terms.
$value = str_replace(
// Even though we've already escaped, we need to replace \ with \\
// because MYSQL unescapes twice inside a LIKE clause. See note
// at mysql.com. However, if the \ is being used to escape a single
// quote ('), then the \ should not be escaped. Thus, after all \
// are replaced with \\, we need to revert instances of \\' back to
// \'.
array('\\', '\\\\\'', '_', '%'),
array('\\\\', '\\\'', '\_', '\%'),
$value);
return $value;
}
private function getConfiguration($key, $default = null) {
return idx($this->configuration, $key, $default);
}
private function closeConnection() {
if ($this->connection) {
$this->connection = null;
$key = $this->getConnectionCacheKey();
unset(self::$connectionCache[$key]);
}
}
private function getConnectionCacheKey() {
$user = $this->getConfiguration('user');
$host = $this->getConfiguration('host');
$database = $this->getConfiguration('database');
return "{$user}:{$host}:{$database}";
}
private function establishConnection() {
$this->closeConnection();
$user = $this->getConfiguration('user');
$host = $this->getConfiguration('host');
$database = $this->getConfiguration('database');
$key = $this->getConnectionCacheKey();
if (isset(self::$connectionCache[$key])) {
$this->connection = self::$connectionCache[$key];
return;
}
$start = microtime(true);
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(
"About to call mysql_connect(), but the PHP MySQL extension is not ".
"available!");
}
$profiler = PhutilServiceProfiler::getInstance();
$call_id = $profiler->beginServiceCall(
array(
'type' => 'connect',
'host' => $host,
'database' => $database,
));
try {
$conn = @mysql_connect(
$host,
$user,
$this->getConfiguration('pass'),
$new_link = true,
$flags = 0);
if (!$conn) {
$errno = mysql_errno();
$error = mysql_error();
throw new AphrontQueryConnectionException(
"Attempt to connect to {$user}@{$host} failed with error #{$errno}: ".
"{$error}.");
}
if ($database !== null) {
$ret = @mysql_select_db($database, $conn);
if (!$ret) {
$this->throwQueryException($conn);
}
}
$profiler->endServiceCall($call_id, array());
} catch (Exception $ex) {
$profiler->endServiceCall($call_id, array());
throw $ex;
}
self::$connectionCache[$key] = $conn;
$this->connection = $conn;
}
public function getInsertID() {
return mysql_insert_id($this->requireConnection());
}
public function getAffectedRows() {
return mysql_affected_rows($this->requireConnection());
}
public function getTransactionKey() {
return (int)$this->requireConnection();
}
private function requireConnection() {
if (!$this->connection) {
$this->establishConnection();
}
return $this->connection;
}
public function selectAllResults() {
$result = array();
$res = $this->lastResult;
if ($res == null) {
throw new Exception('No query result to fetch from!');
}
while (($row = mysql_fetch_assoc($res)) !== false) {
$result[] = $row;
}
return $result;
}
public function executeRawQuery($raw_query) {
$this->lastResult = null;
$retries = 3;
while ($retries--) {
try {
$this->requireConnection();
// TODO: Do we need to include transactional statements here?
- $is_write = !preg_match('/^(SELECT|SHOW)\s/', $raw_query);
+ $is_write = !preg_match('/^(SELECT|SHOW|EXPLAIN)\s/', $raw_query);
if ($is_write) {
AphrontWriteGuard::willWrite();
}
$start = microtime(true);
$profiler = PhutilServiceProfiler::getInstance();
$call_id = $profiler->beginServiceCall(
array(
'type' => 'query',
'config' => $this->configuration,
'query' => $raw_query,
'write' => $is_write,
));
$result = @mysql_query($raw_query, $this->connection);
$profiler->endServiceCall($call_id, array());
if ($result) {
$this->lastResult = $result;
break;
}
$this->throwQueryException($this->connection);
} catch (AphrontQueryConnectionLostException $ex) {
if (!$retries) {
throw $ex;
}
if ($this->isInsideTransaction()) {
throw $ex;
}
$this->closeConnection();
}
}
}
private function throwQueryException($connection) {
$errno = mysql_errno($connection);
$error = mysql_error($connection);
switch ($errno) {
case 2013: // Connection Dropped
case 2006: // Gone Away
throw new AphrontQueryConnectionLostException("#{$errno}: {$error}");
case 1213: // Deadlock
case 1205: // Lock wait timeout exceeded
throw new AphrontQueryRecoverableException("#{$errno}: {$error}");
case 1062: // Duplicate Key
// NOTE: In some versions of MySQL we get a key name back here, but
// older versions just give us a key index ("key 2") so it's not
// portable to parse the key out of the error and attach it to the
// exception.
throw new AphrontQueryDuplicateKeyException("{$errno}: {$error}");
case 1044: // Access denied to database
case 1045: // Access denied (auth)
case 1142: // Access denied to table
case 1143: // Access denied to column
throw new AphrontQueryAccessDeniedException("#{$errno}: {$error}");
default:
// TODO: 1064 is syntax error, and quite terrible in production.
throw new AphrontQueryException("#{$errno}: {$error}");
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Jan 19 2025, 21:26 (6 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128923
Default Alt Text
(8 KB)

Event Timeline