Index: modules/simpletest/tests/database_test.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/database_test.test,v
retrieving revision 1.58
diff -u -p -r1.58 database_test.test
--- modules/simpletest/tests/database_test.test	27 Jun 2009 09:41:56 -0000	1.58
+++ modules/simpletest/tests/database_test.test	1 Jul 2009 22:56:55 -0000
@@ -518,10 +518,14 @@ class DatabaseInsertTestCase extends Dat
    * Test that the INSERT INTO ... SELECT ... syntax works.
    */
   function testInsertSelect() {
-    $query = db_select('test_people', 'tp')->fields('tp', array('name', 'age', 'job'));
+    $query = db_select('test_people', 'tp');
+    // Use an expression to test the correct order of the insert query fields.
+    $query->addExpression('tp.age', 'age');
+    $query
+      ->fields('tp', array('name','job'))
+      ->condition('tp.name', 'Meredith');
 
     db_insert('test')
-      ->fields(array('name', 'age', 'job'))
       ->from($query)
       ->execute();
 
@@ -603,8 +607,13 @@ class DatabaseInsertDefaultsTestCase ext
   function testDefaultEmptyInsert() {
     $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
 
-    $result = db_insert('test')->execute();
-    $this->assertNull($result, t('Return NULL as no fields are specified.'));
+    try {
+      $result = db_insert('test')->execute();
+      // This is only executed if no exception has been thrown.
+      $this->fail(t('Expected exception NoFieldsException has not been thrown.'));
+    } catch (NoFieldsException $e) {
+      $this->pass(t('Expected exception NoFieldsException has been thrown.'));
+    }
 
     $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
     $this->assertIdentical($num_records_before, $num_records_after, t('Do nothing as no fields are specified.'));
Index: includes/database/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/database.inc,v
retrieving revision 1.57
diff -u -p -r1.57 database.inc
--- includes/database/database.inc	1 Jul 2009 08:11:27 -0000	1.57
+++ includes/database/database.inc	1 Jul 2009 22:56:56 -0000
@@ -1423,6 +1423,20 @@ class ExplicitTransactionsNotSupportedEx
 class InvalidMergeQueryException extends Exception {}
 
 /**
+ * Exception thrown if an insert query does specify a field twice.
+ *
+ * It is not allowed to specify a field as default and insert field, this
+ * exception is thrown if that is the case.
+ *
+ */
+class FieldsOverlapException extends Exception {}
+
+/**
+ * Exception thrown if an insert query doesn't specify insert or default fields.
+ */
+class NoFieldsException extends Exception {}
+
+/**
  * A wrapper class for creating and managing database transactions.
  *
  * Not all databases or database configurations support transactions. For
Index: includes/database/query.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/query.inc,v
retrieving revision 1.26
diff -u -p -r1.26 query.inc
--- includes/database/query.inc	27 Jun 2009 09:41:55 -0000	1.26
+++ includes/database/query.inc	1 Jul 2009 22:56:57 -0000
@@ -287,7 +287,8 @@ class InsertQuery extends Query {
 
   /**
    * A SelectQuery object to fetch the rows that should be inserted.
-   * 
+   *
+   * @var SelectQueryInterface
    */
   protected $fromQuery;
 
@@ -432,29 +433,19 @@ class InsertQuery extends Query {
    *   in multi-insert loops.
    */
   public function execute() {
-
-    $last_insert_id = 0;
-
-    // Check if a SelectQuery is passed in and use that.
-    if (!empty($this->fromQuery)) {
-      return $this->connection->query((string) $this, array(), $this->queryOptions);
-    }
-
-    // Confirm that the user did not try to specify an identical
-    //  field and default field.
-    if (array_intersect($this->insertFields, $this->defaultFields)) {
-      throw new PDOException('You may not specify the same field to have a value and a schema-default value.');
-    }
-
-    if (count($this->insertFields) + count($this->defaultFields) == 0) {
+    if (!$this->preExecute()) {
       return NULL;
     }
 
-    // Don't execute query without values.
-    if (!isset($this->insertValues[0]) && count($this->insertFields) > 0) {
-      return NULL;
+    // If a SelectQuery is specified to select from, that takes precedence over
+    // any other configured values.
+    if (!empty($this->fromQuery)) {
+      $sql = (string)$this;
+      return $this->connection->query($sql, $this->fromQuery->getArguments(), $this->queryOptions);
     }
 
+    $last_insert_id = 0;
+
     // Each insert happens in its own query in the degenerate case. However,
     // we wrap it in a transaction so that it is atomic where possible. On many
     // databases, such as SQLite, this is also a notable performance boost.
@@ -490,6 +481,39 @@ class InsertQuery extends Query {
 
     return 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES (' . implode(', ', $placeholders) . ')';
   }
+
+  /**
+   * Generic preparation and validation for an INSERT query.
+   *
+   * @return
+   *   TRUE if the validation was successful, FALSE if not.
+   */
+  protected function preExecute() {
+    // Confirm that the user did not try to specify an identical
+    // field and default field.
+    if (array_intersect($this->insertFields, $this->defaultFields)) {
+      throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
+    }
+
+    if (!empty($this->fromQuery)) {
+      // Use the defined aliases for the INSERT query fields.
+      // Fields are added before expressions in __toString().
+      $this->fields(array_merge(array_keys($this->fromQuery->getFields()), array_keys($this->fromQuery->getExpressions())));
+    }
+
+    // Don't execute query without fields.
+    if (count($this->insertFields) + count($this->defaultFields) == 0) {
+      throw new NoFieldsException("There are no fields available to insert with.");
+    }
+
+    // If no values have been added, silently ignore this query. This can happen
+    // if values are added conditionally, so we don't want to throw an
+    // exception.
+    if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
+      return FALSE;
+    }
+    return TRUE;
+  }
 }
 
 /**
Index: includes/database/mysql/query.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/mysql/query.inc,v
retrieving revision 1.12
diff -u -p -r1.12 query.inc
--- includes/database/mysql/query.inc	5 Jun 2009 16:55:45 -0000	1.12
+++ includes/database/mysql/query.inc	1 Jul 2009 22:56:57 -0000
@@ -15,31 +15,24 @@
 class InsertQuery_mysql extends InsertQuery {
 
   public function execute() {
-
-    // Confirm that the user did not try to specify an identical
-    //  field and default field.
-    if (array_intersect($this->insertFields, $this->defaultFields)) {
-      throw new PDOException('You may not specify the same field to have a value and a schema-default value.');
-    }
-
-    if (count($this->insertFields) + count($this->defaultFields) == 0 && empty($this->fromQuery)) {
+    if (!$this->preExecute()) {
       return NULL;
     }
 
-    // Don't execute query without values.
-    if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
-      return NULL;
-    }
-
-    $last_insert_id = 0;
-
-    $max_placeholder = 0;
-    $values = array();
-    foreach ($this->insertValues as $insert_values) {
-      foreach ($insert_values as $value) {
-        $values[':db_insert_placeholder_' . $max_placeholder++] = $value;
+    // If a SelectQuery is specified to select from, that takes precedence over
+    // any other configured values.
+    if (empty($this->fromQuery)) {
+      $max_placeholder = 0;
+      $values = array();
+      foreach ($this->insertValues as $insert_values) {
+        foreach ($insert_values as $value) {
+          $values[':db_insert_placeholder_' . $max_placeholder++] = $value;
+        }
       }
     }
+    else {
+      $values = $this->fromQuery->getArguments();
+    }
 
     $last_insert_id = $this->connection->query((string)$this, $values, $this->queryOptions);
 
@@ -56,6 +49,8 @@ class InsertQuery_mysql extends InsertQu
     // Default fields are always placed first for consistency.
     $insert_fields = array_merge($this->defaultFields, $this->insertFields);
 
+    // If a SelectQuery is specified to select from, that takes precedence over
+    // any other configured values.
     if (!empty($this->fromQuery)) {
       return "INSERT $delay INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
     }
Index: includes/database/pgsql/query.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/pgsql/query.inc,v
retrieving revision 1.13
diff -u -p -r1.13 query.inc
--- includes/database/pgsql/query.inc	5 Jun 2009 16:55:45 -0000	1.13
+++ includes/database/pgsql/query.inc	1 Jul 2009 22:56:57 -0000
@@ -15,19 +15,7 @@
 class InsertQuery_pgsql extends InsertQuery {
 
   public function execute() {
-
-    // Confirm that the user did not try to specify an identical
-    //  field and default field.
-    if (array_intersect($this->insertFields, $this->defaultFields)) {
-      throw new PDOException('You may not specify the same field to have a value and a schema-default value.');
-    }
-
-    if (count($this->insertFields) + count($this->defaultFields) == 0 && empty($this->fromQuery)) {
-      return NULL;
-    }
-
-    // Don't execute query without values.
-    if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
+    if (!$this->preExecute()) {
       return NULL;
     }
 
@@ -56,6 +44,11 @@ class InsertQuery_pgsql extends InsertQu
         }
       }
     }
+    if (!empty($this->fromQuery)) {
+      foreach ($this->fromQuery->getArguments() as $key => $value) {
+        $stmt->bindParam($key, $value);
+      }
+    }
 
     // PostgreSQL requires the table name to be specified explicitly
     // when requesting the last insert ID, so we pass that in via
@@ -82,6 +75,8 @@ class InsertQuery_pgsql extends InsertQu
     // Default fields are always placed first for consistency.
     $insert_fields = array_merge($this->defaultFields, $this->insertFields);
 
+    // If a SelectQuery is specified to select from, that takes precedence over
+    // any other configured values.
     if (!empty($this->fromQuery)) {
       return "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
     }
Index: includes/database/sqlite/query.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/sqlite/query.inc,v
retrieving revision 1.6
diff -u -p -r1.6 query.inc
--- includes/database/sqlite/query.inc	5 Jun 2009 16:55:45 -0000	1.6
+++ includes/database/sqlite/query.inc	1 Jul 2009 22:56:57 -0000
@@ -21,11 +21,7 @@
 class InsertQuery_sqlite extends InsertQuery {
 
   public function execute() {
-    if (count($this->insertFields) + count($this->defaultFields) == 0 && empty($this->fromQuery)) {
-      return NULL;
-    }
-    // Don't execute query without values.
-    if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
+    if (!$this->preExecute()) {
       return NULL;
     }
     if (count($this->insertFields)) {
@@ -40,6 +36,8 @@ class InsertQuery_sqlite extends InsertQ
     // Produce as many generic placeholders as necessary.
     $placeholders = array_fill(0, count($this->insertFields), '?');
 
+    // If a SelectQuery is specified to select from, that takes precedence over
+    // any other configured values.
     if (!empty($this->fromQuery)) {
       return "INSERT INTO {" . $this->table . '} (' . implode(', ', $this->insertFields) . ') ' . $this->fromQuery;
     }
