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