mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 05:50:55 +01:00
Prepare for MySQLi support
Summary: This separates common MySQL stuff (identifiers and comments escaping, error codes, connection retries) from PHP extension specific stuff (connect, query, fetch, errors, escape string). Test Plan: / Use `AphrontMySQLiDatabaseConnection` in `PhabricatorLiskDAO`, load homepage, edit task, save task. Reviewers: epriestley Reviewed By: epriestley CC: nh, aran Differential Revision: https://secure.phabricator.com/D2113
This commit is contained in:
parent
2211a0b07e
commit
e69ba98e20
10 changed files with 238 additions and 67 deletions
|
@ -62,8 +62,10 @@ phutil_register_library_map(array(
|
|||
'AphrontKeyboardShortcutsAvailableView' => 'view/widget/keyboardshortcuts',
|
||||
'AphrontListFilterView' => 'view/layout/listfilter',
|
||||
'AphrontMiniPanelView' => 'view/layout/minipanel',
|
||||
'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
|
||||
'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql/mysql',
|
||||
'AphrontMySQLDatabaseConnectionBase' => 'storage/connection/mysql/base',
|
||||
'AphrontMySQLDatabaseConnectionTestCase' => 'storage/connection/mysql/__tests__',
|
||||
'AphrontMySQLiDatabaseConnection' => 'storage/connection/mysql/mysqli',
|
||||
'AphrontNullView' => 'view/null',
|
||||
'AphrontPHPHTTPSink' => 'aphront/sink/php',
|
||||
'AphrontPageView' => 'view/page/base',
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/base');
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql');
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql/mysql');
|
||||
phutil_require_module('phabricator', 'storage/queryfx');
|
||||
phutil_require_module('phabricator', 'view/control/table');
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql');
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql/mysql');
|
||||
phutil_require_module('phabricator', 'storage/lisk/dao');
|
||||
|
||||
phutil_require_module('phutil', 'symbols');
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
phutil_require_module('phabricator', 'applications/base/storage/configuration');
|
||||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
phutil_require_module('phabricator', 'infrastructure/setup/sql');
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql');
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql/mysql');
|
||||
phutil_require_module('phabricator', 'storage/queryfx');
|
||||
|
||||
phutil_require_module('phutil', 'filesystem');
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
/**
|
||||
* @group storage
|
||||
*/
|
||||
final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
||||
abstract class AphrontMySQLDatabaseConnectionBase
|
||||
extends AphrontDatabaseConnection {
|
||||
|
||||
private $configuration;
|
||||
private $connection;
|
||||
|
@ -28,15 +29,16 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
|
||||
private static $connectionCache = array();
|
||||
|
||||
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 escapeString($string) {
|
||||
$this->requireConnection();
|
||||
return mysql_real_escape_string($string, $this->connection);
|
||||
}
|
||||
|
||||
public function escapeColumnName($name) {
|
||||
return '`'.str_replace('`', '``', $name).'`';
|
||||
}
|
||||
|
@ -85,7 +87,7 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
return $value;
|
||||
}
|
||||
|
||||
private function getConfiguration($key, $default = null) {
|
||||
protected function getConfiguration($key, $default = null) {
|
||||
return idx($this->configuration, $key, $default);
|
||||
}
|
||||
|
||||
|
@ -105,7 +107,6 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
return "{$user}:{$host}:{$database}";
|
||||
}
|
||||
|
||||
|
||||
private function establishConnection() {
|
||||
$this->closeConnection();
|
||||
|
||||
|
@ -117,17 +118,6 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
|
||||
$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!");
|
||||
}
|
||||
|
||||
$user = $this->getConfiguration('user');
|
||||
$host = $this->getConfiguration('host');
|
||||
$database = $this->getConfiguration('database');
|
||||
|
||||
|
@ -143,32 +133,10 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
$retries = max(1, PhabricatorEnv::getEnvConfig('mysql.connection-retries'));
|
||||
while ($retries--) {
|
||||
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}.", $errno);
|
||||
}
|
||||
|
||||
if ($database !== null) {
|
||||
$ret = @mysql_select_db($database, $conn);
|
||||
if (!$ret) {
|
||||
$this->throwQueryException($conn);
|
||||
}
|
||||
mysql_set_charset('utf8', $conn);
|
||||
}
|
||||
|
||||
$conn = $this->connect();
|
||||
$profiler->endServiceCall($call_id, array());
|
||||
break;
|
||||
} catch (Exception $ex) {
|
||||
} catch (AphrontQueryException $ex) {
|
||||
if ($retries && $ex->getCode() == 2003) {
|
||||
$class = get_class($ex);
|
||||
$message = $ex->getMessage();
|
||||
|
@ -184,19 +152,7 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
$this->connection = $conn;
|
||||
}
|
||||
|
||||
public function getInsertID() {
|
||||
return mysql_insert_id($this->requireConnection());
|
||||
}
|
||||
|
||||
public function getAffectedRows() {
|
||||
return mysql_affected_rows($this->requireConnection());
|
||||
}
|
||||
|
||||
protected function getTransactionKey() {
|
||||
return (int)$this->requireConnection();
|
||||
}
|
||||
|
||||
private function requireConnection() {
|
||||
protected function requireConnection() {
|
||||
if (!$this->connection) {
|
||||
$this->establishConnection();
|
||||
}
|
||||
|
@ -209,7 +165,7 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
if ($res == null) {
|
||||
throw new Exception('No query result to fetch from!');
|
||||
}
|
||||
while (($row = mysql_fetch_assoc($res)) !== false) {
|
||||
while (($row = $this->fetchAssoc($res))) {
|
||||
$result[] = $row;
|
||||
}
|
||||
return $result;
|
||||
|
@ -239,7 +195,7 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
'write' => $is_write,
|
||||
));
|
||||
|
||||
$result = @mysql_query($raw_query, $this->connection);
|
||||
$result = $this->rawQuery($raw_query);
|
||||
|
||||
$profiler->endServiceCall($call_id, array());
|
||||
|
||||
|
@ -284,14 +240,14 @@ final class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
|
|||
}
|
||||
}
|
||||
|
||||
private function throwQueryException($connection) {
|
||||
protected function throwQueryException($connection) {
|
||||
if ($this->nextError) {
|
||||
$errno = $this->nextError;
|
||||
$error = 'Simulated error.';
|
||||
$this->nextError = null;
|
||||
} else {
|
||||
$errno = mysql_errno($connection);
|
||||
$error = mysql_error($connection);
|
||||
$errno = $this->getErrorCode($connection);
|
||||
$error = $this->getErrorDescription($connection);
|
||||
}
|
||||
|
||||
$exmsg = "#{$errno}: {$error}";
|
|
@ -11,7 +11,6 @@ phutil_require_module('phabricator', 'infrastructure/env');
|
|||
phutil_require_module('phabricator', 'storage/connection/base');
|
||||
phutil_require_module('phabricator', 'storage/exception/accessdenied');
|
||||
phutil_require_module('phabricator', 'storage/exception/base');
|
||||
phutil_require_module('phabricator', 'storage/exception/connection');
|
||||
phutil_require_module('phabricator', 'storage/exception/connectionlost');
|
||||
phutil_require_module('phabricator', 'storage/exception/duplicatekey');
|
||||
phutil_require_module('phabricator', 'storage/exception/recoverable');
|
||||
|
@ -22,4 +21,4 @@ phutil_require_module('phutil', 'serviceprofiler');
|
|||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('AphrontMySQLDatabaseConnection.php');
|
||||
phutil_require_source('AphrontMySQLDatabaseConnectionBase.php');
|
|
@ -0,0 +1,99 @@
|
|||
<?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
|
||||
*/
|
||||
final class AphrontMySQLDatabaseConnection
|
||||
extends AphrontMySQLDatabaseConnectionBase {
|
||||
|
||||
public function escapeString($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 getTransactionKey() {
|
||||
return (int)$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(
|
||||
"About to call mysql_connect(), but the PHP MySQL extension is not ".
|
||||
"available!");
|
||||
}
|
||||
|
||||
$user = $this->getConfiguration('user');
|
||||
$host = $this->getConfiguration('host');
|
||||
$database = $this->getConfiguration('database');
|
||||
|
||||
$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}.", $errno);
|
||||
}
|
||||
|
||||
if ($database !== null) {
|
||||
$ret = @mysql_select_db($database, $conn);
|
||||
if (!$ret) {
|
||||
$this->throwQueryException($conn);
|
||||
}
|
||||
}
|
||||
|
||||
mysql_set_charset('utf8', $conn);
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
protected function rawQuery($raw_query) {
|
||||
return @mysql_query($raw_query, $this->requireConnection());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
13
src/storage/connection/mysql/mysql/__init__.php
Normal file
13
src/storage/connection/mysql/mysql/__init__.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql/base');
|
||||
phutil_require_module('phabricator', 'storage/exception/connection');
|
||||
|
||||
|
||||
phutil_require_source('AphrontMySQLDatabaseConnection.php');
|
|
@ -0,0 +1,89 @@
|
|||
<?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
|
||||
*
|
||||
* @phutil-external-symbol class mysqli
|
||||
*/
|
||||
final class AphrontMySQLiDatabaseConnection
|
||||
extends AphrontMySQLDatabaseConnectionBase {
|
||||
|
||||
public function escapeString($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 getTransactionKey() {
|
||||
return spl_object_hash($this->requireConnection());
|
||||
}
|
||||
|
||||
protected function connect() {
|
||||
if (!class_exists('mysqli', false)) {
|
||||
throw new Exception(
|
||||
"About to call new mysqli(), but the PHP MySQLi extension is not ".
|
||||
"available!");
|
||||
}
|
||||
|
||||
$user = $this->getConfiguration('user');
|
||||
$host = $this->getConfiguration('host');
|
||||
$database = $this->getConfiguration('database');
|
||||
|
||||
$conn = @new mysqli(
|
||||
$host,
|
||||
$user,
|
||||
$this->getConfiguration('pass'),
|
||||
$database);
|
||||
|
||||
$errno = $conn->connect_errno;
|
||||
if ($errno) {
|
||||
$error = $conn->connect_error;
|
||||
throw new AphrontQueryConnectionException(
|
||||
"Attempt to connect to {$user}@{$host} failed with error ".
|
||||
"#{$errno}: {$error}.", $errno);
|
||||
}
|
||||
|
||||
$conn->set_charset('utf8');
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
protected function rawQuery($raw_query) {
|
||||
return @$this->requireConnection()->query($raw_query);
|
||||
}
|
||||
|
||||
protected function fetchAssoc($result) {
|
||||
return $result->fetch_assoc();
|
||||
}
|
||||
|
||||
protected function getErrorCode($connection) {
|
||||
return $connection->errno;
|
||||
}
|
||||
|
||||
protected function getErrorDescription($connection) {
|
||||
return $connection->error;
|
||||
}
|
||||
|
||||
}
|
13
src/storage/connection/mysql/mysqli/__init__.php
Normal file
13
src/storage/connection/mysql/mysqli/__init__.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'storage/connection/mysql/base');
|
||||
phutil_require_module('phabricator', 'storage/exception/connection');
|
||||
|
||||
|
||||
phutil_require_source('AphrontMySQLiDatabaseConnection.php');
|
Loading…
Reference in a new issue