@@ -1794,19 +1794,31 @@ public function createCollection(string $id, array $attributes = [], array $inde
17941794 }
17951795 }
17961796
1797+ $ createdPhysicalTable = false ;
1798+
17971799 try {
17981800 $ this ->adapter ->createCollection ($ id , $ attributes , $ indexes );
1801+ $ createdPhysicalTable = true ;
17991802 } catch (DuplicateException $ e ) {
1800- // Metadata check (above) already verified collection is absent
1801- // from metadata. A DuplicateException from the adapter means the
1802- // collection exists only in physical schema — an orphan from a prior
1803- // partial failure. Drop and recreate to ensure schema matches.
1804- try {
1805- $ this ->adapter ->deleteCollection ($ id );
1806- } catch (NotFoundException ) {
1807- // Already removed by a concurrent reconciler.
1803+ if ($ this ->adapter ->getSharedTables ()
1804+ && ($ id === self ::METADATA || $ this ->adapter ->exists ($ this ->adapter ->getDatabase (), $ id ))) {
1805+ // In shared-tables mode the physical table is reused across
1806+ // tenants. A DuplicateException simply means the table already
1807+ // exists for another tenant — not an orphan.
1808+ } else {
1809+ // Metadata check (above) already verified collection is absent
1810+ // from metadata. A DuplicateException from the adapter means
1811+ // the collection exists only in physical schema — an orphan
1812+ // from a prior partial failure. Drop and recreate to ensure
1813+ // schema matches.
1814+ try {
1815+ $ this ->adapter ->deleteCollection ($ id );
1816+ } catch (NotFoundException ) {
1817+ // Already removed by a concurrent reconciler.
1818+ }
1819+ $ this ->adapter ->createCollection ($ id , $ attributes , $ indexes );
1820+ $ createdPhysicalTable = true ;
18081821 }
1809- $ this ->adapter ->createCollection ($ id , $ attributes , $ indexes );
18101822 }
18111823
18121824 if ($ id === self ::METADATA ) {
@@ -1816,10 +1828,12 @@ public function createCollection(string $id, array $attributes = [], array $inde
18161828 try {
18171829 $ createdCollection = $ this ->silent (fn () => $ this ->createDocument (self ::METADATA , $ collection ));
18181830 } catch (\Throwable $ e ) {
1819- try {
1820- $ this ->cleanupCollection ($ id );
1821- } catch (\Throwable $ e ) {
1822- Console::error ("Failed to rollback collection ' {$ id }': " . $ e ->getMessage ());
1831+ if ($ createdPhysicalTable ) {
1832+ try {
1833+ $ this ->cleanupCollection ($ id );
1834+ } catch (\Throwable $ e ) {
1835+ Console::error ("Failed to rollback collection ' {$ id }': " . $ e ->getMessage ());
1836+ }
18231837 }
18241838 throw new DatabaseException ("Failed to create collection metadata for ' {$ id }': " . $ e ->getMessage (), previous: $ e );
18251839 }
@@ -4564,18 +4578,49 @@ public function createIndex(string $collection, string $id, string $type, array
45644578 }
45654579
45664580 $ created = false ;
4581+ $ existsInSchema = false ;
45674582
4568- try {
4569- $ created = $ this ->adapter ->createIndex ($ collection ->getId (), $ id , $ type , $ attributes , $ lengths , $ orders , $ indexAttributesWithTypes , [], $ ttl );
4583+ if ($ this ->adapter ->getSupportForSchemaIndexes ()
4584+ && !($ this ->adapter ->getSharedTables () && $ this ->isMigrating ())) {
4585+ $ schemaIndexes = $ this ->getSchemaIndexes ($ collection ->getId ());
4586+ $ filteredId = $ this ->adapter ->filter ($ id );
45704587
4571- if (!$ created ) {
4572- throw new DatabaseException ('Failed to create index ' );
4588+ foreach ($ schemaIndexes as $ schemaIndex ) {
4589+ if (\strtolower ($ schemaIndex ->getId ()) === \strtolower ($ filteredId )) {
4590+ $ schemaColumns = $ schemaIndex ->getAttribute ('columns ' , []);
4591+ $ schemaLengths = $ schemaIndex ->getAttribute ('lengths ' , []);
4592+
4593+ $ filteredAttributes = \array_map (fn ($ a ) => $ this ->adapter ->filter ($ a ), $ attributes );
4594+ $ match = ($ schemaColumns === $ filteredAttributes && $ schemaLengths === $ lengths );
4595+
4596+ if ($ match ) {
4597+ $ existsInSchema = true ;
4598+ } else {
4599+ // Orphan index with wrong definition — drop so it
4600+ // gets recreated with the correct shape.
4601+ try {
4602+ $ this ->adapter ->deleteIndex ($ collection ->getId (), $ id );
4603+ } catch (NotFoundException ) {
4604+ }
4605+ }
4606+ break ;
4607+ }
4608+ }
4609+ }
4610+
4611+ if (!$ existsInSchema ) {
4612+ try {
4613+ $ created = $ this ->adapter ->createIndex ($ collection ->getId (), $ id , $ type , $ attributes , $ lengths , $ orders , $ indexAttributesWithTypes , [], $ ttl );
4614+
4615+ if (!$ created ) {
4616+ throw new DatabaseException ('Failed to create index ' );
4617+ }
4618+ } catch (DuplicateException ) {
4619+ // Metadata check (lines above) already verified index is absent
4620+ // from metadata. A DuplicateException from the adapter means the
4621+ // index exists only in physical schema — an orphan from a prior
4622+ // partial failure. Skip creation and proceed to metadata update.
45734623 }
4574- } catch (DuplicateException $ e ) {
4575- // Metadata check (lines above) already verified index is absent
4576- // from metadata. A DuplicateException from the adapter means the
4577- // index exists only in physical schema — an orphan from a prior
4578- // partial failure. Skip creation and proceed to metadata update.
45794624 }
45804625
45814626 $ collection ->setAttribute ('indexes ' , $ index , Document::SET_TYPE_APPEND );
@@ -9265,6 +9310,15 @@ public function getSchemaAttributes(string $collection): array
92659310 return $ this ->adapter ->getSchemaAttributes ($ collection );
92669311 }
92679312
9313+ /**
9314+ * @param string $collection
9315+ * @return array<Document>
9316+ */
9317+ public function getSchemaIndexes (string $ collection ): array
9318+ {
9319+ return $ this ->adapter ->getSchemaIndexes ($ collection );
9320+ }
9321+
92689322 /**
92699323 * @param string $collectionId
92709324 * @param string|null $documentId
0 commit comments