qa-db-install.php 65.5 KB
Newer Older
Gideon Greenspan committed
1 2 3 4 5 6 7
<?php

/*
	Question2Answer (c) Gideon Greenspan

	http://www.question2answer.org/

Scott Vivian committed
8

Gideon Greenspan committed
9 10 11 12 13 14 15 16 17
	File: qa-include/qa-db-install.php
	Version: See define()s at top of qa-include/qa-base.php
	Description: Database-level functions for installation and upgrading


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.
Scott Vivian committed
18

Gideon Greenspan committed
19 20 21 22 23 24 25 26 27 28 29 30 31
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

	if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
		header('Location: ../');
		exit;
	}

32
	define('QA_DB_VERSION_CURRENT', 57);
Gideon Greenspan committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52


	function qa_db_user_column_type_verify()
/*
	Return the column type for user ids after verifying it is one of the legal options
*/
	{
		$coltype=strtoupper(qa_get_mysql_user_column_type());

		switch ($coltype) {
			case 'SMALLINT':
			case 'MEDIUMINT':
			case 'INT':
			case 'BIGINT':
			case 'SMALLINT UNSIGNED':
			case 'MEDIUMINT UNSIGNED':
			case 'INT UNSIGNED':
			case 'BIGINT UNSIGNED':
				// these are all OK
				break;
Scott Vivian committed
53

Gideon Greenspan committed
54 55 56 57 58
			default:
				if (!preg_match('/VARCHAR\([0-9]+\)/', $coltype))
					qa_fatal_error('Specified user column type is not one of allowed values - please read documentation');
				break;
		}
Scott Vivian committed
59

Gideon Greenspan committed
60 61 62
		return $coltype;
	}

Scott Vivian committed
63

Gideon Greenspan committed
64 65 66 67 68 69
	function qa_db_table_definitions()
