Added post-commit callback support to DB classes.
[lhc/web/wiklou.git] / includes / SqlDataUpdate.php
1 <?php
2 /**
3 * Base code for update jobs that put some secondary data extracted
4 * from article content into the database.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
20 *
21 * @file
22 */
23
24 /**
25 * Abstract base class for update jobs that put some secondary data extracted
26 * from article content into the database.
27 *
28 * @note: subclasses should NOT start or commit transactions in their doUpdate() method,
29 * a transaction will automatically be wrapped around the update. Starting another
30 * one would break the outer transaction bracket. If need be, subclasses can override
31 * the beginTransaction() and commitTransaction() methods.
32 */
33 abstract class SqlDataUpdate extends DataUpdate {
34
35 protected $mDb; //!< Database connection reference
36 protected $mOptions; //!< SELECT options to be used (array)
37
38 private $mHasTransaction; //!< bool whether a transaction is open on this object (internal use only!)
39
40 /**
41 * Constructor
42 **/
43 public function __construct( ) {
44 global $wgAntiLockFlags;
45
46 parent::__construct( );
47
48 if ( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) {
49 $this->mOptions = array();
50 } else {
51 $this->mOptions = array( 'FOR UPDATE' );
52 }
53
54 // @todo: get connection only when it's needed? make sure that doesn't break anything, especially transactions!
55 $this->mDb = wfGetDB( DB_MASTER );
56 $this->mHasTransaction = false;
57 }
58
59 /**
60 * Begin a database transaction.
61 *
62 * Because nested transactions are not supportred by the Database class, this implementation
63 * checkes Database::trxLevel() and only opens a transaction if none is yet active.
64 */
65 public function beginTransaction() {
66 // NOTE: nested transactions are not supported, only start a transaction if none is open
67 if ( $this->mDb->trxLevel() === 0 ) {
68 $this->mDb->begin( get_class( $this ) . '::beginTransaction' );
69 $this->mHasTransaction = true;
70 }
71 }
72
73 /**
74 * Commit the database transaction started via beginTransaction (if any).
75 */
76 public function commitTransaction() {
77 if ( $this->mHasTransaction ) {
78 $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
79 }
80 }
81
82 /**
83 * Abort the database transaction started via beginTransaction (if any).
84 */
85 public function abortTransaction() {
86 if ( $this->mHasTransaction ) {
87 $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
88 }
89 }
90
91 /**
92 * Invalidate the cache of a list of pages from a single namespace.
93 * This is intended for use by subclasses.
94 *
95 * @param $namespace Integer
96 * @param $dbkeys Array
97 */
98 protected function invalidatePages( $namespace, Array $dbkeys ) {
99 if ( !count( $dbkeys ) ) {
100 return;
101 }
102
103 /**
104 * Determine which pages need to be updated
105 * This is necessary to prevent the job queue from smashing the DB with
106 * large numbers of concurrent invalidations of the same page
107 */
108 $now = $this->mDb->timestamp();
109 $ids = array();
110 $res = $this->mDb->select( 'page', array( 'page_id' ),
111 array(
112 'page_namespace' => $namespace,
113 'page_title' => $dbkeys,
114 'page_touched < ' . $this->mDb->addQuotes( $now )
115 ), __METHOD__
116 );
117 foreach ( $res as $row ) {
118 $ids[] = $row->page_id;
119 }
120 if ( !count( $ids ) ) {
121 return;
122 }
123
124 /**
125 * Do the update
126 * We still need the page_touched condition, in case the row has changed since
127 * the non-locking select above.
128 */
129 $this->mDb->update( 'page', array( 'page_touched' => $now ),
130 array(
131 'page_id' => $ids,
132 'page_touched < ' . $this->mDb->addQuotes( $now )
133 ), __METHOD__
134 );
135 }
136
137 }