From 91b3c6c73a7196b9143b6f13cf5152d8b4772437 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 17 Mar 2013 23:34:05 +0100 Subject: [PATCH] Translating multi row insert in SQLite, all tests passing in all drivers! --- .../Database/Dialect/SqliteDialectTrait.php | 40 +++++++++++++++++++ .../Database/Expression/ValuesExpression.php | 32 ++++++++++++++- lib/Cake/Model/Datasource/Database/Query.php | 11 +++-- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Dialect/SqliteDialectTrait.php b/lib/Cake/Model/Datasource/Database/Dialect/SqliteDialectTrait.php index 5d14b0b4c71..62486223551 100644 --- a/lib/Cake/Model/Datasource/Database/Dialect/SqliteDialectTrait.php +++ b/lib/Cake/Model/Datasource/Database/Dialect/SqliteDialectTrait.php @@ -17,6 +17,7 @@ */ namespace Cake\Model\Datasource\Database\Dialect; +use Cake\Model\Datasource\Database\Expression; use Cake\Model\Datasource\Database\Expression\FunctionExpression; use Cake\Model\Datasource\Database\Query; @@ -68,4 +69,43 @@ protected function _transformFunctionExpression(FunctionExpression $expression) } } +/** + * Transforms an insert query that is meant to insert multiple tows at a time, + * otherwise it leaves the query untouched. + * + * The way SQLite works with multi insert is by having multiple select statements + * joined with UNION. + * + * @return Query + */ + protected function _insertQueryTranslator($query) { + $v = $query->clause('values'); + if (count($v->values()) === 1) { + return $query; + } + + $cols = $v->columns(); + $newQuery = $query->connection()->newQuery(); + $values = []; + foreach ($v->values() as $k => $val) { + $values[] = $val; + $val = array_merge($val, array_fill(0, count($cols) - count($val), null)); + $val = array_map(function($val) { + return $val instanceof Expression ? $val : '?'; + }, $val); + + if ($k === 0) { + array_unshift($values, $newQuery->select(array_combine($cols, $val))); + continue; + } + + $q = $newQuery->connection()->newQuery(); + $newQuery->union($q->select(array_combine($cols, $val)), true); + } + + $v = clone $v; + $v->values($values); + return $query->values($v); + } + } diff --git a/lib/Cake/Model/Datasource/Database/Expression/ValuesExpression.php b/lib/Cake/Model/Datasource/Database/Expression/ValuesExpression.php index 4330977d485..b908757ed57 100644 --- a/lib/Cake/Model/Datasource/Database/Expression/ValuesExpression.php +++ b/lib/Cake/Model/Datasource/Database/Expression/ValuesExpression.php @@ -93,6 +93,36 @@ public function add($data) { $this->_values[] = $data; } +/** + * Sets the columns to be inserted. If no params are passed, then it returns + * the currently stored columns + * + * @param array $cols arrays with columns to be inserted + * @return array|ValuesExpression + */ + public function columns($cols = null) { + if ($cols === null) { + return $this->_columns; + } + $this->_columns = $cols; + return $this; + } + +/** + * Sets the values to be inserted. If no params are passed, then it returns + * the currently stored values + * + * @param array $cols arrays with values to be inserted + * @return array|ValuesExpression + */ + public function values($values = null) { + if ($values === null) { + return $this->_values; + } + $this->_values = $values; + return $this; + } + /** * Convert the rows of data into a format that works with Query::_bindParams() * @@ -128,7 +158,7 @@ public function sql() { if (empty($this->_values)) { return ''; } - if ($this->_hasQuery) { + if ($this->_values[0] instanceof Query) { return ' ' . $this->_values[0]->sql(); } $placeholders = []; diff --git a/lib/Cake/Model/Datasource/Database/Query.php b/lib/Cake/Model/Datasource/Database/Query.php index 65364a8ce48..aef3f1b7b7c 100644 --- a/lib/Cake/Model/Datasource/Database/Query.php +++ b/lib/Cake/Model/Datasource/Database/Query.php @@ -174,9 +174,6 @@ public function connection($connection = null) { * @return Cake\Model\Datasource\Database\Statement */ public function execute() { - $this->_transformedQuery = null; - $this->_dirty = false; - $query = $this->_transformQuery(); $statement = $this->_connection->prepare($query->sql(false)); $query->_bindStatement($statement); @@ -1197,7 +1194,13 @@ public function values($data) { __d('cake_dev', 'You cannot add values before defining columns to use.') ); } + $this->_dirty = true; + if ($data instanceof ValuesExpression) { + $this->_parts['values'] = $data; + return $this; + } + $this->_parts['values']->add($data); return $this; } @@ -1449,7 +1452,7 @@ protected function _bindStatement($statement) { $statement->bind($params, $types); }; - $this->_transformQuery()->traverseExpressions($binder); + $this->traverseExpressions($binder); } /**