]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - maintenance/sql.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / maintenance / sql.php
1 <?php
2 /**
3  * Send SQL queries from the specified file to the database, performing
4  * variable replacement along the way.
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 Maintenance
23  */
24
25 require_once __DIR__ . '/Maintenance.php';
26
27 use Wikimedia\Rdbms\ResultWrapper;
28 use Wikimedia\Rdbms\IDatabase;
29 use Wikimedia\Rdbms\DBQueryError;
30
31 /**
32  * Maintenance script that sends SQL queries from the specified file to the database.
33  *
34  * @ingroup Maintenance
35  */
36 class MwSql extends Maintenance {
37         public function __construct() {
38                 parent::__construct();
39                 $this->addDescription( 'Send SQL queries to a MediaWiki database. ' .
40                         'Takes a file name containing SQL as argument or runs interactively.' );
41                 $this->addOption( 'query',
42                         'Run a single query instead of running interactively', false, true );
43                 $this->addOption( 'cluster', 'Use an external cluster by name', false, true );
44                 $this->addOption( 'wikidb',
45                         'The database wiki ID to use if not the current one', false, true );
46                 $this->addOption( 'replicadb',
47                         'Replica DB server to use instead of the master DB (can be "any")', false, true );
48         }
49
50         public function execute() {
51                 global $IP;
52
53                 // We wan't to allow "" for the wikidb, meaning don't call select_db()
54                 $wiki = $this->hasOption( 'wikidb' ) ? $this->getOption( 'wikidb' ) : false;
55                 // Get the appropriate load balancer (for this wiki)
56                 if ( $this->hasOption( 'cluster' ) ) {
57                         $lb = wfGetLBFactory()->getExternalLB( $this->getOption( 'cluster' ) );
58                 } else {
59                         $lb = wfGetLB( $wiki );
60                 }
61                 // Figure out which server to use
62                 $replicaDB = $this->getOption( 'replicadb', $this->getOption( 'slave', '' ) );
63                 if ( $replicaDB === 'any' ) {
64                         $index = DB_REPLICA;
65                 } elseif ( $replicaDB != '' ) {
66                         $index = null;
67                         $serverCount = $lb->getServerCount();
68                         for ( $i = 0; $i < $serverCount; ++$i ) {
69                                 if ( $lb->getServerName( $i ) === $replicaDB ) {
70                                         $index = $i;
71                                         break;
72                                 }
73                         }
74                         if ( $index === null ) {
75                                 $this->error( "No replica DB server configured with the name '$replicaDB'.", 1 );
76                         }
77                 } else {
78                         $index = DB_MASTER;
79                 }
80
81                 /** @var IDatabase $db DB handle for the appropriate cluster/wiki */
82                 $db = $lb->getConnection( $index, [], $wiki );
83                 if ( $replicaDB != '' && $db->getLBInfo( 'master' ) !== null ) {
84                         $this->error( "The server selected ({$db->getServer()}) is not a replica DB.", 1 );
85                 }
86
87                 if ( $index === DB_MASTER ) {
88                         $updater = DatabaseUpdater::newForDB( $db, true, $this );
89                         $db->setSchemaVars( $updater->getSchemaVars() );
90                 }
91
92                 if ( $this->hasArg( 0 ) ) {
93                         $file = fopen( $this->getArg( 0 ), 'r' );
94                         if ( !$file ) {
95                                 $this->error( "Unable to open input file", true );
96                         }
97
98                         $error = $db->sourceStream( $file, null, [ $this, 'sqlPrintResult' ] );
99                         if ( $error !== true ) {
100                                 $this->error( $error, true );
101                         } else {
102                                 exit( 0 );
103                         }
104                 }
105
106                 if ( $this->hasOption( 'query' ) ) {
107                         $query = $this->getOption( 'query' );
108                         $this->sqlDoQuery( $db, $query, /* dieOnError */ true );
109                         wfWaitForSlaves();
110                         return;
111                 }
112
113                 if (
114                         function_exists( 'readline_add_history' ) &&
115                         Maintenance::posix_isatty( 0 /*STDIN*/ )
116                 ) {
117                         $historyFile = isset( $_ENV['HOME'] ) ?
118                                 "{$_ENV['HOME']}/.mwsql_history" : "$IP/maintenance/.mwsql_history";
119                         readline_read_history( $historyFile );
120                 } else {
121                         $historyFile = null;
122                 }
123
124                 $wholeLine = '';
125                 $newPrompt = '> ';
126                 $prompt = $newPrompt;
127                 $doDie = !Maintenance::posix_isatty( 0 );
128                 while ( ( $line = Maintenance::readconsole( $prompt ) ) !== false ) {
129                         if ( !$line ) {
130                                 # User simply pressed return key
131                                 continue;
132                         }
133                         $done = $db->streamStatementEnd( $wholeLine, $line );
134
135                         $wholeLine .= $line;
136
137                         if ( !$done ) {
138                                 $wholeLine .= ' ';
139                                 $prompt = '    -> ';
140                                 continue;
141                         }
142                         if ( $historyFile ) {
143                                 # Delimiter is eated by streamStatementEnd, we add it
144                                 # up in the history (T39020)
145                                 readline_add_history( $wholeLine . ';' );
146                                 readline_write_history( $historyFile );
147                         }
148                         $this->sqlDoQuery( $db, $wholeLine, $doDie );
149                         $prompt = $newPrompt;
150                         $wholeLine = '';
151                 }
152                 wfWaitForSlaves();
153         }
154
155         protected function sqlDoQuery( IDatabase $db, $line, $dieOnError ) {
156                 try {
157                         $res = $db->query( $line );
158                         $this->sqlPrintResult( $res, $db );
159                 } catch ( DBQueryError $e ) {
160                         $this->error( $e, $dieOnError );
161                 }
162         }
163
164         /**
165          * Print the results, callback for $db->sourceStream()
166          * @param ResultWrapper|bool $res The results object
167          * @param IDatabase $db
168          */
169         public function sqlPrintResult( $res, $db ) {
170                 if ( !$res ) {
171                         // Do nothing
172                         return;
173                 } elseif ( is_object( $res ) && $res->numRows() ) {
174                         foreach ( $res as $row ) {
175                                 $this->output( print_r( $row, true ) );
176                         }
177                 } else {
178                         $affected = $db->affectedRows();
179                         $this->output( "Query OK, $affected row(s) affected\n" );
180                 }
181         }
182
183         /**
184          * @return int DB_TYPE constant
185          */
186         public function getDbType() {
187                 return Maintenance::DB_ADMIN;
188         }
189 }
190
191 $maintClass = "MwSql";
192 require_once RUN_MAINTENANCE_IF_MAIN;