6
6
7
7
use Doctrine \DBAL \Exception ;
8
8
use Doctrine \DBAL \Platforms \AbstractPlatform ;
9
+ use Doctrine \DBAL \Schema \Exception \InvalidName ;
9
10
use Doctrine \DBAL \Schema \Exception \NamespaceAlreadyExists ;
10
11
use Doctrine \DBAL \Schema \Exception \SequenceAlreadyExists ;
11
12
use Doctrine \DBAL \Schema \Exception \SequenceDoesNotExist ;
12
13
use Doctrine \DBAL \Schema \Exception \TableAlreadyExists ;
13
14
use Doctrine \DBAL \Schema \Exception \TableDoesNotExist ;
15
+ use Doctrine \DBAL \Schema \Name \Identifier ;
14
16
use Doctrine \DBAL \Schema \Name \OptionallyQualifiedName ;
17
+ use Doctrine \DBAL \Schema \Name \Parser ;
15
18
use Doctrine \DBAL \Schema \Name \Parser \UnqualifiedNameParser ;
16
19
use Doctrine \DBAL \Schema \Name \Parsers ;
17
20
use Doctrine \DBAL \Schema \Name \UnqualifiedName ;
18
21
use Doctrine \DBAL \SQL \Builder \CreateSchemaObjectsSQLBuilder ;
19
22
use Doctrine \DBAL \SQL \Builder \DropSchemaObjectsSQLBuilder ;
23
+ use Doctrine \Deprecations \Deprecation ;
20
24
21
25
use function array_values ;
22
- use function str_contains ;
26
+ use function count ;
23
27
use function strtolower ;
24
28
25
29
/**
35
39
* Every asset in the doctrine schema has a name. A name consists of either a
36
40
* namespace.local name pair or just a local unqualified name.
37
41
*
38
- * The abstraction layer that covers a PostgreSQL schema is the namespace of an
42
+ * Objects in a schema can be referenced by unqualified names or qualified
43
+ * names but not both. Whether a given schema uses qualified or unqualified
44
+ * names is determined at runtime by the presence of objects with unqualified
45
+ * names and namespaces.
46
+ *
47
+ * The abstraction layer that covers a PostgreSQL schema is the namespace of a
39
48
* database object (asset). A schema can have a name, which will be used as
40
49
* default namespace for the unqualified database objects that are created in
41
- * the schema.
50
+ * the schema. If a schema uses qualified names and has a name, unqualified
51
+ * names will be resolved against the corresponding namespace.
42
52
*
43
53
* In the case of MySQL where cross-database queries are allowed this leads to
44
54
* databases being "misinterpreted" as namespaces. This is intentional, however
@@ -65,6 +75,12 @@ class Schema extends AbstractOptionallyNamedObject
65
75
66
76
protected SchemaConfig $ _schemaConfig ;
67
77
78
+ /**
79
+ * Indicates whether the schema uses unqualified names for its objects. Once this flag is set to true, it won't be
80
+ * unset even after the objects with unqualified names have been dropped from the schema.
81
+ */
82
+ private bool $ usesUnqualifiedNames = false ;
83
+
68
84
/**
69
85
* @param array<Table> $tables
70
86
* @param array<Sequence> $sequences
@@ -104,39 +120,39 @@ protected function getNameParser(): UnqualifiedNameParser
104
120
105
121
protected function _addTable (Table $ table ): void
106
122
{
107
- $ name = $ table ->getObjectName ();
123
+ $ resolvedName = $ this -> resolveName ( $ table ->getObjectName () );
108
124
109
- $ normalizedName = $ this ->normalizeName ( $ name );
125
+ $ key = $ this ->getKeyFromResolvedName ( $ resolvedName );
110
126
111
- if (isset ($ this ->_tables [$ normalizedName ])) {
112
- throw TableAlreadyExists::new ($ normalizedName );
127
+ if (isset ($ this ->_tables [$ key ])) {
128
+ throw TableAlreadyExists::new ($ resolvedName -> toString () );
113
129
}
114
130
115
- $ this ->ensureNamespaceExists ( $ name );
131
+ $ this ->registerQualifier ( $ resolvedName -> getQualifier () );
116
132
117
- $ this ->_tables [$ normalizedName ] = $ table ;
133
+ $ this ->_tables [$ key ] = $ table ;
118
134
}
119
135
120
136
protected function _addSequence (Sequence $ sequence ): void
121
137
{
122
- $ name = $ sequence ->getObjectName ();
138
+ $ resolvedName = $ this -> resolveName ( $ sequence ->getObjectName () );
123
139
124
- $ normalizedName = $ this ->normalizeName ( $ name );
140
+ $ key = $ this ->getKeyFromResolvedName ( $ resolvedName );
125
141
126
- if (isset ($ this ->_sequences [$ normalizedName ])) {
127
- throw SequenceAlreadyExists::new ($ normalizedName );
142
+ if (isset ($ this ->_sequences [$ key ])) {
143
+ throw SequenceAlreadyExists::new ($ resolvedName -> toString () );
128
144
}
129
145
130
- $ this ->ensureNamespaceExists ( $ name );
146
+ $ this ->registerQualifier ( $ resolvedName -> getQualifier () );
131
147
132
- $ this ->_sequences [$ normalizedName ] = $ sequence ;
148
+ $ this ->_sequences [$ key ] = $ sequence ;
133
149
}
134
150
135
- private function ensureNamespaceExists ( OptionallyQualifiedName $ name ): void
151
+ private function registerQualifier (? Identifier $ qualifier ): void
136
152
{
137
- $ qualifier = $ name ->getQualifier ();
138
-
139
153
if ($ qualifier === null ) {
154
+ $ this ->usesUnqualifiedNames = true ;
155
+
140
156
return ;
141
157
}
142
158
@@ -175,41 +191,84 @@ public function getTables(): array
175
191
176
192
public function getTable (string $ name ): Table
177
193
{
178
- $ name = $ this ->getFullQualifiedAssetName ($ name );
179
- if (! isset ($ this ->_tables [$ name ])) {
194
+ $ key = $ this ->getKeyFromName ($ name );
195
+ if (! isset ($ this ->_tables [$ key ])) {
180
196
throw TableDoesNotExist::new ($ name );
181
197
}
182
198
183
- return $ this ->_tables [$ name ];
199
+ return $ this ->_tables [$ key ];
184
200
}
185
201
186
- private function getFullQualifiedAssetName (string $ name ): string
202
+ /**
203
+ * Returns the key that will be used to store the given object in a collection of such objects based on its name.
204
+ *
205
+ * If the schema uses unqualified names, the object name must be unqualified. If the schema uses qualified names,
206
+ * the object name must be qualified.
207
+ *
208
+ * The resulting key is the lower-cased full object name. Lower-casing is
209
+ * actually wrong, but we have to do it to keep our sanity. If you are
210
+ * using database objects that only differentiate in the casing (FOO vs
211
+ * Foo) then you will NOT be able to use Doctrine Schema abstraction.
212
+ */
213
+ private function getKeyFromResolvedName (OptionallyQualifiedName $ name ): string
187
214
{
188
- $ name = $ this ->getUnquotedAssetName ($ name );
215
+ $ key = $ name ->getUnqualifiedName ()->getValue ();
216
+ $ qualifier = $ name ->getQualifier ();
189
217
190
- if (! str_contains ($ name , '. ' )) {
191
- $ name = $ this ->getName () . '. ' . $ name ;
218
+ if ($ qualifier !== null ) {
219
+ if ($ this ->usesUnqualifiedNames ) {
220
+ Deprecation::trigger (
221
+ 'doctrine/dbal ' ,
222
+ 'https://github.com/doctrine/dbal/pull/6677#user-content-qualified-names ' ,
223
+ 'Using qualified names to create or reference objects in a schema that uses unqualified '
224
+ . 'names is deprecated. ' ,
225
+ );
226
+ }
227
+
228
+ $ key = $ qualifier ->getValue () . '. ' . $ key ;
229
+ } elseif (count ($ this ->namespaces ) > 0 ) {
230
+ Deprecation::trigger (
231
+ 'doctrine/dbal ' ,
232
+ 'https://github.com/doctrine/dbal/pull/6677#user-content-unqualified-names ' ,
233
+ 'Using unqualified names to create or reference objects in a schema that uses qualified '
234
+ . 'names and lacks a default namespace configuration is deprecated. ' ,
235
+ );
192
236
}
193
237
194
- return strtolower ($ name );
238
+ return strtolower ($ key );
195
239
}
196
240
197
241
/**
198
- * The normalized name is qualified and lower-cased. Lower-casing is
199
- * actually wrong, but we have to do it to keep our sanity. If you are
200
- * using database objects that only differentiate in the casing (FOO vs
201
- * Foo) then you will NOT be able to use Doctrine Schema abstraction.
242
+ * Returns the key that will be used to store the given object with the given name in a collection of such objects.
202
243
*
203
- * Every non-namespaced element is prefixed with this schema name.
244
+ * If the schema configuration has the default namespace, an unqualified name will be resolved to qualified against
245
+ * that namespace.
204
246
*/
205
- private function normalizeName ( OptionallyQualifiedName $ name ): string
247
+ private function getKeyFromName ( string $ input ): string
206
248
{
207
- $ namespaceName = $ name ->getQualifier ()?->getValue()
208
- ?? $ this ->getName ();
249
+ $ parser = Parsers::getOptionallyQualifiedNameParser ();
250
+
251
+ try {
252
+ $ name = $ parser ->parse ($ input );
253
+ } catch (Parser \Exception $ e ) {
254
+ throw InvalidName::fromParserException ($ input , $ e );
255
+ }
256
+
257
+ return $ this ->getKeyFromResolvedName (
258
+ $ this ->resolveName ($ name ),
259
+ );
260
+ }
209
261
210
- $ name = $ namespaceName . '. ' . $ name ->getUnqualifiedName ()->getValue ();
262
+ /**
263
+ * Resolves the qualified or unqualified name against the current schema name and returns a qualified name.
264
+ */
265
+ private function resolveName (OptionallyQualifiedName $ name ): OptionallyQualifiedName
266
+ {
267
+ if ($ name ->getQualifier () === null && $ this ->name !== null ) {
268
+ return new OptionallyQualifiedName ($ name ->getUnqualifiedName (), $ this ->name ->getIdentifier ());
269
+ }
211
270
212
- return strtolower ( $ name) ;
271
+ return $ name ;
213
272
}
214
273
215
274
/**
@@ -239,26 +298,26 @@ public function hasNamespace(string $name): bool
239
298
*/
240
299
public function hasTable (string $ name ): bool
241
300
{
242
- $ name = $ this ->getFullQualifiedAssetName ($ name );
301
+ $ key = $ this ->getKeyFromName ($ name );
243
302
244
- return isset ($ this ->_tables [$ name ]);
303
+ return isset ($ this ->_tables [$ key ]);
245
304
}
246
305
247
306
public function hasSequence (string $ name ): bool
248
307
{
249
- $ name = $ this ->getFullQualifiedAssetName ($ name );
308
+ $ key = $ this ->getKeyFromName ($ name );
250
309
251
- return isset ($ this ->_sequences [$ name ]);
310
+ return isset ($ this ->_sequences [$ key ]);
252
311
}
253
312
254
313
public function getSequence (string $ name ): Sequence
255
314
{
256
- $ name = $ this ->getFullQualifiedAssetName ($ name );
257
- if (! $ this ->hasSequence ( $ name )) {
315
+ $ key = $ this ->getKeyFromName ($ name );
316
+ if (! isset ( $ this ->_sequences [ $ key ] )) {
258
317
throw SequenceDoesNotExist::new ($ name );
259
318
}
260
319
261
- return $ this ->_sequences [$ name ];
320
+ return $ this ->_sequences [$ key ];
262
321
}
263
322
264
323
/** @return list<Sequence> */
@@ -326,9 +385,12 @@ public function renameTable(string $oldName, string $newName): self
326
385
*/
327
386
public function dropTable (string $ name ): self
328
387
{
329
- $ name = $ this ->getFullQualifiedAssetName ($ name );
330
- $ this ->getTable ($ name );
331
- unset($ this ->_tables [$ name ]);
388
+ $ key = $ this ->getKeyFromName ($ name );
389
+ if (! isset ($ this ->_tables [$ key ])) {
390
+ throw TableDoesNotExist::new ($ name );
391
+ }
392
+
393
+ unset($ this ->_tables [$ key ]);
332
394
333
395
return $ this ;
334
396
}
@@ -347,8 +409,8 @@ public function createSequence(string $name, int $allocationSize = 1, int $initi
347
409
/** @return $this */
348
410
public function dropSequence (string $ name ): self
349
411
{
350
- $ name = $ this ->getFullQualifiedAssetName ($ name );
351
- unset($ this ->_sequences [$ name ]);
412
+ $ key = $ this ->getKeyFromName ($ name );
413
+ unset($ this ->_sequences [$ key ]);
352
414
353
415
return $ this ;
354
416
}
0 commit comments