├── .gitignore ├── Readme.md ├── db-config-sample-16.php ├── db-config-sample-256.php ├── db-config.php ├── db-tools ├── db.php ├── db_array.php ├── db_servers.php ├── db_sql.php ├── index.php └── md5.php ├── db.php ├── fix-db-encoding.php ├── license.txt ├── move-blogs-rollback.php └── move-blogs.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Multi DB 2 | 3 | **INACTIVE NOTICE: This plugin is unsupported by WPMUDEV, we've published it here for those technical types who might want to fork and maintain it for their needs.** 4 | 5 | The standard WordPress Multisite installation requires only one database – which is great for hobbyists or people just looking to host a few dozen or few hundred sites. 6 | 7 | When you get past a few hundred sites, a single database can get cramped. 8 | 9 | Multi-DB works to make better use of your server by creating either 16, 256 or 4096 database tables and spreading new blogs evenly across your system. 10 | 11 | 12 | If you’re in need of a tool that spreads sites on your network across multiple database tables, Multi-DB has been tested and used on thousands of sites since 2008. 13 | 14 | It is a fantastic foundation for anyone looking to spread their content across more than one database. 15 | 16 | ## Usage Docs 17 | 18 | **Important: Please note that this is not your normal “drop-the-files-here” plugin, and it is not to be uploaded to wp-content/plugins** 19 | 20 | This is sysadmin-level stuff. But don’t be scared off, with the right mindset (and server configuration) you can do it! 21 | 22 | #### First, is your server setup right for this? 23 | 24 | * Do you have root access to your server, access to phpMyAdmin, and can you run SQL scripts on your database? 25 | * If you are big enough to need Multi-DB, you really should be using a VPS or dedicated server that gives you root access. 26 | * If your host or server is using a control panel like cPanel, or they run DB servers externally (ie – shared hosting, grid hosting, etc), you may not have permissions to create DBs via an SQL script. 27 | * So first see if you can login as root and run stuff via command line. If not, you may need to create the DBs manually one at a time through the control panel (cPanel). 28 | Your server meets the requirements and you’re ready to roll? Then let’s do this! 29 | 30 | ## Preparation is Key 31 | 32 | Multi-DB is one of those plugins that becomes an absolute necessity for any Multisite network that is seeing or expecting substantial numbers of blogs/sites. 33 | 34 | It is powerful in what it does (spreading the WordPress tables across several databases), but as with anything else, getting it set up correctly with adequate preparation for installation is essential to success. 35 | 36 | The key to success with this plugin is to have the settings edited and triple-checked BEFORE uploading to your site. 37 | 38 | If you’ve not attempted this before, we recommend reading through this entire process first. Then read it again and follow along. Finally, go back through and make sure each setting is correct. Then you should be ready for an install without incident! 39 | 40 | *STOP! Before going any further, please make a full backup of your multisite so, just in case things go kaflooey, you can restore it!* 41 | 42 | ## Creating Your Databases 43 | 44 | Decide how many databases you want (16, 256, 4096) 45 | So, how do you know that this plugin is necessary for your install? Well, there are several factors to consider beyond the scope of this walkthrough, but here’s a basic guideline: 46 | 47 | * 1 – 5,000 blogs/sites: you should be fine with your WordPress default database 48 | * 5,000 – 50,000 blogs/sites: go with 16 databases 49 | * 50,000 – 100,000 blogs/sites: use 256 databases 50 | * 100,000+ blogs/sites: use 4096 databases 51 | 52 | There’s no performance hit for using 256 databases over 16. But if you’re expecting massive growth, planning ahead at this point will save you additional work down the road. 53 | 54 | In fact, unless you already know you won’t be growing past 50,000 blogs/sites it’s best just to do the 256 just in case. Using 4096 DBs is usually overkill unless you plan on being the next wordpress.com or edublogs.org! 55 | 56 | As stated earlier, there are several factors to consider and this is meant to be a general guide. It’s really a decision specific to your site needs. 57 | 58 | ### Decide if you need VIP databases 59 | This plugin has a cool feature that allows for VIP databases. It enables you to place specific blog(s) in specific database(s). 60 | 61 | * Unless you have a blog/site that gets a ton of traffic and you want to put it on another physical server for performance reasons, then it’s not really worth it to bother with this feature. 62 | * The vast majority of installs do not need to use VIP blogs, so skip it unless you are sure you need to use this feature. 63 | * Each VIP blog/site will have its own database, and they will be identified as vip1, vip2, etc. 64 | * If you do decide you need this, you’ll be using the add_vip_blog() function in db-config.php to move specific blogs to these databases (more on that below). 65 | 66 | ## Create the mySQL command 67 | 68 | While you can name your databases anything you like, we know from experience that ordering them as we have in this walkthrough will make management easier down the road, and it also lets you more easily use the included db-config.php file. 69 | 70 | We’ll get to that file in the next step below, but first we need to get the proper SQL command set up. We’ve provided an easy tool for that, you can check it under /db-tools/ within this repository. 71 | 72 | **IMPORTANT: Once you upload the db-tools directory to your site, any logged in user will be able to access it.** 73 | 74 | Be sure you’re on the correct tool for database creation by clicking the DB SQL link at the top of the page. 75 | 76 |  77 | 78 | * In the DB Name field, type in your database name (it should be the same name as your current WordPress DB) followed by an underscore ( _ ). The underscore is very important as this creates the prefix for your new databases. Without that underscore, nothing will work! 79 | * Next, choose the number of databases you need from the dropdown. Then click the Submit button. 80 | 81 | In the textarea below, you’ll now see all the instructions mySQL needs to create your new DBs. 82 | 83 |  84 | 85 | Well, almost all; there is one other line you need to add manually to create the required global database. 86 | 87 | * Copy the final line generated by the database tool, and paste it directly beneath all the others in the textarea of the DB creation tool. 88 | * Then change dbname_f to dbname_global in that new line. That’s it. 89 | 90 | Your final output for the required global database will look something like this: 91 | 92 | `CREATE DATABASE 'dbname_global' DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;` 93 | 94 | Did you also decide to create optional VIP databases for some of your blogs/sites as mentioned above? If so… 95 | 96 | * Add another line in the textarea for each one you need. 97 | * Change dbname_f to dbname_vip1 for the first one. 98 | * If you are creating more than one, change dbname_f to dbname_vip2 for the second one, dbname_vip3 for the third one, and so on. 99 | 100 | The final output for each of your VIP databases would look something like this: 101 | 102 | `CREATE DATABASE 'dbname_vip1' DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;` 103 | 104 | Be sure you’ve added these extra line(s) directly beneath those created by the DB SQL tool. 105 | 106 | ## Create the Databases 107 | Now, you need to create the databases, either via command line (hardcore) or through phpMyAdmin (much easier). 108 | 109 | Log into your server and, if you’re using phpMyAdmin, be sure you’re in the root and click the SQL tab. Then paste in the SQL command you just generated (the contents of the textarea from the DB SQL tool), and click the “Go” button. 110 | 111 | Note that the screenshot below does not include any lines for VIP databases. However, if you are creating some, they would be there. 112 | 113 |  114 | 115 | Now when you click the “Databases” tab in a tool like **phpMyAdmin**, you should see loads of new databases where there used to be only one or two. 116 | 117 |  118 | 119 | Note that even though your new databases may appear instantly in phpMyAdmin, it can sometimes take several minutes – even hours – for them to appear in cPanel. 120 | 121 | In some instances, you may even need to wait until the next day. This is beyond our, and your, control. Patience, Grasshopper. :) 122 | 123 | ## Assigning Users & Passwords 124 | 125 | A username and password must be associated with each database. 126 | 127 | * This could be the same as your username and password for the original WordPress database. Or you could create new users for your new databases. 128 | * It’s up to you, just make sure that there IS a username and password associated with each one and note what they are. You’ll need this information for the next part of the installation. 129 | 130 | You can create and assign usernames & passwords either directly in phpMyAdmin, or use the **MySQL Databases** section in your cPanel. 131 | 132 | ## Creating Users & Passwords in phpMyAdmin 133 | If you have CREATE USER privileges in phpMyAdmin, you can use any of the following commands in the SQL tab (the same place where you just added the command to create your new databases). 134 | 135 | To assign all privileges on all databases to an existing user (probably you), you can use this command: 136 | 137 | `GRANT ALL PRIVILEGES ON * . * TO 'username'@'localhost'; 138 | FLUSH PRIVILEGES;` 139 | 140 | Or, if you want to create a new username/password combination & assign all privileges on all databases to that user, use this command: 141 | 142 | `CREATE USER 'username'@'localhost' IDENTIFIED BY 'password'; 143 | GRANT ALL PRIVILEGES ON * . * TO 'username'@'localhost'; 144 | FLUSH PRIVILEGES;` 145 | 146 | The asterisks in the above commands represent, respectively, all databases & all tables. 147 | 148 | If you are using VIP databases, and want to create and grant privileges to different users in each of those, you’d first create the user(s) and their password(s), then specify the username for each database using a command like this (note the database specified instead of the first asterisk): 149 | 150 | ``` 151 | CREATE USER 'user1'@'localhost' IDENTIFIED BY 'pass1'; 152 | CREATE USER 'user2'@'localhost' IDENTIFIED BY 'pass2'; 153 | GRANT ALL PRIVILEGES ON dbname_vip1 . * TO 'user1'@'localhost'; 154 | GRANT ALL PRIVILEGES ON dbname_vip2 . * TO 'user2'@'localhost'; 155 | GRANT ALL PRIVILEGES ON dbname_global . * TO 'username'@'localhost'; 156 | GRANT ALL PRIVILEGES ON dbname_0 . * TO 'username'@'localhost'; 157 | GRANT ALL PRIVILEGES ON dbname_1 . * TO 'username'@'localhost'; 158 | GRANT ALL PRIVILEGES ON dbname_2 . * TO 'username'@'localhost'; 159 | GRANT ALL PRIVILEGES ON dbname_3 . * TO 'username'@'localhost'; 160 | GRANT ALL PRIVILEGES ON dbname_4 . * TO 'username'@'localhost'; 161 | GRANT ALL PRIVILEGES ON dbname_5 . * TO 'username'@'localhost'; 162 | ... 163 | FLUSH PRIVILEGES; 164 | ``` 165 | 166 | Be sure to include an additional line for each database as indicated by the ellipsis (…). 167 | 168 | In each of the above cases, replace the words `user1`, `user2`, `pass1`, `pass2`, username & password in the code with the actual usernames & passwords. :) 169 | 170 | Also remember to change `dbname_0`, `dbname_1`, etc, to the actual names of your databases. 171 | 172 | [For more on this, see this handy tutorial at DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-create-a-new-user-and-grant-permissions-in-mysql). 173 | 174 | ##Creating Users & Passwords in cPanel 175 | If you do not have CREATE USER privileges in phpMyAdmin, or just don’t want to mess around in there, you can add the username & password to each new database in your cPanel. 176 | 177 | To do that, scroll down to the Databases section on the main page of your cPanel, and click the **MySQL Databases** link. 178 | 179 |  180 | 181 | On the next screen, fill in the fields in the **Add New User** section to create any new user(s) that you may want. 182 | 183 | Then, assign existing user(s) to the appropriate database(s) in the **Add User To Database** section. 184 | 185 |  186 | 187 | When you click the **Add** button to add a user to a database, you’ll get a screen where you can assign the privileges for that user & database. 188 | 189 |  190 | 191 | Simply check the **All Privileges** box and click **Make Changes**. 192 | 193 | You’ll need to add a user to each database and grant permissions for each one in the manner detailed above. 194 | 195 | So, if you have added 16 databases, you’ll do this 16 times; for 256 databases, you’ll do it 256 times; 4096 databases, well, you get the idea. :) 196 | 197 | 198 | ## Configuring the Plugin Files 199 | 200 | Your databases are created and you’ve assigned usernames & passwords to each one? 201 | 202 | Excellent! Now it’s time to start configuring the plugin to handle the heavy lifting. We’ve got several areas to configure in 2 different files. 203 | 204 | So if you haven’t already downloaded your copy of Multi-DB by clicking the big button at the top of this page, please do that now. 205 | 206 | ### Configuring `db-config.php` 207 | Unzip the file you just downloaded, and open `db-config.php` in a text editor like Sublime Text 208 | 209 | The first thing to do is enter the number of new databases you just created, and the IP address of your multisite. 210 | 211 |  212 | 213 | * Line 10: Change the number next to DB_SCALING to however many new databases you created (16, 256, or 4096) 214 | * Line 16: On this line, enter ONLY the first 3 quadrants of your multisite’s IP address, with a dot at the end.For example, if your IP is `111.222.333.444` you would enter `111.222.333`. including the dot. It would look like this: `add_dc_ip('111.222.333.', 'dc1');` 215 | 216 | Next, we want to add in the new global database name. 217 | 218 |  219 | 220 | * Scroll down to line 25, and add a new blank line there. On that line, enter the the command for your new global database.Remember to change `dbname_global` to the actual name of the global database you created. It would look like this: `add_global_table('dbname_global');` 221 | 222 | The other global table lines in there are required by the plugins specified. 223 | 224 | * If you’re not using any of those plugins, you can safely leave those lines there. It’s like having an empty closet: you’re not using it, but it’s nice to know it’s there if you do need it. 225 | 226 | The next thing we want to configure in this file is the *DB Servers* section. 227 | 228 | To do that, we’re going to use another of the online tools we have provided: 229 | http://yourdomain.com/db-tools/db_servers.php 230 | 231 | Click on that link now, and be sure you’re on the correct tool by clicking the DB Servers link at the top of the page. 232 | 233 |  234 | 235 | * In the **DB Name** field, type in your database name just like you did with the previous tool, followed by the underscore ( _ ). Again, the underscore is very important here; without it, stuff won’t work. 236 | * Enter your username in the **DB User** field, and your password in the **DB Pass** field. 237 | * In the **DB Local Host** field, enter the full IP of your multisite. Ex: `111.222.333.444` 238 | * The DB Remote Host field is only required if your databases are hosted on a different IP from your WordPress install (a remote server). If they are, enter that IP here.If, as is usually the case, your databases are at the same IP as your WordPress site, you can ignore the need for a remote server entirely, and leave the field blank. 239 | * Finally, select the number of databases you already created from the dropdown. Then click the **Submit** button. 240 | 241 | In the textarea below, you’ll now see all the instructions you need to paste in the *DB Servers* section of `db-config.php` 242 | 243 | Your output should look similar to the following: 244 | 245 |  246 | 247 | The actual values will be the ones you entered. If you did not enter anything for the remote host, that value will simply be empty. 248 | 249 | Just as we did when creating the new databases, we want to add a special line here too for our global database. 250 | 251 | * Copy one of the lines generated, and paste it directly **above** all the others in the textarea of the DB Servers tool. 252 | * Change the number at the beginning of that new line to`global`. 253 | * Change the `dbname_x` to the name of your global database. For ex: `dbname_global`. 254 | * The new line for your global database will look something like this: 255 | 256 | `add_db_server('global', 'dc1', 1, 1,'','111.222.333.444', 'dbname_global', 'username', 'password');` 257 | 258 | Did you also create VIP database(s)? If so, you’ll want to add a line here too for each one. 259 | 260 | * Copy one of the lines generated, and paste it directly `beneath` the line for your global database. 261 | * Change the number at the beginning of that new line to `vip1` * Change the `dbname_x` to the name of your VIP database. For ex: `dbname_vip1`. 262 | * The new line for your VIP database will look something like this: 263 | 264 | `add_db_server('vip1', 'dc1', 1, 1,'','111.222.333.444', 'dbname_vip1', 'username', 'password');` 265 | 266 | If you have created more than one VIP database, add a similar line for each one. Change `vip1` and `dbname_vip1` in each new line to the corresponding values (`vip2`, `dbname_vip2`, etc). 267 | 268 | **Important: If you have assigned different username(s) & password(s) to different databases in the previous steps, be sure to edit them here now for each of those databases.** 269 | 270 | All done? Please double-check to make sure that you have the correct number of lines in there corresponding to the number of databases you created (16, 256 or 4096), as well as one for the global database, and any VIP databases you may have created too. 271 | 272 | Now copy the entire contents of the textarea, and paste it in db-config.php at line 67, replacing all the existing examples there. 273 | 274 | That section in your `db-config.php` should now look like this: 275 | 276 | Again, this screenshot does not include any lines for VIP databases. But if you created some, they should be there. 277 | 278 |  279 | 280 | 281 | The last section we want to configure is the VIP Blogs section. This is where you will actually designate which database(s) should be used by which blog(s). 282 | 283 | **If you did not create any databases for VIP blogs in the previous steps, you can skip this part.** 284 | 285 | To designate a blog/site as a VIP blog/site and give it its own database, simply follow the example given at the very bottom of the file. 286 | 287 | To add your blog with an ID of ‘4’ to the vip1 database, just enter the following: 288 | 289 | `add_vip_blog(4, 'vip1');` 290 | 291 | Create a new line for each of the VIP databases you need. Paste them all in directly above the `?>` which closes the file. 292 | 293 |  294 | 295 | Note that you can only designate a VIP database for your main site if the table names are prefixed with a number, like this: 296 | `wp_1_posts` 297 | `wp_1_options` 298 | etc... 299 | 300 | However, if they do not have a numerical prefix as follows, then they cannot be moved from the global database (the table names are dependent on how/when multisite was installed). 301 | `wp_posts` 302 | `wp_options` 303 | etc... 304 | 305 | All done with db-config.php? Save your file! 306 | 307 | ## Configuring move-blogs.php 308 | Now open `move-blogs.php` in your text editor. 309 | 310 | This file is much simpler to configure as we only need to enter a few bits of data. 311 | 312 | However, please take care to enter the data exactly as described below. 313 | 314 | * Line 19: Change `old_db_name` to the name of your current database. Following the same examples we’ve been using from the beginning, you would enter dbname here. **Important: do NOT add an underscore ( _ ) here.** 315 | * Line 20: If the main prefix of your current database tables is the default `wp_`, you can leave this as-is. However, if you have changed the prefix of your current database tables, enter that here. 316 | * Line 21: Change `newdbname` to the prefix used for all your new databases. Following the examples we’ve been using, you would enter `dbname_ `here (with the underscore this time, which designates the database prefix). 317 | * Line 25: Change user to the username currently associated with your database. This must not be any of the new usernames you may have created in the previous steps. 318 | * Line 26: Change pass to the password currently associated with your database. Again, this should not be any of the new passwords you may have created earlier. 319 | Line 29: Change 256 to the number of databases you created at the beginning (16, 256 or 4096). 320 | 321 | The screenshot below shows what the move-blogs.php file would look like with all the same sample data as given throughout this usage guide. Yours will, of course, contain your own data. :) 322 | 323 |  324 | 325 | All done with `move-blogs.php`? Remember to save that file too. 326 | 327 | ## Uploading the Plugin Files 328 | 329 | NOW it’s time. Here is where all our labor finally pays off. You did double and triple check everything above, right? 330 | 331 | There are 3 files we need to upload: the 2 you just edited – `db-config.php` and `move-blogs.php` – as well as the `db.php` file which does not require any edits. 332 | 333 | Upload both `db.php` and the `db-config.php` that you edited to your `wp-content` folder. 334 | 335 | You can upload the files either via FTP, or use the File Manager in your cPanel. 336 | The screenshot below shows the result of the upload via FTP using FileZilla. 337 | 338 |  339 | 340 | Next, we want to upload `move-blogs.php`. We recommend creating a folder inside `wp-content` called `scripts` and uploading the file there. Some hosts may prevent direct access to your `wp-content` folder, in which case you can create or move your `/scripts/` directory to the root directory of your WP installation. Your scripts directory will need execute permissions in order for this to work correctly. 341 | 342 | It really doesn’t matter though, as long as you remember where you put it, as we’ll need to head there in our web browser next. 343 | 344 | 345 | ## Final Step: Copy & Verify DBs 346 | 347 | We’re almost done! This last step is the one that actually copies existing tables to their respective databases according to the configuration you just completed, and ensures that new blogs/sites get properly distributed amongst those new databases. 348 | 349 | Open up your web browser and enter the full URL to the `move-blogs.php` file on your server. If you created a scripts folder and uploaded it there, the URL would look like this (of course, replace yourdomain.com with your actual site name): 350 | 351 | ``` 352 | http://yourdomain.com/wp-content/scripts/move-blogs.php 353 | ``` 354 | 355 | You’ll see that there are 10 instructions at the top of this page. 356 | 357 |  358 | 359 | Simply follow the instructions at the top of that screen. The main things to check are: 360 | 361 | * The `new db` column should display a green `exists` on each line. 362 | * The `status` column should show `not in new db` on each line. 363 | 364 | When you’ve verified that everything looks right, click the link in step 5 of that screen. Once the process completes, click the link in step 7 to refresh the page. You should now see 'table in new db' under the status column for each row. 365 | 366 | If all is good there, then you have successfully completed your multi-db installation! Congratulations! 367 | 368 | ## Upgrade Instructions 369 | 370 | To upgrade from one version of Multi-DB to another, unless otherwise noted, you can simply upload the `db.php` file from the new version to overwrite the old one. 371 | 372 | If you have trouble with any aspect of configuration, or if you have a cool feature suggestion to make, please head on over to the community forums where support staff and other helpful members are waiting to lend a hand. 373 | -------------------------------------------------------------------------------- /db-config-sample-16.php: -------------------------------------------------------------------------------- 1 | 6 |
You must be logged in to access this tool.
7 | 10 |
11 | Incsub DB Tools
12 | ====================================================================
13 | DB List | DB SQL | DB Servers | DB Array | MD5
14 | ====================================================================
15 |
You must be logged in to access this tool.
7 | 10 |
11 | Incsub DB Tools
12 | ====================================================================
13 | DB List | DB SQL | DB Servers | DB Array | MD5
14 | ====================================================================
15 |
You must be logged in to access this tool.
7 | 10 |
11 | Incsub DB Tools
12 | ====================================================================
13 | DB List | DB SQL | DB Servers | DB Array | MD5
14 | ====================================================================
15 |
You must be logged in to access this tool.
7 | 10 |
11 | Incsub DB Tools
12 | ====================================================================
13 | DB List | DB SQL | DB Servers | DB Array | MD5
14 | ====================================================================
15 |
You must be logged in to access this tool.
7 | 10 |
11 | Incsub DB Tools
12 | ====================================================================
13 | DB List | DB SQL | DB Servers | DB Array | MD5
14 | ====================================================================
15 |
This either means that the username and password information in your db-config.php
file is incorrect, you haven't declared a global database or we can't contact the global database server. This could mean your host's database server is down.
If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.
269 | "/*/WP_I18N_DB_CONN_ERROR*/ ), 'db_connect_fail' ); 270 | } 271 | 272 | /** 273 | * Returns global database information 274 | * 275 | * @access private 276 | * @global array $db_servers The array of databases. 277 | * @return boolean|array The global database information on success, otherwise FALSE. 278 | */ 279 | private function _get_global_read() { 280 | global $db_servers; 281 | 282 | if ( is_array( $db_servers['global'] ) ) { 283 | if ( count( $db_servers['global'] ) > 1 ) { 284 | $dc = defined( 'DATACENTER' ) ? DATACENTER : false; 285 | foreach ( $db_servers['global'] as $global ) { 286 | if ( $global['dc'] == $dc && $global['read'] > 0 ) { 287 | return $global; 288 | } 289 | } 290 | 291 | // If still here we can't find a local readable global database so return first readable one 292 | foreach ( $db_servers['global'] as $global ) { 293 | if ( $global['read'] > 0 ) { 294 | return $global; 295 | } 296 | } 297 | 298 | // Nope, none of those either so exit. 299 | return false; 300 | } else { 301 | return $db_servers['global'][0]; 302 | } 303 | } 304 | 305 | return false; 306 | } 307 | 308 | /** 309 | * Real escapes, using mysqli_real_escape_string() or addslashes() 310 | * 311 | * @see mysqli_real_escape_string() 312 | * @see addslashes() 313 | * 314 | * @access private 315 | * @param string $string The string to escape. 316 | * @return string Escaped string. 317 | */ 318 | function _real_escape( $string ) { 319 | if ( is_object( $this->dbhglobal ) ) 320 | return mysqli_real_escape_string( $this->dbhglobal,$string ); 321 | else 322 | return addslashes( $string ); 323 | } 324 | 325 | /** 326 | * Returns global database version number. 327 | * 328 | * @access public 329 | * @return false|string The version number on success, otherwise FALSE. 330 | */ 331 | public function db_version() { 332 | return is_object( $this->dbhglobal ) ? 333 | preg_replace( '/[^0-9.].*/', '', mysqli_get_server_info( $this->dbhglobal ) ) 334 | : false; 335 | } 336 | 337 | /** 338 | * Returns all tables available in the database. 339 | * 340 | * @filter tables_to_repair 341 | * 342 | * @access public 343 | * @param array $tables The initial array of tables. 344 | * @return array The array of database tables. 345 | */ 346 | public function get_all_tables( $tables ) { 347 | $blogs_ids = $this->get_col( "SELECT blog_id FROM {$this->base_prefix}blogs WHERE deleted = 0 AND spam = 0 AND archived = '0'" ); 348 | 349 | foreach ( $blogs_ids as $blog_id ) { 350 | $new_tables = $this->get_col( "SHOW TABLES LIKE '{$this->base_prefix}{$blog_id}_%';" ); 351 | if ( $new_tables && is_array( $new_tables ) && count( $new_tables ) > 0 ) { 352 | $tables = array_merge( $tables, $new_tables ); 353 | } 354 | } 355 | 356 | return $tables; 357 | } 358 | 359 | /** 360 | * Connects to database based on incoming query. 361 | * 362 | * @access public 363 | * @param string $query The query which will be executed. 364 | * @return boolean|resource The database connection resource on success, otherwise FALSE. 365 | */ 366 | public function db_connect( $query = 'SELECT 1' ) { 367 | if ( empty( $query ) ) { 368 | return false; 369 | } 370 | 371 | $dbh = false; 372 | $this->_last_query_data = $query_data = $this->analyze_query( $query ); 373 | $this->last_table = $query_data['table_name']; 374 | $this->last_db_used = $query_data['query_type']; 375 | 376 | $operation = $query_data['query_type'] == 'write' ? 'write' : 'read'; 377 | 378 | // Return a global read database as if already have it connected 379 | if ( $operation == 'read' && $query_data['dataset'] == 'global' && is_object( $this->dbhglobal ) ) { 380 | return $this->dbhglobal; 381 | } 382 | 383 | if ( $query_data['query_type'] == 'write' && defined( 'MASTER_DB_DEAD' ) ) { 384 | die( "We're updating the database, please try back in 5 minutes. If you are posting to your blog please hit the refresh button on your browser in a few minutes to post the data again. It will be posted as soon as the database is back online again." ); 385 | } 386 | 387 | // check if we're already connected. 388 | $dataset_key = "{$query_data['dataset']}.{$operation}"; 389 | if ( isset( $this->dbh_connections[$dataset_key] ) && is_object( $this->dbh_connections[$dataset_key]['connection'] ) ) { 390 | return $this->dbh_connections[$dataset_key]['connection']; 391 | } 392 | 393 | foreach ( self::_get_servers( $query_data['dataset'], $operation ) as $server ) { 394 | if ( ( $dbh = $this->_connect_to_server( $server, $query_data['dataset'], $operation ) ) ) { 395 | return $dbh; 396 | } 397 | } 398 | 399 | $this->_bail_db_connection_error(); 400 | return false; 401 | } 402 | 403 | /** 404 | * Closes MySQL connection to a database. 405 | * 406 | * @access public 407 | * @param string $dbhname The database name to close connection to. 408 | */ 409 | public function disconnect( $dbhname ) { 410 | if ( isset( $this->dbh_connections[$dbhname]['connection'] ) && is_object( $this->dbh_connections[$dbhname]['connection'] ) ) { 411 | mysqli_close( $this->dbh_connections[$dbhname]['connection'] ); 412 | unset( $this->dbh_connections[$dbhname] ); 413 | } 414 | } 415 | 416 | /** 417 | * Sanitizes select query's table names by adding database prefixes. 418 | * 419 | * This function solves the issue when we have JOINs in a select query, 420 | * which connects global tables from global database. For instance: 421 | * 422 | * SELECT * FROM {$wpdb->posts} AS p LEFT JOIN {$wpdb->users} AS u ON u.ID = p.post_author WHERE p.ID = 2; 423 | * 424 | * @since 3.1.2 425 | * @filter query 426 | * 427 | * @access public 428 | * @global array $global_tables The array of globals tables. 429 | * @param string $query The initial query. 430 | * @return string Sanitized query. 431 | */ 432 | public function sanitize_multidb_query_tables( $query ) { 433 | global $global_tables; 434 | 435 | // look through all local tables and add blog database prefix if it has been found 436 | if ( $this->blogid > 1 ) { 437 | 438 | // add whitespace at the end of the query to make our patterns working properly 439 | $query = trim( $query ) . ' '; 440 | 441 | $blog_database = self::_get_servers( self::_get_blog_dataset( $this->blogid ), 'read' ); 442 | if ( empty( $blog_database ) ) { 443 | return $query; 444 | } 445 | 446 | $global = $this->_get_global_read(); 447 | 448 | //if not on same physical mysql server, and same username, we can't do joins 449 | if ( $global[0]['host'] != $blog_database[0]['host'] || $global[0]['user'] != $blog_database[0]['user']) { 450 | return $query; 451 | } 452 | 453 | // don't touch non select queries. 454 | if ( !preg_match( '/^SELECT\s+/is', $query ) ) { 455 | return $query; 456 | } 457 | 458 | if ( ! preg_match( '/^JOIN\s+/is', $query ) ) { 459 | return $query; 460 | } 461 | 462 | $global_prefix = isset( $this->base_prefix ) ? $this->base_prefix : $this->prefix; 463 | 464 | // look through all global tables and add global database prefix if it has been found 465 | foreach ( $global_tables as $table ) { 466 | $query = preg_replace( "/\s{$global_prefix}{$table}(\s|\.|,)/", " {$global['name']}.{$global_prefix}{$table}$1", $query ); 467 | } 468 | 469 | $blog_database = $blog_database[0]['name']; 470 | $query = preg_replace( "/\s{$this->prefix}(.*?)(\s|\.|,|\()/", " {$blog_database}.{$this->prefix}$1$2", $query ); 471 | } 472 | 473 | return trim( $query ); 474 | } 475 | 476 | /** 477 | * Performs a MySQL database query, using current database connection. 478 | * 479 | * @access public 480 | * @param string $query Database query to execute. 481 | * @return int|false Number of rows affected/selected or false on error 482 | */ 483 | public function query( $query ) { 484 | if ( !$this->ready ) { 485 | return false; 486 | } 487 | 488 | // some queries are made before the plugins have been loaded, and thus cannot be filtered with this method 489 | if ( function_exists( 'apply_filters' ) ) { 490 | $query = apply_filters( 'query', $query ); 491 | } 492 | 493 | $return_val = 0; 494 | $this->flush(); 495 | 496 | // use $this->dbh for read ops, and $this->dbhwrite for write ops 497 | // use $this->dbhglobal for gloal table ops 498 | //unset( $dbh ); 499 | 500 | // Test the global is set and if not then set it 501 | $dbh = $this->db_connect( $query ); 502 | if ( !is_object( $dbh ) ) { 503 | $this->_bail_db_connection_error(); 504 | return false; 505 | } 506 | 507 | // Log how the function was called 508 | $this->func_call = '$db->query("' . $query . '")'; 509 | // Keep track of the last query for debug.. 510 | $this->last_query = $query; 511 | 512 | if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { 513 | $this->timer_start(); 514 | } 515 | 516 | $this->result = mysqli_query( $dbh, $query ); 517 | $this->num_queries++; 518 | 519 | if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { 520 | $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() ); 521 | } 522 | 523 | // If there is an error then take note of it.. 524 | if ( is_object( $dbh ) && ( $this->last_error = mysqli_error( $dbh ) ) ) { 525 | $this->print_error( $this->last_error ); 526 | return false; 527 | } 528 | 529 | //Remove carriage returns https://stackoverflow.com/questions/3059091/how-to-remove-carriage-returns-from-output-of-string 530 | $query=trim(preg_replace('~[[:cntrl:]]~', ' ', $query)); 531 | 532 | if ( preg_match( "/^\\s*(insert|delete|update|replace|alter|create|drop|truncate) /i", $query ) ) { 533 | $this->rows_affected = mysqli_affected_rows( $dbh ); 534 | // Take note of the insert_id 535 | if ( preg_match( "/^\\s*(insert|replace) /i", $query ) ) { 536 | $this->insert_id = mysqli_insert_id( $dbh ); 537 | } 538 | // Return number of rows affected 539 | $return_val = $this->rows_affected; 540 | } else { 541 | $i = 0; 542 | $thefield_count=$this->result->field_count; 543 | while ( $i < $thefield_count ) { 544 | $this->col_info[$i] = mysqli_fetch_field( $this->result ); 545 | $i++; 546 | } 547 | 548 | $num_rows = 0; 549 | while ( $row = $this->result->fetch_object() ) { 550 | $this->last_result[$num_rows] = $row; 551 | $num_rows++; 552 | } 553 | 554 | //mysqli_free_result( $this->result ); 555 | 556 | // Log number of rows the query returned and return number of rows selected 557 | $this->num_rows = $num_rows; 558 | $return_val = $num_rows; 559 | } 560 | 561 | return $return_val; 562 | } 563 | 564 | /** 565 | * Returns connection information based on incoming query. 566 | * 567 | * @access public 568 | * @global type $original_table_prefix 569 | * @global type $global_tables 570 | * @param string $query The query string to analyze. 571 | * @return array The connection information array. 572 | */ 573 | public function analyze_query( $query ) { 574 | global $original_table_prefix, $global_tables; 575 | 576 | //Remove carriage returns https://stackoverflow.com/questions/3059091/how-to-remove-carriage-returns-from-output-of-string 577 | $query=trim(preg_replace('~[[:cntrl:]]~', ' ', $query)); 578 | // trim query 579 | $query = rtrim( trim( $query ), ';' ); 580 | // Set initial force local stuff. 581 | $forcelocal = false; 582 | 583 | $maybe = $return = array(); 584 | $table_name = 'unknown'; 585 | if ( preg_match( '/^SELECT.*?\s+FROM\s+`?([0-9,a-z,A-Z$_\.]+)`?\s*/is', $query, $maybe ) ) { 586 | $table_name = $maybe[1]; 587 | } else if ( preg_match( '/^UPDATE IGNORE\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 588 | $table_name = $maybe[1]; 589 | } else if ( preg_match( '/^UPDATE\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 590 | $table_name = $maybe[1]; 591 | } else if ( preg_match( '/^INSERT.*?\s+INTO\s+`?([0-9,a-z,A-Z$_]+)`?[\(\s]/is', $query, $maybe ) ) { 592 | $table_name = $maybe[1]; 593 | } else if ( preg_match( '/^REPLACE.*?\s+INTO\s+`?([0-9,a-z,A-Z$_]+)`?[\(\s]/is', $query, $maybe ) ) { 594 | $table_name = $maybe[1]; 595 | } else if ( preg_match( '/^DELETE.*?\s+FROM\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 596 | $table_name = $maybe[1]; 597 | } else if ( preg_match( '/^(?:TRUNCATE|RENAME|OPTIMIZE|LOCK|UNLOCK)\s+TABLE\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 598 | $table_name = $maybe[1]; 599 | } else if ( preg_match( '/^(?:TRUNCATE|RENAME|OPTIMIZE|LOCK|UNLOCK)\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 600 | $table_name = $maybe[1]; 601 | } else if ( preg_match( '/^SHOW TABLE STATUS (LIKE|FROM) \'?`?([0-9,a-z,A-Z$_]+)\'?`?\s*/is', $query, $maybe ) ) { 602 | $table_name = $maybe[1]; 603 | } else if ( preg_match( '/^SHOW TABLES LIKE \'?`?([0-9,a-z,A-Z$_]+)\'?`?\s*/is', $query, $maybe ) ) { 604 | $table_name = $maybe[1]; 605 | } else if ( preg_match( '/^SHOW TABLES/is', $query, $maybe ) ) { 606 | $forcelocal = true; 607 | } else if ( preg_match( '/^SHOW INDEX FROM `?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 608 | $table_name = $maybe[1]; 609 | } else if ( preg_match( '/^SHOW\s+\w*\s*COLUMNS (?:FROM|IN) `?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 610 | $table_name = $maybe[1]; 611 | } else if ( preg_match( '/^CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\s+`?([0-9,a-z,A-Z$_]+)`?\s+/is', $query, $maybe ) ) { 612 | $table_name = $maybe[1]; 613 | } else if ( preg_match( '/^SHOW CREATE TABLE `?([0-9,a-z,A-Z$_]+?)`?\s*/is', $query, $maybe ) ) { 614 | $table_name = $maybe[1]; 615 | } else if ( preg_match( '/^CREATE\s+TABLE\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 616 | $table_name = $maybe[1]; 617 | } else if ( preg_match( '/^DROP\s+TABLE\s+IF\s+EXISTS\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 618 | $table_name = $maybe[1]; 619 | } else if ( preg_match( '/^DROP\s+TABLE\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 620 | $table_name = $maybe[1]; 621 | } else if ( preg_match( '/^DESCRIBE\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 622 | $table_name = $maybe[1]; 623 | } else if ( preg_match( '/^ALTER\s+TABLE\s+`?([0-9,a-z,A-Z$_]+)`?\s+/is', $query, $maybe ) ) { 624 | $table_name = $maybe[1]; 625 | } else if ( preg_match( '/^CHECK\s+TABLE\s+?([0-9,a-z,A-Z$_]+)?\s*/is', $query, $maybe ) ) { 626 | $table_name = $maybe[1]; 627 | } else if ( preg_match( '/^ANALYZE\s+TABLE\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query, $maybe ) ) { 628 | $table_name = $maybe[1]; 629 | } else { 630 | $select_without_from = preg_match( '/^SELECT\s+/is', $query ) && !preg_match( '/^SELECT.*?\s+FROM\s+`?([0-9,a-z,A-Z$_]+)`?\s*/is', $query ); 631 | $transaction_stuff = preg_match( '/^(START TRANSACTION|BEGIN|COMMIT|ROLLBACK)/is', $query ); 632 | $set = preg_match( '/^SET\s+/is', $query ); 633 | if ( $select_without_from || $transaction_stuff || $set ) { 634 | if ( $this->_last_query_data ) { 635 | return $this->_last_query_data; 636 | } 637 | } 638 | } 639 | 640 | $table_name = explode( '.', $table_name ); 641 | $table_name = array_pop( $table_name ); 642 | 643 | // determine whether global or blog table type is 644 | $blog_id = false; 645 | if ( $forcelocal == true ) { 646 | $table_type = 'blog'; 647 | $blog_id = $this->blogid; 648 | } else { 649 | $base_table_name = substr( $table_name, strlen( $original_table_prefix ) ); 650 | if ( in_array( $base_table_name, $global_tables ) ) { 651 | // This is a global table 652 | $table_type = 'global'; 653 | $blog_id = 'global'; 654 | } else { 655 | // Should be a blog related table 656 | $table_type = 'blog'; 657 | 658 | $base_match = array(); 659 | if ( preg_match( "|^[0-9]{1,20}_?|", $base_table_name, $base_match ) && isset( $base_match[0] ) ) { 660 | $base_table_name = str_replace( $base_match[0], '', $base_table_name ); 661 | } 662 | 663 | if ( preg_match( "|^{$original_table_prefix}([0-9]{1,20})_?{$base_table_name}|", $table_name, $match ) ) { 664 | $blog_id = absint( $match[1] ); 665 | } 666 | } 667 | } 668 | 669 | $query_type = 'read'; 670 | $patterns = array( 671 | '/^UPDATE/is', 672 | '/^INSERT/is', 673 | '/^REPLACE/is', 674 | '/^DELETE/is', 675 | '/^OPTIMIZE/is', 676 | '/^SHOW\s+TABLE\s+STATUS/is', 677 | '/^CREATE\s+TABLE/is', 678 | '/^TRUNCATE\s+TABLE/is', 679 | '/^SHOW\s+CREATE\s+TABLE/is', 680 | '/^DROP\s+TABLE/is', 681 | '/^ALTER\s+TABLE/is', 682 | '/^RENAME\s+TABLE/i', 683 | ); 684 | 685 | foreach ( $patterns as $pattern ) { 686 | if ( preg_match( $pattern, $query ) ) { 687 | $query_type = 'write'; 688 | break; 689 | } 690 | } 691 | 692 | // dataset 693 | if ( $table_type == 'global' ) { 694 | $dataset = 'global'; 695 | } elseif ( $table_type == 'blog' ) { 696 | // check if the blog_id is set. 697 | if ( empty( $blog_id ) ) { 698 | // we are on a multi-site blog without a number, or we have an unidentified global table 699 | $blog_id = 'global'; 700 | $dataset = 'global'; 701 | } else { 702 | $dataset = self::_get_blog_dataset( $blog_id ); 703 | } 704 | } 705 | 706 | $return['table_name'] = $table_name; 707 | $return['table_type'] = $table_type; 708 | $return['blog_id'] = $blog_id; 709 | $return['query_type'] = $query_type; 710 | $return['dataset'] = $dataset; 711 | 712 | return $return; 713 | } 714 | 715 | /** 716 | * Returns blog dataset. 717 | * 718 | * @since 3.2.0 719 | * 720 | * @static 721 | * @access protected 722 | * @param int $blog_id The blog ID. 723 | * @return string The dataset string. 724 | */ 725 | protected static function _get_blog_dataset( $blog_id ) { 726 | global $vip_blogs_datasets; 727 | 728 | //check if this is a VIP blog 729 | if ( isset( $vip_blogs_datasets[ $blog_id ] ) ) { 730 | return $vip_blogs_datasets[ $blog_id ]; 731 | } 732 | 733 | $hash_value = md5( $blog_id ); 734 | if ( defined( 'DB_SCALING' ) ) { 735 | if ( DB_SCALING == 4096 ) { 736 | return substr( $hash_value, 0, 3 ); 737 | } elseif ( DB_SCALING == 256 ) { 738 | return substr( $hash_value, 0, 2 ); 739 | } 740 | } 741 | 742 | return substr( $hash_value, 0, 1 ); 743 | } 744 | 745 | /** 746 | * Returns the array of appropriate servers to connect to. 747 | * 748 | * @since 3.2.0 749 | * 750 | * @static 751 | * @access protected 752 | * @global array $db_servers The array of database servers. 753 | * @param string $dataset The current dataset to use. 754 | * @param string $operation The operation type (read/write). 755 | * @return array The array of servers to connect to. 756 | */ 757 | protected static function _get_servers( $dataset, $operation ) { 758 | global $db_servers; 759 | 760 | $dc = defined( 'DATACENTER' ) ? DATACENTER : false; 761 | 762 | // Group eligible servers by R (plus 10,000 if remote) 763 | $server_groups = array(); 764 | if ( isset( $db_servers[$dataset] ) ) { 765 | foreach ( $db_servers[$dataset] as $server ) { 766 | if ( $server[$operation] ) { 767 | // Add a penality to those dbs not in our datacenter 768 | if ( $server['dc'] != $dc ) { 769 | $server[$operation] += 10000; 770 | } 771 | 772 | // Try the local hostname first when connecting within the DC 773 | if ( $server['dc'] == $dc ) { 774 | $lserver = $server; 775 | if ( isset( $lserver['lhost'] ) ) { 776 | $lserver['host'] = $lserver['lhost']; 777 | } 778 | 779 | $priority = $server[$operation] - 0.5; 780 | $server_groups["{$priority}"][] = $lserver; 781 | } 782 | 783 | $priority = $server[$operation]; 784 | $server_groups["{$priority}"][] = $server; 785 | } 786 | } 787 | } 788 | 789 | // Randomize each group and add its members to 790 | $servers = array(); 791 | ksort( $server_groups ); 792 | foreach ( $server_groups as $group ) { 793 | if ( count( $group ) > 1 ) { 794 | shuffle( $group ); 795 | } 796 | 797 | $servers = array_merge( $servers, $group ); 798 | } 799 | 800 | return $servers; 801 | } 802 | 803 | /** 804 | * Connects to the server. 805 | * 806 | * @since 3.2.0 807 | * 808 | * @access protected 809 | * @param array $server The server configuration info. 810 | * @param string $dataset The current dataset to use. 811 | * @param string $operation The operation type (read/write). 812 | * @return resource|boolean The MySQL connection on success, otherwise FALSE. 813 | */ 814 | protected function _connect_to_server( $server, $dataset, $operation ) { 815 | $dbh = mysqli_connect( $server['host'], $server['user'], $server['password'] ); 816 | if ( !is_object( $dbh ) ) { 817 | return false; 818 | } 819 | 820 | $this->set_charset( $dbh ); 821 | $this->select( $server['name'], $dbh ); 822 | 823 | // save connection 824 | $this->dbh_connections["{$dataset}.{$operation}"] = array( 825 | 'connection' => $dbh, 826 | 'name' => $server['name'], 827 | 'ds' => $server['ds'], 828 | 'dc' => $server['dc'], 829 | 'read' => $server['read'], 830 | 'write' => $server['write'], 831 | 'host' => $server['host'], 832 | 'user' => $server['user'], 833 | 'password' => $server['password'], 834 | 'lhost' => isset( $server['lhost'] ) ? $server['lhost'] : '', 835 | ); 836 | 837 | // disconnect old connection if total number of connections more then allowed one 838 | while ( $this->max_connections > 0 && count( $this->dbh_connections ) > $this->max_connections ) { 839 | reset( $this->dbh_connections ); 840 | $this->disconnect( key( $this->dbh_connections ) ); 841 | } 842 | 843 | return $dbh; 844 | } 845 | 846 | } 847 | 848 | // redefine database connection class 849 | $wpdb = new m_wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ); 850 | 851 | add_filter( 'tables_to_repair', array( $wpdb, 'get_all_tables' ) ); 852 | //add_filter( 'query', array( $wpdb, 'sanitize_multidb_query_tables' ) ); 853 | -------------------------------------------------------------------------------- /fix-db-encoding.php: -------------------------------------------------------------------------------- 1 | 18 | 19 | > 20 | 21 | 22 |define('WP_ALLOW_REPAIR', true);
";
32 | } elseif ( isset($_GET['repair']) ) {
33 | $problems = array();
34 |
35 | if ( 2 == $_GET['repair'] )
36 | $optimize = true;
37 | else
38 | $optimize = false;
39 |
40 | $okay = true;
41 |
42 | $tables = $wpdb->tables();
43 | // Sitecategories may not exist if global terms are disabled.
44 | if ( is_multisite() && ! $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->sitecategories'" ) )
45 | unset( $tables['sitecategories'] );
46 | $tables = array_merge( $tables, (array) apply_filters( 'tables_to_repair', array() ) ); // Return tables with table prefixes.
47 | // Loop over the tables, checking and repairing as needed.
48 | foreach ( $tables as $table ) {
49 | if ($_GET['fix_100']) {
50 | $cols = $wpdb->get_results("SHOW FULL COLUMNS FROM `{$table}` WHERE (Type IN ('CHAR', 'TEXT', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT') OR Type LIKE 'VARCHAR%');");
51 | } else {
52 | $cols = $wpdb->get_results("SHOW FULL COLUMNS FROM `{$table}` WHERE (Type IN ('CHAR', 'TEXT', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT') OR Type LIKE 'VARCHAR%') AND Collation NOT LIKE 'utf8%';");
53 | }
54 |
55 | if (is_array($cols) && count($cols) > 0) {
56 | foreach ($cols as $col) {
57 | if ($_GET['fix_100']) {
58 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} {$col->Type} CHARACTER SET latin1 COLLATE latin1_general_ci;");
59 | }
60 | switch (strtolower($col->Type)) {
61 | case 'longtext':
62 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} longblob;");
63 | break;
64 | case 'mediumtext':
65 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} mediumblob;");
66 | break;
67 | case 'tinytext':
68 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} tinyblob;");
69 | break;
70 | case 'text':
71 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} blob;");
72 | break;
73 | case 'char':
74 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} binary;");
75 | break;
76 | case 'varchar':
77 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} varbinary;");
78 | break;
79 | }
80 | $wpdb->query("ALTER TABLE `{$table}` CHANGE {$col->Field} {$col->Field} {$col->Type};");
81 | }
82 | }
83 |
84 | $wpdb->query("ALTER TABLE `{$table}` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;");
85 |
86 | $check = $wpdb->get_row("CHECK TABLE {$table}");
87 | if ( 'OK' == $check->Msg_text ) {
88 | echo "The $table table is okay."; 89 | } else { 90 | echo "
The $table table is not okay. It is reporting the following error: $check->Msg_text
. WordPress will attempt to repair this table…";
91 | $repair = $wpdb->get_row("REPAIR TABLE $table");
92 | if ( 'OK' == $check->Msg_text ) {
93 | echo "
Successfully repaired the $table table.";
94 | } else {
95 | echo "
Failed to repair the $table table. Error: $check->Msg_text
";
96 | $problems["$table"] = $check->Msg_text;
97 | $okay = false;
98 | }
99 | }
100 | if ( $okay && $optimize ) {
101 | $check = $wpdb->get_row("ANALYZE TABLE $table");
102 | if ( 'Table is already up to date' == $check->Msg_text ) {
103 | echo "
The $table table is already optimized.";
104 | } else {
105 | $check = $wpdb->get_row("OPTIMIZE TABLE $table");
106 | if ( 'OK' == $check->Msg_text || 'Table is already up to date' == $check->Msg_text )
107 | echo "
Successfully optimized the $table table.";
108 | else
109 | echo "
Failed to optimize the $table table. Error: $check->Msg_text";
110 | }
111 | }
112 | echo '
'.__('Some database problems could not be repaired. Please copy-and-paste the following list of errors to the WordPress support forums to get additional assistance.').'
', 'http://wordpress.org/support/forum/3'); 117 | $problem_output = array(); 118 | foreach ( $problems as $table => $problem ) 119 | $problem_output[] = "$table: $problem"; 120 | echo ''; 121 | } else { 122 | echo ''.__('Repairs complete. Please remove the following line from wp-config.php to prevent this page from being used by unauthorized users.')."
define('WP_ALLOW_REPAIR', true);
";
123 | }
124 | } else {
125 | if ( isset($_GET['referrer']) && 'is_blog_installed' == $_GET['referrer'] )
126 | _e('One or more database tables are unavailable. To allow WordPress to attempt to repair these tables, press the “Repair Database” button. Repairing can take a while, so please be patient.');
127 | else
128 | _e('WordPress can automatically look for some common database problems and repair them. Repairing can take a while, so please be patient.')
129 | ?>
130 |
131 |
132 |
133 |
134 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
6 |
7 | Everyone is permitted to copy and distribute verbatim copies
8 | of this license document, but changing it is not allowed.
9 |
10 | Preamble
11 |
12 | The licenses for most software are designed to take away your
13 | freedom to share and change it. By contrast, the GNU General Public
14 | License is intended to guarantee your freedom to share and change free
15 | software--to make sure the software is free for all its users. This
16 | General Public License applies to most of the Free Software
17 | Foundation's software and to any other program whose authors commit to
18 | using it. (Some other Free Software Foundation software is covered by
19 | the GNU Library General Public License instead.) You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | this service if you wish), that you receive source code or can get it
26 | if you want it, that you can change the software or use pieces of it
27 | in new free programs; and that you know you can do these things.
28 |
29 | To protect your rights, we need to make restrictions that forbid
30 | anyone to deny you these rights or to ask you to surrender the rights.
31 | These restrictions translate to certain responsibilities for you if you
32 | distribute copies of the software, or if you modify it.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must give the recipients all the rights that
36 | you have. You must make sure that they, too, receive or can get the
37 | source code. And you must show them these terms so they know their
38 | rights.
39 |
40 | We protect your rights with two steps: (1) copyright the software, and
41 | (2) offer you this license which gives you legal permission to copy,
42 | distribute and/or modify the software.
43 |
44 | Also, for each author's protection and ours, we want to make certain
45 | that everyone understands that there is no warranty for this free
46 | software. If the software is modified by someone else and passed on, we
47 | want its recipients to know that what they have is not the original, so
48 | that any problems introduced by others will not reflect on the original
49 | authors' reputations.
50 |
51 | Finally, any free program is threatened constantly by software
52 | patents. We wish to avoid the danger that redistributors of a free
53 | program will individually obtain patent licenses, in effect making the
54 | program proprietary. To prevent this, we have made it clear that any
55 | patent must be licensed for everyone's free use or not licensed at all.
56 |
57 | The precise terms and conditions for copying, distribution and
58 | modification follow.
59 |
60 | GNU GENERAL PUBLIC LICENSE
61 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
62 |
63 | 0. This License applies to any program or other work which contains
64 | a notice placed by the copyright holder saying it may be distributed
65 | under the terms of this General Public License. The "Program", below,
66 | refers to any such program or work, and a "work based on the Program"
67 | means either the Program or any derivative work under copyright law:
68 | that is to say, a work containing the Program or a portion of it,
69 | either verbatim or with modifications and/or translated into another
70 | language. (Hereinafter, translation is included without limitation in
71 | the term "modification".) Each licensee is addressed as "you".
72 |
73 | Activities other than copying, distribution and modification are not
74 | covered by this License; they are outside its scope. The act of
75 | running the Program is not restricted, and the output from the Program
76 | is covered only if its contents constitute a work based on the
77 | Program (independent of having been made by running the Program).
78 | Whether that is true depends on what the Program does.
79 |
80 | 1. You may copy and distribute verbatim copies of the Program's
81 | source code as you receive it, in any medium, provided that you
82 | conspicuously and appropriately publish on each copy an appropriate
83 | copyright notice and disclaimer of warranty; keep intact all the
84 | notices that refer to this License and to the absence of any warranty;
85 | and give any other recipients of the Program a copy of this License
86 | along with the Program.
87 |
88 | You may charge a fee for the physical act of transferring a copy, and
89 | you may at your option offer warranty protection in exchange for a fee.
90 |
91 | 2. You may modify your copy or copies of the Program or any portion
92 | of it, thus forming a work based on the Program, and copy and
93 | distribute such modifications or work under the terms of Section 1
94 | above, provided that you also meet all of these conditions:
95 |
96 | a) You must cause the modified files to carry prominent notices
97 | stating that you changed the files and the date of any change.
98 |
99 | b) You must cause any work that you distribute or publish, that in
100 | whole or in part contains or is derived from the Program or any
101 | part thereof, to be licensed as a whole at no charge to all third
102 | parties under the terms of this License.
103 |
104 | c) If the modified program normally reads commands interactively
105 | when run, you must cause it, when started running for such
106 | interactive use in the most ordinary way, to print or display an
107 | announcement including an appropriate copyright notice and a
108 | notice that there is no warranty (or else, saying that you provide
109 | a warranty) and that users may redistribute the program under
110 | these conditions, and telling the user how to view a copy of this
111 | License. (Exception: if the Program itself is interactive but
112 | does not normally print such an announcement, your work based on
113 | the Program is not required to print an announcement.)
114 |
115 | These requirements apply to the modified work as a whole. If
116 | identifiable sections of that work are not derived from the Program,
117 | and can be reasonably considered independent and separate works in
118 | themselves, then this License, and its terms, do not apply to those
119 | sections when you distribute them as separate works. But when you
120 | distribute the same sections as part of a whole which is a work based
121 | on the Program, the distribution of the whole must be on the terms of
122 | this License, whose permissions for other licensees extend to the
123 | entire whole, and thus to each and every part regardless of who wrote it.
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 |
--------------------------------------------------------------------------------
/move-blogs-rollback.php:
--------------------------------------------------------------------------------
1 |
48 |
49 |
50 |
62 |
|
75 | |||
DB name | 78 |table name | 79 |target db | 80 |status | 81 |
" . $current_db_name . " | "; 119 | echo "{$row[0]} | "; 120 | echo "{$target_dbname} {$testpass} | {$tabletest} |
82 |
|
95 | |||
table name | 98 |table info | 99 |new db | 100 |status | 101 |
{$row[0]} | "; 131 | echo is_numeric( $blogid ) ? 'blog ' . $blogid : 'global'; 132 | echo " | {$this_blog_new_db} {$testpass} | {$tabletest} |