Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
82 / 82 |
|
100.00% |
3 / 3 |
CRAP | |
100.00% |
1 / 1 |
NukeAssociatedQueryBuilder | |
100.00% |
82 / 82 |
|
100.00% |
3 / 3 |
8 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getTalkPages | |
100.00% |
40 / 40 |
|
100.00% |
1 / 1 |
4 | |||
getRedirectPages | |
100.00% |
39 / 39 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Nuke; |
4 | |
5 | use MediaWiki\Config\Config; |
6 | use MediaWiki\Exception\MWException; |
7 | use MediaWiki\MainConfigNames; |
8 | use MediaWiki\Title\NamespaceInfo; |
9 | use MediaWiki\Title\Title; |
10 | use Wikimedia\Rdbms\IReadableDatabase; |
11 | use Wikimedia\Rdbms\SelectQueryBuilder; |
12 | |
13 | class NukeAssociatedQueryBuilder { |
14 | |
15 | private IReadableDatabase $readableDatabase; |
16 | private Config $config; |
17 | private NamespaceInfo $namespaceInfo; |
18 | |
19 | /** |
20 | * @param IReadableDatabase $readableDatabase |
21 | * @param Config $config |
22 | * @param NamespaceInfo $namespaceInfo |
23 | */ |
24 | public function __construct( |
25 | IReadableDatabase $readableDatabase, |
26 | Config $config, |
27 | NamespaceInfo $namespaceInfo |
28 | ) { |
29 | $this->readableDatabase = $readableDatabase; |
30 | $this->config = $config; |
31 | $this->namespaceInfo = $namespaceInfo; |
32 | } |
33 | |
34 | /** |
35 | * Get the associated talk pages that exist for each page in the provided |
36 | * list. |
37 | * |
38 | * @param Title[] $pages |
39 | * @return SelectQueryBuilder |
40 | */ |
41 | public function getTalkPages( array $pages ): SelectQueryBuilder { |
42 | $byNamespace = []; |
43 | |
44 | // Sort the pages by their associated talk namespaces. |
45 | foreach ( $pages as $page ) { |
46 | try { |
47 | $talkNamespace = $this->namespaceInfo->getTalk( $page->getNamespace() ); |
48 | } catch ( MWException $e ) { |
49 | continue; |
50 | } |
51 | $byNamespace[ $talkNamespace ][] = $page->getDBkey(); |
52 | } |
53 | |
54 | // Get the talk pages that exist for each page. |
55 | $dbr = $this->readableDatabase; |
56 | $queryBuilder = $dbr->newSelectQueryBuilder() |
57 | ->select( [ |
58 | 'talk.page_id', |
59 | 'talk.page_namespace', |
60 | 'talk.page_title', |
61 | 'actor_name', |
62 | 'subject_page_id' => 'subject.page_id', |
63 | ] ) |
64 | ->distinct() |
65 | ->from( 'page', 'talk' ) |
66 | ->join( 'revision', 'first', [ |
67 | 'first.rev_id=talk.page_latest', |
68 | 'first.rev_parent_id=0' |
69 | ] ) |
70 | ->join( 'actor', null, 'actor_id=first.rev_actor' ) |
71 | // Self-join to identify the subject page |
72 | ->join( 'page', 'subject', [ |
73 | 'subject.page_title=talk.page_title', |
74 | // Talk namespaces are always 1 greater than the subject namespace. |
75 | 'subject.page_namespace=(talk.page_namespace - 1)' |
76 | ] ) |
77 | ->setMaxExecutionTime( |
78 | $this->config->get( MainConfigNames::MaxExecutionTimeForExpensiveQueries ) |
79 | ); |
80 | $conditions = []; |
81 | foreach ( $byNamespace as $talkNamespace => $names ) { |
82 | $conditions[] = $dbr->andExpr( [ |
83 | $dbr->expr( 'talk.page_namespace', '=', $talkNamespace ), |
84 | $dbr->expr( 'talk.page_title', '=', $names ), |
85 | ] ); |
86 | } |
87 | $queryBuilder->where( [ |
88 | $dbr->orExpr( $conditions ) |
89 | ] ); |
90 | return $queryBuilder; |
91 | } |
92 | |
93 | public function getRedirectPages( array $pages ): SelectQueryBuilder { |
94 | $byNamespace = []; |
95 | |
96 | // Sort the pages by their namespaces. |
97 | foreach ( $pages as $page ) { |
98 | $byNamespace[ $page->getNamespace() ][] = $page->getDBkey(); |
99 | } |
100 | |
101 | $dbr = $this->readableDatabase; |
102 | $queryBuilder = $dbr->newSelectQueryBuilder() |
103 | ->select( [ |
104 | 'rdpage.page_id', |
105 | 'rdpage.page_namespace', |
106 | 'rdpage.page_title', |
107 | 'actor_name', |
108 | 'target_page_id' => 'target.page_id' |
109 | ] ) |
110 | ->distinct() |
111 | ->from( 'redirect' ) |
112 | ->join( 'page', 'rdpage', 'rd_from=rdpage.page_id' ) |
113 | ->join( 'revision', 'first', [ |
114 | 'first.rev_id=rdpage.page_latest', |
115 | 'first.rev_parent_id=0' |
116 | ] ) |
117 | ->join( 'actor', null, 'actor_id=first.rev_actor' ) |
118 | // Self-join to identify the target page |
119 | ->join( 'page', 'target', [ |
120 | 'target.page_title=rd_title', |
121 | 'target.page_namespace=rd_namespace' |
122 | ] ); |
123 | |
124 | $conditions = []; |
125 | foreach ( $byNamespace as $namespace => $names ) { |
126 | $conditions[] = $dbr->andExpr( [ |
127 | $dbr->expr( 'rd_namespace', '=', $namespace ), |
128 | $dbr->expr( 'rd_title', '=', $names ), |
129 | ] ); |
130 | } |
131 | $queryBuilder->where( $dbr->orExpr( $conditions ) ); |
132 | $queryBuilder->andWhere( $dbr->orExpr( [ |
133 | $dbr->expr( 'rd_interwiki', '=', null ), |
134 | $dbr->expr( 'rd_interwiki', '=', "" ) |
135 | ] ) ); |
136 | |
137 | return $queryBuilder->setMaxExecutionTime( |
138 | $this->config->get( MainConfigNames::MaxExecutionTimeForExpensiveQueries ) |
139 | ); |
140 | } |
141 | |
142 | } |