qa-db.php 19.6 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	Description: Common functions for connecting to and accessing database


	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.

	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
*/

22 23
use Q2A\Database\DbResult;

Scott committed
24 25 26 27
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
	header('Location: ../');
	exit;
}
Scott committed
28 29


Scott committed
30 31 32
/**
 * Indicates to the Q2A database layer that database connections are permitted fro this point forwards
 * (before this point, some plugins may not have had a chance to override some database access functions).
33
 * @deprecated 1.9.0 Use DbConnection->allowConnect() instead.
Scott committed
34 35 36 37
 */
function qa_db_allow_connect()
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott committed
38

39
	qa_service('database')->allowConnect();
Scott committed
40
}
41 42


Scott committed
43 44 45
/**
 * Connect to the Q2A database, select the right database, optionally install the $failhandler (and call it if necessary).
 * Uses mysqli as of Q2A 1.7.
46
 * @deprecated 1.9.0 Use DbConnection->connect() instead.
47 48
 * @param string|null $failhandler
 * @return mixed
Scott committed
49 50 51 52
 */
function qa_db_connect($failhandler = null)
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott committed
53

54
	qa_service('database')->connect($failhandler);
Scott committed
55 56 57 58 59
}


/**
 * If a DB error occurs, call the installed fail handler (if any) otherwise report error and exit immediately.
60
 * @deprecated 1.9.0 Use DbConnection->failError() instead.
61
 * @param string $type
Scott committed
62 63 64 65 66 67 68 69 70
 * @param int $errno
 * @param string $error
 * @param string $query
 * @return mixed
 */
function qa_db_fail_error($type, $errno = null, $error = null, $query = null)
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }

71
	qa_service('database')->failError($type, $errno, $error, $query);
Scott committed
72
}
Scott committed
73 74


Scott committed
75 76
/**
 * Return the current connection to the Q2A database, connecting if necessary and $connect is true.
77
 * @deprecated 1.9.0
Scott committed
78 79 80 81 82 83
 * @param bool $connect
 * @return mixed
 */
function qa_db_connection($connect = true)
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott committed
84

85 86 87 88 89 90
	$db = qa_service('database');
	if ($connect && !$db->isConnected()) {
		$db->connect();
	}

	return $db;
Scott committed
91
}
Scott committed
92 93


Scott committed
94 95
/**
 * Disconnect from the Q2A database.
96
 * @deprecated 1.9.0 Use DbConnection->disconnect() instead.
Scott committed
97 98 99 100
 */
function qa_db_disconnect()
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott committed
101

102
	qa_service('database')->disconnect();
Scott committed
103 104 105 106 107 108
}


/**
 * Run the raw $query, call the global failure handler if necessary, otherwise return the result resource.
 * If appropriate, also track the resources used by database queries, and the queries themselves, for performance debugging.
109
 * @deprecated 1.9.0 Use DbConnection->query() instead.
110 111
 * @param string $query
 * @return DbResult
Scott committed
112 113 114 115 116
 */
function qa_db_query_raw($query)
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }

117
	return qa_service('database')->query($query);
Scott committed
118
}
Scott committed
119 120


Scott committed
121 122
/**
 * Lower-level function to execute a query, which automatically retries if there is a MySQL deadlock error.
123
 * @deprecated 1.9.0 Use DbConnection->query() instead.
124 125
 * @param string $query
 * @return DbResult
Scott committed
126 127 128 129
 */
function qa_db_query_execute($query)
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott committed
130

131
	return qa_service('database')->query($query);
Scott committed
132 133 134 135 136
}


/**
 * Return $string escaped for use in queries to the Q2A database (to which a connection must have been made).
137
 * @deprecated 1.9.0 No longer needed: parameters passed to DbConnection->query() are automatically escaped.
138 139
 * @param string $string
 * @return string
Scott committed
140 141 142 143 144
 */
