+ $this->mDb = null;
+ $this->resetQueryParams();
+ }
+
+ /**
+ * Get the cache mode for the data generated by this module. Override
+ * this in the module subclass. For possible return values and other
+ * details about cache modes, see ApiMain::setCacheMode()
+ *
+ * Public caching will only be allowed if *all* the modules that supply
+ * data for a given request return a cache mode of public.
+ */
+ public function getCacheMode( $params ) {
+ return 'private';
+ }
+
+ /**
+ * 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 $tables mixed Table name or array of table names
+ * @param $alias mixed 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 ) ) {
+ ApiBase::dieDebug( __METHOD__, 'Multiple table aliases not supported' );
+ }
+ $this->tables = array_merge( $this->tables, $tables );
+ } else {
+ if ( !is_null( $alias ) ) {
+ $tables = $this->getAliasedName( $tables, $alias );
+ }
+ $this->tables[] = $tables;
+ }
+ }
+
+ /**
+ * Get the SQL for a table name with alias
+ * @param $table string Table name
+ * @param $alias string 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')) . conditions may be a string or an
+ * addWhere()-style array
+ * @param $join_conds array 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 $value mixed Field name or array of field names
+ */
+ protected function addFields( $value ) {
+ if ( is_array( $value ) ) {
+ $this->fields = array_merge( $this->fields, $value );
+ } else {
+ $this->fields[] = $value;
+ }
+ }
+
+ /**
+ * Same as addFields(), but add the fields only if a condition is met
+ * @param $value mixed See addFields()
+ * @param $condition bool If false, do nothing
+ * @return bool $condition
+ */
+ protected function addFieldsIf( $value, $condition ) {
+ if ( $condition ) {
+ $this->addFields( $value );
+ return true;
+ }
+ 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)
+ *
+ * If $value is an empty array, this function does nothing.
+ *
+ * For example, array('foo=bar', 'baz' => 3, 'bla' => 'foo') translates
+ * to "foo=bar AND baz='3' AND bla='foo'"
+ * @param $value mixed String or array
+ */
+ protected function addWhere( $value ) {
+ if ( is_array( $value ) ) {
+ // Sanity check: don't insert empty arrays,
+ // Database::makeList() chokes on them
+ if ( count( $value ) ) {
+ $this->where = array_merge( $this->where, $value );
+ }
+ } else {
+ $this->where[] = $value;
+ }
+ }
+
+ /**
+ * Same as addWhere(), but add the WHERE clauses only if a condition is met
+ * @param $value mixed See addWhere()
+ * @param $condition boolIf false, do nothing
+ * @return bool $condition
+ */
+ protected function addWhereIf( $value, $condition ) {
+ if ( $condition ) {
+ $this->addWhere( $value );
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Equivalent to addWhere(array($field => $value))
+ * @param $field string Field name
+ * @param $value string Value; ignored if null or empty array;
+ */
+ protected function addWhereFld( $field, $value ) {
+ // Use count() to its full documented capabilities to simultaneously
+ // test for null, empty array or empty countable object
+ if ( count( $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 $field string Field name
+ * @param $dir string If 'newer', sort in ascending order, otherwise
+ * sort in descending order
+ * @param $start string Value to start the list at. If $dir == 'newer'
+ * this is the lower boundary, otherwise it's the upper boundary
+ * @param $end string Value to end the list at. If $dir == 'newer' this
+ * is the upper boundary, otherwise it's the lower boundary
+ * @param $sort bool If false, don't add an ORDER BY clause
+ */
+ protected function addWhereRange( $field, $dir, $start, $end, $sort = true ) {
+ $isDirNewer = ( $dir === 'newer' );
+ $after = ( $isDirNewer ? '>=' : '<=' );
+ $before = ( $isDirNewer ? '<=' : '>=' );
+ $db = $this->getDB();
+
+ if ( !is_null( $start ) ) {
+ $this->addWhere( $field . $after . $db->addQuotes( $start ) );
+ }
+
+ if ( !is_null( $end ) ) {
+ $this->addWhere( $field . $before . $db->addQuotes( $end ) );
+ }
+
+ if ( $sort ) {
+ $order = $field . ( $isDirNewer ? '' : ' DESC' );
+ if ( !isset( $this->options['ORDER BY'] ) ) {
+ $this->addOption( 'ORDER BY', $order );
+ } else {
+ $this->addOption( 'ORDER BY', $this->options['ORDER BY'] . ', ' . $order );
+ }
+ }
+ }
+
+ /**
+ * Add an option such as LIMIT or USE INDEX. If an option was set
+ * before, the old value will be overwritten
+ * @param $name string Option name
+ * @param $value string Option value
+ */
+ protected function addOption( $name, $value = null ) {
+ if ( is_null( $value ) ) {
+ $this->options[] = $name;
+ } else {
+ $this->options[$name] = $value;
+ }
+ }
+
+ /**
+ * Execute a SELECT query based on the values in the internal arrays
+ * @param $method string 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, $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 $arr array Result array à la ApiResult
+ * @param $title Title
+ * @param $prefix string Module prefix
+ */
+ public static function addTitleInfo( &$arr, $title, $prefix = '' ) {
+ $arr[$prefix . 'ns'] = intval( $title->getNamespace() );
+ $arr[$prefix . 'title'] = $title->getPrefixedText();