/*
	Return an array of table definitions. For each element of the array, the key is the table name (without prefix)
	and the value is an array of column definitions, [column name] => [definition]. The column name is omitted for indexes.
*/
	{
Gideon Greenspan committed
70
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
71

Gideon Greenspan committed
72 73 74 75 76 77 78 79
		require_once QA_INCLUDE_DIR.'qa-db-maxima.php';
		require_once QA_INCLUDE_DIR.'qa-app-users.php';

	/*
		Important note on character encoding in database and PHP connection to MySQL

		[this note is no longer relevant since we *do* explicitly set the connection character set since Q2A 1.5 - see qa-db.php
	*/
Scott Vivian committed
80

Gideon Greenspan committed
81 82
	/*
		Other notes on the definitions below
Scott Vivian committed
83

Gideon Greenspan committed
84
		* In MySQL versions prior to 5.0.3, VARCHAR(x) columns will be silently converted to TEXT where x>255
Scott Vivian committed
85

Gideon Greenspan committed
86
		* See box at top of qa-app-recalc.php for a list of redundant (non-normal) information in the database
Scott Vivian committed
87

Gideon Greenspan committed
88 89 90 91
		* Starting in version 1.2, we explicitly name keys and foreign key constraints, instead of allowing MySQL
		  to name these by default. Our chosen names match the default names that MySQL would have assigned, and
		  indeed *did* assign for people who installed an earlier version of Q2A. By naming them explicitly, we're
		  on more solid ground for possible future changes to indexes and foreign keys in the schema.
Scott Vivian committed
92

Gideon Greenspan committed
93 94
		* There are other foreign key constraints that it would be valid to add, but that would not serve much
		  purpose in terms of preventing inconsistent data being retrieved, and would just slow down some queries.
Scott Vivian committed
95

Gideon Greenspan committed
96 97 98 99
		* We name some columns here in a not entirely intuitive way. The reason is to match the names of columns in
		  other tables which are of a similar nature. This will save time and space when combining several SELECT
		  queries together via a UNION in qa_db_multi_select() - see comments in qa-db.php for more information.
	*/
Scott Vivian committed
100

Gideon Greenspan committed
101 102 103 104 105 106 107 108 109 110 111 112
		$useridcoltype=qa_db_user_column_type_verify();

		$tables=array(
			'users' => array(
				'userid' => $useridcoltype.' NOT NULL AUTO_INCREMENT',
				'created' => 'DATETIME NOT NULL',
				'createip' => 'INT UNSIGNED NOT NULL', // INET_ATON of IP address when created
				'email' => 'VARCHAR('.QA_DB_MAX_EMAIL_LENGTH.') NOT NULL',
				'handle' => 'VARCHAR('.QA_DB_MAX_HANDLE_LENGTH.') NOT NULL', // username
				'avatarblobid' => 'BIGINT UNSIGNED', // blobid of stored avatar
				'avatarwidth' => 'SMALLINT UNSIGNED', // pixel width of stored avatar
				'avatarheight' => 'SMALLINT UNSIGNED', // pixel height of stored avatar
Gideon Greenspan committed
113
				'passsalt' => 'BINARY(16)', // salt used to calculate passcheck - null if no password set for direct login
Gideon Greenspan committed
114 115 116 117 118 119 120 121 122
				'passcheck' => 'BINARY(20)', // checksum from password and passsalt - null if no passowrd set for direct login
				'level' => 'TINYINT UNSIGNED NOT NULL', // basic, editor, admin, etc...
				'loggedin' => 'DATETIME NOT NULL', // time of last login
				'loginip' => 'INT UNSIGNED NOT NULL', // INET_ATON of IP address of last login
				'written' => 'DATETIME', // time of last write action done by user
				'writeip' => 'INT UNSIGNED', // INET_ATON of IP address of last write action done by user
				'emailcode' => 'CHAR(8) CHARACTER SET ascii NOT NULL DEFAULT \'\'', // for email confirmation or password reset
				'sessioncode' => 'CHAR(8) CHARACTER SET ascii NOT NULL DEFAULT \'\'', // for comparing against session cookie in browser
				'sessionsource' => 'VARCHAR (16) CHARACTER SET ascii DEFAULT \'\'', // e.g. facebook, openid, etc...
Gideon Greenspan committed
123
				'flags' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', // see constants at top of qa-app-users.php
Scott Vivian committed
124
				'wallposts' => 'MEDIUMINT NOT NULL DEFAULT 0', // cached count of wall posts
Gideon Greenspan committed
125 126 127 128
				'PRIMARY KEY (userid)',
				'KEY email (email)',
				'KEY handle (handle)',
				'KEY level (level)',
Gideon Greenspan committed
129
				'kEY created (created, level, flags)',
Gideon Greenspan committed
130
			),
Scott Vivian committed
131

Gideon Greenspan committed
132 133 134 135 136 137 138 139
			'userlogins' => array(
				'userid' => $useridcoltype.' NOT NULL',
				'source' => 'VARCHAR (16) CHARACTER SET ascii NOT NULL', // e.g. facebook, openid, etc...
				'identifier' => 'VARBINARY (1024) NOT NULL', // depends on source, e.g. Facebook uid or OpenID url
				'identifiermd5' => 'BINARY (16) NOT NULL', // used to reduce size of index on identifier
				'KEY source (source, identifiermd5)',
				'KEY userid (userid)',
			),
Scott Vivian committed
140

Gideon Greenspan committed
141 142 143 144 145 146 147 148
			'userlevels' => array(
				'userid' => $useridcoltype.' NOT NULL', // the user who has this level
				'entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", // see qa-app-updates.php
				'entityid' => 'INT UNSIGNED NOT NULL', // relevant postid / userid / tag wordid / categoryid
				'level' => 'TINYINT UNSIGNED', // if not NULL, special permission level for that user and that entity
				'UNIQUE userid (userid, entitytype, entityid)',
				'KEY entitytype (entitytype, entityid)',
			),
Scott Vivian committed
149

Gideon Greenspan committed
150 151 152 153 154 155
			'userprofile' => array(
				'userid' => $useridcoltype.' NOT NULL',
				'title' => 'VARCHAR('.QA_DB_MAX_PROFILE_TITLE_LENGTH.') NOT NULL', // profile field name
				'content' => 'VARCHAR('.QA_DB_MAX_PROFILE_CONTENT_LENGTH.') NOT NULL', // profile field value
				'UNIQUE userid (userid,title)',
			),
Scott Vivian committed
156

Gideon Greenspan committed
157 158 159
			'userfields' => array(
				'fieldid' => 'SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT',
				'title' => 'VARCHAR('.QA_DB_MAX_PROFILE_TITLE_LENGTH.') NOT NULL', // to match title column in userprofile table
Gideon Greenspan committed
160
				'content' => 'VARCHAR('.QA_DB_MAX_PROFILE_TITLE_LENGTH.')', // label for display on user profile pages - NULL means use default
Gideon Greenspan committed
161
				'position' => 'SMALLINT UNSIGNED NOT NULL',
Gideon Greenspan committed
162 163
				'flags' => 'TINYINT UNSIGNED NOT NULL', // QA_FIELD_FLAGS_* at top of qa-app-users.php
				'permit' => 'TINYINT UNSIGNED', // minimum user level required to view (uses QA_PERMIT_* constants), null means no restriction
Gideon Greenspan committed
164 165
				'PRIMARY KEY (fieldid)',
			),
Scott Vivian committed
166

Gideon Greenspan committed
167 168
			'messages' => array(
				'messageid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT',
Gideon Greenspan committed
169
				'type' => "ENUM('PUBLIC', 'PRIVATE') NOT NULL DEFAULT 'PRIVATE'",
Gideon Greenspan committed
170 171
				'fromuserid' => $useridcoltype.' NOT NULL',
				'touserid' => $useridcoltype.' NOT NULL',
172 173
				'fromhidden' => 'TINYINT(1) UNSIGNED NOT NULL DEFAULT 0',
				'tohidden' => 'TINYINT(1) UNSIGNED NOT NULL DEFAULT 0',
Gideon Greenspan committed
174 175 176 177
				'content' => 'VARCHAR('.QA_DB_MAX_CONTENT_LENGTH.') NOT NULL',
				'format' => 'VARCHAR('.QA_DB_MAX_FORMAT_LENGTH.') CHARACTER SET ascii NOT NULL',
				'created' => 'DATETIME NOT NULL',
				'PRIMARY KEY (messageid)',
Gideon Greenspan committed
178 179
				'KEY type (type, fromuserid, touserid, created)',
				'KEY touserid (touserid, type, created)',
180
				'KEY fromhidden (fromhidden, tohidden)',
Gideon Greenspan committed
181
			),
Scott Vivian committed
182

Gideon Greenspan committed
183 184 185 186 187 188 189 190 191
			'userfavorites' => array(
				'userid' => $useridcoltype.' NOT NULL', // the user who favorited the entity
				'entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", // see qa-app-updates.php
				'entityid' => 'INT UNSIGNED NOT NULL', // favorited postid / userid / tag wordid / categoryid
				'nouserevents' => 'TINYINT UNSIGNED NOT NULL', // do we skip writing events to the user stream?
				'PRIMARY KEY (userid, entitytype, entityid)',
				'KEY userid (userid, nouserevents)',
				'KEY entitytype (entitytype, entityid, nouserevents)',
			),
Scott Vivian committed
192

Gideon Greenspan committed
193 194 195 196 197 198 199 200 201 202
			'usernotices' => array(
				'noticeid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT',
				'userid' => $useridcoltype.' NOT NULL', // the user to whom the notice is directed
				'content' => 'VARCHAR('.QA_DB_MAX_CONTENT_LENGTH.') NOT NULL',
				'format' => 'VARCHAR('.QA_DB_MAX_FORMAT_LENGTH.') CHARACTER SET ascii NOT NULL',
				'tags' => 'VARCHAR('.QA_DB_MAX_CAT_PAGE_TAGS_LENGTH.')', // any additional information for a plugin to access
				'created' => 'DATETIME NOT NULL',
				'PRIMARY KEY (noticeid)',
				'KEY userid (userid, created)',
			),
Scott Vivian committed
203

Gideon Greenspan committed
204 205 206 207 208 209 210 211 212 213 214 215
			'userevents' => array(
				'userid' => $useridcoltype.' NOT NULL', // the user to be informed about this event in their updates
				'entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", // see qa-app-updates.php
				'entityid' => 'INT UNSIGNED NOT NULL', // favorited source of event - see userfavorites table - 0 means not from a favorite
				'questionid' => 'INT UNSIGNED NOT NULL', // the affected question
				'lastpostid' => 'INT UNSIGNED NOT NULL', // what part of question was affected
				'updatetype' => 'CHAR(1) CHARACTER SET ascii', // what was done to this part - see qa-app-updates.php
				'lastuserid' => $useridcoltype, // which user (if any) did this action
				'updated' => 'DATETIME NOT NULL', // when the event happened
				'KEY userid (userid, updated)', // for truncation
				'KEY questionid (questionid, userid)', // to limit number of events per question per stream
			),
Scott Vivian committed
216

Gideon Greenspan committed
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
			'sharedevents' => array(
				'entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", // see qa-app-updates.php
				'entityid' => 'INT UNSIGNED NOT NULL', // see userfavorites table
				'questionid' => 'INT UNSIGNED NOT NULL', // see userevents table
				'lastpostid' => 'INT UNSIGNED NOT NULL', // see userevents table
				'updatetype' => 'CHAR(1) CHARACTER SET ascii', // see userevents table
				'lastuserid' => $useridcoltype, // see userevents table
				'updated' => 'DATETIME NOT NULL', // see userevents table
				'KEY entitytype (entitytype, entityid, updated)', // for truncation
				'KEY questionid (questionid, entitytype, entityid)', // to limit number of events per question per stream
			),

			'cookies' => array(
				'cookieid' => 'BIGINT UNSIGNED NOT NULL',
				'created' => 'DATETIME NOT NULL',
				'createip' => 'INT UNSIGNED NOT NULL', // INET_ATON of IP address when cookie created
				'written' => 'DATETIME', // time of last write action done by anon user with cookie
				'writeip' => 'INT UNSIGNED', // INET_ATON of IP address of last write action done by anon user with cookie
				'PRIMARY KEY (cookieid)',
			),
Scott Vivian committed
237

Gideon Greenspan committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
			'categories' => array(
				'categoryid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT',
				'parentid' => 'INT UNSIGNED',
				'title' => 'VARCHAR('.QA_DB_MAX_CAT_PAGE_TITLE_LENGTH.') NOT NULL', // category name
				'tags' => 'VARCHAR('.QA_DB_MAX_CAT_PAGE_TAGS_LENGTH.') NOT NULL', // slug (url fragment) used to identify category
				'content' => 'VARCHAR('.QA_DB_MAX_CAT_CONTENT_LENGTH.') NOT NULL DEFAULT \'\'', // description of category
				'qcount' => 'INT UNSIGNED NOT NULL DEFAULT 0',
				'position' => 'SMALLINT UNSIGNED NOT NULL',
				'backpath' => 'VARCHAR('.(QA_CATEGORY_DEPTH*(QA_DB_MAX_CAT_PAGE_TAGS_LENGTH+1)).') NOT NULL DEFAULT \'\'',
					// full slug path for category, with forward slash separators, in reverse order to make index from effective
				'PRIMARY KEY (categoryid)',
				'UNIQUE parentid (parentid, tags)',
				'UNIQUE parentid_2 (parentid, position)',
				'KEY backpath (backpath('.QA_DB_MAX_CAT_PAGE_TAGS_LENGTH.'))',
			),
Scott Vivian committed
253

Gideon Greenspan committed
254 255 256 257 258 259
			'pages' => array(
				'pageid' => 'SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT',
				'title' => 'VARCHAR('.QA_DB_MAX_CAT_PAGE_TITLE_LENGTH.') NOT NULL', // title for navigation
				'nav' => 'CHAR(1) CHARACTER SET ascii NOT NULL', // which navigation does it go in (M=main, F=footer, B=before main, O=opposite main, other=none)
				'position' => 'SMALLINT UNSIGNED NOT NULL', // global ordering, which allows links to be ordered within each nav area
				'flags' => 'TINYINT UNSIGNED NOT NULL', // local or external, open in new window?
Gideon Greenspan committed
260
				'permit' => 'TINYINT UNSIGNED', // is there a minimum user level required for it (uses QA_PERMIT_* constants), null means no restriction
Gideon Greenspan committed
261
				'tags' => 'VARCHAR('.QA_DB_MAX_CAT_PAGE_TAGS_LENGTH.') NOT NULL', // slug (url fragment) for page, or url for external pages
Gideon Greenspan committed
262
				'heading' => 'VARCHAR('.QA_DB_MAX_TITLE_LENGTH.')', // for display within <h1> tags
Gideon Greenspan committed
263 264
				'content' => 'MEDIUMTEXT', // remainder of page HTML
				'PRIMARY KEY (pageid)',
Gideon Greenspan committed
265
				'KEY tags (tags)',
Gideon Greenspan committed
266 267
				'UNIQUE position (position)',
			),
Scott Vivian committed
268

Gideon Greenspan committed
269 270 271 272 273 274 275 276 277 278 279 280
			'widgets' => array(
				'widgetid' => 'SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT',
				'place' => 'CHAR(2) CHARACTER SET ascii NOT NULL',
					// full region: FT=very top of page, FH=below nav area, FL=above footer, FB = very bottom of page
					// side region: ST=top of side, SH=below sidebar, SL=below categories, SB=very bottom of side
					// main region: MT=top of main, MH=below page title, ML=above links, MB=very bottom of main region
				'position' => 'SMALLINT UNSIGNED NOT NULL', // global ordering, which allows widgets to be ordered within each place
				'tags' => 'VARCHAR('.QA_DB_MAX_WIDGET_TAGS_LENGTH.') CHARACTER SET ascii NOT NULL', // comma-separated list of templates to display on
				'title' => 'VARCHAR('.QA_DB_MAX_WIDGET_TITLE_LENGTH.') NOT NULL', // name of widget module that should be displayed
				'PRIMARY KEY (widgetid)',
				'UNIQUE position (position)',
			),
Scott Vivian committed
281

Gideon Greenspan committed
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
			'posts' => array(
				'postid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT',
				'type' => "ENUM('Q', 'A', 'C', 'Q_HIDDEN', 'A_HIDDEN', 'C_HIDDEN', 'Q_QUEUED', 'A_QUEUED', 'C_QUEUED', 'NOTE') NOT NULL",
				'parentid' => 'INT UNSIGNED', // for follow on questions, all answers and comments
				'categoryid' => 'INT UNSIGNED', // this is the canonical final category id
				'catidpath1' => 'INT UNSIGNED', // the catidpath* columns are calculated from categoryid, for the full hierarchy of that category
				'catidpath2' => 'INT UNSIGNED', // note that QA_CATEGORY_DEPTH=4
				'catidpath3' => 'INT UNSIGNED',
				'acount' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', // number of answers (for questions)
				'amaxvote' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', // highest netvotes of child answers (for questions)
				'selchildid' => 'INT UNSIGNED', // selected answer (for questions)
				'closedbyid' => 'INT UNSIGNED', // not null means question is closed
					// if closed due to being a duplicate, this is the postid of that other question
					// if closed for another reason, that reason should be added as a comment on the question, and this field is the comment's id
				'userid' => $useridcoltype, // which user wrote it
				'cookieid' => 'BIGINT UNSIGNED', // which cookie wrote it, if an anonymous post
				'createip' => 'INT UNSIGNED', // INET_ATON of IP address used to create the post
				'lastuserid' => $useridcoltype, // which user last modified it
				'lastip' => 'INT UNSIGNED', // INET_ATON of IP address which last modified the post
				'upvotes' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0',
				'downvotes' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0',
				'netvotes' => 'SMALLINT NOT NULL DEFAULT 0',
				'lastviewip' => 'INT UNSIGNED', // INET_ATON of IP address which last viewed the post
				'views' => 'INT UNSIGNED NOT NULL DEFAULT 0',
				'hotness' => 'FLOAT',
				'flagcount' => 'TINYINT UNSIGNED NOT NULL DEFAULT 0',
				'format' => 'VARCHAR('.QA_DB_MAX_FORMAT_LENGTH.') CHARACTER SET ascii NOT NULL DEFAULT \'\'', // format of content, e.g. 'html'
				'created' => 'DATETIME NOT NULL',
				'updated' => 'DATETIME', // time of last update
				'updatetype' => 'CHAR(1) CHARACTER SET ascii', // see qa-app-updates.php
				'title' => 'VARCHAR('.QA_DB_MAX_TITLE_LENGTH.')',
				'content' => 'VARCHAR('.QA_DB_MAX_CONTENT_LENGTH.')',
				'tags' => 'VARCHAR('.QA_DB_MAX_TAGS_LENGTH.')', // string of tags separated by commas
Gideon Greenspan committed
315
				'name' => 'VARCHAR('.QA_DB_MAX_NAME_LENGTH.')', // name of author if post anonymonus
Gideon Greenspan committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
				'notify' => 'VARCHAR('.QA_DB_MAX_EMAIL_LENGTH.')', // email address, or @ to get from user, or NULL for none
				'PRIMARY KEY (postid)',
				'KEY type (type, created)', // for getting recent questions, answers, comments
				'KEY type_2 (type, acount, created)', // for getting unanswered questions
				'KEY type_4 (type, netvotes, created)', // for getting posts with the most votes
				'KEY type_5 (type, views, created)', // for getting questions with the most views
				'KEY type_6 (type, hotness)', // for getting 'hot' questions
				'KEY type_7 (type, amaxvote, created)', // for getting questions with no upvoted answers
				'KEY parentid (parentid, type)', // for getting a question's answers, any post's comments and follow-on questions
				'KEY userid (userid, type, created)', // for recent questions, answers or comments by a user
				'KEY selchildid (selchildid, type, created)', // for counting how many of a user's answers have been selected, unselected qs
				'KEY closedbyid (closedbyid)', // for the foreign key constraint
				'KEY catidpath1 (catidpath1, type, created)', // for getting question, answers or comments in a specific level category
				'KEY catidpath2 (catidpath2, type, created)', // note that QA_CATEGORY_DEPTH=4
				'KEY catidpath3 (catidpath3, type, created)',
				'KEY categoryid (categoryid, type, created)', // this can also be used for searching the equivalent of catidpath4
				'KEY createip (createip, created)', // for getting posts created by a specific IP address
				'KEY updated (updated, type)', // for getting recent edits across all categories
				'KEY flagcount (flagcount, created, type)', // for getting posts with the most flags
				'KEY catidpath1_2 (catidpath1, updated, type)', // for getting recent edits in a specific level category
				'KEY catidpath2_2 (catidpath2, updated, type)', // note that QA_CATEGORY_DEPTH=4
				'KEY catidpath3_2 (catidpath3, updated, type)',
				'KEY categoryid_2 (categoryid, updated, type)',
				'KEY lastuserid (lastuserid, updated, type)', // for getting posts edited by a specific user
				'KEY lastip (lastip, updated, type)', // for getting posts edited by a specific IP address
				'CONSTRAINT ^posts_ibfk_2 FOREIGN KEY (parentid) REFERENCES ^posts(postid)', // ^posts_ibfk_1 is set later on userid
				'CONSTRAINT ^posts_ibfk_3 FOREIGN KEY (categoryid) REFERENCES ^categories(categoryid) ON DELETE SET NULL',
				'CONSTRAINT ^posts_ibfk_4 FOREIGN KEY (closedbyid) REFERENCES ^posts(postid)',
			),
Scott Vivian committed
345

Gideon Greenspan committed
346 347 348
			'blobs' => array(
				'blobid' => 'BIGINT UNSIGNED NOT NULL',
				'format' => 'VARCHAR('.QA_DB_MAX_FORMAT_LENGTH.') CHARACTER SET ascii NOT NULL', // format e.g. 'jpeg', 'gif', 'png'
Gideon Greenspan committed
349
				'content' => 'MEDIUMBLOB', // null means it's stored on disk in QA_BLOBS_DIRECTORY
Gideon Greenspan committed
350 351 352 353 354 355 356
				'filename' => 'VARCHAR('.QA_DB_MAX_BLOB_FILE_NAME_LENGTH.')', // name of source file (if appropriate)
				'userid' => $useridcoltype, // which user created it
				'cookieid' => 'BIGINT UNSIGNED', // which cookie created it
				'createip' => 'INT UNSIGNED', // INET_ATON of IP address that created it
				'created' => 'DATETIME', // when it was created
				'PRIMARY KEY (blobid)',
			),
Scott Vivian committed
357

Gideon Greenspan committed
358 359 360 361 362 363 364 365 366 367 368
			'words' => array(
				'wordid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT',
				'word' => 'VARCHAR('.QA_DB_MAX_WORD_LENGTH.') NOT NULL',
				'titlecount' => 'INT UNSIGNED NOT NULL DEFAULT 0', // only counts one per post
				'contentcount' => 'INT UNSIGNED NOT NULL DEFAULT 0', // only counts one per post
				'tagwordcount' => 'INT UNSIGNED NOT NULL DEFAULT 0', // for words in tags - only counts one per post
				'tagcount' => 'INT UNSIGNED NOT NULL DEFAULT 0', // for tags as a whole - only counts one per post (though no duplicate tags anyway)
				'PRIMARY KEY (wordid)',
				'KEY word (word)',
				'KEY tagcount (tagcount)', // for sorting by most popular tags
			),
Scott Vivian committed
369

Gideon Greenspan committed
370 371 372 373 374 375 376 377
			'titlewords' => array(
				'postid' => 'INT UNSIGNED NOT NULL',
				'wordid' => 'INT UNSIGNED NOT NULL',
				'KEY postid (postid)',
				'KEY wordid (wordid)',
				'CONSTRAINT ^titlewords_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
				'CONSTRAINT ^titlewords_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)',
			),
Scott Vivian committed
378

Gideon Greenspan committed
379 380 381 382 383 384 385 386 387 388 389
			'contentwords' => array(
				'postid' => 'INT UNSIGNED NOT NULL',
				'wordid' => 'INT UNSIGNED NOT NULL',
				'count' => 'TINYINT UNSIGNED NOT NULL', // how many times word appears in the post - anything over 255 can be ignored
				'type' => "ENUM('Q', 'A', 'C', 'NOTE') NOT NULL", // the post's type (copied here for quick searching)
				'questionid' => 'INT UNSIGNED NOT NULL', // the id of the post's antecedent parent (here for quick searching)
				'KEY postid (postid)',
				'KEY wordid (wordid)',
				'CONSTRAINT ^contentwords_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
				'CONSTRAINT ^contentwords_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)',
			),
Scott Vivian committed
390

Gideon Greenspan committed
391 392 393 394 395 396 397 398
			'tagwords' => array(
				'postid' => 'INT UNSIGNED NOT NULL',
				'wordid' => 'INT UNSIGNED NOT NULL',
				'KEY postid (postid)',
				'KEY wordid (wordid)',
				'CONSTRAINT ^tagwords_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
				'CONSTRAINT ^tagwords_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)',
			),
Scott Vivian committed
399

Gideon Greenspan committed
400 401 402 403 404 405 406 407 408
			'posttags' => array(
				'postid' => 'INT UNSIGNED NOT NULL',
				'wordid' => 'INT UNSIGNED NOT NULL',
				'postcreated' => 'DATETIME NOT NULL', // created time of post (copied here for tag page's list of recent questions)
				'KEY postid (postid)',
				'KEY wordid (wordid,postcreated)',
				'CONSTRAINT ^posttags_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
				'CONSTRAINT ^posttags_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)',
			),
Scott Vivian committed
409

Gideon Greenspan committed
410 411 412 413 414 415 416 417 418
			'uservotes' => array(
				'postid' => 'INT UNSIGNED NOT NULL',
				'userid' => $useridcoltype.' NOT NULL',
				'vote' => 'TINYINT NOT NULL', // -1, 0 or 1
				'flag' => 'TINYINT NOT NULL', // 0 or 1
				'UNIQUE userid (userid, postid)',
				'KEY postid (postid)',
				'CONSTRAINT ^uservotes_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
			),
Scott Vivian committed
419

Gideon Greenspan committed
420
			// many userpoints columns could be unsigned but MySQL appears to mess up points calculations that go negative as a result
Scott Vivian committed
421

Gideon Greenspan committed
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
			'userpoints' => array(
				'userid' => $useridcoltype.' NOT NULL',
				'points' => 'INT NOT NULL DEFAULT 0', // user's points as displayed, after final multiple
				'qposts' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of questions by user (excluding hidden/queued)
				'aposts' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of answers by user (excluding hidden/queued)
				'cposts' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of comments by user (excluding hidden/queued)
				'aselects' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of questions by user where they've selected an answer
				'aselecteds' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of answers by user that have been selected as the best
				'qupvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of questions the user has voted up
				'qdownvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of questions the user has voted down
				'aupvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of answers the user has voted up
				'adownvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', // number of answers the user has voted down
				'qvoteds' => 'INT NOT NULL DEFAULT 0', // points from votes on this user's questions (applying per-question limits), before final multiple
				'avoteds' => 'INT NOT NULL DEFAULT 0', // points from votes on this user's answers (applying per-answer limits), before final multiple
				'upvoteds' => 'INT NOT NULL DEFAULT 0', // number of up votes received on this user's questions or answers
				'downvoteds' => 'INT NOT NULL DEFAULT 0', // number of down votes received on this user's questions or answers
				'bonus' => 'INT NOT NULL DEFAULT 0', // bonus assigned by administrator to a user
				'PRIMARY KEY (userid)',
				'KEY points (points)',
			),
Scott Vivian committed
442

Gideon Greenspan committed
443 444 445 446 447 448 449
			'userlimits' => array(
				'userid' => $useridcoltype.' NOT NULL',
				'action' => 'CHAR(1) CHARACTER SET ascii NOT NULL', // see constants at top of qa-app-limits.php
				'period' => 'INT UNSIGNED NOT NULL', // integer representing hour of last action
				'count' => 'SMALLINT UNSIGNED NOT NULL', // how many of this action has been performed within that hour
				'UNIQUE userid (userid, action)',
			),
Scott Vivian committed
450

Gideon Greenspan committed
451
			// most columns in iplimits have the same meaning as those in userlimits
Scott Vivian committed
452

Gideon Greenspan committed
453 454 455 456 457 458 459
			'iplimits' => array(
				'ip' => 'INT UNSIGNED NOT NULL', // INET_ATON of IP address
				'action' => 'CHAR(1) CHARACTER SET ascii NOT NULL',
				'period' => 'INT UNSIGNED NOT NULL',
				'count' => 'SMALLINT UNSIGNED NOT NULL',
				'UNIQUE ip (ip, action)',
			),
Scott Vivian committed
460

Gideon Greenspan committed
461 462 463 464 465
			'options' => array(
				'title' => 'VARCHAR('.QA_DB_MAX_OPTION_TITLE_LENGTH.') NOT NULL', // name of option
				'content' => 'VARCHAR('.QA_DB_MAX_CONTENT_LENGTH.') NOT NULL', // value of option
				'PRIMARY KEY (title)',
			),
Scott Vivian committed
466

Gideon Greenspan committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
			'cache' => array(
				'type' => 'CHAR(8) CHARACTER SET ascii NOT NULL', // e.g. 'avXXX' for avatar sized to XXX pixels square
				'cacheid' => 'BIGINT UNSIGNED DEFAULT 0', // optional further identifier, e.g. blobid on which cache entry is based
				'content' => 'MEDIUMBLOB NOT NULL',
				'created' => 'DATETIME NOT NULL',
				'lastread' => 'DATETIME NOT NULL',
				'PRIMARY KEY (type,cacheid)',
				'KEY (lastread)',
			),

			'usermetas' => array(
				'userid' => $useridcoltype.' NOT NULL',
				'title' => 'VARCHAR('.QA_DB_MAX_META_TITLE_LENGTH.') NOT NULL',
				'content' => 'VARCHAR('.QA_DB_MAX_META_CONTENT_LENGTH.') NOT NULL',
				'PRIMARY KEY (userid, title)',
			),
Scott Vivian committed
483

Gideon Greenspan committed
484 485 486 487 488 489 490
			'postmetas' => array(
				'postid' => 'INT UNSIGNED NOT NULL',
				'title' => 'VARCHAR('.QA_DB_MAX_META_TITLE_LENGTH.') NOT NULL',
				'content' => 'VARCHAR('.QA_DB_MAX_META_CONTENT_LENGTH.') NOT NULL',
				'PRIMARY KEY (postid, title)',
				'CONSTRAINT ^postmetas_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
			),
Scott Vivian committed
491

Gideon Greenspan committed
492 493 494 495 496 497 498
			'categorymetas' => array(
				'categoryid' => 'INT UNSIGNED NOT NULL',
				'title' => 'VARCHAR('.QA_DB_MAX_META_TITLE_LENGTH.') NOT NULL',
				'content' => 'VARCHAR('.QA_DB_MAX_META_CONTENT_LENGTH.') NOT NULL',
				'PRIMARY KEY (categoryid, title)',
				'CONSTRAINT ^categorymetas_ibfk_1 FOREIGN KEY (categoryid) REFERENCES ^categories(categoryid) ON DELETE CASCADE',
			),
Scott Vivian committed
499

Gideon Greenspan committed
500 501 502 503 504 505
			'tagmetas' => array(
				'tag' => 'VARCHAR('.QA_DB_MAX_WORD_LENGTH.') NOT NULL',
				'title' => 'VARCHAR('.QA_DB_MAX_META_TITLE_LENGTH.') NOT NULL',
				'content' => 'VARCHAR('.QA_DB_MAX_META_CONTENT_LENGTH.') NOT NULL',
				'PRIMARY KEY (tag, title)',
			),
Scott Vivian committed
506

Gideon Greenspan committed
507
		);
Scott Vivian committed
508

Gideon Greenspan committed
509 510 511 512 513 514 515 516 517
		if (QA_FINAL_EXTERNAL_USERS) {
			unset($tables['users']);
			unset($tables['userlogins']);
			unset($tables['userprofile']);
			unset($tables['userfields']);
			unset($tables['messages']);

		} else {
			$userforeignkey='FOREIGN KEY (userid) REFERENCES ^users(userid)';
Scott Vivian committed
518

Gideon Greenspan committed
519 520 521 522 523 524 525 526
			$tables['userlogins'][]='CONSTRAINT ^userlogins_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
			$tables['userprofile'][]='CONSTRAINT ^userprofile_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
			$tables['posts'][]='CONSTRAINT ^posts_ibfk_1 '.$userforeignkey.' ON DELETE SET NULL';
			$tables['uservotes'][]='CONSTRAINT ^uservotes_ibfk_2 '.$userforeignkey.' ON DELETE CASCADE';
			$tables['userlimits'][]='CONSTRAINT ^userlimits_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
			$tables['userfavorites'][]='CONSTRAINT ^userfavorites_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
			$tables['usernotices'][]='CONSTRAINT ^usernotices_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
			$tables['userevents'][]='CONSTRAINT ^userevents_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
Gideon Greenspan committed
527
			$tables['userlevels'][]='CONSTRAINT ^userlevels_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
Gideon Greenspan committed
528 529 530 531 532 533
			$tables['usermetas'][]='CONSTRAINT ^usermetas_ibfk_1 '.$userforeignkey.' ON DELETE CASCADE';
		}

		return $tables;
	}