function qa_db_escape_string($string)
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }

145
	$pdo = qa_service('database')->getPDO();
146 147 148
	// PDO::quote wraps the value in single quotes, so remove them for backwards compatibility
	$quotedString = $pdo->quote($string);
	return substr($quotedString, 1, -1);
Scott committed
149 150 151 152 153 154
}


/**
 * Return $argument escaped for MySQL. Add quotes around it if $alwaysquote is true or it's not numeric.
 * If $argument is an array, return a comma-separated list of escaped elements, with or without $arraybrackets.
155
 * @deprecated 1.9.0 Use DbQueryHelper->expandParameters() instead.
156 157
 * @param mixed|null $argument
 * @param bool $alwaysquote
Scott committed
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
 * @param bool $arraybrackets
 * @return mixed|string
 */
function qa_db_argument_to_mysql($argument, $alwaysquote, $arraybrackets = false)
{
	if (is_array($argument)) {
		$parts = array();

		foreach ($argument as $subargument)
			$parts[] = qa_db_argument_to_mysql($subargument, $alwaysquote, true);

		if ($arraybrackets)
			$result = '(' . implode(',', $parts) . ')';
		else
			$result = implode(',', $parts);
Scott committed
173

Scott committed
174
	} elseif (isset($argument)) {
175 176 177 178
		if ($alwaysquote || !is_numeric($argument))
			$result = "'" . qa_db_escape_string($argument) . "'";
		else
			$result = qa_db_escape_string($argument);
Scott committed
179 180 181 182 183 184 185 186 187
	} else
		$result = 'NULL';

	return $result;
}


/**
 * Return the full name (with prefix) of database table $rawname, usually if it used after a ^ symbol.
188
 * @deprecated 1.9.0 Use DbQueryHelper->addTablePrefix() instead.
189
 * @param string $rawname
Scott committed
190 191 192 193 194 195
 * @return string
 */
function qa_db_add_table_prefix($rawname)
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }

Scott committed
196
	return (new \Q2A\Database\DbQueryHelper)->addTablePrefix($rawname);
Scott committed
197 198 199 200 201
}


/**
 * Callback function to add table prefixes, as used in qa_db_apply_sub().
202
 * @deprecated 1.9.0 No longer needed.
203
 * @param array $matches
Scott committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217
 * @return string
 */
function qa_db_prefix_callback($matches)
{
	return qa_db_add_table_prefix($matches[1]);
}


/**
 * Substitute ^, $ and # symbols in $query. ^ symbols are replaced with the table prefix set in qa-config.php.
 * $ and # symbols are replaced in order by the corresponding element in $arguments (if the element is an array,
 * it is converted recursively into comma-separated list). Each element in $arguments is escaped.
 * $ is replaced by the argument in quotes (even if it's a number), # only adds quotes if the argument is non-numeric.
 * It's important to use $ when matching a textual column since MySQL won't use indexes to compare text against numbers.
218
 * @deprecated 1.9.0 Use DbQueryHelper->expandParameters() instead.
219 220
 * @param string $query
 * @param array $arguments
Scott committed
221 222 223 224
 * @return mixed
 */
function qa_db_apply_sub($query, $arguments)
{
225
	// function left intact as some code calls this directly
226

Scott committed
227 228 229
	$query = preg_replace_callback('/\^([A-Za-z_0-9]+)/', 'qa_db_prefix_callback', $query);

	if (!is_array($arguments))
Scott committed
230
		return $query;
231

Scott committed
232 233
	$countargs = count($arguments);
	$offset = 0;
Scott committed
234

Scott committed
235 236 237
	for ($argument = 0; $argument < $countargs; $argument++) {
		$stringpos = strpos($query, '$', $offset);
		$numberpos = strpos($query, '#', $offset);
Scott committed
238

Scott committed
239 240 241 242 243 244 245
		if ($stringpos === false || ($numberpos !== false && $numberpos < $stringpos)) {
			$alwaysquote = false;
			$position = $numberpos;
		} else {
			$alwaysquote = true;
			$position = $stringpos;
		}
Scott committed
246

Scott committed
247 248
		if (!is_numeric($position))
			qa_fatal_error('Insufficient parameters in query: ' . $query);
Scott committed
249

Scott committed
250 251 252
		$value = qa_db_argument_to_mysql($arguments[$argument], $alwaysquote);
		$query = substr_replace($query, $value, $position, 1);
		$offset = $position + strlen($value); // allows inserting strings which contain #/$ character
Scott committed
253 254
	}

Scott committed
255 256 257 258 259 260
	return $query;
}


