r49084 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r49083‎ | r49084 | r49085 >
Date:15:54, 31 March 2009
Author:catrope
Status:deferred
Tags:
Comment:
querypage-work: Commit initial work
Modified paths:
  • /branches/querypage-work/phase3/includes/QueryPage.php (modified) (history)
  • /branches/querypage-work/phase3/includes/specials/SpecialAncientpages.php (modified) (history)
  • /branches/querypage-work/phase3/includes/specials/SpecialBrokenRedirects.php (modified) (history)
  • /branches/querypage-work/phase3/includes/specials/SpecialDeadendpages.php (modified) (history)

Diff [purge]

Index: branches/querypage-work/phase3/includes/specials/SpecialBrokenRedirects.php
@@ -39,9 +39,33 @@
4040 AND p2.page_namespace IS NULL";
4141 return $sql;
4242 }
 43+
 44+ function getQueryInfo() {
 45+ return array(
 46+ 'tables' => array( 'redirect', 'page AS p1', 'page AS p2' ),
 47+ 'fields' => array( "'{$this->getName()}' AS type",
 48+ 'p1.page_namespace AS namespace',
 49+ 'p1.page_title AS title',
 50+ 'rd_namespace',
 51+ 'rd_title'
 52+ ),
 53+ 'conds' => array( 'rd_namespace >= 0',
 54+ 'p2.page_namespace IS NULL'
 55+ ),
 56+ // TODO test this join
 57+ 'join_conds' => array( 'page AS p1' => array( 'LEFT JOIN', array(
 58+ 'rd_from=p1.page_id',
 59+ ) ),
 60+ 'page AS p2' => array( 'LEFT JOIN', array(
 61+ 'rd_namespace=p2.page_namespace',
 62+ 'rd_title=p2.page_title'
 63+ ) )
 64+ )
 65+ );
 66+ }
