diff --git a/includes/database/query.inc b/includes/database/query.inc
index 23b652f..8c5231f 100644
--- a/includes/database/query.inc
+++ b/includes/database/query.inc
@@ -142,7 +142,15 @@ interface QueryConditionInterface {
    *   The query this condition belongs to. If not given, the current query is
    *   used.
    */
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL);
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder);
+
+  /**
+   * Check whether a condition has been previously compiled.
+   *
+   * @return
+   *   TRUE if the condition has been previously compiled.
+   */
+  public function compiled();
 }
 
 
@@ -239,6 +247,11 @@ interface QueryAlterableInterface {
 interface QueryPlaceholderInterface {
 
   /**
+   * Returns a unique identifier for this object.
+   */
+  function uniqueIdentifier();
+
+  /**
    * Returns the next placeholder ID for the query.
    *
    * @return
@@ -284,6 +297,11 @@ abstract class Query implements QueryPlaceholderInterface {
   protected $queryOptions;
 
   /**
+   * A unique identifier for this query object.
+   */
+  protected $uniqueIdentifier;
+
+  /**
    * The placeholder counter.
    */
   protected $nextPlaceholder = 0;
@@ -304,6 +322,8 @@ abstract class Query implements QueryPlaceholderInterface {
    *   Array of query options.
    */
   public function __construct(DatabaseConnection $connection, $options) {
+    $this->uniqueIdentifier = uniqid('', TRUE);
+
     $this->connection = $connection;
     $this->connectionKey = $this->connection->getKey();
     $this->connectionTarget = $this->connection->getTarget();
@@ -321,13 +341,20 @@ abstract class Query implements QueryPlaceholderInterface {
   }
 
   /**
-  * Implements the magic __wakeup function to reconnect to the database.
+   * Implements the magic __wakeup function to reconnect to the database.
    */
   public function __wakeup() {
     $this->connection = Database::getConnection($this->connectionTarget, $this->connectionKey);
   }
 
   /**
+   * Implements the magic __clone function.
+   */
+  public function __clone() {
+    $this->uniqueIdentifier = uniqid('', TRUE);
+  }
+
+  /**
    * Runs the query against the database.
    */
   abstract protected function execute();
@@ -344,6 +371,13 @@ abstract class Query implements QueryPlaceholderInterface {
   abstract public function __toString();
 
   /**
+   * Returns a unique identifier for this object.
+   */
+  public function uniqueIdentifier() {
+    return $this->uniqueIdentifier;
+  }
+
+  /**
    * Gets the next placeholder value for this query object.
    *
    * @return int
@@ -790,8 +824,15 @@ class DeleteQuery extends Query implements QueryConditionInterface {
   /**
    * Implements QueryConditionInterface::compile().
    */
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL) {
-    return $this->condition->compile($connection, isset($queryPlaceholder) ? $queryPlaceholder : $this);
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
+    return $this->condition->compile($connection, $queryPlaceholder);
+  }
+
+  /**
+   * Implements QueryConditionInterface::compiled().
+   */
+  public function compiled() {
+    return $this->condition->compiled();
   }
 
   /**
@@ -864,8 +905,15 @@ class TruncateQuery extends Query {
   /**
    * Implements QueryConditionInterface::compile().
    */
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL) {
-    return $this->condition->compile($connection, isset($queryPlaceholder) ? $queryPlaceholder : $this);
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
+    return $this->condition->compile($connection, $queryPlaceholder);
+  }
+
+  /**
+   * Implements QueryConditionInterface::compiled().
+   */
+  public function compiled() {
+    return $this->condition->compiled();
   }
 
   /**
@@ -1025,8 +1073,15 @@ class UpdateQuery extends Query implements QueryConditionInterface {
   /**
    * Implements QueryConditionInterface::compile().
    */
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL) {
-    return $this->condition->compile($connection, isset($queryPlaceholder) ? $queryPlaceholder : $this);
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
+    return $this->condition->compile($connection, $queryPlaceholder);
+  }
+
+  /**
+   * Implements QueryConditionInterface::compiled().
+   */
+  public function compiled() {
+    return $this->condition->compiled();
   }
 
   /**
@@ -1510,8 +1565,15 @@ class MergeQuery extends Query implements QueryConditionInterface {
   /**
    * Implements QueryConditionInterface::compile().
    */
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL) {
-    return $this->condition->compile($connection, isset($queryPlaceholder) ? $queryPlaceholder : $this);
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
+    return $this->condition->compile($connection, $queryPlaceholder);
+  }
+
+  /**
+   * Implements QueryConditionInterface::compiled().
+   */
+  public function compiled() {
+    return $this->condition->compiled();
   }
 
   /**
@@ -1609,6 +1671,11 @@ class DatabaseCondition implements QueryConditionInterface, Countable {
   protected $changed = TRUE;
 
   /**
+   * The identifier of the query placeholder this condition has been compiled against.
+   */
+  protected $queryPlaceholderIdentifier;
+
+  /**
    * Constructs a DataBaseCondition object.
    *
    * @param string $conjunction
@@ -1718,8 +1785,12 @@ class DatabaseCondition implements QueryConditionInterface, Countable {
   /**
    * Implements QueryConditionInterface::compile().
    */
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL) {
-    if ($this->changed) {
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
+    // Re-compile if this condition changed or if we are compiled against a
+    // different query placeholder object.
+    if ($this->changed || isset($this->queryPlaceholderIdentifier) && ($this->queryPlaceholderIdentifier != $queryPlaceholder->uniqueIdentifier())) {
+      $this->queryPlaceholderIdentifier = $queryPlaceholder->uniqueIdentifier();
+
       $condition_fragments = array();
       $arguments = array();
 
@@ -1791,6 +1862,10 @@ class DatabaseCondition implements QueryConditionInterface, Countable {
     }
   }
 
+  public function compiled() {
+    return !$this->changed;
+  }
+
   /**
    * Implements PHP magic __toString method to convert the conditions to string.
    *
diff --git a/includes/database/schema.inc b/includes/database/schema.inc
index de1b2f5..1c90074 100644
--- a/includes/database/schema.inc
+++ b/includes/database/schema.inc
@@ -176,10 +176,27 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
    */
   protected $defaultSchema = 'public';
 
+  /**
+   * A unique identifier for this query object.
+   */
+  protected $uniqueIdentifier;
+
   public function __construct($connection) {
+    $this->uniqueIdentifier = uniqid('', TRUE);
     $this->connection = $connection;
   }
 
+  /**
+   * Implements the magic __clone function.
+   */
+  public function __clone() {
+    $this->uniqueIdentifier = uniqid('', TRUE);
+  }
+
+  public function uniqueIdentifier() {
+    return $this->uniqueIdentifier;
+  }
+
   public function nextPlaceholder() {
     return $this->placeholder++;
   }
diff --git a/includes/database/select.inc b/includes/database/select.inc
index 53be20a..df092f5 100644
--- a/includes/database/select.inc
+++ b/includes/database/select.inc
@@ -549,17 +549,27 @@ class SelectQueryExtender implements SelectQueryInterface {
   protected $connection;
 
   /**
+   * A unique identifier for this query object.
+   */
+  protected $uniqueIdentifier;
+
+  /**
    * The placeholder counter.
    */
   protected $placeholder = 0;
 
   public function __construct(SelectQueryInterface $query, DatabaseConnection $connection) {
+    $this->uniqueIdentifier = uniqid('', TRUE);
     $this->query = $query;
     $this->connection = $connection;
   }
 
   /* Implementations of QueryPlaceholderInterface. */
 
+  public function uniqueIdentifier() {
+    return $this->uniqueIdentifier;
+  }
+
   public function nextPlaceholder() {
     return $this->placeholder++;
   }
@@ -612,8 +622,12 @@ class SelectQueryExtender implements SelectQueryInterface {
     return $this;
   }
 
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL) {
-    return $this->condition->compile($connection, isset($queryPlaceholder) ? $queryPlaceholder : $this);
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
+    return $this->query->compile($connection, $queryPlaceholder);
+  }
+
+  public function compiled() {
+    return $this->query->compiled();
   }
 
   /* Implementations of QueryConditionInterface for the HAVING clause. */
@@ -824,6 +838,8 @@ class SelectQueryExtender implements SelectQueryInterface {
   }
 
   public function __clone() {
+    $this->uniqueIdentifier = uniqid('', TRUE);
+
     // We need to deep-clone the query we're wrapping, which in turn may
     // deep-clone other objects.  Exciting!
     $this->query = clone($this->query);
@@ -1013,7 +1029,35 @@ class SelectQuery extends Query implements SelectQueryInterface {
   }
 
   public function arguments() {
-    return $this->where->arguments();
+    if (!$this->compiled()) {
+      return NULL;
+    }
+
+    $args = $this->where->arguments() + $this->having->arguments();
+
+    foreach ($this->tables as $table) {
+      if ($table['arguments']) {
+        $args += $table['arguments'];
+      }
+      // If this table is a subquery, grab its arguments recursively.
+      if ($table['table'] instanceof SelectQueryInterface) {
+        $args += $table['table']->arguments();
+      }
+    }
+
+    foreach ($this->expressions as $expression) {
+      if ($expression['arguments']) {
+        $args += $expression['arguments'];
+      }
+    }
+
+    // If there are any dependent queries to UNION,
+    // incorporate their arguments recursively.
+    foreach ($this->union as $union) {
+      $args += $union['query']->arguments();
+    }
+
+    return $args;
   }
 
   public function where($snippet, $args = array()) {
@@ -1041,8 +1085,44 @@ class SelectQuery extends Query implements SelectQueryInterface {
     return $this;
   }
   
-  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder = NULL) {
-    return $this->where->compile($connection, isset($queryPlaceholder) ? $queryPlaceholder : $this);
+  public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
+    $this->where->compile($connection, $queryPlaceholder);
+    $this->having->compile($connection, $queryPlaceholder);
+
+    foreach ($this->tables as $table) {
+      // If this table is a subquery, compile it recursively.
+      if ($table['table'] instanceof SelectQueryInterface) {
+        $table['table']->compile($connection, $queryPlaceholder);
+      }
+    }
+
+    // If there are any dependent queries to UNION, compile it recursively.
+    foreach ($this->union as $union) {
+      $union['query']->compile($connection, $queryPlaceholder);
+    }
+  }
+
+  public function compiled() {
+    if (!$this->where->compiled() || !$this->having->compiled()) {
+      return FALSE;
+    }
+
+    foreach ($this->tables as $table) {
+      // If this table is a subquery, check its status recursively.
+      if ($table['table'] instanceof SelectQueryInterface) {
+        if (!$table['table']->compiled()) {
+          return FALSE;
+        }
+      }
+    }
+
+    foreach ($this->union as $union) {
+      if (!$union['query']->compiled()) {
+        return FALSE;
+      }
+    }
+
+    return TRUE;
   }
 
   /* Implementations of QueryConditionInterface for the HAVING clause. */
@@ -1136,33 +1216,8 @@ class SelectQuery extends Query implements SelectQueryInterface {
     if (!isset($queryPlaceholder)) {
       $queryPlaceholder = $this;
     }
-    $this->where->compile($this->connection, $queryPlaceholder);
-    $this->having->compile($this->connection, $queryPlaceholder);
-    $args = $this->where->arguments() + $this->having->arguments();
-
-    foreach ($this->tables as $table) {
-      if ($table['arguments']) {
-        $args += $table['arguments'];
-      }
-      // If this table is a subquery, grab its arguments recursively.
-      if ($table['table'] instanceof SelectQueryInterface) {
-        $args += $table['table']->getArguments($queryPlaceholder);
-      }
-    }
-
-    foreach ($this->expressions as $expression) {
-      if ($expression['arguments']) {
-        $args += $expression['arguments'];
-      }
-    }
-
-    // If there are any dependent queries to UNION,
-    // incorporate their arguments recursively.
-    foreach ($this->union as $union) {
-      $args += $union['query']->getArguments($queryPlaceholder);
-    }
-
-    return $args;
+    $this->compile($this->connection, $queryPlaceholder);
+    return $this->arguments();
   }
 
   /**
@@ -1439,6 +1494,14 @@ class SelectQuery extends Query implements SelectQueryInterface {
   }
 
   public function __toString() {
+    // For convenience, we compile the query ourselves if the caller forgot
+    // to do it. This allows constructs like "(string) $query" to work. When
+    // the query will be executed, it will be recompiled using the proper
+    // placeholder generator anyway.
+    if (!$this->compiled()) {
+      $this->compile($this->connection, $this);
+    }
+
     // Create a sanitized comment string to prepend to the query.
     $comments = $this->connection->makeComment($this->comments);
 
@@ -1496,14 +1559,6 @@ class SelectQuery extends Query implements SelectQueryInterface {
 
     // WHERE
     if (count($this->where)) {
-      // The following line will not generate placeholders correctly if there
-      // is a subquery. Fortunately, it is also called from getArguments() first
-      // so it's not a problem in practice... unless you try to call __toString()
-      // before calling getArguments().  That is a problem that we will have to
-      // fix in Drupal 8, because it requires more refactoring than we are
-      // able to do in Drupal 7.
-      // @todo Move away from __toString() For SelectQuery compilation at least.
-      $this->where->compile($this->connection, $this);
       // There is an implicit string cast on $this->condition.
       $query .= "\nWHERE " . $this->where;
     }
@@ -1515,7 +1570,6 @@ class SelectQuery extends Query implements SelectQueryInterface {
 
     // HAVING
     if (count($this->having)) {
-      $this->having->compile($this->connection, $this);
       // There is an implicit string cast on $this->having.
       $query .= "\nHAVING " . $this->having;
     }