Scott Vivian committed
534

535
	function qa_array_to_keys($array)
Gideon Greenspan committed
536
/*
537
	Return array with all values from $array as keys
Gideon Greenspan committed
538 539
*/
	{
540
		return array_combine($array, array_fill(0, count($array), true));
Gideon Greenspan committed
541 542
	}

Scott Vivian committed
543

Gideon Greenspan committed
544 545 546 547 548
	function qa_db_missing_tables($definitions)
/*
	Return a list of tables missing from the database, [table name] => [column/index definitions]
*/
	{
549
		$keydbtables=qa_array_to_keys(qa_db_list_tables());
Scott Vivian committed
550

Gideon Greenspan committed
551
		$missing=array();
Scott Vivian committed
552

Gideon Greenspan committed
553
		foreach ($definitions as $rawname => $definition)
554
			if (!isset($keydbtables[qa_db_add_table_prefix($rawname)]))
Gideon Greenspan committed
555
				$missing[$rawname]=$definition;
Scott Vivian committed
556

Gideon Greenspan committed
557 558 559
		return $missing;
	}

Scott Vivian committed
560

Gideon Greenspan committed
561 562 563 564 565
	function qa_db_missing_columns($table, $definition)
/*
	Return a list of columns missing from $table in the database, given the full definition set in $definition
*/
	{
566
		$keycolumns=qa_array_to_keys(qa_db_read_all_values(qa_db_query_sub('SHOW COLUMNS FROM ^'.$table)));
Scott Vivian committed
567

Gideon Greenspan committed
568
		$missing=array();
Scott Vivian committed
569

Gideon Greenspan committed
570
		foreach ($definition as $colname => $coldefn)
571
			if ( (!is_int($colname)) && !isset($keycolumns[$colname]) )
Gideon Greenspan committed
572
				$missing[$colname]=$coldefn;
Scott Vivian committed
573

Gideon Greenspan committed
574 575 576
		return $missing;
	}