/**
 * Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
261
 * @deprecated 1.9.0 Use DbConnection->query() instead.
262
 * @param string $query
263
 * @return DbResult
Scott committed
264 265 266
 */
function qa_db_query_sub($query) // arguments for substitution retrieved using func_get_args()
{
267 268
	$params = array_slice(func_get_args(), 1);
	return qa_service('database')->query($query, $params);
269 270
}

271

272 273 274
/**
 * Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
 * Query parameters are passed as an array.
275
 * @deprecated 1.9.0 Use DbConnection->query() instead.
276 277
 * @param string $query
 * @param array $params
278
 * @return DbResult
279 280 281
 */
function qa_db_query_sub_params($query, $params)
{
282
	return qa_service('database')->query($query, $params);
Scott committed
283 284 285 286 287
}


/**
 * Return the number of rows in $result. (Simple wrapper for mysqli_result::num_rows.)
288
 * @deprecated 1.9.0 Use DbResult->affectedRows() instead.
289
 * @param DbResult|mysqli_result $result
Scott committed
290 291 292 293
 * @return int
 */
function qa_db_num_rows($result)
{
294
	if ($result instanceof \Q2A\Database\DbResult)
295
		return $result->affectedRows();
296

297
	// backwards compatibility
Scott committed
298 299 300 301 302 303 304 305 306
	if ($result instanceof mysqli_result)
		return $result->num_rows;

	return 0;
}


/**
 * Return the value of the auto-increment column for the last inserted row.
307
 * @deprecated 1.9.0 Use DbConnection->lastInsertId() instead.
308
 * @return string
Scott committed
309 310 311
 */
function qa_db_last_insert_id()
{
312
	return qa_service('database')->lastInsertId();
Scott committed
313 314 315 316 317
}


/**
 * Return the number of rows affected by the last query.
318
 * @deprecated 1.9.0 Use DbResult->affectedRows() instead.
319
 * @return int
Scott committed
320 321 322
 */
function qa_db_affected_rows()
{
323 324
	// not doable with new DB system (requires a PDOStatement, which if we had we could pass into DbResult instead)
	return 0;
Scott committed
325 326 327 328 329
}


/**
 * For the previous INSERT ... ON DUPLICATE KEY UPDATE query, return whether an insert operation took place.
330
 * @deprecated 1.9.0 Use DbResult->affectedRows() instead.
331
 * @return bool
Scott committed
332 333 334
 */
function qa_db_insert_on_duplicate_inserted()
{
335
	return false;
Scott committed
336 337 338 339 340 341
}


/**
 * Return a random integer (as a string) for use in a BIGINT column.
 * Actual limit is 18,446,744,073,709,551,615 - we aim for 18,446,743,999,999,999,999.
342
 * @return string
Scott committed
343 344 345 346 347 348 349 350 351 352
 */
function qa_db_random_bigint()
{
	return sprintf('%d%06d%06d', mt_rand(1, 18446743), mt_rand(0, 999999), mt_rand(0, 999999));
}


/**
 * Return an array of the names of all tables in the Q2A database, converted to lower case.
 * No longer used by Q2A and shouldn't be needed.
353
 * @return array
Scott committed
354 355 356 357 358 359 360 361 362
 */
