Skip to content
Permalink
Browse files

Add sparse insert support.

You can now omit some or all columns from any row of data.
  • Loading branch information...
markstory committed Mar 4, 2013
1 parent cc37aa6 commit f5cbbc3730865ec0f3e654ec0d26b255260ffc81
@@ -30,6 +30,11 @@
class ValuesExpression implements Expression {
protected $_values = [];
protected $_columns = [];
public function __construct($columns) {
$this->_columns = $columns;
}
/**
* Add a row of data to be inserted.
@@ -49,7 +54,9 @@ public function add($data) {
public function bindings() {
$bindings = [];
$i = 0;
$defaults = array_fill_keys($this->_columns, null);
foreach ($this->_values as $row) {
$row = array_merge($defaults, $row);
foreach ($row as $column => $value) {
$bindings[] = [
// TODO add types.
@@ -70,9 +77,10 @@ public function bindings() {
*/
public function sql() {
$placeholders = [];
$numColumns = count($this->_columns);
foreach ($this->_values as $row) {
if (is_array($row)) {
$placeholders[] = implode(', ', array_fill(0, count($row), '?'));
$placeholders[] = implode(', ', array_fill(0, $numColumns, '?'));
}
}
return sprintf('(%s)', implode('), (', $placeholders));
@@ -18,6 +18,7 @@
namespace Cake\Model\Datasource\Database;
use IteratorAggregate;
use Cake\Error;
use Cake\Model\Datasource\Database\Expression\Comparison;
use Cake\Model\Datasource\Database\Expression\OrderByExpression;
use Cake\Model\Datasource\Database\Expression\QueryExpression;
@@ -1165,6 +1166,9 @@ protected function _buildValuesPart($parts) {
/**
* Create an insert query.
*
* Note calling this method will reset any data previously set
* with Query::values()
*
* @param string $table The table name to insert into.
* @param array $columns The columns to insert into.
* @return Query
@@ -1173,6 +1177,7 @@ public function insert($table, $columns) {
$this->_dirty = true;
$this->_type = 'insert';
$this->_parts['insert'] = [$table, $columns];
$this->_parts['values'] = new ValuesExpression($columns);
return $this;
}
@@ -1185,10 +1190,19 @@ public function insert($table, $columns) {
*
* @param array|Query $data The data to insert.
* @return Query
* @throws Cake\Error\Exception if you try to set values before declaring columns.
* Or if you try to set values on non-insert queries.
*/
public function values($data) {
if (empty($this->_parts['values'])) {
$this->_parts['values'] = new ValuesExpression();
if ($this->_type !== 'insert') {
throw new Error\Exception(
__d('cake_dev', 'You cannot add values before defining columns to use.')
);
}
if (empty($this->_parts['insert'])) {
throw new Error\Exception(
__d('cake_dev', 'You cannot add values before defining columns to use.')
);
}
$this->_dirty = true;
$this->_parts['values']->add($data);
@@ -1737,6 +1737,20 @@ public function testUpdateWithExpression() {
$this->assertCount(1, $result);
}
/**
* You cannot call values() before insert() it causes all sorts of pain.
*
* @expectedException Cake\Error\Exception
*/
public function testInsertValuesBeforeInsertFailure() {
$query = new Query($this->connection);
$query->select('*')->values([
'id' => 1,
'title' => 'mark',
'body' => 'test insert'
]);
}
/**
* Test inserting a single row.
*
@@ -1775,12 +1789,72 @@ public function testInsertSimple() {
$this->assertEquals($expected, $result->fetchAll('assoc')[0]);
}
/**
* Test an insert when not all the listed fields are provided.
* Columns should be matched up where possible.
*
* @return void
*/
public function testInsertSparseRow() {
$this->markTestIncomplete();
$this->_createAuthorsAndArticles();
$query = new Query($this->connection);
$query->insert('articles', ['id', 'title', 'body'])
->values([
'body' => 'test insert',
'title' => 'mark',
]);
$result = $query->sql(false);
$this->assertEquals(
'INSERT INTO articles (id, title, body) VALUES (?, ?, ?)',
$result
);
$result = $query->execute();
$this->assertCount(1, $result, '1 row should be inserted');
$result = (new Query($this->connection))->select('*')
->from('articles')
->execute();
$this->assertCount(1, $result);
$expected = [
'id' => null,
'author_id' => null,
'title' => 'mark',
'body' => 'test insert'
];
$this->assertEquals($expected, $result->fetchAll('assoc')[0]);
}
/**
* Test inserting multiple rows.
*
* @return void
*/
public function testInsertMultipleRows() {
$this->markTestIncomplete();
$this->_createAuthorsAndArticles();
$query = new Query($this->connection);
$query->insert('articles', ['id', 'title', 'body'])
->values([
'id' => 1,
'title' => 'mark',
'body' => 'test insert'
])
->values([
'id' => 2,
'title' => 'jose',
'body' => 'test insert'
]);
$result = $query->sql(false);
$this->assertEquals(
'INSERT INTO articles (id, title, body) VALUES (?, ?, ?), (?, ?, ?)',
$result
);
$result = $query->execute();
$this->assertCount(2, $result, '2 row should be inserted');
}
public function testInsertMultipleRowsSparse() {

0 comments on commit f5cbbc3

Please sign in to comment.
You can’t perform that action at this time.