Scott Vivian committed
577

Gideon Greenspan committed
578 579 580 581 582 583
	function qa_db_get_db_version()
/*
	Return the current version of the Q2A database, to determine need for DB upgrades
*/
	{
		$definitions=qa_db_table_definitions();
Scott Vivian committed
584

Gideon Greenspan committed
585 586
		if (count(qa_db_missing_columns('options', $definitions['options']))==0) {
			$version=(int)qa_db_read_one_value(qa_db_query_sub("SELECT content FROM ^options WHERE title='db_version'"), true);
Scott Vivian committed
587

Gideon Greenspan committed
588 589 590
			if ($version>0)
				return $version;
		}
Scott Vivian committed
591

Gideon Greenspan committed
592 593 594
		return null;
	}

Scott Vivian committed
595

Gideon Greenspan committed
596 597 598 599 600 601 602 603
	function qa_db_set_db_version($version)
/*
	Set the current version in the database
*/
	{
		qa_db_query_sub("REPLACE ^options (title,content) VALUES ('db_version', #)", $version);
	}

Scott Vivian committed
604

Gideon Greenspan committed
605 606 607 608 609 610
	function qa_db_check_tables()
/*
	Return a string describing what is wrong with the database, or false if everything is just fine
*/
	{
		qa_db_query_raw('UNLOCK TABLES'); // we could be inside a lock tables block
Scott Vivian committed
611

Gideon Greenspan committed
612
		$version=qa_db_read_one_value(qa_db_query_raw('SELECT VERSION()'));
Scott Vivian committed
613

Gideon Greenspan committed
614 615
		if (((float)$version)<4.1)
			qa_fatal_error('MySQL version 4.1 or later is required - you appear to be running MySQL '.$version);
Scott Vivian committed
616

Gideon Greenspan committed
617 618
		$definitions=qa_db_table_definitions();
		$missing=qa_db_missing_tables($definitions);
Scott Vivian committed
619

Gideon Greenspan committed
620 621
		if (count($missing) == count($definitions))
			return 'none';
Scott Vivian committed
622

Gideon Greenspan committed
623 624 625
		else {
			if (!isset($missing['options'])) {
				$version=qa_db_get_db_version();
Scott Vivian committed
626

Gideon Greenspan committed
627 628 629
				if (isset($version) && ($version<QA_DB_VERSION_CURRENT))
					return 'old-version';
			}
Scott Vivian committed
630

Gideon Greenspan committed
631 632 633 634
			if (count($missing)) {
				if (defined('QA_MYSQL_USERS_PREFIX')) { // special case if two installations sharing users
					$datacount=0;
					$datamissing=0;
Scott Vivian committed
635

Gideon Greenspan committed
636 637 638
					foreach ($definitions as $rawname => $definition)
						if (qa_db_add_table_prefix($rawname)==(QA_MYSQL_TABLE_PREFIX.$rawname)) {
							$datacount++;
Scott Vivian committed
639

Gideon Greenspan committed
640 641 642
							if (isset($missing[$rawname]))
								$datamissing++;
						}
Scott Vivian committed
643

Gideon Greenspan committed
644 645 646
					if ( ($datacount==$datamissing) && ($datamissing==count($missing)) )
						return 'non-users-missing';
				}
Scott Vivian committed
647

Gideon Greenspan committed
648
				return 'table-missing';
Scott Vivian committed
649

Gideon Greenspan committed
650 651 652 653 654
			} else
				foreach ($definitions as $table => $definition)
					if (count(qa_db_missing_columns($table, $definition)))
						return 'column-missing';
		}
Scott Vivian committed
655

Gideon Greenspan committed
656 657 658
		return false;
	}

Scott Vivian committed
659

Gideon Greenspan committed
660 661 662 663 664 665 666
	function qa_db_install_tables()
/*
	Install any missing database tables and/or columns and automatically set version as latest.
	This is not suitable for use if the database needs upgrading.
*/
	{
		$definitions=qa_db_table_definitions();
Scott Vivian committed
667

Gideon Greenspan committed
668
		$missingtables=qa_db_missing_tables($definitions);
Scott Vivian committed
669

Gideon Greenspan committed
670 671 672 673 674 675
		foreach ($missingtables as $rawname => $definition) {
			qa_db_query_sub(qa_db_create_table_sql($rawname, $definition));

			if ($rawname=='userfields')
				qa_db_query_sub(qa_db_default_userfields_sql());
		}
Scott Vivian committed
676

Gideon Greenspan committed
677 678
		foreach ($definitions as $table => $definition) {
			$missingcolumns=qa_db_missing_columns($table, $definition);
Scott Vivian committed
679

Gideon Greenspan committed
680 681 682
			foreach ($missingcolumns as $colname => $coldefn)
				qa_db_query_sub('ALTER TABLE ^'.$table.' ADD COLUMN '.$colname.' '.$coldefn);
		}
Scott Vivian committed
683

Gideon Greenspan committed
684 685 686
		qa_db_set_db_version(QA_DB_VERSION_CURRENT);
	}

Scott Vivian committed
687

Gideon Greenspan committed
688 689 690 691 692 693 694 695 696
	function qa_db_create_table_sql($rawname, $definition)
/*
	Return the SQL command to create a table with $rawname and $definition obtained from qa_db_table_definitions()
*/
	{
		$querycols='';
		foreach ($definition as $colname => $coldef)
			if (isset($coldef))
				$querycols.=(strlen($querycols) ? ', ' : '').(is_int($colname) ? $coldef : ($colname.' '.$coldef));
Scott Vivian committed
697

Gideon Greenspan committed
698 699
		return 'CREATE TABLE ^'.$rawname.' ('.$querycols.') ENGINE=InnoDB CHARSET=utf8';
	}
Scott Vivian committed
700 701


Gideon Greenspan committed
702 703 704 705 706 707 708 709 710 711 712
	function qa_db_default_userfields_sql()
/*
	Return the SQL to create the default entries in the userfields table (before 1.3 these were hard-coded in PHP)
*/
	{
		$oldprofileflags=array(
			'name' => 0,
			'location' => 0,
			'website' => QA_FIELD_FLAGS_LINK_URL,
			'about' => QA_FIELD_FLAGS_MULTI_LINE,
		);
Scott Vivian committed
713

Gideon Greenspan committed
714
		$sql='INSERT INTO ^userfields (title, position, flags) VALUES '; // content column will be NULL, meaning use default from lang files
Scott Vivian committed
715

Gideon Greenspan committed
716 717 718
		$index=0;
		foreach ($oldprofileflags as $title => $flags)
			$sql.=($index ? ', ' : '')."('".qa_db_escape_string($title)."', ".(++$index).", ".(int)@$oldprofileflags[$title].")";
Scott Vivian committed
719

Gideon Greenspan committed
720 721
		return $sql;
	}
Scott Vivian committed
722 723


Gideon Greenspan committed
724 725 726 727 728 729
	function qa_db_upgrade_tables()
