99
1010namespace OC \Core \BackgroundJobs ;
1111
12- use OC \Preview \Db \Preview ;
1312use OC \Preview \PreviewMigrationService ;
1413use OCP \AppFramework \Utility \ITimeFactory ;
1514use OCP \BackgroundJob \TimedJob ;
16- use OCP \DB \ IResult ;
15+ use OCP \Files \ FileInfo ;
1716use OCP \Files \IRootFolder ;
1817use OCP \IAppConfig ;
1918use OCP \IConfig ;
20- use OCP \IDBConnection ;
2119use Override ;
20+ use Psr \Log \LoggerInterface ;
2221
2322class PreviewMigrationJob extends TimedJob {
2423 private string $ previewRootPath ;
@@ -27,9 +26,9 @@ public function __construct(
2726 ITimeFactory $ time ,
2827 private readonly IAppConfig $ appConfig ,
2928 private readonly IConfig $ config ,
30- private readonly IDBConnection $ connection ,
3129 private readonly IRootFolder $ rootFolder ,
3230 private readonly PreviewMigrationService $ migrationService ,
31+ private readonly LoggerInterface $ logger ,
3332 ) {
3433 parent ::__construct ($ time );
3534
@@ -44,23 +43,52 @@ protected function run(mixed $argument): void {
4443 return ;
4544 }
4645
46+ $ storage = $ this ->rootFolder ->getMountPoint ()->getStorage ();
47+ if ($ storage === null ) {
48+ $ this ->appConfig ->setValueBool ('core ' , 'previewMovedDone ' , true );
49+ return ;
50+ }
51+
52+ $ cache = $ storage ->getCache ();
53+ $ previewRootId = $ cache ->getId (rtrim ($ this ->previewRootPath , '/ ' ));
54+ if ($ previewRootId === -1 ) {
55+ // No previews have ever been generated on this instance.
56+ $ this ->appConfig ->setValueBool ('core ' , 'previewMovedDone ' , true );
57+ return ;
58+ }
59+
4760 $ startTime = time ();
48- while (true ) {
49- $ qb = $ this ->connection ->getQueryBuilder ();
50- $ qb ->select ('path ' )
51- ->from ('filecache ' )
52- // Hierarchical preview folder structure
53- ->where ($ qb ->expr ()->like ('path ' , $ qb ->createNamedParameter ($ this ->previewRootPath . '%/%/%/%/%/%/%/%/% ' )))
54- // Legacy flat preview folder structure
55- ->orWhere ($ qb ->expr ()->like ('path ' , $ qb ->createNamedParameter ($ this ->previewRootPath . '%/%.% ' )))
56- ->hintShardKey ('storage ' , $ this ->rootFolder ->getMountPoint ()->getNumericStorageId ())
57- ->setMaxResults (100 );
58-
59- $ result = $ qb ->executeQuery ();
60- $ foundPreviews = $ this ->processQueryResult ($ result );
61-
62- if (!$ foundPreviews ) {
63- break ;
61+
62+ // Walk the preview folder tree via the `parent` column, which is indexed on
63+ // every supported database platform.
64+ //
65+ // Depth from the preview root tells us which structure a leaf folder holds:
66+ // - depth 1: legacy flat structure, e.g. preview/<fileid>/<size>.png
67+ // - depth 8: hierarchical structure, e.g. preview/a/b/c/d/e/f/g/<fileid>/<size>.png
68+ $ foldersToVisit = [[$ previewRootId , '' , 0 ]];
69+
70+ while ($ foldersToVisit !== []) {
71+ [$ folderId , $ folderName , $ depth ] = array_pop ($ foldersToVisit );
72+
73+ $ hasPreviewFiles = false ;
74+ foreach ($ cache ->getFolderContentsById ($ folderId ) as $ entry ) {
75+ if ($ entry ->getMimeType () === FileInfo::MIMETYPE_FOLDER ) {
76+ $ foldersToVisit [] = [$ entry ->getId (), $ entry ->getName (), $ depth + 1 ];
77+ } else {
78+ $ hasPreviewFiles = true ;
79+ }
80+ }
81+
82+ if (!$ hasPreviewFiles || !ctype_digit ($ folderName )) {
83+ continue ;
84+ }
85+
86+ try {
87+ $ this ->migrationService ->migrateFileId ((int )$ folderName , flatPath: $ depth === 1 );
88+ } catch (\Exception $ e ) {
89+ $ this ->logger ->error ('Failed to migrate preview with fileId: ' . $ folderName , [
90+ 'exception ' => $ e ,
91+ ]);
6492 }
6593
6694 // Stop if execution time is more than one hour.
@@ -71,36 +99,4 @@ protected function run(mixed $argument): void {
7199
72100 $ this ->appConfig ->setValueBool ('core ' , 'previewMovedDone ' , true );
73101 }
74-
75- private function processQueryResult (IResult $ result ): bool {
76- $ foundPreview = false ;
77- $ fileIds = [];
78- $ flatFileIds = [];
79- while ($ row = $ result ->fetch ()) {
80- $ pathSplit = explode ('/ ' , $ row ['path ' ]);
81- assert (count ($ pathSplit ) >= 2 );
82- $ fileId = (int )$ pathSplit [count ($ pathSplit ) - 2 ];
83- if (count ($ pathSplit ) === 11 ) {
84- // Hierarchical structure
85- if (!in_array ($ fileId , $ fileIds )) {
86- $ fileIds [] = $ fileId ;
87- }
88- } else {
89- // Flat structure
90- if (!in_array ($ fileId , $ flatFileIds )) {
91- $ flatFileIds [] = $ fileId ;
92- }
93- }
94- $ foundPreview = true ;
95- }
96-
97- foreach ($ fileIds as $ fileId ) {
98- $ this ->migrationService ->migrateFileId ($ fileId , flatPath: false );
99- }
100-
101- foreach ($ flatFileIds as $ fileId ) {
102- $ this ->migrationService ->migrateFileId ($ fileId , flatPath: true );
103- }
104- return $ foundPreview ;
105- }
106102}
0 commit comments