diff options
Diffstat (limited to 'includes/api/ApiQueryBase.php')
-rw-r--r-- | includes/api/ApiQueryBase.php | 208 |
1 files changed, 164 insertions, 44 deletions
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php index 031e3c02..f392186b 100644 --- a/includes/api/ApiQueryBase.php +++ b/includes/api/ApiQueryBase.php @@ -31,12 +31,12 @@ if (!defined('MEDIAWIKI')) { /** * This is a base class for all Query modules. * It provides some common functionality such as constructing various SQL queries. - * - * @addtogroup API + * + * @ingroup API */ abstract class ApiQueryBase extends ApiBase { - private $mQueryModule, $mDb, $tables, $where, $fields, $options; + private $mQueryModule, $mDb, $tables, $where, $fields, $options, $join_conds; public function __construct($query, $moduleName, $paramPrefix = '') { parent :: __construct($query->getMain(), $moduleName, $paramPrefix); @@ -45,13 +45,22 @@ abstract class ApiQueryBase extends ApiBase { $this->resetQueryParams(); } + /** + * Blank the internal arrays with query parameters + */ protected function resetQueryParams() { $this->tables = array (); $this->where = array (); $this->fields = array (); $this->options = array (); + $this->join_conds = array (); } + /** + * Add a set of tables to the internal array + * @param mixed $tables Table name or array of table names + * @param mixed $alias Table alias, or null for no alias. Cannot be used with multiple tables + */ protected function addTables($tables, $alias = null) { if (is_array($tables)) { if (!is_null($alias)) @@ -59,11 +68,38 @@ abstract class ApiQueryBase extends ApiBase { $this->tables = array_merge($this->tables, $tables); } else { if (!is_null($alias)) - $tables = $this->getDB()->tableName($tables) . ' ' . $alias; + $tables = $this->getAliasedName($tables, $alias); $this->tables[] = $tables; } } + + /** + * Get the SQL for a table name with alias + * @param string $table Table name + * @param string $alias Alias + * @return string SQL + */ + protected function getAliasedName($table, $alias) { + return $this->getDB()->tableName($table) . ' ' . $alias; + } + + /** + * Add a set of JOIN conditions to the internal array + * + * JOIN conditions are formatted as array( tablename => array(jointype, conditions) + * e.g. array('page' => array('LEFT JOIN', 'page_id=rev_page')) + * @param array $join_conds JOIN conditions + */ + protected function addJoinConds($join_conds) { + if(!is_array($join_conds)) + ApiBase::dieDebug(__METHOD__, 'Join conditions have to be arrays'); + $this->join_conds = array_merge($this->join_conds, $join_conds); + } + /** + * Add a set of fields to select to the internal array + * @param mixed $value Field name or array of field names + */ protected function addFields($value) { if (is_array($value)) $this->fields = array_merge($this->fields, $value); @@ -71,6 +107,12 @@ abstract class ApiQueryBase extends ApiBase { $this->fields[] = $value; } + /** + * Same as addFields(), but add the fields only if a condition is met + * @param mixed $value See addFields() + * @param bool $condition If false, do nothing + * @return bool $condition + */ protected function addFieldsIf($value, $condition) { if ($condition) { $this->addFields($value); @@ -79,6 +121,15 @@ abstract class ApiQueryBase extends ApiBase { return false; } + /** + * Add a set of WHERE clauses to the internal array. + * Clauses can be formatted as 'foo=bar' or array('foo' => 'bar'), + * the latter only works if the value is a constant (i.e. not another field) + * + * For example, array('foo=bar', 'baz' => 3, 'bla' => 'foo') translates + * to "foo=bar AND baz='3' AND bla='foo'" + * @param mixed $value String or array + */ protected function addWhere($value) { if (is_array($value)) $this->where = array_merge($this->where, $value); @@ -86,6 +137,12 @@ abstract class ApiQueryBase extends ApiBase { $this->where[] = $value; } + /** + * Same as addWhere(), but add the WHERE clauses only if a condition is met + * @param mixed $value See addWhere() + * @param bool $condition If false, do nothing + * @return bool $condition + */ protected function addWhereIf($value, $condition) { if ($condition) { $this->addWhere($value); @@ -94,11 +151,24 @@ abstract class ApiQueryBase extends ApiBase { return false; } + /** + * Equivalent to addWhere(array($field => $value)) + * @param string $field Field name + * @param string $value Value; ignored if nul; + */ protected function addWhereFld($field, $value) { if (!is_null($value)) $this->where[$field] = $value; } + /** + * Add a WHERE clause corresponding to a range, and an ORDER BY + * clause to sort in the right direction + * @param string $field Field name + * @param string $dir If 'newer', sort in ascending order, otherwise sort in descending order + * @param string $start Value to start the list at. If $dir == 'newer' this is the lower boundary, otherwise it's the upper boundary + * @param string $end Value to end the list at. If $dir == 'newer' this is the upper boundary, otherwise it's the lower boundary + */ protected function addWhereRange($field, $dir, $start, $end) { $isDirNewer = ($dir === 'newer'); $after = ($isDirNewer ? '>=' : '<='); @@ -110,11 +180,19 @@ abstract class ApiQueryBase extends ApiBase { if (!is_null($end)) $this->addWhere($field . $before . $db->addQuotes($end)); - + + $order = $field . ($isDirNewer ? '' : ' DESC'); if (!isset($this->options['ORDER BY'])) - $this->addOption('ORDER BY', $field . ($isDirNewer ? '' : ' DESC')); + $this->addOption('ORDER BY', $order); + else + $this->addOption('ORDER BY', $this->options['ORDER BY'] . ', ' . $order); } + /** + * Add an option such as LIMIT or USE INDEX + * @param string $name Option name + * @param string $value Option value + */ protected function addOption($name, $value = null) { if (is_null($value)) $this->options[] = $name; @@ -122,39 +200,71 @@ abstract class ApiQueryBase extends ApiBase { $this->options[$name] = $value; } + /** + * Execute a SELECT query based on the values in the internal arrays + * @param string $method Function the query should be attributed to. You should usually use __METHOD__ here + * @return ResultWrapper + */ protected function select($method) { // getDB has its own profileDBIn/Out calls $db = $this->getDB(); $this->profileDBIn(); - $res = $db->select($this->tables, $this->fields, $this->where, $method, $this->options); + $res = $db->select($this->tables, $this->fields, $this->where, $method, $this->options, $this->join_conds); $this->profileDBOut(); return $res; } + /** + * Estimate the row count for the SELECT query that would be run if we + * called select() right now, and check if it's acceptable. + * @return bool true if acceptable, false otherwise + */ + protected function checkRowCount() { + $db = $this->getDB(); + $this->profileDBIn(); + $rowcount = $db->estimateRowCount($this->tables, $this->fields, $this->where, __METHOD__, $this->options); + $this->profileDBOut(); + + global $wgAPIMaxDBRows; + if($rowcount > $wgAPIMaxDBRows) + return false; + return true; + } + + /** + * Add information (title and namespace) about a Title object to a result array + * @param array $arr Result array � la ApiResult + * @param Title $title Title object + * @param string $prefix Module prefix + */ public static function addTitleInfo(&$arr, $title, $prefix='') { $arr[$prefix . 'ns'] = intval($title->getNamespace()); $arr[$prefix . 'title'] = $title->getPrefixedText(); } - + /** * Override this method to request extra fields from the pageSet * using $pageSet->requestField('fieldName') + * @param ApiPageSet $pageSet */ public function requestExtraData($pageSet) { } /** * Get the main Query module + * @return ApiQuery */ public function getQuery() { return $this->mQueryModule; } /** - * Add sub-element under the page element with the given pageId. + * Add a sub-element under the page element with the given page ID + * @param int $pageId Page ID + * @param array $data Data array � la ApiResult */ protected function addPageSubItems($pageId, $data) { $result = $this->getResult(); @@ -164,19 +274,21 @@ abstract class ApiQueryBase extends ApiBase { $data); } + /** + * Set a query-continue value + * @param $paramName Parameter name + * @param $paramValue Parameter value + */ protected function setContinueEnumParameter($paramName, $paramValue) { - + $paramName = $this->encodeParamName($paramName); $msg = array( $paramName => $paramValue ); - -// This is an alternative continue format as a part of the URL string -// ApiResult :: setContent($msg, $paramName . '=' . urlencode($paramValue)); - $this->getResult()->addValue('query-continue', $this->getModuleName(), $msg); } /** * Get the Query database connection (readonly) + * @return Database */ protected function getDB() { if (is_null($this->mDb)) @@ -186,57 +298,62 @@ abstract class ApiQueryBase extends ApiBase { /** * Selects the query database connection with the given name. - * If no such connection has been requested before, it will be created. - * Subsequent calls with the same $name will return the same connection - * as the first, regardless of $db or $groups new values. + * If no such connection has been requested before, it will be created. + * Subsequent calls with the same $name will return the same connection + * as the first, regardless of $db or $groups new values. + * @param string $name Name to assign to the database connection + * @param int $db One of the DB_* constants + * @param array $groups Query groups + * @return Database */ public function selectNamedDB($name, $db, $groups) { - $this->mDb = $this->getQuery()->getNamedDB($name, $db, $groups); + $this->mDb = $this->getQuery()->getNamedDB($name, $db, $groups); } /** * Get the PageSet object to work on - * @return ApiPageSet data + * @return ApiPageSet */ protected function getPageSet() { return $this->getQuery()->getPageSet(); } /** - * This is a very simplistic utility function - * to convert a non-namespaced title string to a db key. - * It will replace all ' ' with '_' + * Convert a title to a DB key + * @param string $title Page title with spaces + * @return string Page title with underscores */ - public static function titleToKey($title) { - return str_replace(' ', '_', $title); + public function titleToKey($title) { + $t = Title::newFromText($title); + if(!$t) + $this->dieUsageMsg(array('invalidtitle', $title)); + return $t->getDbKey(); } - public static function keyToTitle($key) { - return str_replace('_', ' ', $key); + /** + * The inverse of titleToKey() + * @param string $key Page title with underscores + * @return string Page title with spaces + */ + public function keyToTitle($key) { + $t = Title::newFromDbKey($key); + # This really shouldn't happen but we gotta check anyway + if(!$t) + $this->dieUsageMsg(array('invalidtitle', $key)); + return $t->getPrefixedText(); } - public function getTokenFlag($tokenArr, $action) { - if ($this->getMain()->getRequest()->getVal('callback') !== null) { - // Don't do any session-specific data. - return false; - } - if (in_array($action, $tokenArr)) { - global $wgUser; - if ($wgUser->isAllowed($action)) - return true; - else - $this->dieUsage("Action '$action' is not allowed for the current user", 'permissiondenied'); - } - return false; - } - + /** + * Get version string for use in the API help output + * @return string + */ public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiQueryBase.php 31484 2008-03-03 05:46:20Z brion $'; + return __CLASS__ . ': $Id: ApiQueryBase.php 37083 2008-07-05 11:18:50Z catrope $'; } } /** - * @addtogroup API + * @ingroup API */ abstract class ApiQueryGeneratorBase extends ApiQueryBase { @@ -247,6 +364,10 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { $this->mIsGenerator = false; } + /** + * Switch this module to generator mode. By default, generator mode is + * switched off and the module acts like a normal query module. + */ public function setGeneratorMode() { $this->mIsGenerator = true; } @@ -267,4 +388,3 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { */ public abstract function executeGenerator($resultPageSet); } - |