- Patch #329080 by chx, Damien Tournoud, pwolanin: statics in objects are not per-instance.

merge-requests/26/head
Dries Buytaert 2008-11-08 22:52:24 +00:00
parent 50b3072f55
commit a7f6efc8a1
3 changed files with 145 additions and 57 deletions

View File

@ -155,6 +155,68 @@ abstract class DatabaseConnection extends PDO {
*/
protected $logger = NULL;
/**
* Cache of prepared statements.
*
* This cache only lasts as long as the current page request, so it's not
* as useful as it could be, but every little bit helps.
*
* @var Array
*/
protected $preparedStatements = array();
/**
* The name of the Select class for this connection.
*
* Normally this and the following class names would be static variables,
* but statics in methods are still global and shared by all instances.
*
* @var string
*/
protected $selectClass = NULL;
/**
* The name of the Delete class for this connection.
*
* @var string
*/
protected $deleteClass = NULL;
/**
* The name of the Insert class for this connection.
*
* @var string
*/
protected $insertClass = NULL;
/**
* The name of the Merge class for this connection.
*
* @var string
*/
protected $mergeClass = NULL;
/**
* The name of the Update class for this connection.
*
* @var string
*/
protected $updateClass = NULL;
/**
* The name of the Transaction class for this connection.
*
* @var string
*/
protected $transactionClass = NULL;
/**
* The schema object for this connection.
*
* @var object
*/
protected $schema = NULL;
function __construct($dsn, $username, $password, $driver_options = array()) {
// Because the other methods don't seem to work right.
$driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
@ -262,7 +324,7 @@ abstract class DatabaseConnection extends PDO {
/**
* Prepare a query string and return the prepared statement.
*
* This method statically caches prepared statements, reusing them when
* This method caches prepared statements, reusing them when
* possible. It also prefixes tables names enclosed in curly-braces.
*
* @param $query
@ -272,13 +334,12 @@ abstract class DatabaseConnection extends PDO {
* A PDO prepared statement ready for its execute() method.
*/
protected function prepareQuery($query) {
static $statements = array();
$query = self::prefixTables($query);
if (empty($statements[$query])) {
if (empty($this->preparedStatements[$query])) {
// Call PDO::prepare.
$statements[$query] = parent::prepare($query);
$this->preparedStatements[$query] = parent::prepare($query);
}
return $statements[$query];
return $this->preparedStatements[$query];
}
/**
@ -352,8 +413,7 @@ abstract class DatabaseConnection extends PDO {
*
* This method provides a central handler for the actual execution
* of every query. All queries executed by Drupal are executed as
* PDO prepared statements. This method statically caches those
* prepared statements, reusing them when possible.
* PDO prepared statements.
*
* @param $query
* The query to execute. In most cases this will be a string containing
@ -444,14 +504,18 @@ abstract class DatabaseConnection extends PDO {
* A new SelectQuery object.
*/
public function select($table, $alias = NULL, Array $options = array()) {
static $class_type;
if (empty($class_type)) {
$class_type = 'SelectQuery_' . $this->driver();
if (!class_exists($class_type)) {
$class_type = 'SelectQuery';
if (empty($this->selectClass)) {
$this->selectClass = 'SelectQuery_' . $this->driver();
if (!class_exists($this->selectClass)) {
$this->selectClass = 'SelectQuery';
}
}
return new $class_type($table, $alias, $this, $options);
$class = $this->selectClass;
// new is documented as the highest precedence operator so this will
// create a class named $class and pass the arguments into the constructor,
// instead of calling a function named $class with the arguments listed and
// then creating using the return value as the class name.
return new $class($table, $alias, $this, $options);
}
/**
@ -464,14 +528,14 @@ abstract class DatabaseConnection extends PDO {
* A new InsertQuery object.
*/
public function insert($table, Array $options = array()) {
static $class_type;
if (empty($class_type)) {
$class_type = 'InsertQuery_' . $this->driver();
if (!class_exists($class_type)) {
$class_type = 'InsertQuery';
if (empty($this->insertClass)) {
$this->insertClass = 'InsertQuery_' . $this->driver();
if (!class_exists($this->insertClass)) {
$this->insertClass = 'InsertQuery';
}
}
return new $class_type($this, $table, $options);
$class = $this->insertClass;
return new $class($this, $table, $options);
}
/**
@ -484,16 +548,17 @@ abstract class DatabaseConnection extends PDO {
* A new MergeQuery object.
*/
public function merge($table, Array $options = array()) {
static $class_type;
if (empty($class_type)) {
$class_type = 'MergeQuery_' . $this->driver();
if (!class_exists($class_type)) {
$class_type = 'MergeQuery';
if (empty($this->mergeClass)) {
$this->mergeClass = 'MergeQuery_' . $this->driver();
if (!class_exists($this->mergeClass)) {
$this->mergeClass = 'MergeQuery';
}
}
return new $class_type($this, $table, $options);
$class = $this->mergeClass;
return new $class($this, $table, $options);
}
/**
* Prepare and return an UPDATE query object with the specified ID.
*
@ -504,14 +569,14 @@ abstract class DatabaseConnection extends PDO {
* A new UpdateQuery object.
*/
public function update($table, Array $options = array()) {
static $class_type;
if (empty($class_type)) {
$class_type = 'UpdateQuery_' . $this->driver();
if (!class_exists($class_type)) {
$class_type = 'UpdateQuery';
if (empty($this->updateClass)) {
$this->updateClass = 'UpdateQuery_' . $this->driver();
if (!class_exists($this->updateClass)) {
$this->updateClass = 'UpdateQuery';
}
}
return new $class_type($this, $table, $options);
$class = $this->updateClass;
return new $class($this, $table, $options);
}
/**
@ -524,14 +589,14 @@ abstract class DatabaseConnection extends PDO {
* A new DeleteQuery object.
*/
public function delete($table, Array $options = array()) {
static $class_type;
if (empty($class_type)) {
$class_type = 'DeleteQuery_' . $this->driver();
if (!class_exists($class_type)) {
$class_type = 'DeleteQuery';
if (empty($this->deleteClass)) {
$this->deleteClass = 'DeleteQuery_' . $this->driver();
if (!class_exists($this->deleteClass)) {
$this->deleteClass = 'DeleteQuery';
}
}
return new $class_type($this, $table, $options);
$class = $this->deleteClass;
return new $class($this, $table, $options);
}
/**
@ -543,12 +608,11 @@ abstract class DatabaseConnection extends PDO {
* The DatabaseSchema object for this connection.
*/
public function schema() {
static $schema;
if (empty($schema)) {
if (empty($this->schema)) {
$class_type = 'DatabaseSchema_' . $this->driver();
$schema = new $class_type($this);
$this->schema = new $class_type($this);
}
return $schema;
return $this->schema;
}
/**
@ -575,19 +639,17 @@ abstract class DatabaseConnection extends PDO {
* @see DatabaseTransaction
*/
public function startTransaction($required = FALSE) {
static $class_type;
if ($required && !$this->supportsTransactions()) {
throw new TransactionsNotSupportedException();
}
if (empty($class_type)) {
$class_type = 'DatabaseTransaction_' . $this->driver();
if (!class_exists($class_type)) {
$class_type = 'DatabaseTransaction';
if (empty($this->transactionClass)) {
$this->transactionClass = 'DatabaseTransaction_' . $this->driver();
if (!class_exists($this->transactionClass)) {
$this->transactionClass = 'DatabaseTransaction';
}
}
return new $class_type($this);
return new $this->transactionClass($this);
}
/**

View File

@ -400,6 +400,13 @@ class SelectQuery extends Query implements QueryConditionInterface, QueryAlterab
return $this;
}
/**
* Private list of aliases already attributed to expression fields.
*
* @var Array
*/
private $expressionAliases = array();
/**
* Adds an expression to the list of "fields" to be SELECTed.
*
@ -412,7 +419,7 @@ class SelectQuery extends Query implements QueryConditionInterface, QueryAlterab
* @param $alias
* The alias for this expression. If not specified, one will be generated
* automatically in the form "expression_#". The alias will be checked for
* uniqueness, so the requested alias may not be the alias that is asigned
* uniqueness, so the requested alias may not be the alias that is assigned
* in all cases.
* @param $arguments
* Any placeholder arguments needed for this expression.
@ -420,19 +427,16 @@ class SelectQuery extends Query implements QueryConditionInterface, QueryAlterab
* The unique alias that was assigned for this expression.
*/
public function addExpression($expression, $alias = NULL, $arguments = array()) {
static $alaises = array();
if (empty($alias)) {
$alias = 'expression';
}
if (empty($aliases[$alias])) {
$aliases[$alias] = 1;
}
if (!empty($this->expressions[$alias])) {
$alias = $alias . '_' . $aliases[$alias]++;
$alias_candidate = $alias;
$count = 2;
while (!empty($this->expressions[$alias_candidate])) {
$alias_candidate = $alias . '_' . $count++;
}
$alias = $alias_candidate;
$this->expressions[$alias] = array(
'expression' => $expression,

View File

@ -1043,6 +1043,28 @@ class DatabaseSelectTestCase extends DatabaseTestCase {
$this->assertEqual($record->$age_field, 27*2, t('Fetched age expression is correct.'));
}
/**
* Test SELECT statements with multiple expressions.
*/
function testSimpleSelectExpressionMultiple() {
$query = db_select('test');
$name_field = $query->addField('test', 'name');
$age_double_field = $query->addExpression("age*2");
$age_triple_field = $query->addExpression("age*3");
$query->condition('age', 27);
$result = $query->execute();
// Check that the aliases are being created the way we want.
$this->assertEqual($age_double_field, 'expression', t('Double age field alias is correct.'));
$this->assertEqual($age_triple_field, 'expression_2', t('Triple age field alias is correct.'));
// Ensure that we got the right record.
$record = $result->fetch();
$this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
$this->assertEqual($record->$age_double_field, 27*2, t('Fetched double age expression is correct.'));
$this->assertEqual($record->$age_triple_field, 27*3, t('Fetched triple age expression is correct.'));
}
/**
* Test adding multiple fields to a select statement at the same time.
*/