function qa_db_list_tables_lc()
{
	return array_map('strtolower', qa_db_list_tables());
}


/**
 * Return an array of the names of all tables in the Q2A database.
363 364 365
 *
 * @param bool $onlyTablesWithPrefix Determine if the result should only include tables with the
 * QA_MYSQL_TABLE_PREFIX or if it should include all tables in the database.
366
 * @return array
Scott committed
367
 */
368
function qa_db_list_tables($onlyTablesWithPrefix = false)
Scott committed
369
{
370 371 372
	$query = 'SHOW TABLES';

	if ($onlyTablesWithPrefix) {
373
		$col = 'Tables_in_' . QA_FINAL_MYSQL_DATABASE;
374 375 376 377
		$query .= ' WHERE `' . $col . '` LIKE "' . str_replace('_', '\\_', QA_MYSQL_TABLE_PREFIX) . '%"';
		if (defined('QA_MYSQL_USERS_PREFIX')) {
			$query .= ' OR `' . $col . '` LIKE "' . str_replace('_', '\\_', QA_MYSQL_USERS_PREFIX) . '%"';
		}
378 379 380
	}

	return qa_db_read_all_values(qa_db_query_raw($query));
Scott committed
381
}
Scott committed
382 383 384


/*
385
	The selectspec array can contain the elements below. See db/selects.php for lots of examples.
Scott committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419

	By default, qa_db_single_select() and qa_db_multi_select() return the data for each selectspec as a numbered
	array of arrays, one per row. The array for each row has column names in the keys, and data in the values.
	But this can be changed using the 'arraykey', 'arrayvalue' and 'single' in the selectspec.

	Note that even if you specify ORDER BY in 'source', the final results may not be ordered. This is because
	the SELECT could be done within a UNION that (annoyingly) doesn't maintain order. Use 'sortasc' or 'sortdesc'
	to fix this. You can however rely on the combination of ORDER BY and LIMIT retrieving the appropriate records.


	'columns' => Array of names of columns to be retrieved (required)

		If a value in the columns array has an integer key, it is retrieved AS itself (in a SQL sense).
		If a value in the columns array has a non-integer key, it is retrieved AS that key.
		Values in the columns array can include table specifiers before the period.

	'source' => Any SQL after FROM, including table names, JOINs, GROUP BY, ORDER BY, WHERE, etc... (required)

	'arguments' => Substitutions in order for $s and #s in the query, applied in qa_db_apply_sub() above (required)

	'arraykey' => Name of column to use for keys of the outer-level returned array, instead of numbers by default

	'arrayvalue' => Name of column to use for values of outer-level returned array, instead of arrays by default

	'single' => If true, return the array for a single row and don't embed it within an outer-level array

	'sortasc' => Sort the output ascending by this column

	'sortdesc' => Sort the output descending by this column


	Why does qa_db_multi_select() combine usually unrelated SELECT statements into a single query?

	Because if the database and web servers are on different computers, there will be latency.
420
	This way we ensure that every read pageview on the site requires as few DB queries as possible, so
Scott committed
421 422 423 424
	that we pay for this latency only one time.

	For writes we worry less, since the user is more likely to be expecting a delay.

425
	If QA_OPTIMIZE_DISTANT_DB is set to false in qa-config.php, we assume zero latency and go back to
Scott committed
426 427 428 429
	simple queries, since this will allow both MySQL and PHP to provide quicker results.
*/


Scott committed
430 431
/**
 * Return the data specified by a single $selectspec - see long comment above.
432
 * @deprecated 1.9.0 Use DbSelect->singleSelect() instead.
433 434
 * @param array $selectspec
 * @return mixed
Scott committed
435 436 437
 */
function qa_db_single_select($selectspec)
{
438
	$dbSelect = qa_service('dbselect');
439 440

	return $dbSelect->singleSelect($selectspec);
Scott committed
441
}
Scott committed
442 443


