Skip to content

Commit

Permalink
Fix composite key generation in Postgres
Browse files Browse the repository at this point in the history
Two columns cannot be serial in postgres. If there is more than one column in a primary key don't flag both of them as
SERIAL. Instead users can use autoIncrement = true to manually flag the serial column.
  • Loading branch information
markstory committed Jan 2, 2014
1 parent 423018d commit 0632500
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 13 deletions.
22 changes: 15 additions & 7 deletions Cake/Database/Schema/PostgresSchema.php
@@ -1,7 +1,5 @@
<?php
/**
* PHP Version 5.4
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
Expand Down Expand Up @@ -203,6 +201,17 @@ public function convertIndexDescription(Table $table, $row) {
'type' => $type,
'columns' => $columns
]);

// If there is only one column in the primary key and it is integery,
// make it autoincrement.
$columnDef = $table->column($columns[0]);
if (
count($columns) === 1 &&
in_array($columnDef['type'], ['integer', 'biginteger'])
) {
$columnDef['autoIncrement'] = true;
$table->addColumn($columns[0], $columnDef);
}
return;
}
$table->addIndex($name, [
Expand Down Expand Up @@ -284,7 +293,6 @@ public function columnSql(Table $table, $name) {
$data = $table->column($name);
$out = $this->_driver->quoteIdentifier($name);
$typeMap = [
'biginteger' => ' BIGINT',
'boolean' => ' BOOLEAN',
'binary' => ' BYTEA',
'float' => ' FLOAT',
Expand All @@ -301,10 +309,10 @@ public function columnSql(Table $table, $name) {
$out .= $typeMap[$data['type']];
}

if ($data['type'] === 'integer') {
$type = ' INTEGER';
if (in_array($name, (array)$table->primaryKey())) {
$type = ' SERIAL';
if ($data['type'] === 'integer' || $data['type'] === 'biginteger') {
$type = $data['type'] === 'integer' ? ' INTEGER' : ' BIGINT';
if ([$name] === $table->primaryKey() || $data['autoIncrement'] === true) {
$type = $data['type'] === 'integer' ? ' SERIAL' : ' BIGSERIAL';
unset($data['null'], $data['default']);
}
$out .= $type;
Expand Down
85 changes: 79 additions & 6 deletions Cake/Test/TestCase/Database/Schema/PostgresSchemaTest.php
Expand Up @@ -256,8 +256,9 @@ public function testDescribeTable() {
'default' => null,
'length' => 20,
'precision' => null,
'fixed' => null,
'unsigned' => null,
'comment' => null,
'autoIncrement' => true,
],
'title' => [
'type' => 'string',
Expand All @@ -274,7 +275,6 @@ public function testDescribeTable() {
'default' => null,
'length' => null,
'precision' => null,
'fixed' => null,
'comment' => null,
],
'author_id' => [
Expand All @@ -283,16 +283,16 @@ public function testDescribeTable() {
'default' => null,
'length' => 10,
'precision' => null,
'fixed' => null,
'unsigned' => null,
'comment' => null,
'autoIncrement' => null,
],
'published' => [
'type' => 'boolean',
'null' => true,
'default' => 0,
'length' => null,
'precision' => null,
'fixed' => null,
'comment' => null,
],
'views' => [
Expand All @@ -301,16 +301,16 @@ public function testDescribeTable() {
'default' => 0,
'length' => 5,
'precision' => null,
'fixed' => null,
'unsigned' => null,
'comment' => null,
'autoIncrement' => null,
],
'created' => [
'type' => 'datetime',
'null' => true,
'default' => null,
'length' => null,
'precision' => null,
'fixed' => null,
'comment' => null,
],
];
Expand Down Expand Up @@ -433,6 +433,16 @@ public static function columnSqlProvider() {
['type' => 'biginteger', 'length' => 20],
'"post_id" BIGINT'
],
[
'post_id',
['type' => 'integer', 'autoIncrement' => true, 'length' => 11],
'"post_id" SERIAL'
],
[
'post_id',
['type' => 'biginteger', 'autoIncrement' => true, 'length' => 20],
'"post_id" BIGSERIAL'
],
// Decimal
[
'value',
Expand Down Expand Up @@ -662,6 +672,69 @@ public function testCreateSql() {
);
}

/**
* Test primary key generation & auto-increment.
*
* @return void
*/
public function testCreateSqlCompositeIntegerKey() {
$driver = $this->_getMockedDriver();
$connection = $this->getMock('Cake\Database\Connection', [], [], '', false);
$connection->expects($this->any())->method('driver')
->will($this->returnValue($driver));

$table = (new Table('articles_tags'))
->addColumn('article_id', [
'type' => 'integer',
'null' => false
])
->addColumn('tag_id', [
'type' => 'integer',
'null' => false,
])
->addConstraint('primary', [
'type' => 'primary',
'columns' => ['article_id', 'tag_id']
]);

$expected = <<<SQL
CREATE TABLE "articles_tags" (
"article_id" INTEGER NOT NULL,
"tag_id" INTEGER NOT NULL,
PRIMARY KEY ("article_id", "tag_id")
)
SQL;
$result = $table->createSql($connection);
$this->assertCount(1, $result);
$this->assertEquals($expected, $result[0]);

$table = (new Table('composite_key'))
->addColumn('id', [
'type' => 'integer',
'null' => false,
'autoIncrement' => true
])
->addColumn('account_id', [
'type' => 'integer',
'null' => false,
])
->addConstraint('primary', [
'type' => 'primary',
'columns' => ['id', 'account_id']
]);

$expected = <<<SQL
CREATE TABLE "composite_key" (
"id" SERIAL,
"account_id" INTEGER NOT NULL,
PRIMARY KEY ("id", "account_id")
)
SQL;
$result = $table->createSql($connection);
$this->assertCount(1, $result);
$this->assertEquals($expected, $result[0]);
}

/**
* test dropSql
*
Expand Down

0 comments on commit 0632500

Please sign in to comment.