Skip to content

Commit

Permalink
Insert to table with multi primary or without autoincrement primary (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ricco24 authored and dg committed Feb 2, 2017
1 parent c584689 commit debc7db
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 32 deletions.
7 changes: 7 additions & 0 deletions src/Database/IStructure.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ function getColumns($table);
*/
function getPrimaryKey($table);

/**
* Returns autoincrement primary key name.
* @param string
* @return string|NULL
*/
//function getPrimaryAutoincrementKey($table);

/**
* Returns table primary key sequence.
* @param string
Expand Down
36 changes: 33 additions & 3 deletions src/Database/Structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,35 @@ public function getPrimaryKey($table)
}


public function getPrimaryAutoincrementKey($table)
{
$primaryKey = $this->getPrimaryKey($table);
if (!$primaryKey) {
return NULL;
}

// Search for autoincrement key from multi primary key
if (is_array($primaryKey)) {
$keys = array_flip($primaryKey);
foreach ($this->getColumns($table) as $column) {
if (isset($keys[$column['name']]) && $column['autoincrement']) {
return $column['name'];
}
}
return NULL;
}

// Search for autoincrement key from simple primary key
foreach ($this->getColumns($table) as $column) {
if ($column['name'] == $primaryKey) {
return $column['autoincrement'] ? $column['name'] : NULL;
}
}

return NULL;
}


public function getPrimaryKeySequence($table)
{
$this->needStructure();
Expand All @@ -75,13 +104,14 @@ public function getPrimaryKeySequence($table)
return NULL;
}

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

// Search for sequence from simple primary key
foreach ($this->structure['columns'][$table] as $columnMeta) {
if ($columnMeta['name'] === $primary) {
if ($columnMeta['name'] === $autoincrementPrimaryKeyName) {
return isset($columnMeta['vendor']['sequence']) ? $columnMeta['vendor']['sequence'] : NULL;
}
}
Expand Down
43 changes: 27 additions & 16 deletions src/Database/Table/Selection.php
Original file line number Diff line number Diff line change
Expand Up @@ -828,29 +828,40 @@ public function insert($data)
return $return->getRowCount();
}

$primaryKey = $this->context->getInsertId(
($tmp = $this->getPrimarySequence())
? implode('.', array_map([$this->context->getConnection()->getSupplementalDriver(), 'delimite'], explode('.', $tmp)))
: NULL
);
if (!$primaryKey) {
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
return $return->getRowCount();
$primarySequenceName = $this->getPrimarySequence();
$primaryAutoincrementKey = $this->context->getStructure()->getPrimaryAutoincrementKey($this->name);

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

if (is_array($this->getPrimary())) {
$primaryKey = [];
// First check sequence
if (!empty($primarySequenceName) && $primaryAutoincrementKey) {
$primaryKey[$primaryAutoincrementKey] = $this->context->getInsertId($this->context->getConnection()->getSupplementalDriver()->delimite($primarySequenceName));

// Autoincrement primary without sequence
} elseif ($primaryAutoincrementKey) {
$primaryKey[$primaryAutoincrementKey] = $this->context->getInsertId($primarySequenceName);

foreach ((array) $this->getPrimary() as $key) {
// Multi column primary without autoincrement
} elseif (is_array($this->primary)) {
foreach ($this->primary as $key) {
if (!isset($data[$key])) {
return $data;
}

$primaryKey[$key] = $data[$key];
}
if (count($primaryKey) === 1) {
$primaryKey = reset($primaryKey);
}

// Primary without autoincrement, try get primary from inserting data
} elseif ($this->primary && isset($data[$this->primary])) {
$primaryKey = $data[$this->primary];

// If primaryKey cannot be prepared, return inserted rows count
} else {
unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
return $return->getRowCount();
}

$row = $this->createSelectionInstance()
Expand Down
24 changes: 12 additions & 12 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' => FALSE, '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' => FALSE, 'vendor' => []],
['name' => 'name', 'primary' => FALSE,'autoincrement' => FALSE, 'vendor' => []],
];

Assert::same($columns, $this->structure->getColumns('tags'));
Expand Down
110 changes: 110 additions & 0 deletions tests/Database/Table/Selection.insert().primaryKeys.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

/**
* Test: Nette\Database\Table\Selection: Different setup for primary keys
* @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 with simple primary index (autoincrement)
test(function() use ($context) {
$simplePkAutoincrementResult = $context->table('simple_pk_autoincrement')->insert([
'note' => 'Some note here'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $simplePkAutoincrementResult);
Assert::equal(1, $simplePkAutoincrementResult->identifier1);
Assert::equal('Some note here', $simplePkAutoincrementResult->note);

$simplePkAutoincrementResult2 = $context->table('simple_pk_autoincrement')->insert([
'note' => 'Some note here 2'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $simplePkAutoincrementResult2);
Assert::equal(2, $simplePkAutoincrementResult2->identifier1);
Assert::equal('Some note here 2', $simplePkAutoincrementResult2->note);
});

// Insert into table with simple primary index (no autoincrement)
test(function() use ($context) {
$simplePkNoAutoincrementResult = $context->table('simple_pk_no_autoincrement')->insert([
'identifier1' => 100,
'note' => 'Some note here'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $simplePkNoAutoincrementResult);
Assert::equal(100, $simplePkNoAutoincrementResult->identifier1);
Assert::equal('Some note here', $simplePkNoAutoincrementResult->note);

$simplePkNoAutoincrementResult2 = $context->table('simple_pk_no_autoincrement')->insert([
'identifier1' => 200,
'note' => 'Some note here 2'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $simplePkNoAutoincrementResult2);
Assert::equal(200, $simplePkNoAutoincrementResult2->identifier1);
Assert::equal('Some note here 2', $simplePkNoAutoincrementResult2->note);
});

// Insert into table with multi column primary index (no autoincrement)
test(function() use ($context) {
$multiPkNoAutoincrementResult = $context->table('multi_pk_no_autoincrement')->insert([
'identifier1' => 5,
'identifier2' => 10,
'note' => 'Some note here'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $multiPkNoAutoincrementResult);
Assert::equal(5, $multiPkNoAutoincrementResult->identifier1);
Assert::equal(10, $multiPkNoAutoincrementResult->identifier2);
Assert::equal('Some note here', $multiPkNoAutoincrementResult->note);

$multiPkNoAutoincrementResult2 = $context->table('multi_pk_no_autoincrement')->insert([
'identifier1' => 5,
'identifier2' => 100,
'note' => 'Some note here 2'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $multiPkNoAutoincrementResult2);
Assert::equal(5, $multiPkNoAutoincrementResult2->identifier1);
Assert::equal(100, $multiPkNoAutoincrementResult2->identifier2);
Assert::equal('Some note here 2', $multiPkNoAutoincrementResult2->note);
});

// Insert into table with multi column primary index (autoincrement)
test(function() use ($driverName, $context) {
if (in_array($driverName, ['mysql', 'pgsql'])) {
$multiPkAutoincrementResult = $context->table('multi_pk_autoincrement')->insert([
'identifier2' => 999,
'note' => 'Some note here'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $multiPkAutoincrementResult);
Assert::equal(1, $multiPkAutoincrementResult->identifier1);
Assert::equal(999, $multiPkAutoincrementResult->identifier2);
Assert::equal('Some note here', $multiPkAutoincrementResult->note);

$multiPkAutoincrementResult2 = $context->table('multi_pk_autoincrement')->insert([
'identifier2' => 999,
'note' => 'Some note here 2'
]);

Assert::type(Nette\Database\Table\ActiveRow::class, $multiPkAutoincrementResult2);
Assert::equal(2, $multiPkAutoincrementResult2->identifier1);
Assert::equal(999, $multiPkAutoincrementResult2->identifier2);
Assert::equal('Some note here 2', $multiPkAutoincrementResult2->note);
}
});

// Insert into table without primary key
test(function() use ($context) {
$noPkResult1 = $context->table('no_pk')->insert([
'note' => 'Some note here',
]);
Assert::equal(1, $noPkResult1);
});
11 changes: 10 additions & 1 deletion tests/Database/Table/bugs/bug1342.postgre.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,13 @@ $insertedRows = $context->table('bug1342')->insert([
'a2' => 2,
]);

Assert::same(1, $insertedRows);
Assert::same($insertedRows->a1, 1);
Assert::same($insertedRows->a2, 2);

$insertedRows = $context->table('bug1342')->insert([
'a1' => 24,
'a2' => 48,
]);

Assert::same($insertedRows->a1, 24);
Assert::same($insertedRows->a2, 48);
33 changes: 33 additions & 0 deletions tests/Database/files/mysql-nette_test4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
DROP DATABASE IF EXISTS nette_test;
CREATE DATABASE nette_test;
USE nette_test;

CREATE TABLE simple_pk_autoincrement (
identifier1 int NOT NULL AUTO_INCREMENT,
note varchar(100),
PRIMARY KEY (identifier1)
) ENGINE=InnoDB;

CREATE TABLE simple_pk_no_autoincrement (
identifier1 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1)
) ENGINE=InnoDB;

CREATE TABLE multi_pk_no_autoincrement (
identifier1 int NOT NULL,
identifier2 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1, identifier2)
) ENGINE=InnoDB;

CREATE TABLE multi_pk_autoincrement(
identifier1 int NOT NULL AUTO_INCREMENT,
identifier2 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1, identifier2)
) ENGINE=InnoDB;

CREATE TABLE no_pk (
note varchar(100)
) ENGINE=InnoDB;
32 changes: 32 additions & 0 deletions tests/Database/files/pgsql-nette_test4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
DROP SCHEMA IF EXISTS public CASCADE;
CREATE SCHEMA public;

CREATE TABLE simple_pk_autoincrement (
identifier1 serial NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1)
);

CREATE TABLE simple_pk_no_autoincrement (
identifier1 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1)
);

CREATE TABLE multi_pk_no_autoincrement (
identifier1 int NOT NULL,
identifier2 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1, identifier2)
);

CREATE TABLE multi_pk_autoincrement(
identifier1 serial NOT NULL,
identifier2 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1, identifier2)
);

CREATE TABLE no_pk (
note varchar(100)
);
27 changes: 27 additions & 0 deletions tests/Database/files/sqlite-nette_test4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
DROP TABLE IF EXISTS simple_pk_autoincrement;
DROP TABLE IF EXISTS simple_pk_no_autoincrement;
DROP TABLE IF EXISTS multi_pk_no_autoincrement;
DROP TABLE IF EXISTS multi_pk_autoincrement;
DROP TABLE IF EXISTS no_pk;

CREATE TABLE simple_pk_autoincrement (
identifier1 integer PRIMARY KEY AUTOINCREMENT,
note varchar(100)
);

CREATE TABLE simple_pk_no_autoincrement (
identifier1 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1)
);

CREATE TABLE multi_pk_no_autoincrement (
identifier1 int NOT NULL,
identifier2 int NOT NULL,
note varchar(100),
PRIMARY KEY (identifier1, identifier2)
);

CREATE TABLE no_pk (
note varchar(100)
);
Loading

0 comments on commit debc7db

Please sign in to comment.