From 9079d78481c6148b9aca36f90e8bf34ffb01f03d Mon Sep 17 00:00:00 2001 From: Dries Buytaert <dries@buytaert.net> Date: Fri, 5 Jun 2009 16:55:45 +0000 Subject: [PATCH] - Patch #481288 by Berdir: add support for INSERT INTO ... SELECT FROM ... queries. --- includes/database/mysql/query.inc | 8 ++++++-- includes/database/pgsql/query.inc | 8 ++++++-- includes/database/query.inc | 20 ++++++++++++++++++++ includes/database/sqlite/query.inc | 9 +++++++-- modules/simpletest/tests/database_test.test | 15 +++++++++++++++ 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/includes/database/mysql/query.inc b/includes/database/mysql/query.inc index b2dbae04c9a3..84caf03868d5 100644 --- a/includes/database/mysql/query.inc +++ b/includes/database/mysql/query.inc @@ -22,12 +22,12 @@ public function execute() { 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 (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) { + if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) { return NULL; } @@ -56,6 +56,10 @@ public function __toString() { // Default fields are always placed first for consistency. $insert_fields = array_merge($this->defaultFields, $this->insertFields); + if (!empty($this->fromQuery)) { + return "INSERT $delay INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; + } + $query = "INSERT $delay INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; $max_placeholder = 0; diff --git a/includes/database/pgsql/query.inc b/includes/database/pgsql/query.inc index d177212218b5..489d57e657f4 100644 --- a/includes/database/pgsql/query.inc +++ b/includes/database/pgsql/query.inc @@ -22,12 +22,12 @@ public function execute() { 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 (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) { + if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) { return NULL; } @@ -82,6 +82,10 @@ public function __toString() { // Default fields are always placed first for consistency. $insert_fields = array_merge($this->defaultFields, $this->insertFields); + if (!empty($this->fromQuery)) { + return "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; + } + $query = "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; $max_placeholder = 0; diff --git a/includes/database/query.inc b/includes/database/query.inc index 8d614a0dd12f..ac14c4a2e5e7 100644 --- a/includes/database/query.inc +++ b/includes/database/query.inc @@ -287,6 +287,12 @@ class InsertQuery extends Query { */ protected $insertValues = array(); + /** + * A SelectQuery object to fetch the rows that should be inserted. + * + */ + protected $fromQuery; + public function __construct($connection, $table, array $options = array()) { if (!isset($options['return'])) { $options['return'] = Database::RETURN_INSERT_ID; @@ -410,6 +416,11 @@ public function delay($delay = TRUE) { return $this; } + public function from(SelectQueryInterface $query) { + $this->fromQuery = $query; + return $this; + } + /** * Executes the insert query. * @@ -426,6 +437,11 @@ 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)) { @@ -463,6 +479,10 @@ public function __toString() { // Default fields are always placed first for consistency. $insert_fields = array_merge($this->defaultFields, $this->insertFields); + if (!empty($this->fromQuery)) { + return "INSERT $delay INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; + } + // For simplicity, we will use the $placeholders array to inject // default keywords even though they are not, strictly speaking, // placeholders for prepared statements. diff --git a/includes/database/sqlite/query.inc b/includes/database/sqlite/query.inc index 98fa8075449d..287fc9008d6f 100644 --- a/includes/database/sqlite/query.inc +++ b/includes/database/sqlite/query.inc @@ -21,11 +21,11 @@ class InsertQuery_sqlite extends InsertQuery { public function execute() { - if (count($this->insertFields) + count($this->defaultFields) == 0) { + 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) { + if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) { return NULL; } if (count($this->insertFields)) { @@ -39,6 +39,11 @@ public function execute() { public function __toString() { // Produce as many generic placeholders as necessary. $placeholders = array_fill(0, count($this->insertFields), '?'); + + if (!empty($this->fromQuery)) { + return "INSERT INTO {" . $this->table . '} (' . implode(', ', $this->insertFields) . ') ' . $this->fromQuery; + } + return 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') VALUES (' . implode(', ', $placeholders) . ')'; } diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test index 7084ccf7e172..6256ff22c674 100644 --- a/modules/simpletest/tests/database_test.test +++ b/modules/simpletest/tests/database_test.test @@ -513,6 +513,21 @@ class DatabaseInsertTestCase extends DatabaseTestCase { $this->assertIdentical($id, '5', t('Auto-increment ID returned successfully.')); } + + /** + * Test that the INSERT INTO ... SELECT ... syntax works. + */ + function testInsertSelect() { + $query = db_select('test_people', 'tp')->fields('tp', array('name', 'age', 'job')); + + db_insert('test') + ->fields(array('name', 'age', 'job')) + ->from($query) + ->execute(); + + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); + $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.')); + } } /** -- GitLab