/*
	Upgrade the database schema to the latest version, outputting progress to the browser
*/
	{
		require_once QA_INCLUDE_DIR.'qa-app-recalc.php';
Scott Vivian committed
730

Gideon Greenspan committed
731 732
		$definitions=qa_db_table_definitions();
		$keyrecalc=array();
Scott Vivian committed
733

Gideon Greenspan committed
734 735
	//	Write-lock all Q2A tables before we start so no one can read or write anything

736
		$keydbtables=qa_array_to_keys(qa_db_list_tables());
Gideon Greenspan committed
737 738

		foreach ($definitions as $rawname => $definition)
739
			if (isset($keydbtables[qa_db_add_table_prefix($rawname)]))
Gideon Greenspan committed
740
				$locks[]='^'.$rawname.' WRITE';
Scott Vivian committed
741

Gideon Greenspan committed
742
		$locktablesquery='LOCK TABLES '.implode(', ', $locks);
Scott Vivian committed
743

Gideon Greenspan committed
744
		qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
745

Gideon Greenspan committed
746 747 748 749
	//	Upgrade it step-by-step until it's up to date (do LOCK TABLES after ALTER TABLE because the lock can sometimes be lost)

		while (1) {
			$version=qa_db_get_db_version();
Scott Vivian committed
750

Gideon Greenspan committed
751 752
			if ($version>=QA_DB_VERSION_CURRENT)
				break;
Scott Vivian committed
753

Gideon Greenspan committed
754
			$newversion=$version+1;
Scott Vivian committed
755

Gideon Greenspan committed
756
			qa_db_upgrade_progress(QA_DB_VERSION_CURRENT-$version.' upgrade step/s remaining...');
Scott Vivian committed
757

Gideon Greenspan committed
758
			switch ($newversion) {
Scott Vivian committed
759

Gideon Greenspan committed
760
			//	Up to here: Version 1.0 beta 1
Scott Vivian committed
761

Gideon Greenspan committed
762
				case 2:
Gideon Greenspan committed
763 764
					qa_db_upgrade_query('ALTER TABLE ^posts DROP COLUMN votes, ADD COLUMN upvotes '.$definitions['posts']['upvotes'].
						' AFTER cookieid, ADD COLUMN downvotes '.$definitions['posts']['downvotes'].' AFTER upvotes');
Gideon Greenspan committed
765 766 767
					qa_db_upgrade_query($locktablesquery);
					$keyrecalc['dorecountposts']=true;
					break;
Scott Vivian committed
768

Gideon Greenspan committed
769
				case 3:
Gideon Greenspan committed
770 771
					qa_db_upgrade_query('ALTER TABLE ^userpoints ADD COLUMN upvoteds '.$definitions['userpoints']['upvoteds'].
						' AFTER avoteds, ADD COLUMN downvoteds '.$definitions['userpoints']['downvoteds'].' AFTER upvoteds');
Gideon Greenspan committed
772 773 774
					qa_db_upgrade_query($locktablesquery);
					$keyrecalc['dorecalcpoints']=true;
					break;
Scott Vivian committed
775

Gideon Greenspan committed
776
				case 4:
Gideon Greenspan committed
777
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN lastuserid '.$definitions['posts']['lastuserid'].' AFTER cookieid, CHANGE COLUMN updated updated '.$definitions['posts']['updated']);
Gideon Greenspan committed
778 779 780
					qa_db_upgrade_query($locktablesquery);
					qa_db_upgrade_query('UPDATE ^posts SET updated=NULL WHERE updated=0 OR updated=created');
					break;
Scott Vivian committed
781

Gideon Greenspan committed
782
				case 5:
Gideon Greenspan committed
783
					qa_db_upgrade_query('ALTER TABLE ^contentwords ADD COLUMN type '.$definitions['contentwords']['type'].' AFTER count, ADD COLUMN questionid '.$definitions['contentwords']['questionid'].' AFTER type');
Gideon Greenspan committed
784
					qa_db_upgrade_query($locktablesquery);
Gideon Greenspan committed
785
					$keyrecalc['doreindexcontent']=true;
Gideon Greenspan committed
786
					break;
Scott Vivian committed
787

Gideon Greenspan committed
788
			//	Up to here: Version 1.0 beta 2
Scott Vivian committed
789

Gideon Greenspan committed
790
				case 6:
Gideon Greenspan committed
791
					qa_db_upgrade_query('ALTER TABLE ^userpoints ADD COLUMN cposts '.$definitions['userpoints']['cposts'].' AFTER aposts');
Gideon Greenspan committed
792 793 794
					qa_db_upgrade_query($locktablesquery);
					$keyrecalc['dorecalcpoints']=true;
					break;
Scott Vivian committed
795

Gideon Greenspan committed
796 797
				case 7:
					if (!QA_FINAL_EXTERNAL_USERS) {
Gideon Greenspan committed
798
						qa_db_upgrade_query('ALTER TABLE ^users ADD COLUMN sessioncode '.$definitions['users']['sessioncode'].' AFTER writeip');
Gideon Greenspan committed
799 800 801
						qa_db_upgrade_query($locktablesquery);
					}
					break;
Scott Vivian committed
802

Gideon Greenspan committed
803 804 805 806 807 808 809
				case 8:
					qa_db_upgrade_query('ALTER TABLE ^posts ADD KEY (type, acount, created)');
					qa_db_upgrade_query($locktablesquery);
					$keyrecalc['dorecountposts']=true; // for unanswered question count
					break;

			//	Up to here: Version 1.0 beta 3, 1.0, 1.0.1 beta, 1.0.1
Scott Vivian committed
810

Gideon Greenspan committed
811 812
				case 9:
					if (!QA_FINAL_EXTERNAL_USERS) {
Gideon Greenspan committed
813
						qa_db_upgrade_query('ALTER TABLE ^users CHANGE COLUMN resetcode emailcode '.$definitions['users']['emailcode'].', ADD COLUMN flags '.$definitions['users']['flags'].' AFTER sessioncode');
Gideon Greenspan committed
814 815 816 817
						qa_db_upgrade_query($locktablesquery);
						qa_db_upgrade_query('UPDATE ^users SET flags=1');
					}
					break;
Scott Vivian committed
818

Gideon Greenspan committed
819
				case 10:
Gideon Greenspan committed
820
					qa_db_upgrade_query('UNLOCK TABLES');
Gideon Greenspan committed
821 822 823 824 825 826 827 828 829 830 831 832 833 834
					qa_db_upgrade_query(qa_db_create_table_sql('categories', array(
						'categoryid' => $definitions['categories']['categoryid'],
						'title' => $definitions['categories']['title'],
						'tags' => $definitions['categories']['tags'],
						'qcount' => $definitions['categories']['qcount'],
						'position' => $definitions['categories']['position'],
						'PRIMARY KEY (categoryid)',
						'UNIQUE tags (tags)',
						'UNIQUE position (position)',
					))); // hard-code list of columns and indexes to ensure we ignore any added at a later stage

					$locktablesquery.=', ^categories WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
835

Gideon Greenspan committed
836
				case 11:
Gideon Greenspan committed
837
					qa_db_upgrade_query('ALTER TABLE ^posts ADD CONSTRAINT ^posts_ibfk_2 FOREIGN KEY (parentid) REFERENCES ^posts(postid), ADD COLUMN categoryid '.$definitions['posts']['categoryid'].' AFTER parentid, ADD KEY categoryid (categoryid, type, created), ADD CONSTRAINT ^posts_ibfk_3 FOREIGN KEY (categoryid) REFERENCES ^categories(categoryid) ON DELETE SET NULL');
Gideon Greenspan committed
838 839 840
						// foreign key on parentid important now that deletion is possible
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
841

Gideon Greenspan committed
842
				case 12:
Gideon Greenspan committed
843
					qa_db_upgrade_query('UNLOCK TABLES');
Gideon Greenspan committed
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
					qa_db_upgrade_query(qa_db_create_table_sql('pages', array(
						'pageid' => $definitions['pages']['pageid'],
						'title' => $definitions['pages']['title'],
						'nav' => $definitions['pages']['nav'],
						'position' => $definitions['pages']['position'],
						'flags' => $definitions['pages']['flags'],
						'tags' => $definitions['pages']['tags'],
						'heading' => $definitions['pages']['heading'],
						'content' => $definitions['pages']['content'],
						'PRIMARY KEY (pageid)',
						'UNIQUE tags (tags)',
						'UNIQUE position (position)',
					))); // hard-code list of columns and indexes to ensure we ignore any added at a later stage
					$locktablesquery.=', ^pages WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
860

Gideon Greenspan committed
861
				case 13:
Gideon Greenspan committed
862
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN createip '.$definitions['posts']['createip'].' AFTER cookieid, ADD KEY createip (createip, created)');
Gideon Greenspan committed
863 864
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
865

Gideon Greenspan committed
866
				case 14:
Gideon Greenspan committed
867
					qa_db_upgrade_query('ALTER TABLE ^userpoints DROP COLUMN qvotes, DROP COLUMN avotes, ADD COLUMN qupvotes '.$definitions['userpoints']['qupvotes'].' AFTER aselecteds, ADD COLUMN qdownvotes '.$definitions['userpoints']['qdownvotes'].' AFTER qupvotes, ADD COLUMN aupvotes '.$definitions['userpoints']['aupvotes'].' AFTER qdownvotes, ADD COLUMN adownvotes '.$definitions['userpoints']['adownvotes'].' AFTER aupvotes');
Gideon Greenspan committed
868 869 870
					qa_db_upgrade_query($locktablesquery);
					$keyrecalc['dorecalcpoints']=true;
					break;
Scott Vivian committed
871

Gideon Greenspan committed
872
			//	Up to here: Version 1.2 beta 1
Scott Vivian committed
873

Gideon Greenspan committed
874 875 876
				case 15:
					if (!QA_FINAL_EXTERNAL_USERS)
						qa_db_upgrade_table_columns($definitions, 'users', array('emailcode', 'sessioncode', 'flags'));
Scott Vivian committed
877

Gideon Greenspan committed
878 879 880 881 882 883 884 885 886
					qa_db_upgrade_table_columns($definitions, 'posts', array('acount', 'upvotes', 'downvotes', 'format'));
					qa_db_upgrade_table_columns($definitions, 'categories', array('qcount'));
					qa_db_upgrade_table_columns($definitions, 'words', array('titlecount', 'contentcount', 'tagcount'));
					qa_db_upgrade_table_columns($definitions, 'userpoints', array('points', 'qposts', 'aposts', 'cposts',
						'aselects', 'aselecteds', 'qupvotes', 'qdownvotes', 'aupvotes', 'adownvotes', 'qvoteds', 'avoteds', 'upvoteds', 'downvoteds'));
					qa_db_upgrade_query($locktablesquery);
					break;

			//	Up to here: Version 1.2 (release)
Scott Vivian committed
887

Gideon Greenspan committed
888 889 890
				case 16:
					qa_db_upgrade_table_columns($definitions, 'posts', array('format'));
					qa_db_upgrade_query($locktablesquery);
Gideon Greenspan committed
891
					$keyrecalc['doreindexcontent']=true; // because of new treatment of apostrophes in words
Gideon Greenspan committed
892
					break;
Scott Vivian committed
893

Gideon Greenspan committed
894 895 896 897
				case 17:
					qa_db_upgrade_query('ALTER TABLE ^posts ADD KEY updated (updated, type), ADD KEY categoryid_2 (categoryid, updated, type)');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
898

Gideon Greenspan committed
899
				case 18:
Gideon Greenspan committed
900
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN lastip '.$definitions['posts']['lastip'].' AFTER lastuserid, ADD KEY lastip (lastip, updated, type)');
Gideon Greenspan committed
901 902
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
903

Gideon Greenspan committed
904 905
				case 19:
					if (!QA_FINAL_EXTERNAL_USERS)
Gideon Greenspan committed
906
						qa_db_upgrade_query('ALTER TABLE ^users ADD COLUMN avatarblobid '.$definitions['users']['avatarblobid'].' AFTER handle, ADD COLUMN avatarwidth '.$definitions['users']['avatarwidth'].' AFTER avatarblobid, ADD COLUMN avatarheight '.$definitions['users']['avatarheight'].' AFTER avatarwidth');
Scott Vivian committed
907

Gideon Greenspan committed
908 909
					// hard-code list of columns and indexes to ensure we ignore any added at a later stage

Gideon Greenspan committed
910
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
911

Gideon Greenspan committed
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
					qa_db_upgrade_query(qa_db_create_table_sql('blobs', array(
						'blobid' => $definitions['blobs']['blobid'],
						'format' => $definitions['blobs']['format'],
						'content' => $definitions['blobs']['content'],
						'PRIMARY KEY (blobid)'
					)));

					qa_db_upgrade_query(qa_db_create_table_sql('cache', array(
						'type' => $definitions['cache']['type'],
						'cacheid' => $definitions['cache']['cacheid'],
						'content' => $definitions['cache']['content'],
						'created' => $definitions['cache']['created'],
						'lastread' => $definitions['cache']['lastread'],
						'PRIMARY KEY (type,cacheid)',
						'KEY (lastread)',
					))); // hard-code list of columns and indexes to ensure we ignore any added at a later stage

					$locktablesquery.=', ^blobs WRITE, ^cache WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
932

Gideon Greenspan committed
933 934
				case 20:
					if (!QA_FINAL_EXTERNAL_USERS) {
Gideon Greenspan committed
935
						qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
936

Gideon Greenspan committed
937 938 939 940 941 942 943 944 945
						qa_db_upgrade_query(qa_db_create_table_sql('userlogins', array(
							'userid' => $definitions['userlogins']['userid'],
							'source' => $definitions['userlogins']['source'],
							'identifier' => $definitions['userlogins']['identifier'],
							'identifiermd5' => $definitions['userlogins']['identifiermd5'],
							'KEY source (source, identifiermd5)',
							'KEY userid (userid)',
							'CONSTRAINT ^userlogins_ibfk_1 FOREIGN KEY (userid) REFERENCES ^users(userid) ON DELETE CASCADE',
						)));
Scott Vivian committed
946

Gideon Greenspan committed
947
						qa_db_upgrade_query('ALTER TABLE ^users CHANGE COLUMN passsalt passsalt '.$definitions['users']['passsalt'].', CHANGE COLUMN passcheck passcheck '.$definitions['users']['passcheck']);
Scott Vivian committed
948

Gideon Greenspan committed
949 950 951 952
						$locktablesquery.=', ^userlogins WRITE';
						qa_db_upgrade_query($locktablesquery);
					}
					break;
Scott Vivian committed
953

Gideon Greenspan committed
954 955
				case 21:
					if (!QA_FINAL_EXTERNAL_USERS) {
Gideon Greenspan committed
956
						qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
957

Gideon Greenspan committed
958 959 960 961 962 963 964 965
						qa_db_upgrade_query(qa_db_create_table_sql('userfields', array(
							'fieldid' => $definitions['userfields']['fieldid'],
							'title' => $definitions['userfields']['title'],
							'content' => $definitions['userfields']['content'],
							'position' => $definitions['userfields']['position'],
							'flags' => $definitions['userfields']['flags'],
							'PRIMARY KEY (fieldid)',
						)));
Scott Vivian committed
966

Gideon Greenspan committed
967 968
						$locktablesquery.=', ^userfields WRITE';
						qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
969

Gideon Greenspan committed
970 971 972
						qa_db_upgrade_query(qa_db_default_userfields_sql());
					}
					break;
Scott Vivian committed
973

Gideon Greenspan committed
974
			//	Up to here: Version 1.3 beta 1
Scott Vivian committed
975

Gideon Greenspan committed
976 977
				case 22:
					if (!QA_FINAL_EXTERNAL_USERS) {
Gideon Greenspan committed
978
						qa_db_upgrade_query('ALTER TABLE ^users ADD COLUMN sessionsource '.$definitions['users']['sessionsource'].' AFTER sessioncode');
Gideon Greenspan committed
979 980 981
						qa_db_upgrade_query($locktablesquery);
					}
					break;
Scott Vivian committed
982

Gideon Greenspan committed
983
			//	Up to here: Version 1.3 beta 2 and release
Scott Vivian committed
984

Gideon Greenspan committed
985
				case 23:
Gideon Greenspan committed
986
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
987

Gideon Greenspan committed
988 989 990 991 992 993 994 995 996
					qa_db_upgrade_query(qa_db_create_table_sql('widgets', array(
						'widgetid' => $definitions['widgets']['widgetid'],
						'place' => $definitions['widgets']['place'],
						'position' => $definitions['widgets']['position'],
						'tags' => $definitions['widgets']['tags'],
						'title' => $definitions['widgets']['title'],
						'PRIMARY KEY (widgetid)',
						'UNIQUE position (position)',
					)));
Scott Vivian committed
997

Gideon Greenspan committed
998 999 1000
					$locktablesquery.=', ^widgets WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1001

Gideon Greenspan committed
1002
				case 24:
Gideon Greenspan committed
1003
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1004

Gideon Greenspan committed
1005 1006 1007 1008 1009 1010 1011 1012
					qa_db_upgrade_query(qa_db_create_table_sql('tagwords', array(
						'postid' => $definitions['tagwords']['postid'],
						'wordid' => $definitions['tagwords']['wordid'],
						'KEY postid (postid)',
						'KEY wordid (wordid)',
						'CONSTRAINT ^tagwords_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
						'CONSTRAINT ^tagwords_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)',
					)));
Scott Vivian committed
1013

Gideon Greenspan committed
1014
					$locktablesquery.=', ^tagwords WRITE';
Scott Vivian committed
1015

Gideon Greenspan committed
1016
					qa_db_upgrade_query('ALTER TABLE ^words ADD COLUMN tagwordcount '.$definitions['words']['tagwordcount'].' AFTER contentcount');
Gideon Greenspan committed
1017
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1018

Gideon Greenspan committed
1019
					$keyrecalc['doreindexcontent']=true;
Gideon Greenspan committed
1020
					break;
Scott Vivian committed
1021

Gideon Greenspan committed
1022
			//	Up to here: Version 1.4 developer preview
Scott Vivian committed
1023

Gideon Greenspan committed
1024
				case 25:
1025
					$keycolumns=qa_array_to_keys(qa_db_read_all_values(qa_db_query_sub('SHOW COLUMNS FROM ^blobs')));
Gideon Greenspan committed
1026
						// might be using blobs table shared with another installation, so check if we need to upgrade
Scott Vivian committed
1027

Gideon Greenspan committed
1028 1029 1030 1031
					if (isset($keycolumns['filename']))
						qa_db_upgrade_progress('Skipping upgrading blobs table since it was already upgraded by another Q2A site sharing it.');

					else {
Gideon Greenspan committed
1032
						qa_db_upgrade_query('ALTER TABLE ^blobs ADD COLUMN filename '.$definitions['blobs']['filename'].' AFTER content, ADD COLUMN userid '.$definitions['blobs']['userid'].' AFTER filename, ADD COLUMN cookieid '.$definitions['blobs']['cookieid'].' AFTER userid, ADD COLUMN createip '.$definitions['blobs']['createip'].' AFTER cookieid, ADD COLUMN created '.$definitions['blobs']['created'].' AFTER createip');
Gideon Greenspan committed
1033 1034 1035
						qa_db_upgrade_query($locktablesquery);
					}
					break;
Scott Vivian committed
1036

Gideon Greenspan committed
1037
				case 26:
Gideon Greenspan committed
1038
					qa_db_upgrade_query('ALTER TABLE ^uservotes ADD COLUMN flag '.$definitions['uservotes']['flag'].' AFTER vote');
Gideon Greenspan committed
1039
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1040

Gideon Greenspan committed
1041
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN flagcount '.$definitions['posts']['flagcount'].' AFTER downvotes, ADD KEY type_3 (type, flagcount, created)');
Gideon Greenspan committed
1042
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1043

Gideon Greenspan committed
1044 1045
					$keyrecalc['dorecountposts']=true;
					break;
Scott Vivian committed
1046

Gideon Greenspan committed
1047
				case 27:
Gideon Greenspan committed
1048
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN netvotes '.$definitions['posts']['netvotes'].' AFTER downvotes, ADD KEY type_4 (type, netvotes, created)');
Gideon Greenspan committed
1049
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1050

Gideon Greenspan committed
1051 1052
					$keyrecalc['dorecountposts']=true;
					break;
Scott Vivian committed
1053

Gideon Greenspan committed
1054
				case 28:
Gideon Greenspan committed
1055
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN views '.$definitions['posts']['views'].' AFTER netvotes, ADD COLUMN hotness '.$definitions['posts']['hotness'].' AFTER views, ADD KEY type_5 (type, views, created), ADD KEY type_6 (type, hotness)');
Gideon Greenspan committed
1056
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1057

Gideon Greenspan committed
1058 1059
					$keyrecalc['dorecountposts']=true;
					break;
Scott Vivian committed
1060

Gideon Greenspan committed
1061
				case 29:
Gideon Greenspan committed
1062
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN lastviewip '.$definitions['posts']['lastviewip'].' AFTER netvotes');
Gideon Greenspan committed
1063 1064
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1065

Gideon Greenspan committed
1066 1067 1068
				case 30:
					qa_db_upgrade_query('ALTER TABLE ^posts DROP FOREIGN KEY ^posts_ibfk_3'); // to allow category column types to be changed
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1069

Gideon Greenspan committed
1070 1071
					qa_db_upgrade_query('ALTER TABLE ^posts DROP KEY categoryid, DROP KEY categoryid_2');
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1072

Gideon Greenspan committed
1073
					qa_db_upgrade_query('ALTER TABLE ^categories CHANGE COLUMN categoryid categoryid '.$definitions['categories']['categoryid'].', ADD COLUMN parentid '.$definitions['categories']['parentid'].' AFTER categoryid, ADD COLUMN backpath '.$definitions['categories']['backpath'].' AFTER position, ADD COLUMN content '.$definitions['categories']['content'].' AFTER tags, DROP INDEX tags, DROP INDEX position, ADD UNIQUE parentid (parentid, tags), ADD UNIQUE parentid_2 (parentid, position), ADD KEY backpath (backpath('.QA_DB_MAX_CAT_PAGE_TAGS_LENGTH.'))');
Gideon Greenspan committed
1074
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1075

Gideon Greenspan committed
1076
					qa_db_upgrade_query('ALTER TABLE ^posts CHANGE COLUMN categoryid categoryid '.$definitions['posts']['categoryid'].', ADD COLUMN catidpath1 '.$definitions['posts']['catidpath1'].' AFTER categoryid, ADD COLUMN catidpath2 '.$definitions['posts']['catidpath2'].' AFTER catidpath1, ADD COLUMN catidpath3 '.$definitions['posts']['catidpath3'].' AFTER catidpath2'); // QA_CATEGORY_DEPTH=4
Gideon Greenspan committed
1077
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1078

Gideon Greenspan committed
1079 1080
					qa_db_upgrade_query('ALTER TABLE ^posts ADD KEY catidpath1 (catidpath1, type, created), ADD KEY catidpath2 (catidpath2, type, created), ADD KEY catidpath3 (catidpath3, type, created), ADD KEY categoryid (categoryid, type, created), ADD KEY catidpath1_2 (catidpath1, updated, type), ADD KEY catidpath2_2 (catidpath2, updated, type), ADD KEY catidpath3_2 (catidpath3, updated, type), ADD KEY categoryid_2 (categoryid, updated, type)');
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1081

Gideon Greenspan committed
1082 1083
					qa_db_upgrade_query('ALTER TABLE ^posts ADD CONSTRAINT ^posts_ibfk_3 FOREIGN KEY (categoryid) REFERENCES ^categories(categoryid) ON DELETE SET NULL');
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1084

Gideon Greenspan committed
1085
					$keyrecalc['dorecalccategories']=true;
Scott Vivian committed
1086 1087
					break;

Gideon Greenspan committed
1088
			//	Up to here: Version 1.4 betas and release
Scott Vivian committed
1089

Gideon Greenspan committed
1090 1091 1092 1093
				case 31:
					qa_db_upgrade_query('ALTER TABLE ^posts CHANGE COLUMN type type '.$definitions['posts']['type'].', ADD COLUMN updatetype '.$definitions['posts']['updatetype'].' AFTER updated, ADD COLUMN closedbyid '.$definitions['posts']['closedbyid'].' AFTER selchildid, ADD KEY closedbyid (closedbyid), ADD CONSTRAINT ^posts_ibfk_4 FOREIGN KEY (closedbyid) REFERENCES ^posts(postid)');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1094

Gideon Greenspan committed
1095 1096 1097
				case 32:
					qa_db_upgrade_query("UPDATE ^posts SET updatetype=IF(INSTR(type, '_HIDDEN')>0, 'H', 'E') WHERE updated IS NOT NULL");
					break;
Scott Vivian committed
1098

Gideon Greenspan committed
1099 1100 1101 1102
				case 33:
					qa_db_upgrade_query('ALTER TABLE ^contentwords CHANGE COLUMN type type '.$definitions['contentwords']['type']);
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1103

Gideon Greenspan committed
1104 1105
				case 34:
					if (!QA_FINAL_EXTERNAL_USERS) {
1106
						$keytables=qa_array_to_keys(qa_db_read_all_values(qa_db_query_sub('SHOW TABLES')));
Gideon Greenspan committed
1107
							// might be using messages table shared with another installation, so check if we need to upgrade
Scott Vivian committed
1108

Gideon Greenspan committed
1109 1110
						if (isset($keytables[qa_db_add_table_prefix('messages')]))
							qa_db_upgrade_progress('Skipping messages table since it was already added by another Q2A site sharing these users.');
Scott Vivian committed
1111

Gideon Greenspan committed
1112
						else {
Gideon Greenspan committed
1113
							qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1114

Gideon Greenspan committed
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
							qa_db_upgrade_query(qa_db_create_table_sql('messages', array(
								'messageid' => $definitions['messages']['messageid'],
								'fromuserid' => $definitions['messages']['fromuserid'],
								'touserid' => $definitions['messages']['touserid'],
								'content' => $definitions['messages']['content'],
								'format' => $definitions['messages']['format'],
								'created' => $definitions['messages']['created'],
								'PRIMARY KEY (messageid)',
								'KEY fromuserid (fromuserid, touserid, created)',
							)));
Scott Vivian committed
1125

Gideon Greenspan committed
1126 1127 1128 1129 1130
							$locktablesquery.=', ^messages WRITE';
							qa_db_upgrade_query($locktablesquery);
						}
					}
					break;
Scott Vivian committed
1131

Gideon Greenspan committed
1132
				case 35:
Gideon Greenspan committed
1133
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1134

Gideon Greenspan committed
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
					qa_db_upgrade_query(qa_db_create_table_sql('userfavorites', array(
						'userid' => $definitions['userfavorites']['userid'],
						'entitytype' => $definitions['userfavorites']['entitytype'],
						'entityid' => $definitions['userfavorites']['entityid'],
						'nouserevents' => $definitions['userfavorites']['nouserevents'],
						'PRIMARY KEY (userid, entitytype, entityid)',
						'KEY userid (userid, nouserevents)',
						'KEY entitytype (entitytype, entityid, nouserevents)',
						QA_FINAL_EXTERNAL_USERS ? null : 'CONSTRAINT ^userfavorites_ibfk_1 FOREIGN KEY (userid) REFERENCES ^users(userid) ON DELETE CASCADE',
					)));
Scott Vivian committed
1145

Gideon Greenspan committed
1146 1147 1148
					$locktablesquery.=', ^userfavorites WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1149

Gideon Greenspan committed
1150
				case 36:
Gideon Greenspan committed
1151
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1152

Gideon Greenspan committed
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
					qa_db_upgrade_query(qa_db_create_table_sql('userevents', array(
						'userid' => $definitions['userevents']['userid'],
						'entitytype' => $definitions['userevents']['entitytype'],
						'entityid' => $definitions['userevents']['entityid'],
						'questionid' => $definitions['userevents']['questionid'],
						'lastpostid' => $definitions['userevents']['lastpostid'],
						'updatetype' => $definitions['userevents']['updatetype'],
						'lastuserid' => $definitions['userevents']['lastuserid'],
						'updated' => $definitions['userevents']['updated'],
						'KEY userid (userid, updated)',
						'KEY questionid (questionid, userid)',
						QA_FINAL_EXTERNAL_USERS ? null : 'CONSTRAINT ^userevents_ibfk_1 FOREIGN KEY (userid) REFERENCES ^users(userid) ON DELETE CASCADE',
					)));
Scott Vivian committed
1166

Gideon Greenspan committed
1167 1168
					$locktablesquery.=', ^userevents WRITE';
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1169

Gideon Greenspan committed
1170 1171
					$keyrecalc['dorefillevents']=true;
					break;
Scott Vivian committed
1172

Gideon Greenspan committed
1173
				case 37:
Gideon Greenspan committed
1174
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1175

Gideon Greenspan committed
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
					qa_db_upgrade_query(qa_db_create_table_sql('sharedevents', array(
						'entitytype' => $definitions['sharedevents']['entitytype'],
						'entityid' => $definitions['sharedevents']['entityid'],
						'questionid' => $definitions['sharedevents']['questionid'],
						'lastpostid' => $definitions['sharedevents']['lastpostid'],
						'updatetype' => $definitions['sharedevents']['updatetype'],
						'lastuserid' => $definitions['sharedevents']['lastuserid'],
						'updated' => $definitions['sharedevents']['updated'],
						'KEY entitytype (entitytype, entityid, updated)',
						'KEY questionid (questionid, entitytype, entityid)',
					)));
Scott Vivian committed
1187

Gideon Greenspan committed
1188 1189
					$locktablesquery.=', ^sharedevents WRITE';
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1190

Gideon Greenspan committed
1191 1192
					$keyrecalc['dorefillevents']=true;
					break;
Scott Vivian committed
1193

Gideon Greenspan committed
1194 1195 1196 1197
				case 38:
					qa_db_upgrade_query('ALTER TABLE ^posts ADD KEY lastuserid (lastuserid, updated, type)');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1198

Gideon Greenspan committed
1199 1200 1201 1202
				case 39:
					qa_db_upgrade_query('ALTER TABLE ^posts DROP KEY type_3, ADD KEY flagcount (flagcount, created, type)');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1203

Gideon Greenspan committed
1204 1205 1206 1207
				case 40:
					qa_db_upgrade_query('ALTER TABLE ^userpoints ADD COLUMN bonus '.$definitions['userpoints']['bonus'].' AFTER downvoteds');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1208

Gideon Greenspan committed
1209 1210 1211 1212
				case 41:
					qa_db_upgrade_query('ALTER TABLE ^pages ADD COLUMN permit '.$definitions['pages']['permit'].' AFTER flags');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1213

Gideon Greenspan committed
1214
				case 42:
Gideon Greenspan committed
1215
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1216

Gideon Greenspan committed
1217 1218 1219 1220 1221 1222 1223
					qa_db_upgrade_query(qa_db_create_table_sql('usermetas', array(
						'userid' => $definitions['usermetas']['userid'],
						'title' => $definitions['usermetas']['title'],
						'content' => $definitions['usermetas']['content'],
						'PRIMARY KEY (userid, title)',
						QA_FINAL_EXTERNAL_USERS ? null : 'CONSTRAINT ^usermetas_ibfk_1 FOREIGN KEY (userid) REFERENCES ^users(userid) ON DELETE CASCADE',
					)));
Scott Vivian committed
1224

Gideon Greenspan committed
1225 1226 1227
					$locktablesquery.=', ^usermetas WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1228

Gideon Greenspan committed
1229
				case 43:
Gideon Greenspan committed
1230
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1231

Gideon Greenspan committed
1232 1233 1234 1235 1236 1237 1238
					qa_db_upgrade_query(qa_db_create_table_sql('postmetas', array(
						'postid' => $definitions['postmetas']['postid'],
						'title' => $definitions['postmetas']['title'],
						'content' => $definitions['postmetas']['content'],
						'PRIMARY KEY (postid, title)',
						'CONSTRAINT ^postmetas_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE',
					)));
Scott Vivian committed
1239

Gideon Greenspan committed
1240 1241 1242
					$locktablesquery.=', ^postmetas WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1243

Gideon Greenspan committed
1244
				case 44:
Gideon Greenspan committed
1245
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1246

Gideon Greenspan committed
1247 1248 1249 1250 1251 1252 1253
					qa_db_upgrade_query(qa_db_create_table_sql('categorymetas', array(
						'categoryid' => $definitions['categorymetas']['categoryid'],
						'title' => $definitions['categorymetas']['title'],
						'content' => $definitions['categorymetas']['content'],
						'PRIMARY KEY (categoryid, title)',
						'CONSTRAINT ^categorymetas_ibfk_1 FOREIGN KEY (categoryid) REFERENCES ^categories(categoryid) ON DELETE CASCADE',
					)));
Scott Vivian committed
1254

Gideon Greenspan committed
1255 1256 1257
					$locktablesquery.=', ^categorymetas WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1258

Gideon Greenspan committed
1259
				case 45:
Gideon Greenspan committed
1260
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1261

Gideon Greenspan committed
1262 1263 1264 1265 1266 1267
					qa_db_upgrade_query(qa_db_create_table_sql('tagmetas', array(
						'tag' => $definitions['tagmetas']['tag'],
						'title' => $definitions['tagmetas']['title'],
						'content' => $definitions['tagmetas']['content'],
						'PRIMARY KEY (tag, title)',
					)));
Scott Vivian committed
1268

Gideon Greenspan committed
1269 1270 1271
					$locktablesquery.=', ^tagmetas WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1272

Gideon Greenspan committed
1273 1274 1275
				case 46:
					qa_db_upgrade_query('ALTER TABLE ^posts DROP KEY selchildid, ADD KEY selchildid (selchildid, type, created), ADD COLUMN amaxvote SMALLINT UNSIGNED NOT NULL DEFAULT 0 AFTER acount, ADD KEY type_7 (type, amaxvote, created)');
					qa_db_upgrade_query($locktablesquery);
Scott Vivian committed
1276

Gideon Greenspan committed
1277 1278
					$keyrecalc['dorecountposts']=true;
					break;
Scott Vivian committed
1279

Gideon Greenspan committed
1280
				 case 47:
Gideon Greenspan committed
1281
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1282

Gideon Greenspan committed
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
					qa_db_upgrade_query(qa_db_create_table_sql('usernotices', array(
						'noticeid' => $definitions['usernotices']['noticeid'],
						'userid' => $definitions['usernotices']['userid'],
						'content' => $definitions['usernotices']['content'],
						'format' => $definitions['usernotices']['format'],
						'tags' => $definitions['usernotices']['tags'],
						'created' => $definitions['usernotices']['created'],
						'PRIMARY KEY (noticeid)',
						'KEY userid (userid, created)',
						QA_FINAL_EXTERNAL_USERS ? null : 'CONSTRAINT ^usernotices_ibfk_1 FOREIGN KEY (userid) REFERENCES ^users(userid) ON DELETE CASCADE',
					)));
Scott Vivian committed
1294

Gideon Greenspan committed
1295 1296 1297 1298
					$locktablesquery.=', ^usernotices WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;

Gideon Greenspan committed
1299
			//	Up to here: Version 1.5.x
Scott Vivian committed
1300

Gideon Greenspan committed
1301 1302
				case 48:
					if (!QA_FINAL_EXTERNAL_USERS) {
1303
						$keycolumns=qa_array_to_keys(qa_db_read_all_values(qa_db_query_sub('SHOW COLUMNS FROM ^messages')));
Gideon Greenspan committed
1304
							// might be using messages table shared with another installation, so check if we need to upgrade
Scott Vivian committed
1305

Gideon Greenspan committed
1306 1307
						if (isset($keycolumns['type']))
							qa_db_upgrade_progress('Skipping upgrading messages table since it was already upgraded by another Q2A site sharing it.');
Scott Vivian committed
1308

Gideon Greenspan committed
1309 1310 1311 1312 1313 1314
						else {
							qa_db_upgrade_query('ALTER TABLE ^messages ADD COLUMN type '.$definitions['messages']['type'].' AFTER messageid, DROP KEY fromuserid, ADD key type (type, fromuserid, touserid, created), ADD KEY touserid (touserid, type, created)');
							qa_db_upgrade_query($locktablesquery);
						}
					}
					break;
Scott Vivian committed
1315

Gideon Greenspan committed
1316 1317 1318 1319 1320 1321
				case 49:
					if (!QA_FINAL_EXTERNAL_USERS) {
						qa_db_upgrade_query('ALTER TABLE ^users CHANGE COLUMN flags flags '.$definitions['users']['flags']);
						qa_db_upgrade_query($locktablesquery);
					}
					break;
Scott Vivian committed
1322

Gideon Greenspan committed
1323 1324 1325 1326
				case 50:
					qa_db_upgrade_query('ALTER TABLE ^posts ADD COLUMN name '.$definitions['posts']['name'].' AFTER tags');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1327

Gideon Greenspan committed
1328 1329
				case 51:
					if (!QA_FINAL_EXTERNAL_USERS) {
1330
						$keycolumns=qa_array_to_keys(qa_db_read_all_values(qa_db_query_sub('SHOW COLUMNS FROM ^userfields')));
Gideon Greenspan committed
1331
							// might be using userfields table shared with another installation, so check if we need to upgrade
Scott Vivian committed
1332

Gideon Greenspan committed
1333 1334
						if (isset($keycolumns['permit']))
							qa_db_upgrade_progress('Skipping upgrading userfields table since it was already upgraded by another Q2A site sharing it.');
Scott Vivian committed
1335

Gideon Greenspan committed
1336 1337 1338 1339 1340 1341
						else {
							qa_db_upgrade_query('ALTER TABLE ^userfields ADD COLUMN permit '.$definitions['userfields']['permit'].' AFTER flags');
							qa_db_upgrade_query($locktablesquery);
						}
					}
					break;
Scott Vivian committed
1342

Gideon Greenspan committed
1343 1344
				case 52:
					if (!QA_FINAL_EXTERNAL_USERS) {
1345
						$keyindexes=qa_array_to_keys(qa_db_read_all_assoc(qa_db_query_sub('SHOW INDEX FROM ^users'), null, 'Key_name'));
Scott Vivian committed
1346

Gideon Greenspan committed
1347 1348
						if (isset($keyindexes['created']))
							qa_db_upgrade_progress('Skipping upgrading users table since it was already upgraded by another Q2A site sharing it.');
Scott Vivian committed
1349

Gideon Greenspan committed
1350 1351 1352 1353 1354 1355
						else {
							qa_db_upgrade_query('ALTER TABLE ^users ADD KEY created (created, level, flags)');
							qa_db_upgrade_query($locktablesquery);
						}
					}
					break;
Scott Vivian committed
1356

Gideon Greenspan committed
1357 1358 1359 1360
				case 53:
					qa_db_upgrade_query('ALTER TABLE ^blobs CHANGE COLUMN content content '.$definitions['blobs']['content']);
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1361

Gideon Greenspan committed
1362 1363
				case 54:
					qa_db_upgrade_query('UNLOCK TABLES');
Scott Vivian committed
1364

Gideon Greenspan committed
1365
					qa_db_upgrade_query('SET FOREIGN_KEY_CHECKS=0'); // in case InnoDB not available
Scott Vivian committed
1366

Gideon Greenspan committed
1367 1368 1369 1370 1371 1372 1373 1374 1375
					qa_db_upgrade_query(qa_db_create_table_sql('userlevels', array(
						'userid' => $definitions['userlevels']['userid'],
						'entitytype' => $definitions['userlevels']['entitytype'],
						'entityid' => $definitions['userlevels']['entityid'],
						'level' => $definitions['userlevels']['level'],
						'UNIQUE userid (userid, entitytype, entityid)',
						'KEY entitytype (entitytype, entityid)',
						QA_FINAL_EXTERNAL_USERS ? null : 'CONSTRAINT ^userlevels_ibfk_1 FOREIGN KEY (userid) REFERENCES ^users(userid) ON DELETE CASCADE',
					)));
Scott Vivian committed
1376

Gideon Greenspan committed
1377 1378 1379
					$locktablesquery.=', ^userlevels WRITE';
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1380

Gideon Greenspan committed
1381
			//	Up to here: Version 1.6 beta 1
Scott Vivian committed
1382

Gideon Greenspan committed
1383 1384
				case 55:
					if (!QA_FINAL_EXTERNAL_USERS) {
1385
						$keycolumns=qa_array_to_keys(qa_db_read_all_values(qa_db_query_sub('SHOW COLUMNS FROM ^users')));
Gideon Greenspan committed
1386
							// might be using messages table shared with another installation, so check if we need to upgrade
Scott Vivian committed
1387

Gideon Greenspan committed
1388 1389
						if (isset($keycolumns['wallposts']))
							qa_db_upgrade_progress('Skipping upgrading users table since it was already upgraded by another Q2A site sharing it.');
Scott Vivian committed
1390

Gideon Greenspan committed
1391 1392 1393 1394 1395 1396
						else {
							qa_db_upgrade_query('ALTER TABLE ^users ADD COLUMN wallposts '.$definitions['users']['wallposts'].' AFTER flags');
							qa_db_upgrade_query($locktablesquery);
						}
					}
					break;
Scott Vivian committed
1397

Gideon Greenspan committed
1398
			//	Up to here: Version 1.6 beta 2
Scott Vivian committed
1399

Gideon Greenspan committed
1400 1401 1402 1403
				case 56:
					qa_db_upgrade_query('ALTER TABLE ^pages DROP INDEX tags, ADD KEY tags (tags)');
					qa_db_upgrade_query($locktablesquery);
					break;
Scott Vivian committed
1404

Gideon Greenspan committed
1405
			//	Up to here: Version 1.6 (release)
Scott Vivian committed
1406

1407 1408 1409
				case 57:
					if (!QA_FINAL_EXTERNAL_USERS) {
						// might be using messages table shared with another installation, so check if we need to upgrade
1410
						$keycolumns = qa_array_to_keys(qa_db_read_all_values(qa_db_query_sub('SHOW COLUMNS FROM ^messages')));
1411 1412 1413 1414 1415 1416

						if (isset($keycolumns['fromhidden']))
							qa_db_upgrade_progress('Skipping upgrading messages table since it was already upgraded by another Q2A site sharing it.');
						else {
							qa_db_upgrade_query('ALTER TABLE ^messages ADD COLUMN fromhidden '.$definitions['messages']['fromhidden'].' AFTER touserid');
							qa_db_upgrade_query('ALTER TABLE ^messages ADD COLUMN tohidden '.$definitions['messages']['tohidden'].' AFTER fromhidden');
1417
							qa_db_upgrade_query('ALTER TABLE ^messages ADD KEY fromhidden (fromhidden), ADD KEY tohidden (tohidden)');
1418 1419 1420 1421 1422 1423 1424 1425

							qa_db_upgrade_query($locktablesquery);
						}
					}
					break;

			//	Up to here: Verison 1.7 alpha 1

Gideon Greenspan committed
1426
			}
Scott Vivian committed
1427

Gideon Greenspan committed
1428
			qa_db_set_db_version($newversion);
Scott Vivian committed
1429

Gideon Greenspan committed
1430 1431 1432
			if (qa_db_get_db_version()!=$newversion)
				qa_fatal_error('Could not increment database version');
		}
Scott Vivian committed
1433

Gideon Greenspan committed
1434 1435 1436
		qa_db_upgrade_query('UNLOCK TABLES');

	//	Perform any necessary recalculations, as determined by upgrade steps
Scott Vivian committed
1437

Gideon Greenspan committed
1438 1439 1440
		foreach ($keyrecalc as $state => $dummy)
			while ($state) {
				set_time_limit(60);
Scott Vivian committed
1441

Gideon Greenspan committed
1442
				$stoptime=time()+2;
Scott Vivian committed
1443

Gideon Greenspan committed
1444 1445
				while ( qa_recalc_perform_step($state) && (time()<$stoptime) )
					;
Scott Vivian committed
1446

Gideon Greenspan committed
1447 1448 1449
				qa_db_upgrade_progress(qa_recalc_get_message($state));
			}
	}
