Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 197
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
FileSelectQueryBuilder
0.00% covered (danger)
0.00%
0 / 197
0.00% covered (danger)
0.00%
0 / 9
600
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 newForFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newForOldFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newForArchivedFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 initFileOld
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
12
 initFileNew
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
12
 initOldFileOld
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
12
 initOldFileNew
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
12
 initArchivedFile
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\FileRepo\File;
22
23use InvalidArgumentException;
24use MediaWiki\MainConfigNames;
25use MediaWiki\MediaWikiServices;
26use Wikimedia\Rdbms\IReadableDatabase;
27use Wikimedia\Rdbms\SelectQueryBuilder;
28
29class FileSelectQueryBuilder extends SelectQueryBuilder {
30
31    private int $migrationStage;
32
33    /**
34     * @internal use ::newFor* instead.
35     * @param IReadableDatabase $db
36     * @param string $type either 'file', 'oldfile' or 'archivedfile'
37     * @param array $options
38     *   - omit-lazy: Omit fields that are lazily cached.
39     */
40    public function __construct( IReadableDatabase $db, string $type = 'file', array $options = [] ) {
41        parent::__construct( $db );
42        $this->migrationStage = MediaWikiServices::getInstance()->getMainConfig()->get(
43            MainConfigNames::FileSchemaMigrationStage
44        );
45        if ( $type === 'file' ) {
46            if ( $this->migrationStage & SCHEMA_COMPAT_READ_OLD ) {
47                $this->initFileOld( $options );
48            } else {
49                $this->initFileNew( $options );
50            }
51        } elseif ( $type === 'oldfile' ) {
52            if ( $this->migrationStage & SCHEMA_COMPAT_READ_OLD ) {
53                $this->initOldFileOld( $options );
54            } else {
55                $this->initOldFileNew( $options );
56            }
57        } elseif ( $type === 'archivedfile' ) {
58            $this->initArchivedFile( $options );
59        } else {
60            throw new InvalidArgumentException( "Type $type is not among accepted values" );
61        }
62    }
63
64    public static function newForFile( IReadableDatabase $db, array $options = [] ): FileSelectQueryBuilder {
65        return new FileSelectQueryBuilder( $db, 'file', $options );
66    }
67
68    public static function newForOldFile( IReadableDatabase $db, array $options = [] ): FileSelectQueryBuilder {
69        return new FileSelectQueryBuilder( $db, 'oldfile', $options );
70    }
71
72    public static function newForArchivedFile( IReadableDatabase $db, array $options = [] ): FileSelectQueryBuilder {
73        return new FileSelectQueryBuilder( $db, 'archivedfile', $options );
74    }
75
76    private function initFileOld( array $options ) {
77        $this->table( 'image' )
78            ->join( 'actor', 'image_actor', 'actor_id=img_actor' )
79            ->join(
80                'comment',
81                'comment_img_description',
82                'comment_img_description.comment_id = img_description_id'
83            );
84
85        if ( !in_array( 'omit-nonlazy', $options, true ) ) {
86            $this->fields(
87                [
88                    'img_name',
89                    'img_size',
90                    'img_width',
91                    'img_height',
92                    'img_metadata',
93                    'img_bits',
94                    'img_media_type',
95                    'img_major_mime',
96                    'img_minor_mime',
97                    'img_timestamp',
98                    'img_sha1',
99                    'img_actor',
100                    'img_user' => 'image_actor.actor_user',
101                    'img_user_text' => 'image_actor.actor_name',
102                    'img_description_text' => 'comment_img_description.comment_text',
103                    'img_description_data' => 'comment_img_description.comment_data',
104                    'img_description_cid' => 'comment_img_description.comment_id'
105                ]
106            );
107        }
108        if ( !in_array( 'omit-lazy', $options, true ) ) {
109            // Note: Keep this in sync with LocalFile::getLazyCacheFields() and
110            // LocalFile::loadExtraFromDB()
111            $this->field( 'img_metadata' );
112        }
113    }
114
115    private function initFileNew( array $options ) {
116        $subquery = $this->newSubquery();
117        $subquery->table( 'file' )
118            ->join( 'filerevision', null, 'file_latest = fr_id' )
119            ->join( 'filetypes', null, 'file_type = ft_id' )
120            ->join( 'actor', 'image_actor', 'actor_id=fr_actor' )
121            ->join(
122                'comment',
123                'comment_img_description',
124                'comment_img_description.comment_id = fr_description_id'
125            );
126
127        if ( !in_array( 'omit-nonlazy', $options, true ) ) {
128            $subquery->fields(
129                [
130                    'img_file_id' => 'file_id',
131                    'img_filerevision_id' => 'fr_id',
132                    'img_name' => 'file_name',
133                    'img_size' => 'fr_size',
134                    'img_width' => 'fr_width',
135                    'img_height' => 'fr_height',
136                    'img_metadata' => 'fr_metadata',
137                    'img_bits' => 'fr_bits',
138                    'img_media_type' => 'ft_media_type',
139                    'img_major_mime' => 'ft_major_mime',
140                    'img_minor_mime' => 'ft_minor_mime',
141                    'img_timestamp' => 'fr_timestamp',
142                    'img_sha1' => 'fr_sha1',
143                    'img_actor' => 'fr_actor',
144                    'img_user' => 'image_actor.actor_user',
145                    'img_user_text' => 'image_actor.actor_name',
146                    'img_description_text' => 'comment_img_description.comment_text',
147                    'img_description_data' => 'comment_img_description.comment_data',
148                    'img_description_cid' => 'comment_img_description.comment_id'
149                ]
150            );
151        }
152        if ( !in_array( 'omit-lazy', $options, true ) ) {
153            // Note: Keep this in sync with LocalFile::getLazyCacheFields() and
154            // LocalFile::loadExtraFromDB()
155            $subquery->field( 'fr_metadata', 'img_metadata' );
156        }
157
158        $subquery->where( [ 'file_deleted' => 0 ] );
159
160        // Without the wrapper, callers can't make conditions
161        // on the old field names so all of them would need updating.
162        // See https://stackoverflow.com/a/8370146
163        $this->field( '*' )
164            ->from( $subquery );
165    }
166
167    private function initOldFileOld( array $options ) {
168        $this->table( 'oldimage' )
169            ->join( 'actor', 'oldimage_actor', 'actor_id=oi_actor' )
170            ->join(
171                'comment',
172                'comment_oi_description',
173                'comment_oi_description.comment_id = oi_description_id'
174            );
175
176        if ( !in_array( 'omit-nonlazy', $options, true ) ) {
177            $this->fields(
178                [
179                    'oi_name',
180                    'oi_archive_name',
181                    'oi_size',
182                    'oi_width',
183                    'oi_height',
184                    'oi_bits',
185                    'oi_media_type',
186                    'oi_major_mime',
187                    'oi_minor_mime',
188                    'oi_timestamp',
189                    'oi_deleted',
190                    'oi_sha1',
191                    'oi_actor',
192                    'oi_user' => 'oldimage_actor.actor_user',
193                    'oi_user_text' => 'oldimage_actor.actor_name',
194                    'oi_description_text' => 'comment_oi_description.comment_text',
195                    'oi_description_data' => 'comment_oi_description.comment_data',
196                    'oi_description_cid' => 'comment_oi_description.comment_id'
197                ]
198            );
199        }
200        if ( !in_array( 'omit-lazy', $options, true ) ) {
201            // Note: Keep this in sync with LocalFile::getLazyCacheFields() and
202            // LocalFile::loadExtraFromDB()
203            $this->field( 'oi_metadata' );
204        }
205    }
206
207    private function initOldFileNew( array $options ) {
208        $subquery = $this->newSubquery();
209        $subquery->table( 'filerevision' )
210            ->join( 'file', null, 'fr_file = file_id' )
211            ->join( 'filetypes', null, 'file_type = ft_id' )
212            ->join( 'actor', 'oldimage_actor', 'actor_id = fr_actor' )
213            ->join(
214                'comment',
215                'comment_oi_description',
216                'comment_oi_description.comment_id = fr_description_id'
217            );
218
219        if ( !in_array( 'omit-nonlazy', $options, true ) ) {
220            $subquery->fields(
221                [
222                    'oi_file_id' => 'file_id',
223                    'oi_filerevision_id' => 'fr_id',
224                    'oi_name' => 'file_name',
225                    'oi_archive_name' => 'fr_archive_name',
226                    'oi_size' => 'fr_size',
227                    'oi_width' => 'fr_width',
228                    'oi_height' => 'fr_height',
229                    'oi_bits' => 'fr_bits',
230                    'oi_media_type' => 'ft_media_type',
231                    'oi_major_mime' => 'ft_major_mime',
232                    'oi_minor_mime' => 'ft_minor_mime',
233                    'oi_timestamp' => 'fr_timestamp',
234                    'oi_deleted' => 'fr_deleted',
235                    'oi_sha1' => 'fr_sha1',
236                    'oi_actor' => 'fr_actor',
237                    'oi_user' => 'oldimage_actor.actor_user',
238                    'oi_user_text' => 'oldimage_actor.actor_name',
239                    'oi_description_text' => 'comment_oi_description.comment_text',
240                    'oi_description_data' => 'comment_oi_description.comment_data',
241                    'oi_description_cid' => 'comment_oi_description.comment_id'
242                ]
243            );
244        }
245        if ( !in_array( 'omit-lazy', $options, true ) ) {
246            // Note: Keep this in sync with LocalFile::getLazyCacheFields() and
247            // LocalFile::loadExtraFromDB()
248            $subquery->field( 'fr_metadata', 'oi_metadata' );
249        }
250
251        $subquery->where( 'file_latest != fr_id' );
252        $this->field( '*' )
253            ->from( $subquery );
254    }
255
256    private function initArchivedFile( array $options ) {
257        $this->table( 'filearchive' )
258            ->join( 'actor', 'filearchive_actor', 'actor_id=fa_actor' )
259            ->join(
260                'comment',
261                'comment_fa_description',
262                'comment_fa_description.comment_id = fa_description_id'
263            );
264
265        if ( !in_array( 'omit-nonlazy', $options, true ) ) {
266            $this->fields(
267                [
268                    'fa_id',
269                    'fa_name',
270                    'fa_archive_name',
271                    'fa_storage_key',
272                    'fa_storage_group',
273                    'fa_size',
274                    'fa_bits',
275                    'fa_width',
276                    'fa_height',
277                    'fa_metadata',
278                    'fa_media_type',
279                    'fa_major_mime',
280                    'fa_minor_mime',
281                    'fa_timestamp',
282                    'fa_deleted',
283                    'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
284                    'fa_sha1',
285                    'fa_actor',
286                    'fa_user' => 'filearchive_actor.actor_user',
287                    'fa_user_text' => 'filearchive_actor.actor_name',
288                    'fa_description_text' => 'comment_fa_description.comment_text',
289                    'fa_description_data' => 'comment_fa_description.comment_data',
290                    'fa_description_cid' => 'comment_fa_description.comment_id'
291                ]
292            );
293        }
294        if ( !in_array( 'omit-lazy', $options, true ) ) {
295            // Note: Keep this in sync with LocalFile::getLazyCacheFields() and
296            // LocalFile::loadExtraFromDB()
297            $this->field( 'fa_metadata' );
298        }
299    }
300}