Our support for "INSERT ... SELECT" queries only works with "SELECT (an explicit list of fields)".
Queries like "INSERT INTO new SELECT * FROM old WHERE..." cannot be written, although they are supported, in that exact syntax, in MySQL, pgSQL & SQLite.

All our current uses of db_insert($table)->from($select)->execute(); in current core are done with a $select query that has an explicit list of fields - i.e. built with $select->fields('table', array('some', 'specific', 'fields')), not $select->fields('table'). The latter fails with the exception below.

From /core/lib/Drupal/Core/Database/Query/Insert.php:

    // 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.');
    }

From /core/lib/Drupal/Core/Database/Query/Insert.php,
and present exactly as is in /core/lib/Drupal/Core/Database/Driver/*/Insert.php:

     if (!empty($this->fromQuery)) {
      return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
    }

Reading through #481288: Add support for INSERT INTO ... SELECT FROM ..., though, I can't really convince myself that it's a deliberate restriction rather than simply an omission.
Supporting "copy rows as is, whatever the columns are", is needed for the upgrade path in #1497374: Switch from Field-based storage to Entity-based storage.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yched’s picture

Status: Active » Needs review
FileSize
6.99 KB

Temptative patch.

yched’s picture

Issue summary: View changes

rephrase

chx’s picture

Assigned: Unassigned » Crell
FileSize
1.37 KB
7.41 KB

Minimal formatting fixes, assigning to Crell for review.

chx’s picture

Issue summary: View changes

rephrasse

yched’s picture

Issue summary: View changes

clarification

yched’s picture

Priority: Normal » Critical
yched’s picture

Minor - fix double spacing in the generated query if there is a list of fields.

Crell’s picture

How does this block #1497374: Switch from Field-based storage to Entity-based storage? I haven't looked at that patch. Readers' Digest version, please. :-)

yched’s picture

@Crell:
The upgrade path for #1497374: Switch from Field-based storage to Entity-based storage needs to reshuffle field table storage, and move data rows from old to new tables.
But it doesn't know the column names in those tables, because those vary with the field type, and the info lies in a method in the various "field type" plugins, and plugins are out of reach during updates (#2055605: Disallow use of plugins during update).
We don't really need that info anyway, all we need is "take whatever's in here, put it over there" - aka "INSERT INTO new SELECT * FROM old WHERE ..."

Crell’s picture

Hm, I see. Fair enough. One comment on the code.

+++ b/core/modules/system/lib/Drupal/system/Tests/Database/InsertTest.php
@@ -165,5 +165,21 @@ function testInsertSelect() {
+    // Test the "INSERT INTO ... SELECT * FROM" syntax.
+    $query = db_select('test_people', 'tp')
+      ->fields('tp')
+      ->condition('tp.name', 'Meredith');
+    // The resulting query should be equivalent to:
+    // INSERT INTO test_people_copy
+    // SELECT *
+    // FROM test_people tp
+    // WHERE tp.name = 'Meredith'
+    db_insert('test_people_copy')
+      ->from($query)
+      ->execute();
+
+    $saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();

This belongs in a separate test method from the previous test. It's testing a different thing (explicit vs. implicit field specification).

yched’s picture

Works for me.

yched’s picture

Whoops. Did not merge HEAD before diffing.
Correct patch attached, interdiff is the same.

Crell’s picture

Status: Needs review » Reviewed & tested by the community

Bot can object if it wants.

chx’s picture

#6 is not correct, the situation is MUCH worse: the information about field tables are only available in Drupal 7 -- or by introspection. For example, file fields now use target_id and they used fid before. Someone might have added custom indexes to their field tables. And so on.

yched’s picture

Well, #6 is correct, but right, on top of that, asking for the field schema through the API would return an info that is not yet in sync with the state of the db at the time this specific update runs.
So field schema is doubly unusable here, and we do need to just SELECT *

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to 8.x. Thanks all. I'm excited about the Entity-based storage.

chx’s picture

Version: 8.x-dev » 7.x-dev
Assigned: Crell » Unassigned
Priority: Critical » Normal
Status: Fixed » Patch (to be ported)
Sweetchuck’s picture

Status: Patch (to be ported) » Needs review
FileSize
6.32 KB

Backport to D7

chx’s picture

Status: Needs review » Reviewed & tested by the community

Yeah this is the same as D8, thanks!

chx’s picture

Issue summary: View changes

bla bla

David_Rothstein’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Fixed
Issue tags: +7.25 release notes

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.