#496516 by Crell and Berdir: Moved query_alter() into a preExecute() method, so that modules can know the final query/arguments before they are run.

merge-requests/26/head
Angie Byron 2009-08-26 04:58:23 +00:00
parent e832899658
commit fd164e9d02
4 changed files with 123 additions and 7 deletions

View File

@ -433,6 +433,8 @@ class InsertQuery extends Query {
* in multi-insert loops. * in multi-insert loops.
*/ */
public function execute() { public function execute() {
// If validation fails, simply return NULL.
// Note that validation routines in preExecute() may throw exceptions instead.
if (!$this->preExecute()) { if (!$this->preExecute()) {
return NULL; return NULL;
} }
@ -489,7 +491,7 @@ class InsertQuery extends Query {
* @return * @return
* TRUE if the validation was successful, FALSE if not. * TRUE if the validation was successful, FALSE if not.
*/ */
protected function preExecute() { public function preExecute() {
// Confirm that the user did not try to specify an identical // Confirm that the user did not try to specify an identical
// field and default field. // field and default field.
if (array_intersect($this->insertFields, $this->defaultFields)) { if (array_intersect($this->insertFields, $this->defaultFields)) {
@ -721,13 +723,29 @@ class MergeQuery extends Query {
return $this; return $this;
} }
public function execute() { /**
* Generic preparation and validation for a MERGE query.
*
* @return
* TRUE if the validation was successful, FALSE if not.
*/
public function preExecute() {
// A merge query without any key field is invalid. // A merge query without any key field is invalid.
if (count($this->keyFields) == 0) { if (count($this->keyFields) == 0) {
throw new InvalidMergeQueryException("You need to specify key fields before executing a merge query"); throw new InvalidMergeQueryException("You need to specify key fields before executing a merge query");
} }
return TRUE;
}
public function execute() {
// If validation fails, simply return NULL.
// Note that validation routines in preExecute() may throw exceptions instead.
if (!$this->preExecute()) {
return NULL;
}
// In the degenerate case of this query type, we have to run multiple // In the degenerate case of this query type, we have to run multiple
// queries as there is no universal single-query mechanism that will work. // queries as there is no universal single-query mechanism that will work.
// Our degenerate case is not designed for performance efficiency but // Our degenerate case is not designed for performance efficiency but

View File

@ -358,6 +358,19 @@ interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableIn
*/ */
public function countQuery(); public function countQuery();
/**
* Indicates if preExecute() has already been called on that object.
*/
public function isPrepared();
/**
* Generic preparation and validation for a SELECT query.
*
* @return
* TRUE if the validation was successful, FALSE if not.
*/
public function preExecute(SelectQueryInterface $query = NULL);
/** /**
* Clone magic method. * Clone magic method.
* *
@ -501,7 +514,27 @@ class SelectQueryExtender implements SelectQueryInterface {
return $this->query->getArguments(); return $this->query->getArguments();
} }
public function isPrepared() {
return $this->query->isPrepared();
}
public function preExecute(SelectQueryInterface $query = NULL) {
// If no query object is passed in, use $this.
if (is_null($query)) {
$query = $this;
}
return $this->query->preExecute($query);
}
public function execute() { public function execute() {
// By calling preExecute() here, we force it to preprocess the extender
// object rather than just the base query object. That means
// hook_query_alter() gets access to the extended object.
if (!$this->preExecute($this)) {
return NULL;
}
return $this->query->execute(); return $this->query->execute();
} }
@ -718,6 +751,12 @@ class SelectQuery extends Query implements SelectQueryInterface {
*/ */
protected $range; protected $range;
/**
* Indicates if preExecute() has already been called.
* @var boolean
*/
protected $prepared = FALSE;
public function __construct($table, $alias = NULL, DatabaseConnection $connection, $options = array()) { public function __construct($table, $alias = NULL, DatabaseConnection $connection, $options = array()) {
$options['return'] = Database::RETURN_STATEMENT; $options['return'] = Database::RETURN_STATEMENT;
parent::__construct($connection, $options); parent::__construct($connection, $options);
@ -880,14 +919,46 @@ class SelectQuery extends Query implements SelectQueryInterface {
return $args; return $args;
} }
public function execute() { /**
* Indicates if preExecute() has already been called on that object.
*/
public function isPrepared() {
return $this->prepared;
}
/**
* Generic preparation and validation for a SELECT query.
*
* @return
* TRUE if the validation was successful, FALSE if not.
*/
public function preExecute(SelectQueryInterface $query = NULL) {
// If no query object is passed in, use $this.
if (is_null($query)) {
$query = $this;
}
// Only execute this once.
if ($query->isPrepared()) {
return TRUE;
}
// Modules may alter all queries or only those having a particular tag. // Modules may alter all queries or only those having a particular tag.
drupal_alter('query', $this); drupal_alter('query', $query);
if (isset($this->alterTags)) { if (isset($this->alterTags)) {
foreach ($this->alterTags as $tag => $value) { foreach ($this->alterTags as $tag => $value) {
drupal_alter("query_$tag", $this); drupal_alter("query_$tag", $query);
} }
} }
return $this->prepared = TRUE;
}
public function execute() {
// If validation fails, simply return NULL.
// Note that validation routines in preExecute() may throw exceptions instead.
if (!$this->preExecute()) {
return NULL;
}
$args = $this->getArguments(); $args = $this->getArguments();

View File

@ -43,6 +43,14 @@ class PagerDefault extends SelectQueryExtender {
*/ */
protected $customCountQuery = FALSE; protected $customCountQuery = FALSE;
public function __construct(SelectQueryInterface $query, DatabaseConnection $connection) {
parent::__construct($query, $connection);
// Add pager tag. Do this here to ensure that it is always added before
// preExecute() is called.
$this->addTag('pager');
}
/** /**
* Override the execute method. * Override the execute method.
* *
@ -52,6 +60,13 @@ class PagerDefault extends SelectQueryExtender {
public function execute() { public function execute() {
global $pager_page_array, $pager_total, $pager_total_items, $pager_limits; global $pager_page_array, $pager_total, $pager_total_items, $pager_limits;
// Add convenience tag to mark that this is an extended query. We have to
// do this in the constructor to ensure that it is set before preExecute()
// gets called.
if (!$this->preExecute($this)) {
return NULL;
}
// A NULL limit is the "kill switch" for pager queries. // A NULL limit is the "kill switch" for pager queries.
if (empty($this->limit)) { if (empty($this->limit)) {
return; return;

View File

@ -22,10 +22,22 @@ class TableSort extends SelectQueryExtender {
*/ */
protected $header = array(); protected $header = array();
public function execute() { public function __construct(SelectQueryInterface $query, DatabaseConnection $connection) {
return $this->query->execute(); parent::__construct($query, $connection);
// Add convenience tag to mark that this is an extended query. We have to
// do this in the constructor to ensure that it is set before preExecute()
// gets called.
$this->addTag('tablesort');
} }
/**
* Order the query based on a header array.
*
* @see theme_table()
* @param $header
* Table header array.
*/
public function orderByHeader(Array $header) { public function orderByHeader(Array $header) {
$this->header = $header; $this->header = $header;
$ts = $this->init(); $ts = $this->init();