4367
4468 function getOrder() {
45 - return '';
 69+ return array ();
4670 }
4771
4872 function formatResult( $skin, $result ) {
Index: branches/querypage-work/phase3/includes/specials/SpecialAncientpages.php
@@ -20,21 +20,23 @@
2121
2222 function isSyndicated() { return false; }
2323
24 - function getSQL() {
 24+ function getQueryInfo() {
 25+ // FIXME convert timestamps elsewhere
 26+ // Possibly add bool returnsTimestamps()
 27+ // FIXME standardize 'name' AS type ?
2528 global $wgDBtype;
26 - $db = wfGetDB( DB_SLAVE );
27 - $page = $db->tableName( 'page' );
28 - $revision = $db->tableName( 'revision' );
2929 $epoch = $wgDBtype == 'mysql' ? 'UNIX_TIMESTAMP(rev_timestamp)' :
30 - 'EXTRACT(epoch FROM rev_timestamp)';
31 - return
32 - "SELECT 'Ancientpages' as type,
33 - page_namespace as namespace,
34 - page_title as title,
35 - $epoch as value
36 - FROM $page, $revision
37 - WHERE page_namespace=".NS_MAIN." AND page_is_redirect=0
38 - AND page_latest=rev_id";
 30+ 'EXTRACT(epoch FROM rev_timestamp)';
 31+ return array(
 32+ 'tables' => array( 'page', 'revision' ),
 33+ 'fields' => array( "'{$this->getName()}' AS type",
 34+ 'page_namespace AS namespace',
 35+ 'page_title AS title',
 36+ "$epoch AS value" ),
 37+ 'conds' => array( 'page_namespace' => NS_MAIN,
 38+ 'page_is_redirect' => 0,
 39+ 'page_latest=rev_id' )
 40+ );
3941 }
4042
4143 function sortDescending() {
Index: branches/querypage-work/phase3/includes/specials/SpecialDeadendpages.php
@@ -47,6 +47,24 @@
4848 "AND page_namespace = 0 " .
4949 "AND page_is_redirect = 0";
5050 }
 51+
 52+ function getQueryInfo() {
 53+ return array(
 54+ 'tables' => array( 'page', 'pagelinks' ),
 55+ 'fields' => array( "'{$this->getName()} AS type",
 56+ 'page_namespace AS namespace',
 57+ 'page_title AS title',
 58+ 'page_title AS value'
 59+ ),
 60+ 'conds' => array( 'pl_from IS NULL',
 61+ 'page_namespace' => NS_MAIN,
 62+ 'page_is_redirect' => 0
 63+ ),
 64+ 'join_conds' => array( 'pagelinks' => array( 'LEFT JOIN', array(
 65+ 'page_id=pl_from'
 66+ ) ) )
 67+ );
 68+ }
5169 }
5270
5371 /**
Index: branches/querypage-work/phase3/includes/QueryPage.php
@@ -60,7 +60,7 @@
6161 * subclasses derive from it.
6262 * @ingroup SpecialPage
6363 */
64 -class QueryPage {
 64+abstract class QueryPage {
6565 /**
6666 * Whether or not we want plain listoutput rather than an ordered list
6767 *
@@ -104,8 +104,14 @@
105105 }
106106
107107 /**
108 - * Subclasses return an SQL query here.
109 - *
 108+ * Subclasses return an SQL query here, formatted as an array with the
 109+ * following keys:
 110+ * tables => Table(s) for passing to Database::select()
 111+ * fields => Field(s) for passing to Database::select(), may be *
 112+ * conds => WHERE conditions
 113+ * options => options
 114+ * join_conds => JOIN conditions
 115+ *
110116 * Note that the query itself should return the following four columns:
111117 * 'type' (your special page's name), 'namespace', 'title', and 'value'
112118 * *in that order*. 'value' is used for sorting.
@@ -116,10 +122,19 @@
117123 * an integer; non-numeric values are useful only for sorting the initial
118124 * query.
119125 *
120 - * Don't include an ORDER or LIMIT clause, this will be added.
 126+ * Don't include an ORDER or LIMIT clause, they will be added
 127+ * @return array
121128 */
122 - function getSQL() {
123 - return "SELECT 'sample' as type, 0 as namespace, 'Sample result' as title, 42 as value";
 129+ abstract function getQueryInfo();
 130+
 131+ /**
 132+ * Subclasses return an array of fields to order by here. Don't append
 133+ * DESC to the field names, that'll be done automatically if
 134+ * sortDescending() returns true
 135+ * @return array
 136+ */
 137+ function getOrderFields() {
 138+ return array('value');
124139 }
125140
126141 /**
@@ -129,11 +144,6 @@
130145 return true;
131146 }
132147
133 - function getOrder() {
134 - return ' ORDER BY value ' .
135 - ($this->sortDescending() ? 'DESC' : '');
136 - }
137 -
138148 /**
139149 * Is this query expensive (for some definition of expensive)? Then we
140150 * don't let it run in miser mode. $wgDisableQueryPages causes all query
@@ -145,7 +155,7 @@
146156 }
147157
148158 /**
149 - * Whether or not the output of the page in question is retrived from
 159+ * Whether or not the output of the page in question is retrieved from
150160 * the database cache.
151161 *
152162 * @return bool
@@ -167,16 +177,17 @@
168178 * Formats the results of the query for display. The skin is the current
169179 * skin; you can use it for making links. The result is a single row of
170180 * result data. You should be able to grab SQL results off of it.
171 - * If the function return "false", the line output will be skipped.
 181+ * If the function returns false, the line output will be skipped.
 182+ * @param $skin Skin
 183+ * @param $result object Result row
 184+ * @return mixed String or false to skip
172185 */
173 - function formatResult( $skin, $result ) {
174 - return '';
175 - }
 186+ abstract function formatResult( $skin, $result );
176187
177188 /**
178189 * The content returned by this function will be output before any result
179190 */
180 - function getPageHeader( ) {
 191+ function getPageHeader() {
181192 return '';
182193 }
183194
@@ -193,15 +204,19 @@
194205 /**
195206 * Some special pages (for example SpecialListusers) might not return the
196207 * current object formatted, but return the previous one instead.
197 - * Setting this to return true, will call one more time wfFormatResult to
198 - * be sure that the very last result is formatted and shown.
 208+ * Setting this to return true will ensure formatResult() is called
 209+ * one more time to make sure that the very last result is formatted
 210+ * as well.
199211 */
200 - function tryLastResult( ) {
 212+ function tryLastResult() {
201213 return false;
202214 }
203215
204216 /**
205217 * Clear the cache and save new results
 218+ * @param $limit mixed Numerical limit or false for no limit
 219+ * @param $ignoreErrors bool If true, don't die on database errors
 220+ * @return mixed Number of rows cached on success, or false on failure
206221 */
207222 function recache( $limit, $ignoreErrors = true ) {
208223 $fname = get_class( $this ) . '::recache';
@@ -211,8 +226,6 @@
212227 return false;
213228 }
214229
215 - $querycache = $dbr->tableName( 'querycache' );
216 -
217230 if ( $ignoreErrors ) {
218231 $ignoreW = $dbw->ignoreErrors( true );
219232 $ignoreR = $dbr->ignoreErrors( true );
@@ -221,10 +234,7 @@
222235 # Clear out any old cached data
223236 $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
224237 # Do query
225 - $sql = $this->getSQL() . $this->getOrder();
226 - if ( $limit !== false )
227 - $sql = $dbr->limitResult( $sql, $limit, 0 );
228 - $res = $dbr->query( $sql, $fname );
 238+ $res = $this->reallyDoQuery( $limit, false );
229239 $num = false;
230240 if ( $res ) {
231241 $num = $dbr->numRows( $res );
@@ -248,7 +258,7 @@
249259 if ( !$dbw->insert( 'querycache', $vals, __METHOD__ ) ) {
250260 // Set result to false to indicate error
251261 $dbr->freeResult( $res );
252 - $res = false;
 262+ $num = false;
253263 }
254264 }
255265 if ( $res ) {
@@ -266,6 +276,68 @@
267277 }
268278 return $num;
269279 }
 280+
 281+ /**
 282+ * Run the query and return the result
 283+ * @param $limit mixed Numerical limit or false for no limit
 284+ * @param $offset mixed Numerical offset or false for no offset
 285+ * @return ResultWrapper
 286+ */
 287+ function reallyDoQuery( $limit, $offset = false ) {
 288+ $fname = get_class( $this ) . "::reallyDoQuery";
 289+ $query = $this->getQueryInfo();
 290+ $order = $this->getOrderFields();
 291+ if( $this->sortDescending() ) {
 292+ foreach( $order as &$field ) {
 293+ $field .= ' DESC';
 294+ }
 295+ }
 296+ if( !is_array( $query['options'] ) ) {
 297+ $options = array ();
 298+ }
 299+ if( count( $order ) ) {
 300+ $query['options']['ORDER BY'] = implode( ', ', $order );
 301+ }
 302+ if( $limit !== false) {
 303+ $query['options']['LIMIT'] = intval( $limit );
 304+ }
 305+ if( $offset !== false) {
 306+ $query['options']['OFFSET'] = intval( $offset );
 307+ }
 308+
 309+ $dbr = wfGetDB( DB_SLAVE );
 310+ $res = $dbr->select( (array)$query['tables'],
 311+ (array)$query['fields'],
 312+ (array)$query['conds'], $fname,
 313+ $query['options'], (array)$query['join_conds']
 314+ );
 315+ return $dbr->resultObject( $res );
 316+ }
 317+
 318+ /**
 319+ * Fetch the query results from the query cache
 320+ * @param $limit mixed Numerical limit or false for no limit
 321+ * @param $offset mixed Numerical offset or false for no offset
 322+ * @return ResultWrapper
 323+ */
 324+ function fetchFromCache( $limit, $offset = false ) {
 325+ $dbr = wfGetDB( DB_SLAVE );
 326+ $options = array ();
 327+ if( $limit !== false ) {
 328+ $options['LIMIT'] = intval( $limit );
 329+ }
 330+ if( $offset !== false) {
 331+ $options['OFFSET'] = intval( $offset );
 332+ }
 333+ $res = $dbr->select( 'querycache', array( 'qc_type',
 334+ 'qc_namespace AS namespace',
 335+ 'qc_title AS title',
 336+ 'qc_value AS value' ),
 337+ array( 'qc_type' => $this->getName() ),
 338+ __METHOD__, $options
 339+ );
 340+ return $dbr->resultObject( $res );
 341+ }
270342
271343 /**
272344 * This is the actual workhorse. It does everything needed to make a
@@ -287,16 +359,12 @@
288360
289361 $wgOut->setSyndicated( $this->isSyndicated() );
290362
 363+ //$res = null;
291364 if ( !$this->isCached() ) {
292 - $sql = $this->getSQL();
 365+ $res = $this->reallyDoQuery( $limit, $offset );
293366 } else {
294367 # Get the cached result
295 - $querycache = $dbr->tableName( 'querycache' );
296 - $type = $dbr->strencode( $sname );
297 - $sql =
298 - "SELECT qc_type as type, qc_namespace as namespace,qc_title as title, qc_value as value
299 - FROM $querycache WHERE qc_type='$type'";
300 -
 368+ $res = $this->fetchFromCache( $limit, $offset );
301369 if( !$this->listoutput ) {
302370
303371 # Fetch the timestamp of this update
@@ -323,9 +391,6 @@
324392
325393 }
326394
327 - $sql .= $this->getOrder();
328 - $sql = $dbr->limitResult($sql, $limit, $offset);
329 - $res = $dbr->query( $sql );
330395 $num = $dbr->numRows($res);
331396
332397 $this->preprocessResults( $dbr, $res );
@@ -466,9 +531,7 @@
467532 $feed->outHeader();
468533
469534 $dbr = wfGetDB( DB_SLAVE );
470 - $sql = $this->getSQL() . $this->getOrder();
471 - $sql = $dbr->limitResult( $sql, $limit, 0 );
472 - $res = $dbr->query( $sql, 'QueryPage::doFeed' );
 535+ $res = $this->reallyDoQuery( $limit, 0 );
473536 while( $obj = $dbr->fetchObject( $res ) ) {
474537 $item = $this->feedResult( $obj );
475538 if( $item ) $feed->outItem( $item );

Follow-up revisions

RevisionCommit summaryAuthorDate
r65311querypage-work2: Merge r49084 skip r49527 (already in trunk)reedy13:49, 20 April 2010

Status & tagging log