WriteTest.php 11.5 KB
Newer Older
1 2
<?php

Michael Moravec's avatar
Michael Moravec committed
3 4
declare(strict_types=1);

5
namespace Doctrine\Tests\DBAL\Functional;
6

Sergei Morozov's avatar
Sergei Morozov committed
7
use DateTime;
8
use Doctrine\DBAL\Driver\DriverException;
9
use Doctrine\DBAL\ParameterType;
Sergei Morozov's avatar
Sergei Morozov committed
10 11 12 13
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use Doctrine\Tests\DbalFunctionalTestCase;
use Throwable;
14 15
use function array_filter;
use function strtolower;
16

Sergei Morozov's avatar
Sergei Morozov committed
17
class WriteTest extends DbalFunctionalTestCase
18
{
19
    protected function setUp() : void
20 21 22
    {
        parent::setUp();

23 24 25 26 27 28 29 30 31 32 33
        $table = new Table('write_table');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->addColumn('test_int', 'integer');
        $table->addColumn('test_string', 'string', [
            'length' => 32,
            'notnull' => false,
        ]);
        $table->setPrimaryKey(['id']);

        $this->connection->getSchemaManager()->dropAndCreateTable($table);

Sergei Morozov's avatar
Sergei Morozov committed
34
        $this->connection->executeUpdate('DELETE FROM write_table');
35 36
    }

37
    public function testExecuteUpdate() : void
38
    {
39
        $sql      = 'INSERT INTO write_table (test_int) VALUES (1)';
Sergei Morozov's avatar
Sergei Morozov committed
40
        $affected = $this->connection->executeUpdate($sql);
41

Sergei Morozov's avatar
Sergei Morozov committed
42
        self::assertEquals(1, $affected, 'executeUpdate() should return the number of affected rows!');
43 44
    }

45
    public function testExecuteUpdateWithTypes() : void
46
    {
Sergei Morozov's avatar
Sergei Morozov committed
47
        $sql      = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
Sergei Morozov's avatar
Sergei Morozov committed
48
        $affected = $this->connection->executeUpdate(
49
            $sql,
Sergei Morozov's avatar
Sergei Morozov committed
50 51
            [1, 'foo'],
            [ParameterType::INTEGER, ParameterType::STRING]
52
        );
53

Sergei Morozov's avatar
Sergei Morozov committed
54
        self::assertEquals(1, $affected, 'executeUpdate() should return the number of affected rows!');
55 56
    }

57
    public function testPrepareRowCountReturnsAffectedRows() : void
58
    {
Sergei Morozov's avatar
Sergei Morozov committed
59
        $sql  = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
Sergei Morozov's avatar
Sergei Morozov committed
60
        $stmt = $this->connection->prepare($sql);
61 62

        $stmt->bindValue(1, 1);
Sergei Morozov's avatar
Sergei Morozov committed
63
        $stmt->bindValue(2, 'foo');
64 65
        $stmt->execute();

66
        self::assertEquals(1, $stmt->rowCount());
67 68
    }

69
    public function testPrepareWithPdoTypes() : void
70
    {
Sergei Morozov's avatar
Sergei Morozov committed
71
        $sql  = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
Sergei Morozov's avatar
Sergei Morozov committed
72
        $stmt = $this->connection->prepare($sql);
73

74
        $stmt->bindValue(1, 1, ParameterType::INTEGER);
Sergei Morozov's avatar
Sergei Morozov committed
75
        $stmt->bindValue(2, 'foo', ParameterType::STRING);
76 77
        $stmt->execute();

78
        self::assertEquals(1, $stmt->rowCount());
79 80
    }

81
    public function testPrepareWithDbalTypes() : void
82
    {
Sergei Morozov's avatar
Sergei Morozov committed
83
        $sql  = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
Sergei Morozov's avatar
Sergei Morozov committed
84
        $stmt = $this->connection->prepare($sql);
85

86 87
        $stmt->bindValue(1, 1, ParameterType::INTEGER);
        $stmt->bindValue(2, 'foo', ParameterType::STRING);
88 89
        $stmt->execute();

90
        self::assertEquals(1, $stmt->rowCount());
91 92
    }

93
    public function testPrepareWithDbalTypeNames() : void
94
    {
Sergei Morozov's avatar
Sergei Morozov committed
95
        $sql  = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
Sergei Morozov's avatar
Sergei Morozov committed
96
        $stmt = $this->connection->prepare($sql);
97

98 99
        $stmt->bindValue(1, 1, ParameterType::INTEGER);
        $stmt->bindValue(2, 'foo', ParameterType::STRING);
100 101
        $stmt->execute();

102
        self::assertEquals(1, $stmt->rowCount());
103 104
    }

105
    public function insertRows() : void
106
    {
Sergei Morozov's avatar
Sergei Morozov committed
107 108
        self::assertEquals(1, $this->connection->insert('write_table', ['test_int' => 1, 'test_string' => 'foo']));
        self::assertEquals(1, $this->connection->insert('write_table', ['test_int' => 2, 'test_string' => 'bar']));
109 110
    }

111
    public function testInsert() : void
112 113 114 115
    {
        $this->insertRows();
    }

116
    public function testDelete() : void
117 118 119
    {
        $this->insertRows();

Sergei Morozov's avatar
Sergei Morozov committed
120 121
        self::assertEquals(1, $this->connection->delete('write_table', ['test_int' => 2]));
        self::assertCount(1, $this->connection->fetchAll('SELECT * FROM write_table'));
122

Sergei Morozov's avatar
Sergei Morozov committed
123 124
        self::assertEquals(1, $this->connection->delete('write_table', ['test_int' => 1]));
        self::assertCount(0, $this->connection->fetchAll('SELECT * FROM write_table'));
125 126
    }

127
    public function testUpdate() : void
128 129 130
    {
        $this->insertRows();

Sergei Morozov's avatar
Sergei Morozov committed
131 132 133
        self::assertEquals(1, $this->connection->update('write_table', ['test_string' => 'bar'], ['test_string' => 'foo']));
        self::assertEquals(2, $this->connection->update('write_table', ['test_string' => 'baz'], ['test_string' => 'bar']));
        self::assertEquals(0, $this->connection->update('write_table', ['test_string' => 'baz'], ['test_string' => 'bar']));
134
    }
135

136
    public function testLastInsertId() : void
137
    {
Sergei Morozov's avatar
Sergei Morozov committed
138
        if (! $this->connection->getDatabasePlatform()->prefersIdentityColumns()) {
139 140 141
            $this->markTestSkipped('Test only works on platforms with identity columns.');
        }

Sergei Morozov's avatar
Sergei Morozov committed
142
        self::assertEquals(1, $this->connection->insert('write_table', ['test_int' => 2, 'test_string' => 'bar']));
143
        $num = $this->lastInsertId();
144

Sergei Morozov's avatar
Sergei Morozov committed
145
        self::assertGreaterThan(0, $num, 'LastInsertId() should be non-negative number.');
146
    }
147

148
    public function testLastInsertIdSequence() : void
149
    {
Sergei Morozov's avatar
Sergei Morozov committed
150
        if (! $this->connection->getDatabasePlatform()->supportsSequences()) {
151 152 153
            $this->markTestSkipped('Test only works on platforms with sequences.');
        }

Sergei Morozov's avatar
Sergei Morozov committed
154
        $sequence = new Sequence('write_table_id_seq');
155
        try {
Sergei Morozov's avatar
Sergei Morozov committed
156
            $this->connection->getSchemaManager()->createSequence($sequence);
Sergei Morozov's avatar
Sergei Morozov committed
157
        } catch (Throwable $e) {
158 159
        }

Sergei Morozov's avatar
Sergei Morozov committed
160
        $sequences = $this->connection->getSchemaManager()->listSequences();
Sergei Morozov's avatar
Sergei Morozov committed
161
        self::assertCount(1, array_filter($sequences, static function ($sequence) {
162
            return strtolower($sequence->getName()) === 'write_table_id_seq';
Gabriel Caruso's avatar
Gabriel Caruso committed
163
        }));
164

Sergei Morozov's avatar
Sergei Morozov committed
165
        $stmt            = $this->connection->query($this->connection->getDatabasePlatform()->getSequenceNextValSQL('write_table_id_seq'));
166 167
        $nextSequenceVal = $stmt->fetchColumn();

168
        $lastInsertId = $this->lastInsertId('write_table_id_seq');
169

Gabriel Caruso's avatar
Gabriel Caruso committed
170
        self::assertGreaterThan(0, $lastInsertId);
171
        self::assertEquals($nextSequenceVal, $lastInsertId);
172 173
    }

174
    public function testLastInsertIdNoSequenceGiven() : void
175
    {
Sergei Morozov's avatar
Sergei Morozov committed
176
        if (! $this->connection->getDatabasePlatform()->supportsSequences() || $this->connection->getDatabasePlatform()->supportsIdentityColumns()) {
177
            $this->markTestSkipped("Test only works consistently on platforms that support sequences and don't support identity columns.");
178 179
        }

180 181
        $this->expectException(DriverException::class);
        $this->lastInsertId();
182
    }
183 184 185 186

    /**
     * @group DBAL-445
     */
187
    public function testInsertWithKeyValueTypes() : void
188
    {
Sergei Morozov's avatar
Sergei Morozov committed
189
        $testString = new DateTime('2013-04-14 10:10:10');
190

Sergei Morozov's avatar
Sergei Morozov committed
191
        $this->connection->insert(
192
            'write_table',
Sergei Morozov's avatar
Sergei Morozov committed
193 194
            ['test_int' => '30', 'test_string' => $testString],
            ['test_string' => 'datetime', 'test_int' => 'integer']
195 196
        );

Sergei Morozov's avatar
Sergei Morozov committed
197
        $data = $this->connection->fetchColumn('SELECT test_string FROM write_table WHERE test_int = 30');
198

Sergei Morozov's avatar
Sergei Morozov committed
199
        self::assertEquals($testString->format($this->connection->getDatabasePlatform()->getDateTimeFormatString()), $data);
200 201 202 203 204
    }

    /**
     * @group DBAL-445
     */
205
    public function testUpdateWithKeyValueTypes() : void
206
    {
Sergei Morozov's avatar
Sergei Morozov committed
207
        $testString = new DateTime('2013-04-14 10:10:10');
208

Sergei Morozov's avatar
Sergei Morozov committed
209
        $this->connection->insert(
210
            'write_table',
Sergei Morozov's avatar
Sergei Morozov committed
211 212
            ['test_int' => '30', 'test_string' => $testString],
            ['test_string' => 'datetime', 'test_int' => 'integer']
213 214
        );

Sergei Morozov's avatar
Sergei Morozov committed
215
        $testString = new DateTime('2013-04-15 10:10:10');
216

Sergei Morozov's avatar
Sergei Morozov committed
217
        $this->connection->update(
218
            'write_table',
Sergei Morozov's avatar
Sergei Morozov committed
219 220 221
            ['test_string' => $testString],
            ['test_int' => '30'],
            ['test_string' => 'datetime', 'test_int' => 'integer']
222 223
        );

Sergei Morozov's avatar
Sergei Morozov committed
224
        $data = $this->connection->fetchColumn('SELECT test_string FROM write_table WHERE test_int = 30');
225

Sergei Morozov's avatar
Sergei Morozov committed
226
        self::assertEquals($testString->format($this->connection->getDatabasePlatform()->getDateTimeFormatString()), $data);
227 228 229 230 231
    }

    /**
     * @group DBAL-445
     */
232
    public function testDeleteWithKeyValueTypes() : void
233
    {
Sergei Morozov's avatar
Sergei Morozov committed
234
        $val = new DateTime('2013-04-14 10:10:10');
Sergei Morozov's avatar
Sergei Morozov committed
235
        $this->connection->insert(
236
            'write_table',
Sergei Morozov's avatar
Sergei Morozov committed
237 238
            ['test_int' => '30', 'test_string' => $val],
            ['test_string' => 'datetime', 'test_int' => 'integer']
239 240
        );

Sergei Morozov's avatar
Sergei Morozov committed
241
        $this->connection->delete('write_table', ['test_int' => 30, 'test_string' => $val], ['test_string' => 'datetime', 'test_int' => 'integer']);
242

Sergei Morozov's avatar
Sergei Morozov committed
243
        $data = $this->connection->fetchColumn('SELECT test_string FROM write_table WHERE test_int = 30');
244

245
        self::assertFalse($data);
246
    }
247

248
    public function testEmptyIdentityInsert() : void
249
    {
Sergei Morozov's avatar
Sergei Morozov committed
250
        $platform = $this->connection->getDatabasePlatform();
251

Sergei Morozov's avatar
Sergei Morozov committed
252
        if (! ($platform->supportsIdentityColumns() || $platform->usesSequenceEmulatedIdentityColumns())) {
253 254 255 256 257
            $this->markTestSkipped(
                'Test only works on platforms with identity columns or sequence emulated identity columns.'
            );
        }

Sergei Morozov's avatar
Sergei Morozov committed
258 259 260
        $table = new Table('test_empty_identity');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->setPrimaryKey(['id']);
261 262

        try {
Sergei Morozov's avatar
Sergei Morozov committed
263
            $this->connection->getSchemaManager()->dropTable($table->getQuotedName($platform));
Sergei Morozov's avatar
Sergei Morozov committed
264 265
        } catch (Throwable $e) {
        }
266 267

        foreach ($platform->getCreateTableSQL($table) as $sql) {
Sergei Morozov's avatar
Sergei Morozov committed
268
            $this->connection->exec($sql);
269 270 271 272 273 274 275 276
        }

        $seqName = $platform->usesSequenceEmulatedIdentityColumns()
            ? $platform->getIdentitySequenceName('test_empty_identity', 'id')
            : null;

        $sql = $platform->getEmptyIdentityInsertSQL('test_empty_identity', 'id');

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

279
        $firstId = $this->lastInsertId($seqName);
280

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

283
        $secondId = $this->lastInsertId($seqName);
284

Gabriel Caruso's avatar
Gabriel Caruso committed
285
        self::assertGreaterThan($firstId, $secondId);
286 287
    }

288 289 290
    /**
     * @group DBAL-2688
     */
291
    public function testUpdateWhereIsNull() : void
292
    {
Sergei Morozov's avatar
Sergei Morozov committed
293
        $this->connection->insert(
294 295 296 297 298
            'write_table',
            ['test_int' => '30', 'test_string' => null],
            ['test_string' => 'string', 'test_int' => 'integer']
        );

Sergei Morozov's avatar
Sergei Morozov committed
299
        $data = $this->connection->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
300

301
        self::assertCount(1, $data);
302

Sergei Morozov's avatar
Sergei Morozov committed
303
        $this->connection->update('write_table', ['test_int' => 10], ['test_string' => null], ['test_string' => 'string', 'test_int' => 'integer']);
304

Sergei Morozov's avatar
Sergei Morozov committed
305
        $data = $this->connection->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
306

307
        self::assertCount(0, $data);
308 309
    }

310
    public function testDeleteWhereIsNull() : void
311
    {
Sergei Morozov's avatar
Sergei Morozov committed
312
        $this->connection->insert(
313 314 315 316 317
            'write_table',
            ['test_int' => '30', 'test_string' => null],
            ['test_string' => 'string', 'test_int' => 'integer']
        );

Sergei Morozov's avatar
Sergei Morozov committed
318
        $data = $this->connection->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
319

320
        self::assertCount(1, $data);
321

Sergei Morozov's avatar
Sergei Morozov committed
322
        $this->connection->delete('write_table', ['test_string' => null], ['test_string' => 'string']);
323

Sergei Morozov's avatar
Sergei Morozov committed
324
        $data = $this->connection->fetchAll('SELECT * FROM write_table WHERE test_int = 30');
325

326
        self::assertCount(0, $data);
327
    }
328 329 330 331 332 333 334

    /**
     * Returns the ID of the last inserted row or skips the test if the currently used driver
     * doesn't support this feature
     *
     * @throws DriverException
     */
335
    private function lastInsertId(?string $name = null) : string
336 337 338 339
    {
        try {
            return $this->connection->lastInsertId($name);
        } catch (DriverException $e) {
340
            if ($e->getSQLState() === 'IM001') {
341 342 343 344 345 346
                $this->markTestSkipped($e->getMessage());
            }

            throw $e;
        }
    }
347
}