Skip to content

Commit 338fa52

Browse files
committed
Selection::insert(): refactored new record fetch (BC break)
1 parent d8aab0a commit 338fa52

13 files changed

+205
-57
lines changed

src/Database/IStructure.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ function getColumns($table);
4646
function getPrimaryKey($table);
4747

4848
/**
49-
* Returns table primary key sequence.
49+
* Returns table autoincrement primary key and sequence his name, if exists.
5050
* @param string
51-
* @return string|NULL
51+
* @return array|NULL
5252
*/
5353
function getPrimaryKeySequence($table);
5454

src/Database/Structure.php

+11-8
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,21 @@ public function getPrimaryKeySequence($table)
7171
$this->needStructure();
7272
$table = $this->resolveFQTableName($table);
7373

74-
if (!$this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::SUPPORT_SEQUENCE)) {
75-
return NULL;
76-
}
77-
7874
$primary = $this->getPrimaryKey($table);
79-
if (!$primary || is_array($primary)) {
75+
if (!$primary) {
8076
return NULL;
8177
}
8278

83-
foreach ($this->structure['columns'][$table] as $columnMeta) {
84-
if ($columnMeta['name'] === $primary) {
85-
return isset($columnMeta['vendor']['sequence']) ? $columnMeta['vendor']['sequence'] : NULL;
79+
$isSupported = $this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::SUPPORT_SEQUENCE);
80+
81+
foreach ((array) $primary as $key) {
82+
foreach ($this->structure['columns'][$table] as $columnMeta) {
83+
if ($columnMeta['name'] === $key && $columnMeta['autoincrement']) {
84+
return [
85+
'name' => $key,
86+
'sequence' => ($isSupported && isset($columnMeta['vendor']['sequence'])) ? $columnMeta['vendor']['sequence'] : NULL
87+
];
88+
}
8689
}
8790
}
8891

src/Database/Table/GroupedSelection.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ protected function emptyResultSet($saveCache = TRUE, $deleteRererencedCache = TR
211211
/********************* manipulation ****************d*g**/
212212

213213

214-
public function insert($data)
214+
public function insert($data, $returnRow = TRUE)
215215
{
216216
if ($data instanceof \Traversable && !$data instanceof Selection) {
217217
$data = iterator_to_array($data);

src/Database/Table/Selection.php

+26-27
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Selection implements \Iterator, IRowContainer, \ArrayAccess, \Countable
3838
/** @var string|array|NULL primary key field name */
3939
protected $primary;
4040

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

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

137137

138138
/**
139-
* @return string
139+
* @return array|NULL
140140
*/
141141
public function getPrimarySequence()
142142
{
@@ -149,10 +149,10 @@ public function getPrimarySequence()
149149

150150

151151
/**
152-
* @param string
152+
* @param array
153153
* @return self
154154
*/
155-
public function setPrimarySequence($sequence)
155+
public function setPrimarySequence(array $sequence)
156156
{
157157
$this->primarySequence = $sequence;
158158
return $this;
@@ -801,9 +801,10 @@ public function getDataRefreshed()
801801
/**
802802
* Inserts row in a table.
803803
* @param array|\Traversable|Selection array($column => $value)|\Traversable|Selection for INSERT ... SELECT
804-
* @return IRow|int|bool Returns IRow or number of affected rows for Selection or table without primary key
804+
* @param bool
805+
* @return IRow|int|bool Returns IRow or number of affected rows for Selection, multi insert or table without primary key
805806
*/
806-
public function insert($data)
807+
public function insert($data, $returnRow = TRUE)
807808
{
808809
if ($data instanceof self) {
809810
$return = $this->context->queryArgs($this->sqlBuilder->buildInsertQuery() . ' ' . $data->getSql(), $data->getSqlBuilder()->getParameters());
@@ -817,36 +818,34 @@ public function insert($data)
817818

818819
$this->loadRefCache();
819820

820-
if ($data instanceof self || $this->primary === NULL) {
821-
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
822-
return $return->getRowCount();
823-
}
824-
825-
$primaryKey = $this->context->getInsertId(
826-
($tmp = $this->getPrimarySequence())
827-
? implode('.', array_map([$this->context->getConnection()->getSupplementalDriver(), 'delimite'], explode('.', $tmp)))
828-
: NULL
829-
);
830-
if ($primaryKey === FALSE) {
821+
if ($data instanceof self || $this->primary === NULL || Nette\Utils\Arrays::isList($data) || !$returnRow) {
831822
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
832823
return $return->getRowCount();
833824
}
834825

835-
if (is_array($this->getPrimary())) {
836-
$primaryKey = [];
837-
838-
foreach ((array) $this->getPrimary() as $key) {
839-
if (!isset($data[$key])) {
840-
return $data;
841-
}
842-
826+
$primaryKey = [];
827+
foreach ((array) $this->getPrimary() as $key) {
828+
if (isset($data[$key])) {
843829
$primaryKey[$key] = $data[$key];
844830
}
845-
if (count($primaryKey) === 1) {
846-
$primaryKey = reset($primaryKey);
831+
}
832+
833+
if ($sequenceColumn = $this->getPrimarySequence()) {
834+
$primaryKey[$sequenceColumn['name']] = $this->context->getInsertId(
835+
($sequenceColumn['sequence'])
836+
? implode('.', array_map([$this->context->getConnection()->getSupplementalDriver(), 'delimite'], explode('.', $sequenceColumn['sequence'])))
837+
: NULL
838+
);
839+
if ($primaryKey[$sequenceColumn['name']] === FALSE) {
840+
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
841+
return $return->getRowCount();
847842
}
848843
}
849844

845+
if (count($primaryKey) === 1) {
846+
$primaryKey = reset($primaryKey);
847+
}
848+
850849
$row = $this->createSelectionInstance()
851850
->select('*')
852851
->wherePrimary($primaryKey)

tests/Database/Structure.phpt

+15-15
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,24 @@ class StructureTestCase extends TestCase
5858
['name' => 'books_view', 'view' => TRUE],
5959
]);
6060
$this->driver->shouldReceive('getColumns')->with('authors')->once()->andReturn([
61-
['name' => 'id', 'primary' => TRUE, 'vendor' => ['sequence' => '"public"."authors_id_seq"']],
62-
['name' => 'name', 'primary' => FALSE, 'vendor' => []],
61+
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => ['sequence' => '"public"."authors_id_seq"']],
62+
['name' => 'name', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
6363
]);
6464
$this->driver->shouldReceive('getColumns')->with('Books')->once()->andReturn([
65-
['name' => 'id', 'primary' => TRUE, 'vendor' => ['sequence' => '"public"."Books_id_seq"']],
66-
['name' => 'title', 'primary' => FALSE, 'vendor' => []],
65+
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => ['sequence' => '"public"."Books_id_seq"']],
66+
['name' => 'title', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
6767
]);
6868
$this->driver->shouldReceive('getColumns')->with('tags')->once()->andReturn([
69-
['name' => 'id', 'primary' => TRUE, 'vendor' => []],
70-
['name' => 'name', 'primary' => FALSE, 'vendor' => []],
69+
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => []],
70+
['name' => 'name', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
7171
]);
7272
$this->driver->shouldReceive('getColumns')->with('books_x_tags')->once()->andReturn([
73-
['name' => 'book_id', 'primary' => TRUE, 'vendor' => []],
74-
['name' => 'tag_id', 'primary' => TRUE, 'vendor' => []],
73+
['name' => 'book_id', 'primary' => TRUE, 'autoincrement' => FALSE, 'vendor' => []],
74+
['name' => 'tag_id', 'primary' => TRUE, 'autoincrement' => FALSE, 'vendor' => []],
7575
]);
7676
$this->driver->shouldReceive('getColumns')->with('books_view')->once()->andReturn([
77-
['name' => 'id', 'primary' => FALSE, 'vendor' => []],
78-
['name' => 'title', 'primary' => FALSE, 'vendor' => []],
77+
['name' => 'id', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
78+
['name' => 'title', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
7979
]);
8080
$this->connection->shouldReceive('getSupplementalDriver')->times(4)->andReturn($this->driver);
8181
$this->driver->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]);
@@ -108,8 +108,8 @@ class StructureTestCase extends TestCase
108108
public function testGetColumns()
109109
{
110110
$columns = [
111-
['name' => 'id', 'primary' => TRUE, 'vendor' => []],
112-
['name' => 'name', 'primary' => FALSE, 'vendor' => []],
111+
['name' => 'id', 'primary' => TRUE, 'autoincrement' => TRUE, 'vendor' => []],
112+
['name' => 'name', 'primary' => FALSE, 'autoincrement' => FALSE, 'vendor' => []],
113113
];
114114

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

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

tests/Database/Table/Selection.insert().multi.phpt

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverN
1414

1515
test(function () use ($context) {
1616
Assert::same(3, $context->table('author')->count());
17-
$context->table('author')->insert([
17+
$insert = $context->table('author')->insert([
1818
[
1919
'name' => 'Catelyn Stark',
2020
'web' => 'http://example.com',
@@ -26,15 +26,17 @@ test(function () use ($context) {
2626
'born' => new DateTime('2021-11-11'),
2727
],
2828
]); // 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')
29+
Assert::same(2, $insert);
2930
Assert::same(5, $context->table('author')->count());
3031

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

3334
Assert::same(4, $context->table('book_tag')->count());
34-
$context->table('book')->get(1)->related('book_tag')->insert([ // SELECT * FROM `book` WHERE (`id` = ?)
35+
$insert = $context->table('book')->get(1)->related('book_tag')->insert([ // SELECT * FROM `book` WHERE (`id` = ?)
3536
['tag_id' => 21],
3637
['tag_id' => 22],
3738
['tag_id' => 23],
3839
]); // INSERT INTO `book_tag` (`tag_id`, `book_id`) VALUES (21, 1), (22, 1), (23, 1)
40+
Assert::same(3, $insert);
3941
Assert::same(7, $context->table('book_tag')->count());
4042
});

tests/Database/Table/Selection.insert().phpt

+6
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,9 @@ $inserted = $context->table('note')->insert([
8484
'note' => 'Good one!',
8585
]);
8686
Assert::equal(1, $inserted);
87+
88+
$affected = $context->table('note')->insert([
89+
'book_id' => 2,
90+
'note' => 'Second one!',
91+
], FALSE);
92+
Assert::equal(1, $affected);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
/**
3+
* @dataProvider? ../../databases.ini
4+
*/
5+
6+
use Tester\Assert;
7+
8+
require __DIR__ . '/../../connect.inc.php'; // create $connection
9+
10+
Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test4.sql");
11+
12+
//Insert into table without auto_increament primary key
13+
test(function () use ($context) {
14+
15+
$inserted = $context->table('simple_pk')->insert([
16+
'id' => 8,
17+
'name' => 'Michal'
18+
]);
19+
20+
Assert::equal(8, $inserted->id);
21+
Assert::equal('Michal', $inserted->name);
22+
});
23+
24+
//Insert into table with composite primary key
25+
test(function () use ($context) {
26+
27+
$inserted = $context->table('composite_pk')->insert([
28+
'id1' => 8,
29+
'id2' => 10,
30+
'name' => 'Michal'
31+
]);
32+
33+
Assert::equal(8, $inserted->id1);
34+
Assert::equal(10, $inserted->id2);
35+
Assert::equal('Michal', $inserted->name);
36+
});
37+
38+
//Insert into table with composite primary key and one of them is auto_increment
39+
test(function () use ($context, $driverName) {
40+
41+
//Sqlite doesn't allow this type of table and sqlsrv's driver don't implement reflection
42+
if ($driverName == 'mysql' || $driverName == 'pgsql') {
43+
$inserted = $context->table('composite_pk_ai')->insert([
44+
'id2' => 10,
45+
'name' => 'Michal'
46+
]);
47+
48+
Assert::equal(10, $inserted->id2);
49+
Assert::equal('Michal', $inserted->name);
50+
}
51+
});

tests/Database/Table/bugs/bug1342.postgre.phpt

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ $context->query('
2020
');
2121

2222

23-
$insertedRows = $context->table('bug1342')->insert([
23+
$row = $context->table('bug1342')->insert([
2424
'a1' => 1,
2525
'a2' => 2,
2626
]);
2727

28-
Assert::same(1, $insertedRows);
28+
Assert::same(1, $row->a1);
29+
Assert::same(2, $row->a2);
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*!40102 SET storage_engine = InnoDB */;
2+
3+
DROP DATABASE IF EXISTS nette_test;
4+
CREATE DATABASE nette_test;
5+
USE nette_test;
6+
7+
8+
CREATE TABLE simple_pk (
9+
id int NOT NULL,
10+
name varchar(100),
11+
PRIMARY KEY (id)
12+
);
13+
14+
CREATE TABLE composite_pk (
15+
id1 int NOT NULL,
16+
id2 int NOT NULL,
17+
name varchar(100),
18+
PRIMARY KEY (id1, id2)
19+
);
20+
21+
CREATE TABLE composite_pk_ai (
22+
id1 int NOT NULL AUTO_INCREMENT,
23+
id2 int NOT NULL,
24+
name varchar(100),
25+
PRIMARY KEY (id1, id2)
26+
);
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
DROP SCHEMA IF EXISTS public CASCADE;
2+
CREATE SCHEMA public;
3+
4+
CREATE TABLE simple_pk (
5+
id int NOT NULL,
6+
name varchar(100),
7+
PRIMARY KEY (id)
8+
);
9+
10+
CREATE TABLE composite_pk (
11+
id1 int NOT NULL,
12+
id2 int NOT NULL,
13+
name varchar(100),
14+
PRIMARY KEY (id1, id2)
15+
);
16+
17+
CREATE TABLE composite_pk_ai (
18+
id1 serial NOT NULL,
19+
id2 int NOT NULL,
20+
name varchar(100),
21+
PRIMARY KEY (id1, id2)
22+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
DROP TABLE IF EXISTS simple_pk;
2+
DROP TABLE IF EXISTS composite_pk;
3+
4+
CREATE TABLE simple_pk (
5+
id INT NOT NULL,
6+
name TEXT,
7+
PRIMARY KEY (id)
8+
);
9+
10+
CREATE TABLE composite_pk (
11+
id1 int NOT NULL,
12+
id2 int NOT NULL,
13+
name TEXT,
14+
PRIMARY KEY (id1, id2)
15+
);

0 commit comments

Comments
 (0)