Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
FixPagePropsSortkey
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 3
30
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 doDBUpdates
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
12
 getUpdateKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare( strict_types = 1 );
4
5namespace Wikibase\Lexeme\Maintenance;
6
7use MediaWiki\Maintenance\LoggedUpdateMaintenance;
8
9/**
10 * Maintenance script to fix the pp_sortkey column of the page_props table
11 * for pp_propname wb-claims, wbl-forms and wbl-senses.
12 *
13 * Due to a bug (T350224), some lexemes had their pp_sortkey set to NULL,
14 * rather than the number of statements, forms or senses.
15 * To fix this, set the pp_sortkey based on the (assumed correct) pp_value.
16 * Note that we do not limit this to the Lexeme namespace (no JOIN with the page table):
17 * the pp_sortkey is not expected to be NULL anywhere else,
18 * but even if it is, setting it to the numerical value of the pp_value should be correct.
19 * (But we only do this for the three pp_propname strings known to be affected,
20 * not any random other page prop.)
21 *
22 * Usually, this script is run via update.php and there is no need to run it manually.
23 *
24 * This maintenance script can be removed again after a while
25 * (once we assume all installations have run this script to fix their old data).
26 * The bug was first introduced in the REL1_39 branch (MediaWiki 1.39).
27 *
28 * @license GPL-2.0-or-later
29 */
30class FixPagePropsSortkey extends LoggedUpdateMaintenance {
31
32    public function __construct() {
33        parent::__construct();
34
35        $this->addDescription(
36            'Fix the pp_sortkey of wb-claims, wbl-forms and wbl-senses page props ' .
37            'where it is NULL (see T350224).'
38        );
39        $this->requireExtension( 'WikibaseLexeme' );
40        $this->requireExtension( 'WikibaseRepository' );
41    }
42
43    public function doDBUpdates(): bool {
44        $dbr = $this->getDB( DB_REPLICA );
45        $dbw = $this->getDB( DB_PRIMARY );
46
47        $pageIdsQueryBuilder = $dbr->newSelectQueryBuilder()
48            ->select( 'pp_page' )
49            ->from( 'page_props' )
50            ->where( [
51                'pp_propname' => [ 'wb-claims', 'wbl-forms', 'wbl-senses' ],
52                'pp_sortkey' => null,
53            ] )
54            ->orderBy( [ 'pp_propname', 'pp_page' ] ) // uses pp_propname_sortkey_page index
55            ->limit( $this->getBatchSize() )
56            ->caller( __METHOD__ );
57        $updateQueryBuilder = $dbw->newUpdateQueryBuilder()
58            ->update( 'page_props' )
59            ->set( 'pp_sortkey = CAST(pp_value AS FLOAT)' )
60            ->where( [
61                'pp_propname' => [ 'wb-claims', 'wbl-forms', 'wbl-senses' ],
62                'pp_sortkey' => null,
63                // pp_page condition added in loop, per iteration
64            ] )
65            ->caller( __METHOD__ );
66
67        $this->output( __CLASS__ . ' running...' . PHP_EOL );
68
69        while ( true ) {
70            $pageIds = ( clone $pageIdsQueryBuilder )->fetchFieldValues();
71            if ( !$pageIds ) {
72                break;
73            }
74            $pageIds = array_values( array_unique( $pageIds ) );
75
76            $this->beginTransaction( $dbw, __METHOD__ );
77            ( clone $updateQueryBuilder )
78                ->andWhere( [ 'pp_page' => $pageIds ] )
79                ->execute();
80            $this->commitTransaction( $dbw, __METHOD__ ); // this also waits for replication
81            $minPageId = reset( $pageIds );
82            $maxPageId = end( $pageIds );
83            // note: because we order by pp_propname first, it’s theoretically possible that
84            // $minPageId > $maxPageId; however, the UPDATE affects all page props at once,
85            // so in practice the script will most likely only make one pass through the table
86            $this->output( "Update page IDs from $minPageId to $maxPageId..." . PHP_EOL );
87        }
88
89        $this->output( 'Done.' . PHP_EOL );
90
91        return true;
92    }
93
94    protected function getUpdateKey(): string {
95        return __CLASS__;
96    }
97
98}
99
100return FixPagePropsSortkey::class;