@@ -1790,19 +1790,31 @@ public function createCollection(string $id, array $attributes = [], array $inde
17901790 }
17911791 }
17921792
1793+ $ createdPhysicalTable = false ;
1794+
17931795 try {
17941796 $ this ->adapter ->createCollection ($ id , $ attributes , $ indexes );
1797+ $ createdPhysicalTable = true ;
17951798 } catch (DuplicateException $ e ) {
1796- // Metadata check (above) already verified collection is absent
1797- // from metadata. A DuplicateException from the adapter means the
1798- // collection exists only in physical schema — an orphan from a prior
1799- // partial failure. Drop and recreate to ensure schema matches.
1800- try {
1801- $ this ->adapter ->deleteCollection ($ id );
1802- } catch (NotFoundException ) {
1803- // Already removed by a concurrent reconciler.
1799+ if ($ this ->adapter ->getSharedTables ()
1800+ && ($ id === self ::METADATA || $ this ->adapter ->exists ($ this ->adapter ->getDatabase (), $ id ))) {
1801+ // In shared-tables mode the physical table is reused across
1802+ // tenants. A DuplicateException simply means the table already
1803+ // exists for another tenant — not an orphan.
1804+ } else {
1805+ // Metadata check (above) already verified collection is absent
1806+ // from metadata. A DuplicateException from the adapter means
1807+ // the collection exists only in physical schema — an orphan
1808+ // from a prior partial failure. Drop and recreate to ensure
1809+ // schema matches.
1810+ try {
1811+ $ this ->adapter ->deleteCollection ($ id );
1812+ } catch (NotFoundException ) {
1813+ // Already removed by a concurrent reconciler.
1814+ }
1815+ $ this ->adapter ->createCollection ($ id , $ attributes , $ indexes );
1816+ $ createdPhysicalTable = true ;
18041817 }
1805- $ this ->adapter ->createCollection ($ id , $ attributes , $ indexes );
18061818 }
18071819
18081820 if ($ id === self ::METADATA ) {
@@ -1812,10 +1824,12 @@ public function createCollection(string $id, array $attributes = [], array $inde
18121824 try {
18131825 $ createdCollection = $ this ->silent (fn () => $ this ->createDocument (self ::METADATA , $ collection ));
18141826 } catch (\Throwable $ e ) {
1815- try {
1816- $ this ->cleanupCollection ($ id );
1817- } catch (\Throwable $ e ) {
1818- Console::error ("Failed to rollback collection ' {$ id }': " . $ e ->getMessage ());
1827+ if ($ createdPhysicalTable ) {
1828+ try {
1829+ $ this ->cleanupCollection ($ id );
1830+ } catch (\Throwable $ e ) {
1831+ Console::error ("Failed to rollback collection ' {$ id }': " . $ e ->getMessage ());
1832+ }
18191833 }
18201834 throw new DatabaseException ("Failed to create collection metadata for ' {$ id }': " . $ e ->getMessage (), previous: $ e );
18211835 }
@@ -4562,18 +4576,49 @@ public function createIndex(string $collection, string $id, string $type, array
45624576 }
45634577
45644578 $ created = false ;
4579+ $ existsInSchema = false ;
45654580
4566- try {
4567- $ created = $ this ->adapter ->createIndex ($ collection ->getId (), $ id , $ type , $ attributes , $ lengths , $ orders , $ indexAttributesWithTypes , [], $ ttl );
4581+ if ($ this ->adapter ->getSupportForSchemaIndexes ()
4582+ && !($ this ->adapter ->getSharedTables () && $ this ->isMigrating ())) {
4583+ $ schemaIndexes = $ this ->getSchemaIndexes ($ collection ->getId ());
4584+ $ filteredId = $ this ->adapter ->filter ($ id );
45684585
4569- if (!$ created ) {
4570- throw new DatabaseException ('Failed to create index ' );
4586+ foreach ($ schemaIndexes as $ schemaIndex ) {
4587+ if (\strtolower ($ schemaIndex ->getId ()) === \strtolower ($ filteredId )) {
4588+ $ schemaColumns = $ schemaIndex ->getAttribute ('columns ' , []);
4589+ $ schemaLengths = $ schemaIndex ->getAttribute ('lengths ' , []);
4590+
4591+ $ filteredAttributes = \array_map (fn ($ a ) => $ this ->adapter ->filter ($ a ), $ attributes );
4592+ $ match = ($ schemaColumns === $ filteredAttributes && $ schemaLengths === $ lengths );
4593+
4594+ if ($ match ) {
4595+ $ existsInSchema = true ;
4596+ } else {
4597+ // Orphan index with wrong definition — drop so it
4598+ // gets recreated with the correct shape.
4599+ try {
4600+ $ this ->adapter ->deleteIndex ($ collection ->getId (), $ id );
4601+ } catch (NotFoundException ) {
4602+ }
4603+ }
4604+ break ;
4605+ }
4606+ }
4607+ }
4608+
4609+ if (!$ existsInSchema ) {
4610+ try {
4611+ $ created = $ this ->adapter ->createIndex ($ collection ->getId (), $ id , $ type , $ attributes , $ lengths , $ orders , $ indexAttributesWithTypes , [], $ ttl );
4612+
4613+ if (!$ created ) {
4614+ throw new DatabaseException ('Failed to create index ' );
4615+ }
4616+ } catch (DuplicateException ) {
4617+ // Metadata check (lines above) already verified index is absent
4618+ // from metadata. A DuplicateException from the adapter means the
4619+ // index exists only in physical schema — an orphan from a prior
4620+ // partial failure. Skip creation and proceed to metadata update.
45714621 }
4572- } catch (DuplicateException $ e ) {
4573- // Metadata check (lines above) already verified index is absent
4574- // from metadata. A DuplicateException from the adapter means the
4575- // index exists only in physical schema — an orphan from a prior
4576- // partial failure. Skip creation and proceed to metadata update.
45774622 }
45784623
45794624 $ collection ->setAttribute ('indexes ' , $ index , Document::SET_TYPE_APPEND );
@@ -9208,6 +9253,15 @@ public function getSchemaAttributes(string $collection): array
92089253 return $ this ->adapter ->getSchemaAttributes ($ collection );
92099254 }
92109255
9256+ /**
9257+ * @param string $collection
9258+ * @return array<Document>
9259+ */
9260+ public function getSchemaIndexes (string $ collection ): array
9261+ {
9262+ return $ this ->adapter ->getSchemaIndexes ($ collection );
9263+ }
9264+
92119265 /**
92129266 * @param string $collectionId
92139267 * @param string|null $documentId
0 commit comments