Scott Vivian committed
1450 1451


Gideon Greenspan committed
1452 1453 1454 1455 1456 1457
	function qa_db_upgrade_table_columns($definitions, $table, $columns)
/*
	Reset the definitions of $columns in $table according to the $definitions array
*/
	{
		$sqlchanges=array();
Scott Vivian committed
1458

Gideon Greenspan committed
1459 1460 1461 1462 1463 1464
		foreach ($columns as $column)
			$sqlchanges[]='CHANGE COLUMN '.$column.' '.$column.' '.$definitions[$table][$column];

		qa_db_upgrade_query('ALTER TABLE ^'.$table.' '.implode(', ', $sqlchanges));
	}

Scott Vivian committed
1465

Gideon Greenspan committed
1466 1467 1468 1469 1470 1471 1472 1473 1474
	function qa_db_upgrade_query($query)
/*
	Perform upgrade $query and output progress to the browser
*/
	{
		qa_db_upgrade_progress('Running query: '.qa_db_apply_sub($query, array()).' ...');
		qa_db_query_sub($query);
	}

Scott Vivian committed
1475

Gideon Greenspan committed
1476 1477 1478 1479 1480
	function qa_db_upgrade_progress($text)
/*
	Output $text to the browser (after converting to HTML) and do all we can to get it displayed
*/
	{
Gideon Greenspan committed
1481
		echo qa_html($text).str_repeat('    ', 1024)."<br><br>\n";
Gideon Greenspan committed
1482 1483 1484 1485 1486 1487 1488
		flush();
	}


/*
	Omit PHP closing tag to help avoid accidental output
*/