Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 130 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
FlowUpdateUserWiki | |
0.00% |
0 / 124 |
|
0.00% |
0 / 9 |
702 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
doDBUpdates | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
42 | |||
updateHeader | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
20 | |||
updateTopicList | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
20 | |||
updatePost | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
updateHistory | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
updateRevision | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
12 | |||
checkForReplica | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getUpdateKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Flow\Maintenance; |
4 | |
5 | use Flow\Container; |
6 | use Flow\Model\PostRevision; |
7 | use Flow\Model\UUID; |
8 | use Flow\Model\Workflow; |
9 | use LoggedUpdateMaintenance; |
10 | |
11 | $IP = getenv( 'MW_INSTALL_PATH' ); |
12 | if ( $IP === false ) { |
13 | $IP = __DIR__ . '/../../..'; |
14 | } |
15 | |
16 | require_once "$IP/maintenance/Maintenance.php"; |
17 | |
18 | /** |
19 | * Update all xxx_user_wiki field to have the correct wiki name |
20 | * |
21 | * @ingroup Maintenance |
22 | */ |
23 | class FlowUpdateUserWiki extends LoggedUpdateMaintenance { |
24 | |
25 | /** |
26 | * Used to track the number of current updated count |
27 | * |
28 | * @var int |
29 | */ |
30 | private $updatedCount = 0; |
31 | |
32 | public function __construct() { |
33 | parent::__construct(); |
34 | $this->addDescription( "Update xxx_user_wiki field in tables: flow_workflow, flow_tree_revision, flow_revision" ); |
35 | $this->requireExtension( 'Flow' ); |
36 | $this->setBatchSize( 300 ); |
37 | } |
38 | |
39 | /** |
40 | * This is a top-to-bottom update, the process is like this: |
41 | * workflow -> header -> header revision -> history |
42 | * workflow -> topic list -> post tree revision -> post revision -> history |
43 | * |
44 | * Some side effect, the script will also update those *_user_wiki fields with |
45 | * empty *_user_id and *_user_ip, but this doesn't hurt. Alternatively, we could |
46 | * add a check user_id != 0 and user_ip is not null to the query, but this will |
47 | * result in more db queries |
48 | * @return true |
49 | */ |
50 | protected function doDBUpdates() { |
51 | $id = ''; |
52 | $batchSize = $this->getBatchSize(); |
53 | $count = $batchSize; |
54 | $dbr = Container::get( 'db.factory' )->getDB( DB_REPLICA ); |
55 | |
56 | // If table flow_header_revision does not exist, that means the wiki |
57 | // has run the data migration before or the wiki starts from scratch, |
58 | // there is no point to run the script againt invalid tables |
59 | if ( !$dbr->tableExists( 'flow_header_revision', __METHOD__ ) ) { |
60 | return true; |
61 | } |
62 | |
63 | while ( $count == $this->mBatchSize ) { |
64 | $count = 0; |
65 | $res = $dbr->newSelectQueryBuilder() |
66 | ->select( [ 'workflow_wiki', 'workflow_id', 'workflow_type' ] ) |
67 | ->from( 'flow_workflow' ) |
68 | ->where( $dbr->expr( 'workflow_id', '>', $id ) ) |
69 | ->orderBy( 'workflow_id' ) |
70 | ->limit( $batchSize ) |
71 | ->caller( __METHOD__ ) |
72 | ->fetchResultSet(); |
73 | foreach ( $res as $row ) { |
74 | $count++; |
75 | $id = $row->workflow_id; |
76 | $uuid = UUID::create( $row->workflow_id ); |
77 | $workflow = Container::get( 'storage.workflow' )->get( $uuid ); |
78 | if ( $workflow ) { |
79 | // definition type 'topic' is always under a 'discussion' and they |
80 | // will be handled while processing 'discussion' |
81 | if ( $row->workflow_type == 'discussion' ) { |
82 | $this->updateHeader( $workflow, $row->workflow_wiki ); |
83 | $this->updateTopicList( $workflow, $row->workflow_wiki ); |
84 | } |
85 | } |
86 | } |
87 | } |
88 | |
89 | return true; |
90 | } |
91 | |
92 | /** |
93 | * Update header |
94 | * @param Workflow $workflow |
95 | * @param string $wiki |
96 | */ |
97 | private function updateHeader( $workflow, $wiki ) { |
98 | $id = ''; |
99 | $batchSize = $this->getBatchSize(); |
100 | $count = $batchSize; |
101 | $dbr = Container::get( 'db.factory' )->getDB( DB_REPLICA ); |
102 | |
103 | while ( $count == $batchSize ) { |
104 | $count = 0; |
105 | $res = $dbr->newSelectQueryBuilder() |
106 | ->select( [ 'rev_id', 'rev_type' ] ) |
107 | ->from( 'flow_header_revision' ) |
108 | ->join( 'flow_revision', null, 'header_rev_id = rev_id' ) |
109 | ->where( [ |
110 | $dbr->expr( 'rev_id', '>', $id ), |
111 | 'header_workflow_id' => $workflow->getId()->getBinary() |
112 | ] ) |
113 | ->orderBy( 'header_rev_id' ) |
114 | ->limit( $batchSize ) |
115 | ->caller( __METHOD__ ) |
116 | ->fetchResultset(); |
117 | foreach ( $res as $row ) { |
118 | $count++; |
119 | $id = $row->rev_id; |
120 | $revision = Container::get( 'storage.header' )->get( UUID::create( $row->rev_id ) ); |
121 | if ( $revision ) { |
122 | $this->updateRevision( $revision, $wiki ); |
123 | } |
124 | } |
125 | } |
126 | } |
127 | |
128 | /** |
129 | * Update topic list |
130 | * @param Workflow $workflow |
131 | * @param string $wiki |
132 | */ |
133 | private function updateTopicList( $workflow, $wiki ) { |
134 | $id = ''; |
135 | $batchSize = $this->getBatchSize(); |
136 | $count = $batchSize; |
137 | $dbr = Container::get( 'db.factory' )->getDB( DB_REPLICA ); |
138 | |
139 | while ( $count == $batchSize ) { |
140 | $count = 0; |
141 | $res = $dbr->newSelectQueryBuilder() |
142 | ->select( 'topic_id' ) |
143 | ->from( 'flow_topic_list' ) |
144 | ->where( [ |
145 | 'topic_list_id' => $workflow->getId()->getBinary(), |
146 | $dbr->expr( 'topic_id', '>', $id ), |
147 | ] ) |
148 | ->orderBy( 'topic_id' ) |
149 | ->limit( $batchSize ) |
150 | ->caller( __METHOD__ ) |
151 | ->fetchResultSet(); |
152 | $index = 0; |
153 | foreach ( $res as $row ) { |
154 | $count++; |
155 | $index++; |
156 | $id = $row->topic_id; |
157 | $post = Container::get( 'loader.root_post' )->get( UUID::create( $row->topic_id ) ); |
158 | if ( $post ) { |
159 | $this->updatePost( $post, $wiki ); |
160 | } |
161 | } |
162 | } |
163 | } |
164 | |
165 | /** |
166 | * Update post |
167 | * @param PostRevision $post |
168 | * @param string $wiki |
169 | */ |
170 | private function updatePost( $post, $wiki ) { |
171 | $this->updateHistory( $post, $wiki ); |
172 | $this->updateRevision( $post, $wiki ); |
173 | foreach ( $post->getChildren() as $child ) { |
174 | $this->updatePost( $child, $wiki ); |
175 | } |
176 | } |
177 | |
178 | /** |
179 | * Update history revision |
180 | * @param PostRevision $post |
181 | * @param string $wiki |
182 | */ |
183 | private function updateHistory( PostRevision $post, $wiki ) { |
184 | if ( $post->getPrevRevisionId() ) { |
185 | $parent = Container::get( 'storage.post' )->get( UUID::create( $post->getPrevRevisionId() ) ); |
186 | if ( $parent ) { |
187 | $this->updateRevision( $parent, $wiki ); |
188 | $this->updateHistory( $parent, $wiki ); |
189 | } |
190 | } |
191 | } |
192 | |
193 | /** |
194 | * Update either header or post revision |
195 | * @param PostRevision $revision |
196 | * @param string $wiki |
197 | */ |
198 | private function updateRevision( $revision, $wiki ) { |
199 | if ( !$revision ) { |
200 | return; |
201 | } |
202 | $type = $revision->getRevisionType(); |
203 | |
204 | $dbw = Container::get( 'db.factory' )->getDB( DB_PRIMARY ); |
205 | $dbw->newUpdateQueryBuilder() |
206 | ->update( 'flow_revision' ) |
207 | ->set( [ |
208 | 'rev_user_wiki' => $wiki, |
209 | 'rev_mod_user_wiki' => $wiki, |
210 | 'rev_edit_user_wiki' => $wiki, |
211 | ] ) |
212 | ->where( [ |
213 | 'rev_id' => $revision->getRevisionId()->getBinary(), |
214 | ] ) |
215 | ->caller( __METHOD__ ) |
216 | ->execute(); |
217 | $this->checkForReplica(); |
218 | |
219 | if ( $type === 'post' ) { |
220 | $dbw->newUpdateQueryBuilder() |
221 | ->update( 'flow_tree_revision' ) |
222 | ->set( [ |
223 | 'tree_orig_user_wiki' => $wiki, |
224 | ] ) |
225 | ->where( [ |
226 | 'tree_rev_id' => $revision->getRevisionId()->getBinary(), |
227 | ] ) |
228 | ->caller( __METHOD__ ) |
229 | ->execute(); |
230 | $this->checkForReplica(); |
231 | } |
232 | } |
233 | |
234 | private function checkForReplica() { |
235 | global $wgFlowCluster; |
236 | |
237 | $this->updatedCount++; |
238 | if ( $this->updatedCount > $this->getBatchSize() ) { |
239 | $lbFactory = $this->getServiceContainer()->getDBLoadBalancerFactory(); |
240 | $lbFactory->waitForReplication( [ 'cluster' => $wgFlowCluster ] ); |
241 | $this->updatedCount = 0; |
242 | } |
243 | } |
244 | |
245 | /** |
246 | * Get the update key name to go in the update log table |
247 | * |
248 | * @return string |
249 | */ |
250 | protected function getUpdateKey() { |
251 | return 'FlowUpdateUserWiki'; |
252 | } |
253 | } |
254 | |
255 | $maintClass = FlowUpdateUserWiki::class; |
256 | require_once RUN_MAINTENANCE_IF_MAIN; |