Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PopulateRevisionSha1
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 5
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getUpdateKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doDBUpdates
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
20
 doSha1Updates
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
42
 upgradeRow
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Fills the rev_sha1 and ar_sha1 columns of revision
4 * and archive tables for revisions created before MW 1.19.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
20 *
21 * @file
22 * @ingroup Maintenance
23 */
24
25require_once __DIR__ . '/Maintenance.php';
26
27/**
28 * Maintenance script that fills the rev_sha1 and ar_sha1 columns of revision
29 * and archive tables for revisions created before MW 1.19.
30 *
31 * @ingroup Maintenance
32 */
33class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
34    public function __construct() {
35        parent::__construct();
36        $this->addDescription( 'Populates the rev_sha1 and ar_sha1 fields' );
37        $this->setBatchSize( 200 );
38    }
39
40    protected function getUpdateKey() {
41        return 'populate rev_sha1';
42    }
43
44    protected function doDBUpdates() {
45        $db = $this->getDB( DB_PRIMARY );
46
47        if ( !$db->tableExists( 'revision', __METHOD__ ) ) {
48            $this->fatalError( "revision table does not exist" );
49        } elseif ( !$db->tableExists( 'archive', __METHOD__ ) ) {
50            $this->fatalError( "archive table does not exist" );
51        } elseif ( !$db->fieldExists( 'revision', 'rev_sha1', __METHOD__ ) ) {
52            $this->output( "rev_sha1 column does not exist\n\n", true );
53            return false;
54        }
55
56        $revStore = $this->getServiceContainer()->getRevisionStore();
57
58        $this->output( "Populating rev_sha1 column\n" );
59        $rc = $this->doSha1Updates( $revStore, 'revision', 'rev_id',
60            $revStore->newSelectQueryBuilder( $this->getPrimaryDB() )->joinComment(),
61            'rev'
62        );
63
64        $this->output( "Populating ar_sha1 column\n" );
65        $ac = $this->doSha1Updates( $revStore, 'archive', 'ar_rev_id',
66            $revStore->newArchiveSelectQueryBuilder( $this->getPrimaryDB() )->joinComment(),
67            'ar'
68        );
69
70        $this->output( "rev_sha1 and ar_sha1 population complete "
71            . "[$rc revision rows, $ac archive rows].\n" );
72
73        return true;
74    }
75
76    /**
77     * @param MediaWiki\Revision\RevisionStore $revStore
78     * @param string $table
79     * @param string $idCol
80     * @param \Wikimedia\Rdbms\SelectQueryBuilder $queryBuilder should use a primary db
81     * @param string $prefix
82     * @return int Rows changed
83     */
84    protected function doSha1Updates( $revStore, $table, $idCol, $queryBuilder, $prefix ) {
85        $db = $this->getPrimaryDB();
86        $batchSize = $this->getBatchSize();
87        $start = $db->newSelectQueryBuilder()
88            ->select( "MIN($idCol)" )
89            ->from( $table )
90            ->caller( __METHOD__ )->fetchField();
91        $end = $db->newSelectQueryBuilder()
92            ->select( "MAX($idCol)" )
93            ->from( $table )
94            ->caller( __METHOD__ )->fetchField();
95        if ( !$start || !$end ) {
96            $this->output( "...$table table seems to be empty.\n" );
97
98            return 0;
99        }
100
101        $count = 0;
102        # Do remaining chunk
103        $end += $batchSize - 1;
104        $blockStart = $start;
105        $blockEnd = $start + $batchSize - 1;
106        while ( $blockEnd <= $end ) {
107            $this->output( "...doing $idCol from $blockStart to $blockEnd\n" );
108
109            $cond = "$idCol BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd .
110                " AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''";
111
112            $res = $queryBuilder->where( $cond )
113                ->caller( __METHOD__ )->fetchResultSet();
114
115            $this->beginTransaction( $db, __METHOD__ );
116            foreach ( $res as $row ) {
117                if ( $this->upgradeRow( $revStore, $row, $table, $idCol, $prefix ) ) {
118                    $count++;
119                }
120            }
121            $this->commitTransaction( $db, __METHOD__ );
122
123            $blockStart += $batchSize;
124            $blockEnd += $batchSize;
125        }
126
127        return $count;
128    }
129
130    /**
131     * @param MediaWiki\Revision\RevisionStore $revStore
132     * @param stdClass $row
133     * @param string $table
134     * @param string $idCol
135     * @param string $prefix
136     * @return bool
137     */
138    protected function upgradeRow( $revStore, $row, $table, $idCol, $prefix ) {
139        $db = $this->getPrimaryDB();
140
141        // Create a revision and use it to get the sha1 from the content table, if possible.
142        try {
143            $rev = ( $table === 'archive' )
144                ? $revStore->newRevisionFromArchiveRow( $row )
145                : $revStore->newRevisionFromRow( $row );
146            $sha1 = $rev->getSha1();
147        } catch ( Exception $e ) {
148            $this->output( "Data of revision with {$idCol}={$row->$idCol} unavailable!\n" );
149            return false; // T24624? T22757?
150        }
151
152        $db->newUpdateQueryBuilder()
153            ->update( $table )
154            ->set( [ "{$prefix}_sha1" => $sha1 ] )
155            ->where( [ $idCol => $row->$idCol ] )
156            ->caller( __METHOD__ )->execute();
157
158        return true;
159    }
160}
161
162$maintClass = PopulateRevisionSha1::class;
163require_once RUN_MAINTENANCE_IF_MAIN;