diff --git a/includes/database/database.inc b/includes/database/database.inc
index fcbd925a2beace95508920cbc50c612e94d86254..a5da925de980dc0a1e0a1c24f193fe792b60c744 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -1412,6 +1412,19 @@ class ExplicitTransactionsNotSupportedException extends Exception { }
  */
 class InvalidMergeQueryException extends Exception {}
 
+/**
+ * Exception thrown if an insert query specifies 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.
  *
diff --git a/includes/database/mysql/query.inc b/includes/database/mysql/query.inc
index 84caf03868d584d2a16893c4478f9a64b25bd32a..09878f96dc8ebc6a157c652041f9a4fd53ce4169 100644
--- a/includes/database/mysql/query.inc
+++ b/includes/database/mysql/query.inc
@@ -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 we're selecting from a SelectQuery, finish building the query and
+    // pass it back, as any remaining options are irrelevant.
+    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 @@ public function __toString() {
     // Default fields are always placed first for consistency.
     $insert_fields = array_merge($this->defaultFields, $this->insertFields);
 
+    // If we're selecting from a SelectQuery, finish building the query and
+    // pass it back, as any remaining options are irrelevant.
     if (!empty($this->fromQuery)) {
       return "INSERT $delay INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
     }
diff --git a/includes/database/pgsql/query.inc b/includes/database/pgsql/query.inc
index 489d57e657f460a5cb30755d57459a59215cf26c..ab16a70ec0e7dbd8015a633cd4077c81ab77b090 100644
--- a/includes/database/pgsql/query.inc
+++ b/includes/database/pgsql/query.inc
@@ -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 @@ public function execute() {
         }
       }
     }
+    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 @@ public function __toString() {
     // Default fields are always placed first for consistency.
     $insert_fields = array_merge($this->defaultFields, $this->insertFields);
 
+    // If we're selecting from a SelectQuery, finish building the query and
+    // pass it back, as any remaining options are irrelevant.
     if (!empty($this->fromQuery)) {
       return "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
     }
diff --git a/includes/database/query.inc b/includes/database/query.inc
index b27eb6a0c003aa55482447774238f23675a9cbe3..b6b47f48d90175632b0239df3c36e83f14eb183e 100644
--- a/includes/database/query.inc
+++ b/includes/database/query.inc
@@ -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,20 @@ public function from(SelectQueryInterface $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 we're selecting from a SelectQuery, finish building the query and
+    // pass it back, as any remaining options are irrelevant.
+    if (!empty($this->fromQuery)) {
+      $sql = (string)$this;
+      // The SelectQuery may contain arguments, load and pass them through.
+      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 +482,42 @@ public function __toString() {
 
     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)) {
+      // We have to assume that the used aliases match the insert fields.
+      // Regular fields are added to the query before expressions, maintain the
+      // same order for the insert fields.
+      // This behavior can be overriden by calling fields() manually as only the
+      // first call to fields() does have an effect.
+      $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;
+  }
 }
 
 /**
diff --git a/includes/database/sqlite/query.inc b/includes/database/sqlite/query.inc
index 287fc9008d6f300359ffee26caf1ff1601ccc117..35314e8a81e5d3c398c4cdfd8b5a2152de7c28f9 100644
--- a/includes/database/sqlite/query.inc
+++ b/includes/database/sqlite/query.inc
@@ -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 @@ public function __toString() {
     // Produce as many generic placeholders as necessary.
     $placeholders = array_fill(0, count($this->insertFields), '?');
 
+    // If we're selecting from a SelectQuery, finish building the query and
+    // pass it back, as any remaining options are irrelevant.
     if (!empty($this->fromQuery)) {
       return "INSERT INTO {" . $this->table . '} (' . implode(', ', $this->insertFields) . ') ' . $this->fromQuery;
     }
diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test
index bae378c9c2b8a69c0f1cc5117689ec85aaae2c9a..c354e9a8ed9b52e65f8ebe10eea6c6c88353a0de 100644
--- a/modules/simpletest/tests/database_test.test
+++ b/modules/simpletest/tests/database_test.test
@@ -518,10 +518,21 @@ class DatabaseInsertTestCase extends DatabaseTestCase {
    * 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');
+    // The query builder will always append expressions after fields.
+    // Add the expression first to test that the insert fields are correctly
+    // re-ordered.
+    $query->addExpression('tp.age', 'age');
+    $query
+      ->fields('tp', array('name','job'))
+      ->condition('tp.name', 'Meredith');
 
+    // The resulting query should be equivalent to:
+    // INSERT INTO test (age, name, job)
+    // SELECT tp.age AS age, tp.name AS name, tp.job AS job
+    // FROM test_people tp
+    // WHERE tp.name = 'Meredith'
     db_insert('test')
-      ->fields(array('name', 'age', 'job'))
       ->from($query)
       ->execute();
 
@@ -603,8 +614,13 @@ class DatabaseInsertDefaultsTestCase extends DatabaseTestCase {
   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.'));