Skip to content

Commit

Permalink
Selection::insert(): refactored new record fetch (BC break)
Browse files Browse the repository at this point in the history
  • Loading branch information
Unlink committed Jun 2, 2016
1 parent d8aab0a commit 338fa52
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 57 deletions.
4 changes: 2 additions & 2 deletions src/Database/IStructure.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ function getColumns($table);
function getPrimaryKey($table);

/**
* Returns table primary key sequence.
* Returns table autoincrement primary key and sequence his name, if exists.
* @param string
* @return string|NULL
* @return array|NULL
*/
function getPrimaryKeySequence($table);

Expand Down
19 changes: 11 additions & 8 deletions src/Database/Structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,21 @@ public function getPrimaryKeySequence($table)
$this->needStructure();
$table = $this->resolveFQTableName($table);

if (!$this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::SUPPORT_SEQUENCE)) {
return NULL;
}

$primary = $this->getPrimaryKey($table);
if (!$primary || is_array($primary)) {
if (!$primary) {
return NULL;
}

foreach ($this->structure['columns'][$table] as $columnMeta) {
if ($columnMeta['name'] === $primary) {
return isset($columnMeta['vendor']['sequence']) ? $columnMeta['vendor']['sequence'] : NULL;
$isSupported = $this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::SUPPORT_SEQUENCE);

foreach ((array) $primary as $key) {
foreach ($this->structure['columns'][$table] as $columnMeta) {
if ($columnMeta['name'] === $key && $columnMeta['autoincrement']) {
return [
'name' => $key,
'sequence' => ($isSupported && isset($columnMeta['vendor']['sequence'])) ? $columnMeta['vendor']['sequence'] : NULL
];
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Database/Table/GroupedSelection.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ protected function emptyResultSet($saveCache = TRUE, $deleteRererencedCache = TR
/********************* manipulation ****************d*g**/


public function insert($data)
public function insert($data, $returnRow = TRUE)
{
if ($data instanceof \Traversable && !$data instanceof Selection) {
$data = iterator_to_array($data);
Expand Down
53 changes: 26 additions & 27 deletions src/Database/Table/Selection.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Selection implements \Iterator, IRowContainer, \ArrayAccess, \Countable
/** @var string|array|NULL primary key field name */
protected $primary;

/** @var string|bool primary column sequence name, FALSE for autodetection */
/** @var array|bool|NULL primary column sequence name, FALSE for autodetection */
protected $primarySequence = FALSE;

/** @var IRow[] data read from database in [primary key => IRow] format */
Expand Down Expand Up @@ -136,7 +136,7 @@ public function getPrimary($need = TRUE)


/**
* @return string
* @return array|NULL
*/
public function getPrimarySequence()
{
Expand All @@ -149,10 +149,10 @@ public function getPrimarySequence()


/**
* @param string
* @param array
* @return self
*/
public function setPrimarySequence($sequence)
public function setPrimarySequence(array $sequence)
{
$this->primarySequence = $sequence;
return $this;
Expand Down Expand Up @@ -801,9 +801,10 @@ public function getDataRefreshed()
/**
* Inserts row in a table.
* @param array|\Traversable|Selection array($column => $value)|\Traversable|Selection for INSERT ... SELECT
* @return IRow|int|bool Returns IRow or number of affected rows for Selection or table without primary key
* @param bool
* @return IRow|int|bool Returns IRow or number of affected rows for Selection, multi insert or table without primary key
*/
public function insert($data)
public function insert($data, $returnRow = TRUE)
{
if ($data instanceof self) {
$return = $this->context->queryArgs($this->sqlBuilder->buildInsertQuery() . ' ' . $data->getSql(), $data->getSqlBuilder()->getParameters());
Expand All @@ -817,36 +818,34 @@ public function insert($data)

$this->loadRefCache();

if ($data instanceof self || $this->primary === NULL) {
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
return $return->getRowCount();
}

$primaryKey = $this->context->getInsertId(
($tmp = $this->getPrimarySequence())
? implode('.', array_map([$this->context->getConnection()->getSupplementalDriver(), 'delimite'], explode('.', $tmp)))
: NULL
);
if ($primaryKey === FALSE) {
if ($data instanceof self || $this->primary === NULL || Nette\Utils\Arrays::isList($data) || !$returnRow) {
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
return $return->getRowCount();
}

if (is_array($this->getPrimary())) {
$primaryKey = [];

foreach ((array) $this->getPrimary() as $key) {
if (!isset($data[$key])) {
return $data;
}

$primaryKey = [];
foreach ((array) $this->getPrimary() as $key) {
if (isset($data[$key])) {
$primaryKey[$key] = $data[$key];
}
if (count($primaryKey) === 1) {
$primaryKey = reset($primaryKey);
}

if ($sequenceColumn = $this->getPrimarySequence()) {
$primaryKey[$sequenceColumn['name']] = $this->context->getInsertId(
($sequenceColumn['sequence'])
? implode('.', array_map([$this->context->getConnection()->getSupplementalDriver(), 'delimite'], explode('.', $sequenceColumn['sequence'])))
: NULL
);
if ($primaryKey[$sequenceColumn['name']] === FALSE) {
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
return $return->getRowCount();
}
}

if (count($primaryKey) === 1) {
$primaryKey = reset($primaryKey);
}

$row = $this->createSelectionInstance()
->select('*')
->wherePrimary($primaryKey)
Expand Down
30 changes: 15 additions & 15 deletions tests/Database/Structure.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,24 @@ class StructureTestCase extends TestCase
['name' => 'books_view', 'view' => TRUE],
]);
$this->driver->shouldReceive('getColumns')->with('authors')->once()->andReturn([
['name' => 'id', 'primary' => TRUE, 'vendor' => ['sequence' => '"public"."authors_id_seq"']],
['name' => 'name', 'primary' => FALSE, 'vendor' => []],
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => ['sequence' => '"public"."authors_id_seq"']],
['name' => 'name', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
]);
$this->driver->shouldReceive('getColumns')->with('Books')->once()->andReturn([
['name' => 'id', 'primary' => TRUE, 'vendor' => ['sequence' => '"public"."Books_id_seq"']],
['name' => 'title', 'primary' => FALSE, 'vendor' => []],
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => ['sequence' => '"public"."Books_id_seq"']],
['name' => 'title', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
]);
$this->driver->shouldReceive('getColumns')->with('tags')->once()->andReturn([
['name' => 'id', 'primary' => TRUE, 'vendor' => []],
['name' => 'name', 'primary' => FALSE, 'vendor' => []],
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => []],
['name' => 'name', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
]);
$this->driver->shouldReceive('getColumns')->with('books_x_tags')->once()->andReturn([
['name' => 'book_id', 'primary' => TRUE, 'vendor' => []],
['name' => 'tag_id', 'primary' => TRUE, 'vendor' => []],
['name' => 'book_id', 'primary' => TRUE, 'autoincrement' => FALSE, 'vendor' => []],
['name' => 'tag_id', 'primary' => TRUE, 'autoincrement' => FALSE, 'vendor' => []],
]);
$this->driver->shouldReceive('getColumns')->with('books_view')->once()->andReturn([
['name' => 'id', 'primary' => FALSE, 'vendor' => []],
['name' => 'title', 'primary' => FALSE, 'vendor' => []],
['name' => 'id', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
['name' => 'title', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
]);
$this->connection->shouldReceive('getSupplementalDriver')->times(4)->andReturn($this->driver);
$this->driver->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]);
Expand Down Expand Up @@ -108,8 +108,8 @@ class StructureTestCase extends TestCase
public function testGetColumns()
{
$columns = [
['name' => 'id', 'primary' => TRUE, 'vendor' => []],
['name' => 'name', 'primary' => FALSE, 'vendor' => []],
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => []],
['name' => 'name', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
];

Assert::same($columns, $this->structure->getColumns('tags'));
Expand Down Expand Up @@ -138,9 +138,9 @@ class StructureTestCase extends TestCase
$this->driver->shouldReceive('isSupported')->with('sequence')->once()->andReturn(FALSE);
$this->driver->shouldReceive('isSupported')->with('sequence')->times(3)->andReturn(TRUE);

Assert::null($this->structure->getPrimaryKeySequence('Authors'));
Assert::same('"public"."authors_id_seq"', $this->structure->getPrimaryKeySequence('Authors'));
Assert::null($this->structure->getPrimaryKeySequence('tags'));
Assert::same(['name' => 'id', 'sequence' => NULL], $this->structure->getPrimaryKeySequence('Authors'));
Assert::same(['name' => 'id', 'sequence' => '"public"."authors_id_seq"'], $this->structure->getPrimaryKeySequence('Authors'));
Assert::same(['name' => 'id', 'sequence' => NULL] ,$this->structure->getPrimaryKeySequence('tags'));
Assert::null($this->structure->getPrimaryKeySequence('books_x_tags'));
}

Expand Down
6 changes: 4 additions & 2 deletions tests/Database/Table/Selection.insert().multi.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverN

test(function () use ($context) {
Assert::same(3, $context->table('author')->count());
$context->table('author')->insert([
$insert = $context->table('author')->insert([
[
'name' => 'Catelyn Stark',
'web' => 'http://example.com',
Expand All @@ -26,15 +26,17 @@ test(function () use ($context) {
'born' => new DateTime('2021-11-11'),
],
]); // INSERT INTO `author` (`name`, `web`, `born`) VALUES ('Catelyn Stark', 'http://example.com', '2011-11-11 00:00:00'), ('Sansa Stark', 'http://example.com', '2021-11-11 00:00:00')
Assert::same(2, $insert);
Assert::same(5, $context->table('author')->count());

$context->table('book_tag')->where('book_id', 1)->delete(); // DELETE FROM `book_tag` WHERE (`book_id` = ?)

Assert::same(4, $context->table('book_tag')->count());
$context->table('book')->get(1)->related('book_tag')->insert([ // SELECT * FROM `book` WHERE (`id` = ?)
$insert = $context->table('book')->get(1)->related('book_tag')->insert([ // SELECT * FROM `book` WHERE (`id` = ?)
['tag_id' => 21],
['tag_id' => 22],
['tag_id' => 23],
]); // INSERT INTO `book_tag` (`tag_id`, `book_id`) VALUES (21, 1), (22, 1), (23, 1)
Assert::same(3, $insert);
Assert::same(7, $context->table('book_tag')->count());
});
6 changes: 6 additions & 0 deletions tests/Database/Table/Selection.insert().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,9 @@ $inserted = $context->table('note')->insert([
'note' => 'Good one!',
]);
Assert::equal(1, $inserted);

$affected = $context->table('note')->insert([
'book_id' => 2,
'note' => 'Second one!',
], FALSE);
Assert::equal(1, $affected);
51 changes: 51 additions & 0 deletions tests/Database/Table/bugs/Selection.insert().phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* @dataProvider? ../../databases.ini
*/

use Tester\Assert;

require __DIR__ . '/../../connect.inc.php'; // create $connection

Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test4.sql");

//Insert into table without auto_increament primary key
test(function () use ($context) {

$inserted = $context->table('simple_pk')->insert([
'id' => 8,
'name' => 'Michal'
]);

Assert::equal(8, $inserted->id);
Assert::equal('Michal', $inserted->name);
});

//Insert into table with composite primary key
test(function () use ($context) {

$inserted = $context->table('composite_pk')->insert([
'id1' => 8,
'id2' => 10,
'name' => 'Michal'
]);

Assert::equal(8, $inserted->id1);
Assert::equal(10, $inserted->id2);
Assert::equal('Michal', $inserted->name);
});

//Insert into table with composite primary key and one of them is auto_increment
test(function () use ($context, $driverName) {

//Sqlite doesn't allow this type of table and sqlsrv's driver don't implement reflection
if ($driverName == 'mysql' || $driverName == 'pgsql') {
$inserted = $context->table('composite_pk_ai')->insert([
'id2' => 10,
'name' => 'Michal'
]);

Assert::equal(10, $inserted->id2);
Assert::equal('Michal', $inserted->name);
}
});
5 changes: 3 additions & 2 deletions tests/Database/Table/bugs/bug1342.postgre.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ $context->query('
');


$insertedRows = $context->table('bug1342')->insert([
$row = $context->table('bug1342')->insert([
'a1' => 1,
'a2' => 2,
]);

Assert::same(1, $insertedRows);
Assert::same(1, $row->a1);
Assert::same(2, $row->a2);
26 changes: 26 additions & 0 deletions tests/Database/files/mysql-nette_test4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*!40102 SET storage_engine = InnoDB */;

DROP DATABASE IF EXISTS nette_test;
CREATE DATABASE nette_test;
USE nette_test;


CREATE TABLE simple_pk (
id int NOT NULL,
name varchar(100),
PRIMARY KEY (id)
);

CREATE TABLE composite_pk (
id1 int NOT NULL,
id2 int NOT NULL,
name varchar(100),
PRIMARY KEY (id1, id2)
);

CREATE TABLE composite_pk_ai (
id1 int NOT NULL AUTO_INCREMENT,
id2 int NOT NULL,
name varchar(100),
PRIMARY KEY (id1, id2)
);
22 changes: 22 additions & 0 deletions tests/Database/files/pgsql-nette_test4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
DROP SCHEMA IF EXISTS public CASCADE;
CREATE SCHEMA public;

CREATE TABLE simple_pk (
id int NOT NULL,
name varchar(100),
PRIMARY KEY (id)
);

CREATE TABLE composite_pk (
id1 int NOT NULL,
id2 int NOT NULL,
name varchar(100),
PRIMARY KEY (id1, id2)
);

CREATE TABLE composite_pk_ai (
id1 serial NOT NULL,
id2 int NOT NULL,
name varchar(100),
PRIMARY KEY (id1, id2)
);
15 changes: 15 additions & 0 deletions tests/Database/files/sqlite-nette_test4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
DROP TABLE IF EXISTS simple_pk;
DROP TABLE IF EXISTS composite_pk;

CREATE TABLE simple_pk (
id INT NOT NULL,
name TEXT,
PRIMARY KEY (id)
);

CREATE TABLE composite_pk (
id1 int NOT NULL,
id2 int NOT NULL,
name TEXT,
PRIMARY KEY (id1, id2)
);
Loading

0 comments on commit 338fa52

Please sign in to comment.