Move files in Phabricator one level up
Summary: - `kill_init.php` said "Moving 1000 files" - I hope that this is not some limit in `FileFinder`. - [src/infrastructure/celerity] `git mv utils.php map.php; git mv api/utils.php api.php` - Comment `phutil_libraries` in `.arcconfig` and run `arc liberate`. NOTE: `arc diff` timed out so I'm pushing it without review. Test Plan: /D1234 Browsed around, especially in `applications/repository/worker/commitchangeparser` and `applications/` in general. Auditors: epriestley Maniphest Tasks: T1103
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*/
|
||||
abstract class AphrontMySQLDatabaseConnectionBase
|
||||
extends AphrontDatabaseConnection {
|
||||
|
||||
private $configuration;
|
||||
private $connection;
|
||||
|
||||
private $nextError;
|
||||
|
||||
abstract protected function connect();
|
||||
abstract protected function rawQuery($raw_query);
|
||||
abstract protected function fetchAssoc($result);
|
||||
abstract protected function getErrorCode($connection);
|
||||
abstract protected function getErrorDescription($connection);
|
||||
|
||||
public function __construct(array $configuration) {
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
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 = addcslashes($value, '\%_');
|
||||
$value = $this->escapeString($value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getConfiguration($key, $default = null) {
|
||||
return idx($this->configuration, $key, $default);
|
||||
}
|
||||
|
||||
private function closeConnection() {
|
||||
if ($this->connection) {
|
||||
$this->connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function establishConnection() {
|
||||
$start = microtime(true);
|
||||
|
||||
$host = $this->getConfiguration('host');
|
||||
$database = $this->getConfiguration('database');
|
||||
|
||||
$profiler = PhutilServiceProfiler::getInstance();
|
||||
$call_id = $profiler->beginServiceCall(
|
||||
array(
|
||||
'type' => 'connect',
|
||||
'host' => $host,
|
||||
'database' => $database,
|
||||
));
|
||||
|
||||
$retries = max(1, PhabricatorEnv::getEnvConfig('mysql.connection-retries'));
|
||||
while ($retries--) {
|
||||
try {
|
||||
$conn = $this->connect();
|
||||
$profiler->endServiceCall($call_id, array());
|
||||
break;
|
||||
} catch (AphrontQueryException $ex) {
|
||||
if ($retries && $ex->getCode() == 2003) {
|
||||
$class = get_class($ex);
|
||||
$message = $ex->getMessage();
|
||||
phlog("Retrying ({$retries}) after {$class}: {$message}");
|
||||
} else {
|
||||
$profiler->endServiceCall($call_id, array());
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->connection = $conn;
|
||||
}
|
||||
|
||||
protected 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 = $this->fetchAssoc($res))) {
|
||||
$result[] = $row;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function executeRawQuery($raw_query) {
|
||||
$this->lastResult = null;
|
||||
$retries = max(1, PhabricatorEnv::getEnvConfig('mysql.connection-retries'));
|
||||
while ($retries--) {
|
||||
try {
|
||||
$this->requireConnection();
|
||||
|
||||
// TODO: Do we need to include transactional statements here?
|
||||
$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 = $this->rawQuery($raw_query);
|
||||
|
||||
$profiler->endServiceCall($call_id, array());
|
||||
|
||||
if ($this->nextError) {
|
||||
$result = null;
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$this->lastResult = $result;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->throwQueryException($this->connection);
|
||||
} catch (AphrontQueryConnectionLostException $ex) {
|
||||
if ($this->isInsideTransaction()) {
|
||||
// Zero out the transaction state to prevent a second exception
|
||||
// ("program exited with open transaction") from being thrown, since
|
||||
// we're about to throw a more relevant/useful one instead.
|
||||
$state = $this->getTransactionState();
|
||||
while ($state->getDepth()) {
|
||||
$state->decreaseDepth();
|
||||
}
|
||||
|
||||
// We can't close the connection before this because
|
||||
// isInsideTransaction() and getTransactionState() depend on the
|
||||
// connection.
|
||||
$this->closeConnection();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$this->closeConnection();
|
||||
|
||||
if (!$retries) {
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$class = get_class($ex);
|
||||
$message = $ex->getMessage();
|
||||
phlog("Retrying ({$retries}) after {$class}: {$message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function throwQueryException($connection) {
|
||||
if ($this->nextError) {
|
||||
$errno = $this->nextError;
|
||||
$error = 'Simulated error.';
|
||||
$this->nextError = null;
|
||||
} else {
|
||||
$errno = $this->getErrorCode($connection);
|
||||
$error = $this->getErrorDescription($connection);
|
||||
}
|
||||
|
||||
$exmsg = "#{$errno}: {$error}";
|
||||
|
||||
switch ($errno) {
|
||||
case 2013: // Connection Dropped
|
||||
case 2006: // Gone Away
|
||||
throw new AphrontQueryConnectionLostException($exmsg);
|
||||
case 1213: // Deadlock
|
||||
case 1205: // Lock wait timeout exceeded
|
||||
throw new AphrontQueryDeadlockException($exmsg);
|
||||
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($exmsg);
|
||||
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($exmsg);
|
||||
case 1146: // No such table
|
||||
case 1049: // No such database
|
||||
case 1054: // Unknown column "..." in field list
|
||||
throw new AphrontQuerySchemaException($exmsg);
|
||||
default:
|
||||
// TODO: 1064 is syntax error, and quite terrible in production.
|
||||
throw new AphrontQueryException($exmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the next query to fail with a simulated error. This should be used
|
||||
* ONLY for unit tests.
|
||||
*/
|
||||
public function simulateErrorOnNextQuery($error) {
|
||||
$this->nextError = $error;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user