-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Open
Copy link
Description
Bug Report
| Q | A |
|---|---|
| Version | 4.4.1 (also 4.5.x-dev) |
| Previous Version if the bug is a regression | x.y.z |
Summary
In SQLite, a primary key consisting of an integer column is implicitly autoincrement.
However, a composite primary key does not have this.
This is demonstrated by this SQLite session, which shows the implicit behavour (table user) and that it doesn't apply to composite keys (table user_setting).
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> CREATE TABLE user (id integer not null primary key, username varchar not null, password varchar not null);
sqlite> INSERT INTO user (username, password) VALUES ('admin', 'secret');
sqlite> SELECT * FROM user;
1|admin|secret
sqlite>
sqlite> CREATE TABLE user_setting (user_id integer not null, setting_name varchar not null, setting_value varchar not null, primary key (user_id, setting_name));
sqlite> INSERT INTO user_setting (setting_name, setting_value) VALUES ('role', 'admin');
Runtime error: NOT NULL constraint failed: user_setting.user_id (19)
Current behavior
Introspecting the user_setting.user_id column in the above example wrongly shows it as autoincrement.
Furthermore, the primary key is introspected as ONLY consisting of the integer column, and the second column is lost.
Expected behavior
The autoincrement attribute should only be set when the primary key consists of a single integer column.
How to reproduce
<?php
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Schema;
require 'vendor/autoload.php';
$connection = DriverManager::getConnection(['path' => ':memory:', 'driver' => 'pdo_sqlite']);
$connection->executeStatement('CREATE TABLE user (id integer not null primary key, username varchar not null, password varchar not null)');
$connection->executeStatement('CREATE TABLE user_setting (user_id integer not null, setting_name varchar not null, setting_value varchar not null, primary key (user_id, setting_name))');
$platform = $connection->getDatabasePlatform();
$schema_manager = $connection->createSchemaManager();
$comparator = $schema_manager->createComparator();
$old_schema = $schema_manager->introspectSchema();
// This column is not auto-increment
$autoincrement = $schema_manager->introspectSchema()->getTable('user_setting')->getColumn('user_id')->getAutoincrement();
var_dump($autoincrement); // true, but should be false
// Modify the table (add a FK)
$new_schema = new Schema([
$old_schema->getTable('user'),
$old_schema->getTable('user_setting')
->edit()
->addForeignKeyConstraint(new ForeignKeyConstraint(['user_id'], 'user', ['id']))
->create(),
]);
$queries = $platform->getAlterSchemaSQL($comparator->compareSchemas($old_schema, $new_schema));
// Breaks the PK - tries to change it to JUST the numeric column.
foreach ($queries as $query) {
echo $query, PHP_EOL;
}Script output. Note the CREATE TABLE command turns the composite primary key into a single-column primary key.
bool(true)
CREATE TEMPORARY TABLE __temp__user_setting AS SELECT user_id, setting_name, setting_value FROM user_setting
DROP TABLE user_setting
CREATE TABLE user_setting (user_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, setting_name VARCHAR NOT NULL, setting_value VARCHAR NOT NULL, FOREIGN KEY (user_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)
INSERT INTO user_setting (user_id, setting_name, setting_value) SELECT user_id, setting_name, setting_value FROM __temp__user_setting
DROP TABLE __temp__user_setting
CREATE INDEX IDX_C779A692A76ED395 ON user_setting (user_id)
Reactions are currently unavailable