SqliteSchemaManagerTest.php 9.6 KB
Newer Older
1 2
<?php

3
namespace Doctrine\DBAL\Tests\Functional\Schema;
4

5
use Doctrine\DBAL\DBALException;
Sergei Morozov's avatar
Sergei Morozov committed
6
use Doctrine\DBAL\Driver\Connection;
7
use Doctrine\DBAL\Schema;
Sergei Morozov's avatar
Sergei Morozov committed
8
use Doctrine\DBAL\Schema\Table;
Sergei Morozov's avatar
Sergei Morozov committed
9
use Doctrine\DBAL\Types\BlobType;
10
use Doctrine\DBAL\Types\Type;
11
use Doctrine\DBAL\Types\Types;
Sergei Morozov's avatar
Sergei Morozov committed
12
use SQLite3;
13

14 15 16 17
use function array_map;
use function dirname;
use function extension_loaded;
use function version_compare;
18

romanb's avatar
romanb committed
19
class SqliteSchemaManagerTest extends SchemaManagerFunctionalTestCase
20
{
romanb's avatar
romanb committed
21
    /**
22
     * SQLITE does not support databases.
romanb's avatar
romanb committed
23
     */
24
    public function testListDatabases(): void
25
    {
26 27
        $this->expectException(DBALException::class);

Sergei Morozov's avatar
Sergei Morozov committed
28
        $this->schemaManager->listDatabases();
29 30
    }

31
    public function testCreateAndDropDatabase(): void
32
    {
Sergei Morozov's avatar
Sergei Morozov committed
33
        $path = dirname(__FILE__) . '/test_create_and_drop_sqlite_database.sqlite';
34

Sergei Morozov's avatar
Sergei Morozov committed
35
        $this->schemaManager->createDatabase($path);
Gabriel Caruso's avatar
Gabriel Caruso committed
36
        self::assertFileExists($path);
Sergei Morozov's avatar
Sergei Morozov committed
37
        $this->schemaManager->dropDatabase($path);
38
        self::assertFileDoesNotExist($path);
jwage's avatar
jwage committed
39
    }
40

41 42 43
    /**
     * @group DBAL-1220
     */
44
    public function testDropsDatabaseWithActiveConnections(): void
45
    {
Sergei Morozov's avatar
Sergei Morozov committed
46
        $this->schemaManager->dropAndCreateDatabase('test_drop_database');
47

48
        self::assertFileExists('test_drop_database');
49

Sergei Morozov's avatar
Sergei Morozov committed
50
        $params           = $this->connection->getParams();
51 52
        $params['dbname'] = 'test_drop_database';

53
        $connection = $this->connection->getDriver()->connect($params);
54

Sergei Morozov's avatar
Sergei Morozov committed
55
        self::assertInstanceOf(Connection::class, $connection);
56

Sergei Morozov's avatar
Sergei Morozov committed
57
        $this->schemaManager->dropDatabase('test_drop_database');
58

59
        self::assertFileDoesNotExist('test_drop_database');
60 61 62 63

        unset($connection);
    }

64
    public function testRenameTable(): void
65
    {
66
        $this->createTestTable('oldname');
Sergei Morozov's avatar
Sergei Morozov committed
67
        $this->schemaManager->renameTable('oldname', 'newname');
68

Sergei Morozov's avatar
Sergei Morozov committed
69
        $tables = $this->schemaManager->listTableNames();
70 71
        self::assertContains('newname', $tables);
        self::assertNotContains('oldname', $tables);
72
    }
73

74
    public function createListTableColumns(): Table
75
    {
76 77 78 79
        $table = parent::createListTableColumns();
        $table->getColumn('id')->setAutoincrement(true);

        return $table;
80
    }
81

82
    public function testListForeignKeysFromExistingDatabase(): void
83
    {
Sergei Morozov's avatar
Sergei Morozov committed
84
        $this->connection->exec(<<<EOS
85 86 87 88 89 90 91 92 93 94
CREATE TABLE user (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    page INTEGER CONSTRAINT FK_1 REFERENCES page (key) DEFERRABLE INITIALLY DEFERRED,
    parent INTEGER REFERENCES user(id) ON DELETE CASCADE,
    log INTEGER,
    CONSTRAINT FK_3 FOREIGN KEY (log) REFERENCES log ON UPDATE SET NULL NOT DEFERRABLE
)
EOS
        );

Sergei Morozov's avatar
Sergei Morozov committed
95 96 97 98
        $expected = [
            new Schema\ForeignKeyConstraint(
                ['log'],
                'log',
Grégoire Paris's avatar
Grégoire Paris committed
99
                [''],
Sergei Morozov's avatar
Sergei Morozov committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
                'FK_3',
                ['onUpdate' => 'SET NULL', 'onDelete' => 'NO ACTION', 'deferrable' => false, 'deferred' => false]
            ),
            new Schema\ForeignKeyConstraint(
                ['parent'],
                'user',
                ['id'],
                '1',
                ['onUpdate' => 'NO ACTION', 'onDelete' => 'CASCADE', 'deferrable' => false, 'deferred' => false]
            ),
            new Schema\ForeignKeyConstraint(
                ['page'],
                'page',
                ['key'],
                'FK_1',
                ['onUpdate' => 'NO ACTION', 'onDelete' => 'NO ACTION', 'deferrable' => true, 'deferred' => true]
            ),
        ];
118

Sergei Morozov's avatar
Sergei Morozov committed
119
        self::assertEquals($expected, $this->schemaManager->listTableForeignKeys('user'));
120
    }
Steve Müller's avatar
Steve Müller committed
121

122
    public function testColumnCollation(): void
123 124 125 126 127 128
    {
        $table = new Schema\Table('test_collation');
        $table->addColumn('id', 'integer');
        $table->addColumn('text', 'text');
        $table->addColumn('foo', 'text')->setPlatformOption('collation', 'BINARY');
        $table->addColumn('bar', 'text')->setPlatformOption('collation', 'NOCASE');
Sergei Morozov's avatar
Sergei Morozov committed
129
        $this->schemaManager->dropAndCreateTable($table);
130

Sergei Morozov's avatar
Sergei Morozov committed
131
        $columns = $this->schemaManager->listTableColumns('test_collation');
132

133 134 135 136
        self::assertArrayNotHasKey('collation', $columns['id']->getPlatformOptions());
        self::assertEquals('BINARY', $columns['text']->getPlatformOption('collation'));
        self::assertEquals('BINARY', $columns['foo']->getPlatformOption('collation'));
        self::assertEquals('NOCASE', $columns['bar']->getPlatformOption('collation'));
137 138
    }

139
    public function testListTableWithBinary(): void
Steve Müller's avatar
Steve Müller committed
140 141 142
    {
        $tableName = 'test_binary_table';

Sergei Morozov's avatar
Sergei Morozov committed
143
        $table = new Table($tableName);
Steve Müller's avatar
Steve Müller committed
144
        $table->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
145 146 147
        $table->addColumn('column_varbinary', 'binary', []);
        $table->addColumn('column_binary', 'binary', ['fixed' => true]);
        $table->setPrimaryKey(['id']);
Steve Müller's avatar
Steve Müller committed
148

Sergei Morozov's avatar
Sergei Morozov committed
149
        $this->schemaManager->createTable($table);
Steve Müller's avatar
Steve Müller committed
150

Sergei Morozov's avatar
Sergei Morozov committed
151
        $table = $this->schemaManager->listTableDetails($tableName);
Steve Müller's avatar
Steve Müller committed
152

Sergei Morozov's avatar
Sergei Morozov committed
153
        self::assertInstanceOf(BlobType::class, $table->getColumn('column_varbinary')->getType());
154
        self::assertFalse($table->getColumn('column_varbinary')->getFixed());
Steve Müller's avatar
Steve Müller committed
155

Sergei Morozov's avatar
Sergei Morozov committed
156
        self::assertInstanceOf(BlobType::class, $table->getColumn('column_binary')->getType());
157
        self::assertFalse($table->getColumn('column_binary')->getFixed());
Steve Müller's avatar
Steve Müller committed
158
    }
159

160
    public function testNonDefaultPKOrder(): void
161
    {
Sergei Morozov's avatar
Sergei Morozov committed
162
        if (! extension_loaded('sqlite3')) {
163
            self::markTestSkipped('This test requires the SQLite3 extension.');
164 165
        }

Sergei Morozov's avatar
Sergei Morozov committed
166 167
        $version = SQLite3::version();
        if (version_compare($version['versionString'], '3.7.16', '<')) {
168
            self::markTestSkipped('This version of sqlite doesn\'t return the order of the Primary Key.');
169
        }
Grégoire Paris's avatar
Grégoire Paris committed
170

Sergei Morozov's avatar
Sergei Morozov committed
171
        $this->connection->exec(<<<EOS
172 173 174 175 176 177 178 179
CREATE TABLE non_default_pk_order (
    id INTEGER,
    other_id INTEGER,
    PRIMARY KEY(other_id, id)
)
EOS
        );

Sergei Morozov's avatar
Sergei Morozov committed
180
        $tableIndexes = $this->schemaManager->listTableIndexes('non_default_pk_order');
181

Gabriel Caruso's avatar
Gabriel Caruso committed
182
         self::assertCount(1, $tableIndexes);
183

184
        self::assertArrayHasKey('primary', $tableIndexes, 'listTableIndexes() has to return a "primary" array key.');
Sergei Morozov's avatar
Sergei Morozov committed
185
        self::assertEquals(['other_id', 'id'], array_map('strtolower', $tableIndexes['primary']->getColumns()));
186
    }
187

188 189 190
    /**
     * @group DBAL-1779
     */
191
    public function testListTableColumnsWithWhitespacesInTypeDeclarations(): void
192 193 194 195 196 197 198 199
    {
        $sql = <<<SQL
CREATE TABLE dbal_1779 (
    foo VARCHAR (64) ,
    bar TEXT (100)
)
SQL;

Sergei Morozov's avatar
Sergei Morozov committed
200
        $this->connection->exec($sql);
201

Sergei Morozov's avatar
Sergei Morozov committed
202
        $columns = $this->schemaManager->listTableColumns('dbal_1779');
203

204
        self::assertCount(2, $columns);
205

206 207
        self::assertArrayHasKey('foo', $columns);
        self::assertArrayHasKey('bar', $columns);
208

209 210
        self::assertSame(Type::getType(Types::STRING), $columns['foo']->getType());
        self::assertSame(Type::getType(Types::TEXT), $columns['bar']->getType());
211

212 213
        self::assertSame(64, $columns['foo']->getLength());
        self::assertSame(100, $columns['bar']->getLength());
214 215
    }

216 217 218 219
    /**
     * @dataProvider getDiffListIntegerAutoincrementTableColumnsData
     * @group DBAL-924
     */
220
    public function testDiffListIntegerAutoincrementTableColumns(string $integerType, bool $unsigned, bool $expectedComparatorDiff): void
221 222 223
    {
        $tableName = 'test_int_autoincrement_table';

Sergei Morozov's avatar
Sergei Morozov committed
224 225 226
        $offlineTable = new Table($tableName);
        $offlineTable->addColumn('id', $integerType, ['autoincrement' => true, 'unsigned' => $unsigned]);
        $offlineTable->setPrimaryKey(['id']);
227

Sergei Morozov's avatar
Sergei Morozov committed
228
        $this->schemaManager->dropAndCreateTable($offlineTable);
229

Sergei Morozov's avatar
Sergei Morozov committed
230
        $onlineTable = $this->schemaManager->listTableDetails($tableName);
Sergei Morozov's avatar
Sergei Morozov committed
231 232
        $comparator  = new Schema\Comparator();
        $diff        = $comparator->diffTable($offlineTable, $onlineTable);
233 234

        if ($expectedComparatorDiff) {
Sergei Morozov's avatar
Sergei Morozov committed
235
            self::assertEmpty($this->schemaManager->getDatabasePlatform()->getAlterTableSQL($diff));
236
        } else {
237
            self::assertFalse($diff);
238 239 240 241
        }
    }

    /**
Sergei Morozov's avatar
Sergei Morozov committed
242
     * @return mixed[][]
243
     */
244
    public static function getDiffListIntegerAutoincrementTableColumnsData(): iterable
245
    {
Sergei Morozov's avatar
Sergei Morozov committed
246 247 248 249 250 251 252 253
        return [
            ['smallint', false, true],
            ['smallint', true, true],
            ['integer', false, false],
            ['integer', true, true],
            ['bigint', false, true],
            ['bigint', true, true],
        ];
254
    }
255 256 257 258

    /**
     * @group DBAL-2921
     */
259
    public function testPrimaryKeyNoAutoIncrement(): void
260 261 262 263 264
    {
        $table = new Schema\Table('test_pk_auto_increment');
        $table->addColumn('id', 'integer');
        $table->addColumn('text', 'text');
        $table->setPrimaryKey(['id']);
Sergei Morozov's avatar
Sergei Morozov committed
265
        $this->schemaManager->dropAndCreateTable($table);
266

Sergei Morozov's avatar
Sergei Morozov committed
267
        $this->connection->insert('test_pk_auto_increment', ['text' => '1']);
268

Sergei Morozov's avatar
Sergei Morozov committed
269
        $this->connection->query('DELETE FROM test_pk_auto_increment');
270

Sergei Morozov's avatar
Sergei Morozov committed
271
        $this->connection->insert('test_pk_auto_increment', ['text' => '2']);
272

273 274 275
        $result = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = "2"');

        $lastUsedIdAfterDelete = (int) $result->fetchOne();
276 277

        // with an empty table, non autoincrement rowid is always 1
278
        self::assertEquals(1, $lastUsedIdAfterDelete);
279
    }
280

281
    public function testOnlyOwnCommentIsParsed(): void
282 283 284 285 286 287 288 289 290
    {
        $table = new Table('own_column_comment');
        $table->addColumn('col1', 'string', ['length' => 16]);
        $table->addColumn('col2', 'string', ['length' => 16, 'comment' => 'Column #2']);
        $table->addColumn('col3', 'string', ['length' => 16]);

        $sm = $this->connection->getSchemaManager();
        $sm->createTable($table);

291
        self::assertNull($sm->listTableDetails('own_column_comment')
292 293 294
            ->getColumn('col1')
            ->getComment());
    }
295
}