Scott committed
444 445 446
/**
 * Return the data specified by each element of $selectspecs, where the keys of the
 * returned array match the keys of the supplied $selectspecs array. See long comment above.
447
 * @deprecated 1.9.0 Use DbSelect->multiSelect() instead.
Scott committed
448 449 450 451 452
 * @param array $selectspecs
 * @return array
 */
function qa_db_multi_select($selectspecs)
{
453
	$dbSelect = qa_service('dbselect');
454 455

	return $dbSelect->multiSelect($selectspecs);
Scott committed
456
}
Scott committed
457 458


Scott committed
459 460
/**
 * Post-process $outresult according to $selectspec, applying 'sortasc', 'sortdesc', 'arrayvalue' and 'single'.
461
 * @deprecated 1.9.0 Private method in DbConnection (code left in place for backwards compatibility)
Scott committed
462 463 464 465 466 467 468
 * @param array $outresult
 * @param array $selectspec
 */
function qa_db_post_select(&$outresult, $selectspec)
{
	// PHP's sorting algorithm is not 'stable', so we use '_order_' element to keep stability.
	// By contrast, MySQL's ORDER BY does seem to give the results in a reliable order.
Scott committed
469

Scott committed
470 471
	if (isset($selectspec['sortasc'])) {
		require_once QA_INCLUDE_DIR . 'util/sort.php';
Scott committed
472

Scott committed
473 474 475
		$index = 0;
		foreach ($outresult as $key => $value)
			$outresult[$key]['_order_'] = $index++;
Scott committed
476

Scott committed
477
		qa_sort_by($outresult, $selectspec['sortasc'], '_order_');
Scott committed
478

Scott committed
479 480
	} elseif (isset($selectspec['sortdesc'])) {
		require_once QA_INCLUDE_DIR . 'util/sort.php';
Scott committed
481

Scott committed
482 483
		if (isset($selectspec['sortdesc_2']))
			qa_sort_by($outresult, $selectspec['sortdesc'], $selectspec['sortdesc_2']);
Scott committed
484

Scott committed
485 486 487 488
		else {
			$index = count($outresult);
			foreach ($outresult as $key => $value)
				$outresult[$key]['_order_'] = $index--;
Scott committed
489

Scott committed
490 491
			qa_sort_by($outresult, $selectspec['sortdesc'], '_order_');
		}
Scott committed
492

Scott committed
493
		$outresult = array_reverse($outresult, true);
Scott committed
494 495
	}

Scott committed
496 497 498 499 500 501 502 503 504 505 506 507 508
	if (isset($selectspec['arrayvalue']))
		foreach ($outresult as $key => $value)
			$outresult[$key] = $value[$selectspec['arrayvalue']];

	if (@$selectspec['single'])
		$outresult = count($outresult) ? reset($outresult) : null;
}


/**
 * Return the full results from the $result resource as an array. The key of each element in the returned array
 * is from column $key if specified, otherwise it's integer. The value of each element in the returned array
 * is from column $value if specified, otherwise it's a named array of all columns, given an array of arrays.
509
 * @deprecated 1.9.0 Use DbResult->fetchAllAssoc() instead.
510 511 512
 * @param DbResult|mysqli_result $result
 * @param string|null $key
 * @param int|string|null $value
Scott committed
513 514 515 516
 * @return array
 */
function qa_db_read_all_assoc($result, $key = null, $value = null)
{
517 518 519 520 521
	if ($result instanceof \Q2A\Database\DbResult) {
		return $result->fetchAllAssoc($key, $value);
	}


522
	// backwards compatibility
Scott committed
523 524 525 526 527 528 529 530
	if (!($result instanceof mysqli_result))
		qa_fatal_error('Reading all assoc from invalid result');

	$assocs = array();

	while ($assoc = $result->fetch_assoc()) {
		if (isset($key))
			$assocs[$assoc[$key]] = isset($value) ? $assoc[$value] : $assoc;
Scott committed
531
		else
Scott committed
532
			$assocs[] = isset($value) ? $assoc[$value] : $assoc;
Scott committed
533 534
	}

Scott committed
535 536 537 538 539 540 541
	return $assocs;
}


