Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Doctrine\DBAL\Id;
4
 
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Driver;
7
use Doctrine\DBAL\DriverManager;
8
use Doctrine\DBAL\Exception;
9
use Doctrine\DBAL\LockMode;
10
use Doctrine\Deprecations\Deprecation;
11
use Throwable;
12
 
13
use function array_change_key_case;
14
use function assert;
15
use function is_int;
16
 
17
use const CASE_LOWER;
18
 
19
/**
20
 * Table ID Generator for those poor languages that are missing sequences.
21
 *
22
 * WARNING: The Table Id Generator clones a second independent database
23
 * connection to work correctly. This means using the generator requests that
24
 * generate IDs will have two open database connections. This is necessary to
25
 * be safe from transaction failures in the main connection. Make sure to only
26
 * ever use one TableGenerator otherwise you end up with many connections.
27
 *
28
 * TableID Generator does not work with SQLite.
29
 *
30
 * The TableGenerator does not take care of creating the SQL Table itself. You
31
 * should look at the `TableGeneratorSchemaVisitor` to do this for you.
32
 * Otherwise the schema for a table looks like:
33
 *
34
 * CREATE sequences (
35
 *   sequence_name VARCHAR(255) NOT NULL,
36
 *   sequence_value INT NOT NULL DEFAULT 1,
37
 *   sequence_increment_by INT NOT NULL DEFAULT 1,
38
 *   PRIMARY KEY (sequence_name)
39
 * );
40
 *
41
 * Technically this generator works as follows:
42
 *
43
 * 1. Use a robust transaction serialization level.
44
 * 2. Open transaction
45
 * 3. Acquire a read lock on the table row (SELECT .. FOR UPDATE)
46
 * 4. Increment current value by one and write back to database
47
 * 5. Commit transaction
48
 *
49
 * If you are using a sequence_increment_by value that is larger than one the
50
 * ID Generator will keep incrementing values until it hits the incrementation
51
 * gap before issuing another query.
52
 *
53
 * If no row is present for a given sequence a new one will be created with the
54
 * default values 'value' = 1 and 'increment_by' = 1
55
 *
56
 * @deprecated
57
 */
58
class TableGenerator
59
{
60
    private Connection $conn;
61
 
62
    /** @var string */
63
    private $generatorTableName;
64
 
65
    /** @var mixed[][] */
66
    private array $sequences = [];
67
 
68
    /**
69
     * @param string $generatorTableName
70
     *
71
     * @throws Exception
72
     */
73
    public function __construct(Connection $conn, $generatorTableName = 'sequences')
74
    {
75
        Deprecation::trigger(
76
            'doctrine/dbal',
77
            'https://github.com/doctrine/dbal/pull/4681',
78
            'The TableGenerator class is is deprecated.',
79
        );
80
 
81
        if ($conn->getDriver() instanceof Driver\PDO\SQLite\Driver) {
82
            throw new Exception('Cannot use TableGenerator with SQLite.');
83
        }
84
 
85
        $this->conn = DriverManager::getConnection(
86
            $conn->getParams(),
87
            $conn->getConfiguration(),
88
            $conn->getEventManager(),
89
        );
90
 
91
        $this->generatorTableName = $generatorTableName;
92
    }
93
 
94
    /**
95
     * Generates the next unused value for the given sequence name.
96
     *
97
     * @param string $sequence
98
     *
99
     * @return int
100
     *
101
     * @throws Exception
102
     */
103
    public function nextValue($sequence)
104
    {
105
        if (isset($this->sequences[$sequence])) {
106
            $value = $this->sequences[$sequence]['value'];
107
            $this->sequences[$sequence]['value']++;
108
            if ($this->sequences[$sequence]['value'] >= $this->sequences[$sequence]['max']) {
109
                unset($this->sequences[$sequence]);
110
            }
111
 
112
            return $value;
113
        }
114
 
115
        $this->conn->beginTransaction();
116
 
117
        try {
118
            $platform = $this->conn->getDatabasePlatform();
119
            $sql      = 'SELECT sequence_value, sequence_increment_by'
120
                . ' FROM ' . $platform->appendLockHint($this->generatorTableName, LockMode::PESSIMISTIC_WRITE)
121
                . ' WHERE sequence_name = ? ' . $platform->getWriteLockSQL();
122
            $row      = $this->conn->fetchAssociative($sql, [$sequence]);
123
 
124
            if ($row !== false) {
125
                $row = array_change_key_case($row, CASE_LOWER);
126
 
127
                $value = $row['sequence_value'];
128
                $value++;
129
 
130
                assert(is_int($value));
131
 
132
                if ($row['sequence_increment_by'] > 1) {
133
                    $this->sequences[$sequence] = [
134
                        'value' => $value,
135
                        'max' => $row['sequence_value'] + $row['sequence_increment_by'],
136
                    ];
137
                }
138
 
139
                $sql  = 'UPDATE ' . $this->generatorTableName . ' ' .
140
                       'SET sequence_value = sequence_value + sequence_increment_by ' .
141
                       'WHERE sequence_name = ? AND sequence_value = ?';
142
                $rows = $this->conn->executeStatement($sql, [$sequence, $row['sequence_value']]);
143
 
144
                if ($rows !== 1) {
145
                    throw new Exception('Race-condition detected while updating sequence. Aborting generation');
146
                }
147
            } else {
148
                $this->conn->insert(
149
                    $this->generatorTableName,
150
                    ['sequence_name' => $sequence, 'sequence_value' => 1, 'sequence_increment_by' => 1],
151
                );
152
                $value = 1;
153
            }
154
 
155
            $this->conn->commit();
156
        } catch (Throwable $e) {
157
            $this->conn->rollBack();
158
 
159
            throw new Exception(
160
                'Error occurred while generating ID with TableGenerator, aborted generation: ' . $e->getMessage(),
161
                0,
162
                $e,
163
            );
164
        }
165
 
166
        return $value;
167
    }
168
}