Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
66.07% |
37 / 56 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
LinksDeletionUpdate | |
66.07% |
37 / 56 |
|
0.00% |
0 / 3 |
12.16 | |
0.00% |
0 / 1 |
__construct | |
72.73% |
8 / 11 |
|
0.00% |
0 / 1 |
4.32 | |||
doIncrementalUpdate | |
80.56% |
29 / 36 |
|
0.00% |
0 / 1 |
4.12 | |||
getAsJobSpecification | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Updater for link tracking tables after a page edit. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | namespace MediaWiki\Deferred\LinksUpdate; |
24 | |
25 | use InvalidArgumentException; |
26 | use JobSpecification; |
27 | use MediaWiki\Category\Category; |
28 | use MediaWiki\Deferred\DeferredUpdates; |
29 | use MediaWiki\Deferred\EnqueueableDataUpdate; |
30 | use MediaWiki\MainConfigNames; |
31 | use MediaWiki\MediaWikiServices; |
32 | use MediaWiki\Parser\ParserOutput; |
33 | use WikiPage; |
34 | |
35 | /** |
36 | * Update object handling the cleanup of links tables after a page was deleted. |
37 | */ |
38 | class LinksDeletionUpdate extends LinksUpdate implements EnqueueableDataUpdate { |
39 | /** @var WikiPage */ |
40 | protected $page; |
41 | /** @var string */ |
42 | protected $timestamp; |
43 | |
44 | /** |
45 | * @param WikiPage $page Page we are updating |
46 | * @param int|null $pageId ID of the page we are updating [optional] |
47 | * @param string|null $timestamp TS_MW timestamp of deletion |
48 | */ |
49 | public function __construct( WikiPage $page, $pageId = null, $timestamp = null ) { |
50 | $this->page = $page; |
51 | if ( $pageId ) { |
52 | $this->mId = $pageId; // page ID at time of deletion |
53 | } elseif ( $page->exists() ) { |
54 | $this->mId = $page->getId(); |
55 | } else { |
56 | throw new InvalidArgumentException( "Page ID not known. Page doesn't exist?" ); |
57 | } |
58 | |
59 | $this->timestamp = $timestamp ?: wfTimestampNow(); |
60 | |
61 | $fakePO = new ParserOutput(); |
62 | $fakePO->setCacheTime( $timestamp ); |
63 | // Use immutable page identity to keep reference to the page id at time of deletion - T299244 |
64 | $immutablePageIdentity = $page->getTitle()->toPageIdentity(); |
65 | parent::__construct( $immutablePageIdentity, $fakePO, false ); |
66 | } |
67 | |
68 | protected function doIncrementalUpdate() { |
69 | $services = MediaWikiServices::getInstance(); |
70 | $config = $services->getMainConfig(); |
71 | $dbProvider = $services->getConnectionProvider(); |
72 | $batchSize = $config->get( MainConfigNames::UpdateRowsPerQuery ); |
73 | |
74 | $id = $this->mId; |
75 | $title = $this->mTitle; |
76 | |
77 | $dbw = $this->getDB(); // convenience |
78 | |
79 | parent::doIncrementalUpdate(); |
80 | |
81 | // Typically, a category is empty when deleted, so check that we don't leave |
82 | // spurious row in the category table. |
83 | if ( $title->getNamespace() === NS_CATEGORY ) { |
84 | // T166757: do the update after the main job DB commit |
85 | DeferredUpdates::addCallableUpdate( static function () use ( $title ) { |
86 | $cat = Category::newFromName( $title->getDBkey() ); |
87 | $cat->refreshCountsIfSmall(); |
88 | } ); |
89 | } |
90 | |
91 | // Delete restrictions for the deleted page |
92 | $dbw->newDeleteQueryBuilder() |
93 | ->deleteFrom( 'page_restrictions' ) |
94 | ->where( [ 'pr_page' => $id ] ) |
95 | ->caller( __METHOD__ )->execute(); |
96 | |
97 | // Delete any redirect entry |
98 | $dbw->newDeleteQueryBuilder() |
99 | ->deleteFrom( 'redirect' ) |
100 | ->where( [ 'rd_from' => $id ] ) |
101 | ->caller( __METHOD__ )->execute(); |
102 | |
103 | // Find recentchanges entries to clean up... |
104 | // Select RC IDs just by curid, and not by title (see T307865 and T140960) |
105 | $rcIdsForPage = $dbw->newSelectQueryBuilder() |
106 | ->select( 'rc_id' ) |
107 | ->from( 'recentchanges' ) |
108 | ->where( [ 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ] ) |
109 | ->caller( __METHOD__ )->fetchFieldValues(); |
110 | |
111 | // T98706: delete by PK to avoid lock contention with RC delete log insertions |
112 | $rcIdBatches = array_chunk( $rcIdsForPage, $batchSize ); |
113 | foreach ( $rcIdBatches as $rcIdBatch ) { |
114 | $dbw->newDeleteQueryBuilder() |
115 | ->deleteFrom( 'recentchanges' ) |
116 | ->where( [ 'rc_id' => $rcIdBatch ] ) |
117 | ->caller( __METHOD__ )->execute(); |
118 | if ( count( $rcIdBatches ) > 1 ) { |
119 | $dbProvider->commitAndWaitForReplication( |
120 | __METHOD__, $this->ticket, [ 'domain' => $dbw->getDomainID() ] |
121 | ); |
122 | } |
123 | } |
124 | } |
125 | |
126 | public function getAsJobSpecification() { |
127 | return [ |
128 | 'domain' => $this->getDB()->getDomainID(), |
129 | 'job' => new JobSpecification( |
130 | 'deleteLinks', |
131 | [ 'pageId' => $this->mId, 'timestamp' => $this->timestamp ], |
132 | [ 'removeDuplicates' => true ], |
133 | $this->mTitle |
134 | ) |
135 | ]; |
136 | } |
137 | } |