/**
 * Return the first row from the $result resource as an array of [column name] => [column value].
 * If there's no first row, throw a fatal error unless $allowempty is true.
542
 * @deprecated 1.9.0 Use DbResult->fetchNextAssoc() instead.
543
 * @param DbResult|mysqli_result $result
Scott committed
544 545 546 547 548
 * @param bool $allowempty
 * @return array|null
 */
function qa_db_read_one_assoc($result, $allowempty = false)
{
549 550 551 552
	if ($result instanceof \Q2A\Database\DbResult) {
		return $allowempty ? $result->fetchNextAssoc() : $result->fetchNextAssocOrFail();
	}

553
	// backwards compatibility
Scott committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
	if (!($result instanceof mysqli_result))
		qa_fatal_error('Reading one assoc from invalid result');

	$assoc = $result->fetch_assoc();

	if (is_array($assoc))
		return $assoc;

	if ($allowempty)
		return null;
	else
		qa_fatal_error('Reading one assoc from empty results');
}


/**
 * Return a numbered array containing the first (and presumably only) column from the $result resource.
571
 * @deprecated 1.9.0 Use DbResult->fetchAllValues() instead.
572
 * @param DbResult|mysqli_result $result
Scott committed
573 574 575 576
 * @return array
 */
function qa_db_read_all_values($result)
{
577 578 579 580
	if ($result instanceof \Q2A\Database\DbResult) {
		return $result->fetchAllValues(0);
	}

581
	// backwards compatibility
Scott committed
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
	if (!($result instanceof mysqli_result))
		qa_fatal_error('Reading column from invalid result');

	$output = array();

	while ($row = $result->fetch_row())
		$output[] = $row[0];

	return $output;
}


/**
 * Return the first column of the first row (and presumably only cell) from the $result resource.
 * If there's no first row, throw a fatal error unless $allowempty is true.
597
 * @deprecated 1.9.0 Use DbResult->fetchOneValue() instead.
598
 * @param DbResult|mysqli_result $result
Scott committed
599
 * @param bool $allowempty
600
 * @return string|null
Scott committed
601 602 603
 */
function qa_db_read_one_value($result, $allowempty = false)
{
604 605 606 607
	if ($result instanceof \Q2A\Database\DbResult) {
		return $allowempty ? $result->fetchOneValue(0) : $result->fetchOneValueOrFail(0);
	}

608
	// backwards compatibility
Scott committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
	if (!($result instanceof mysqli_result))
		qa_fatal_error('Reading one value from invalid result');

	$row = $result->fetch_row();

	if (is_array($row))
		return $row[0];

	if ($allowempty)
		return null;
	else
		qa_fatal_error('Reading one value from empty results');
}


/**
 * Suspend the updating of counts (of many different types) in the database, to save time when making a lot of changes
 * if $suspend is true, otherwise reinstate it. A counter is kept to allow multiple calls.
627
 * @deprecated 1.9.0 Use DbConnection->suspendUpdateCounts() instead.
Scott committed
628 629 630 631
 * @param bool $suspend
 */
function qa_suspend_update_counts($suspend = true)
{
632
	qa_service('database')->suspendUpdateCounts($suspend);
Scott committed
633 634 635 636 637
}


/**
 * Returns whether counts should currently be updated (i.e. if count updating has not been suspended).
638
 * @deprecated 1.9.0 Use DbConnection->shouldUpdateCounts() instead.
Scott committed
639
 * @return bool
Scott committed
640 641 642
 */
function qa_should_update_counts()
{
643
	return qa_service('database')->shouldUpdateCounts();
Scott committed
644
}