Merge "Convert Special:DeletedContributions to use OOUI."
[lhc/web/wiklou.git] / includes / libs / lockmanager / PostgreSqlLockManager.php
1 <?php
2 /**
3 * PostgreSQL version of DBLockManager that supports shared locks.
4 * All locks are non-blocking, which avoids deadlocks.
5 *
6 * @ingroup LockManager
7 */
8 class PostgreSqlLockManager extends DBLockManager {
9 /** @var array Mapping of lock types to the type actually used */
10 protected $lockTypeMap = [
11 self::LOCK_SH => self::LOCK_SH,
12 self::LOCK_UW => self::LOCK_SH,
13 self::LOCK_EX => self::LOCK_EX
14 ];
15
16 protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
17 $status = StatusValue::newGood();
18 if ( !count( $paths ) ) {
19 return $status; // nothing to lock
20 }
21
22 $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
23 $bigints = array_unique( array_map(
24 function ( $key ) {
25 return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
26 },
27 array_map( [ $this, 'sha1Base16Absolute' ], $paths )
28 ) );
29
30 // Try to acquire all the locks...
31 $fields = [];
32 foreach ( $bigints as $bigint ) {
33 $fields[] = ( $type == self::LOCK_SH )
34 ? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
35 : "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
36 }
37 $res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
38 $row = $res->fetchRow();
39
40 if ( in_array( 'f', $row ) ) {
41 // Release any acquired locks if some could not be acquired...
42 $fields = [];
43 foreach ( $row as $kbigint => $ok ) {
44 if ( $ok === 't' ) { // locked
45 $bigint = substr( $kbigint, 1 ); // strip off the "K"
46 $fields[] = ( $type == self::LOCK_SH )
47 ? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
48 : "pg_advisory_unlock({$db->addQuotes( $bigint )})";
49 }
50 }
51 if ( count( $fields ) ) {
52 $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
53 }
54 foreach ( $paths as $path ) {
55 $status->fatal( 'lockmanager-fail-acquirelock', $path );
56 }
57 }
58
59 return $status;
60 }
61
62 /**
63 * @see QuorumLockManager::releaseAllLocks()
64 * @return StatusValue
65 */
66 protected function releaseAllLocks() {
67 $status = StatusValue::newGood();
68
69 foreach ( $this->conns as $lockDb => $db ) {
70 try {
71 $db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
72 } catch ( DBError $e ) {
73 $status->fatal( 'lockmanager-fail-db-release', $lockDb );
74 }
75 }
76
77 return $status;
78 }
79 }