Skip to content

Commit

Permalink
Translating multi row insert in SQLite, all tests passing in all
Browse files Browse the repository at this point in the history
drivers!
  • Loading branch information
lorenzo committed Mar 17, 2013
1 parent f00bb63 commit 91b3c6c
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 5 deletions.
40 changes: 40 additions & 0 deletions lib/Cake/Model/Datasource/Database/Dialect/SqliteDialectTrait.php
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}

}
Expand Up @@ -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()
*
Expand Down Expand Up @@ -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 = [];
Expand Down
11 changes: 7 additions & 4 deletions lib/Cake/Model/Datasource/Database/Query.php
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -1449,7 +1452,7 @@ protected function _bindStatement($statement) {
$statement->bind($params, $types);
};

$this->_transformQuery()->traverseExpressions($binder);
$this->traverseExpressions($binder);
}

/**
Expand Down

0 comments on commit 91b3c6c

Please sign in to comment.