Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 76 |
|
0.00% |
0 / 22 |
CRAP | |
0.00% |
0 / 1 |
DatabaseInstaller | |
0.00% |
0 / 76 |
|
0.00% |
0 / 22 |
1980 | |
0.00% |
0 / 1 |
meetsMinimumRequirement | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getName | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
isCompiled | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
checkPrerequisites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
openConnection | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getConnection | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
72 | |||
definitelyGetConnection | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
changeConnType | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
56 | |||
changeConnTypeFromSchemaToTables | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDbType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getConfigVar | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getOption | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
provide | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getProvision | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getLocalSettings | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getSchemaVars | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preUpgrade | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGlobalNames | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
checkExtension | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getReadableName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGlobalDefaults | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getInternalDefaults | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getVar | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
setVar | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getConnectForm | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getSettingsForm | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
needsUpgrade | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | /** |
4 | * DBMS-specific installation helper. |
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 Installer |
23 | */ |
24 | |
25 | namespace MediaWiki\Installer; |
26 | |
27 | use MediaWiki\Installer\Task\ITaskContext; |
28 | use MediaWiki\Status\Status; |
29 | use RuntimeException; |
30 | use Wikimedia\Rdbms\DatabaseDomain; |
31 | use Wikimedia\Rdbms\IDatabase; |
32 | use Wikimedia\Rdbms\IMaintainableDatabase; |
33 | |
34 | /** |
35 | * Base class for DBMS-specific installation helper classes. |
36 | * |
37 | * @ingroup Installer |
38 | * @since 1.17 |
39 | */ |
40 | abstract class DatabaseInstaller implements ITaskContext { |
41 | |
42 | /** |
43 | * The Installer object. |
44 | * |
45 | * @var Installer |
46 | */ |
47 | public $parent; |
48 | |
49 | /** |
50 | * @var string Set by subclasses |
51 | */ |
52 | public static $minimumVersion; |
53 | |
54 | /** |
55 | * @var string Set by subclasses |
56 | */ |
57 | protected static $notMinimumVersionMessage; |
58 | |
59 | /** |
60 | * @deprecated since 1.43 -- use definitelyGetConnection() |
61 | * @var IMaintainableDatabase |
62 | */ |
63 | public $db = null; |
64 | |
65 | /** @var IMaintainableDatabase|null */ |
66 | private $cachedConn; |
67 | /** @var string|null */ |
68 | private $cachedConnType; |
69 | |
70 | /** |
71 | * Internal variables for installation. |
72 | * |
73 | * @var array |
74 | */ |
75 | protected $internalDefaults = []; |
76 | |
77 | /** |
78 | * Array of MW configuration globals this class uses. |
79 | * |
80 | * @var array |
81 | */ |
82 | protected $globalNames = []; |
83 | |
84 | /** @var array */ |
85 | private $provisions = []; |
86 | |
87 | /** |
88 | * Whether the provided version meets the necessary requirements for this type |
89 | * |
90 | * @param IDatabase $conn |
91 | * @return Status |
92 | * @since 1.30 |
93 | */ |
94 | public static function meetsMinimumRequirement( IDatabase $conn ) { |
95 | $serverVersion = $conn->getServerVersion(); |
96 | if ( version_compare( $serverVersion, static::$minimumVersion ) < 0 ) { |
97 | return Status::newFatal( |
98 | static::$notMinimumVersionMessage, static::$minimumVersion, $serverVersion |
99 | ); |
100 | } |
101 | |
102 | return Status::newGood(); |
103 | } |
104 | |
105 | /** |
106 | * Return the internal name, e.g. 'mysql', or 'sqlite'. |
107 | */ |
108 | abstract public function getName(); |
109 | |
110 | /** |
111 | * @return bool Returns true if the client library is compiled in. |
112 | */ |
113 | abstract public function isCompiled(); |
114 | |
115 | /** |
116 | * Checks for installation prerequisites other than those checked by isCompiled() |
117 | * @since 1.19 |
118 | * @return Status |
119 | */ |
120 | public function checkPrerequisites() { |
121 | return Status::newGood(); |
122 | } |
123 | |
124 | /** |
125 | * Open a connection to the database using the administrative user/password |
126 | * currently defined in the session, without any caching. Returns a status |
127 | * object. On success, the status object will contain a Database object in |
128 | * its value member. |
129 | * |
130 | * The database should not be implicitly created. |
131 | * |
132 | * @param string $type One of the self::CONN_* constants, except CONN_DONT_KNOW |
133 | * @return ConnectionStatus |
134 | */ |
135 | abstract protected function openConnection( string $type ); |
136 | |
137 | /** |
138 | * Connect to the database using the administrative user/password currently |
139 | * defined in the session. Returns a status object. On success, the status |
140 | * object will contain a Database object in its value member. |
141 | * |
142 | * This will return a cached connection if one is available. |
143 | * |
144 | * @param string $type One of the self::CONN_* constants. Using CONN_DONT_KNOW |
145 | * is deprecated and will cause an exception to be thrown in a future release. |
146 | * @return ConnectionStatus |
147 | */ |
148 | public function getConnection( $type = self::CONN_DONT_KNOW ): ConnectionStatus { |
149 | if ( $type === self::CONN_DONT_KNOW ) { |
150 | if ( $this->cachedConnType ) { |
151 | $type = $this->cachedConnType; |
152 | } else { |
153 | $type = self::CONN_CREATE_DATABASE; |
154 | } |
155 | } |
156 | if ( $this->cachedConn ) { |
157 | if ( $this->cachedConnType === $type ) { |
158 | return new ConnectionStatus( $this->cachedConn ); |
159 | } else { |
160 | return $this->changeConnType( $this->cachedConn, $this->cachedConnType, $type ); |
161 | } |
162 | } |
163 | $status = $this->openConnection( $type ); |
164 | if ( $status->isOK() ) { |
165 | $this->cachedConn = $status->getDB(); |
166 | $this->cachedConnType = $type; |
167 | // Assign to $this->db for b/c |
168 | $this->db = $this->cachedConn; |
169 | |
170 | if ( $type === self::CONN_CREATE_SCHEMA || $type === self::CONN_CREATE_TABLES ) { |
171 | $this->cachedConn->setSchemaVars( $this->getSchemaVars() ); |
172 | } |
173 | } |
174 | |
175 | return $status; |
176 | } |
177 | |
178 | /** |
179 | * Get a connection and unwrap it from its Status object, throwing an |
180 | * exception on failure. |
181 | * |
182 | * @param string $type |
183 | * @return IMaintainableDatabase |
184 | */ |
185 | public function definitelyGetConnection( string $type ): IMaintainableDatabase { |
186 | $status = $this->getConnection( $type ); |
187 | if ( !$status->isOK() ) { |
188 | throw new RuntimeException( __METHOD__ . ': unexpected DB connection error' ); |
189 | } |
190 | return $status->getDB(); |
191 | } |
192 | |
193 | /** |
194 | * Change the type of a connection. |
195 | * |
196 | * CONN_CREATE_DATABASE means the domain is indeterminate and irrelevant, |
197 | * so converting from this type can be done by selecting the domain, and |
198 | * converting to it is a no-op. |
199 | * |
200 | * CONN_CREATE_SCHEMA means the domain is correct but tables created by |
201 | * PostgreSQL will have the incorrect role. So to convert from this to |
202 | * CONN_CREATE_TABLES, we set the role. |
203 | * |
204 | * CONN_CREATE_TABLES means a fully-configured connection, suitable for |
205 | * most tasks, so converting from it is a no-op. |
206 | * |
207 | * @param IMaintainableDatabase $conn |
208 | * @param string &$storedType One of the self::CONN_* constants. An in/out |
209 | * parameter, set to the new type on success. It is set to the "real" new |
210 | * type, reflecting the highest configuration level reached, to avoid |
211 | * unnecessary selectDomain() calls when we need to temporarily give an |
212 | * unconfigured connection. |
213 | * @param string $newType One of the self::CONN_* constants |
214 | * @return ConnectionStatus |
215 | */ |
216 | protected function changeConnType( IMaintainableDatabase $conn, &$storedType, $newType ) { |
217 | // Change type from database to schema, if requested |
218 | if ( $storedType === self::CONN_CREATE_DATABASE ) { |
219 | if ( $newType === self::CONN_CREATE_SCHEMA || $newType === self::CONN_CREATE_TABLES ) { |
220 | // TODO: catch exceptions from selectDomain and report as a Status |
221 | $conn->selectDomain( new DatabaseDomain( |
222 | $this->getVar( 'wgDBname' ), |
223 | $this->getVar( 'wgDBmwschema' ), |
224 | $this->getVar( 'wgDBprefix' ) ?? '' |
225 | ) ); |
226 | $conn->setSchemaVars( $this->getSchemaVars() ); |
227 | $storedType = self::CONN_CREATE_SCHEMA; |
228 | } |
229 | } |
230 | // Change type from schema to tables, if requested |
231 | if ( $newType === self::CONN_CREATE_TABLES && $storedType === self::CONN_CREATE_SCHEMA ) { |
232 | $status = $this->changeConnTypeFromSchemaToTables( $conn ); |
233 | if ( $status->isOK() ) { |
234 | $storedType = self::CONN_CREATE_TABLES; |
235 | } |
236 | return $status; |
237 | } |
238 | return new ConnectionStatus( $conn ); |
239 | } |
240 | |
241 | /** |
242 | * Change the type of a connection from CONN_CREATE_SCHEMA to CONN_CREATE_TABLES. |
243 | * Postgres overrides this. |
244 | * |
245 | * @param IMaintainableDatabase $conn |
246 | * @return ConnectionStatus |
247 | */ |
248 | protected function changeConnTypeFromSchemaToTables( IMaintainableDatabase $conn ) { |
249 | return new ConnectionStatus( $conn ); |
250 | } |
251 | |
252 | public function getDbType(): string { |
253 | return $this->getName(); |
254 | } |
255 | |
256 | public function getConfigVar( string $name ) { |
257 | return $this->getVar( "wg$name" ); |
258 | } |
259 | |
260 | public function getOption( string $name ) { |
261 | return $this->getVar( "_$name" ); |
262 | } |
263 | |
264 | public function provide( string $name, $value ) { |
265 | $this->provisions[$name] = $value; |
266 | } |
267 | |
268 | public function getProvision( string $name ) { |
269 | if ( isset( $this->provisions[$name] ) ) { |
270 | return $this->provisions[$name]; |
271 | } else { |
272 | throw new \RuntimeException( "Can't find provided data \"$name\"" ); |
273 | } |
274 | } |
275 | |
276 | /** |
277 | * Get the DBMS-specific options for LocalSettings.php generation. |
278 | * |
279 | * @return string |
280 | */ |
281 | abstract public function getLocalSettings(); |
282 | |
283 | /** |
284 | * Override this to provide DBMS-specific schema variables, to be |
285 | * substituted into tables.sql and other schema files. |
286 | * @return array |
287 | */ |
288 | public function getSchemaVars() { |
289 | return []; |
290 | } |
291 | |
292 | /** |
293 | * Allow DB installers a chance to make checks before upgrade. |
294 | */ |
295 | public function preUpgrade() { |
296 | } |
297 | |
298 | /** |
299 | * Get an array of MW configuration globals that will be configured by this class. |
300 | * @return array |
301 | */ |
302 | public function getGlobalNames() { |
303 | return $this->globalNames; |
304 | } |
305 | |
306 | /** |
307 | * Construct and initialise parent. |
308 | * This is typically only called from Installer::getDBInstaller() |
309 | * @param Installer $parent |
310 | */ |
311 | public function __construct( $parent ) { |
312 | $this->parent = $parent; |
313 | } |
314 | |
315 | /** |
316 | * Convenience function. |
317 | * Check if a named extension is present. |
318 | * |
319 | * @param string $name |
320 | * @return bool |
321 | */ |
322 | protected static function checkExtension( $name ) { |
323 | return extension_loaded( $name ); |
324 | } |
325 | |
326 | /** |
327 | * Get the internationalised name for this DBMS. |
328 | * @return string |
329 | */ |
330 | public function getReadableName() { |
331 | // Messages: config-type-mysql, config-type-postgres, config-type-sqlite |
332 | return wfMessage( 'config-type-' . $this->getName() )->text(); |
333 | } |
334 | |
335 | /** |
336 | * Get a name=>value map of MW configuration globals for the default values. |
337 | * @return array |
338 | * @return-taint none |
339 | */ |
340 | public function getGlobalDefaults() { |
341 | $defaults = []; |
342 | foreach ( $this->getGlobalNames() as $var ) { |
343 | if ( isset( $GLOBALS[$var] ) ) { |
344 | $defaults[$var] = $GLOBALS[$var]; |
345 | } |
346 | } |
347 | return $defaults; |
348 | } |
349 | |
350 | /** |
351 | * Get a name=>value map of internal variables used during installation. |
352 | * @return array |
353 | */ |
354 | public function getInternalDefaults() { |
355 | return $this->internalDefaults; |
356 | } |
357 | |
358 | /** |
359 | * Get a variable, taking local defaults into account. |
360 | * @param string $var |
361 | * @param mixed|null $default |
362 | * @return mixed |
363 | */ |
364 | public function getVar( $var, $default = null ) { |
365 | $defaults = $this->getGlobalDefaults(); |
366 | $internal = $this->getInternalDefaults(); |
367 | if ( isset( $defaults[$var] ) ) { |
368 | $default = $defaults[$var]; |
369 | } elseif ( isset( $internal[$var] ) ) { |
370 | $default = $internal[$var]; |
371 | } |
372 | |
373 | return $this->parent->getVar( $var, $default ); |
374 | } |
375 | |
376 | /** |
377 | * Convenience alias for $this->parent->setVar() |
378 | * @param string $name |
379 | * @param mixed $value |
380 | */ |
381 | public function setVar( $name, $value ) { |
382 | $this->parent->setVar( $name, $value ); |
383 | } |
384 | |
385 | abstract public function getConnectForm( WebInstaller $webInstaller ): DatabaseConnectForm; |
386 | |
387 | abstract public function getSettingsForm( WebInstaller $webInstaller ): DatabaseSettingsForm; |
388 | |
389 | /** |
390 | * Determine whether an existing installation of MediaWiki is present in |
391 | * the configured administrative connection. Returns true if there is |
392 | * such a wiki, false if the database doesn't exist. |
393 | * |
394 | * Traditionally, this is done by testing for the existence of either |
395 | * the revision table or the cur table. |
396 | * |
397 | * @return bool |
398 | */ |
399 | public function needsUpgrade() { |
400 | $status = $this->getConnection( self::CONN_CREATE_SCHEMA ); |
401 | if ( !$status->isOK() ) { |
402 | return false; |
403 | } |
404 | $db = $status->getDB(); |
405 | return $db->tableExists( 'cur', __METHOD__ ) || |
406 | $db->tableExists( 'revision', __METHOD__ ); |
407 | } |
408 | |
409 | } |