Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
22.69% |
49 / 216 |
|
7.64% |
11 / 144 |
CRAP | |
0.00% |
0 / 1 |
DBConnRef | |
22.69% |
49 / 216 |
|
7.64% |
11 / 144 |
13056.06 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
ensureConnection | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
6 | |||
__call | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getReferenceRole | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getServerInfo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
trxLevel | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
trxTimestamp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
explicitTrxActive | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
tablePrefix | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
dbSchema | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
getLBInfo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setLBInfo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
implicitOrderby | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
lastDoneWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
writesPending | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
writesOrCallbacksPending | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
pendingWriteQueryDuration | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
pendingWriteCallers | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isOpen | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setFlag | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
clearFlag | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
restoreFlags | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFlag | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getProperty | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDomainID | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getType | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
3.07 | |||
insertId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
lastErrno | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
lastError | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
affectedRows | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSoftwareLink | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getServerVersion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
close | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
query | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
newSelectQueryBuilder | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
newUnionQueryBuilder | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
newUpdateQueryBuilder | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
newDeleteQueryBuilder | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
newInsertQueryBuilder | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
newReplaceQueryBuilder | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
selectField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
selectFieldValues | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
select | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
selectSQLText | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
limitResult | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
selectRow | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
estimateRowCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
selectRowCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
lockForUpdate | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
fieldExists | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
indexExists | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
tableExists | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
insert | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
update | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
buildComparison | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
makeList | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
makeWhereFrom2d | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
factorConds | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
bitNot | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
bitAnd | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
bitOr | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildConcat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildGroupConcatField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildGreatest | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildLeast | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildSubstring | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildStringCast | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildIntegerCast | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildExcludedValue | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildSelectSubquery | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
databasesAreIndependent | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
selectDomain | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDBname | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getServer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getServerName | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
addQuotes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
expr | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
andExpr | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
orExpr | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addIdentifierQuotes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildLike | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
anyChar | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
anyString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
replace | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
upsert | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
deleteJoin | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
delete | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
insertSelect | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
unionSupportsOrderAndLimit | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
unionQueries | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
conditional | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
strreplace | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
primaryPosWait | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPrimaryPos | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
serverIsReadOnly | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onTransactionResolution | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onTransactionCommitOrIdle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onTransactionPreCommitOrIdle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setTransactionListener | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
startAtomic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
endAtomic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
cancelAtomic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doAtomicSection | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
begin | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
commit | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
rollback | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
flushSession | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
flushSnapshot | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
timestamp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
timestampOrNull | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
ping | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getLag | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSessionLagStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
encodeBlob | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
decodeBlob | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setSessionOptions | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setSchemaVars | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
lockIsFree | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
lock | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
unlock | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
getScopedLockAndFlush | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
1.12 | |||
getInfinity | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
encodeExpiry | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
decodeExpiry | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isReadOnly | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setTableAliases | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTableAliases | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setIndexAliases | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
tableName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
tableNames | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
tableNamesN | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
sourceFile | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
sourceStream | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
dropTable | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
truncateTable | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
streamStatementEnd | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
duplicateTableStructure | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
indexUnique | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
listTables | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
fieldInfo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
__toString | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
assertRoleAllowsWrites | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getDomainChangeException | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
normalizeServerIndex | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Wikimedia\Rdbms; |
4 | |
5 | use InvalidArgumentException; |
6 | use Stringable; |
7 | |
8 | /** |
9 | * Helper class used for automatically re-using IDatabase connections and lazily |
10 | * establishing the actual network connection to a database host. |
11 | * |
12 | * It does this by deferring to ILoadBalancer::getConnectionInternal, which in |
13 | * turn ensures we share and re-use a single connection for a given database |
14 | * wherever possible. |
15 | * |
16 | * This class previously used an RAII-style pattern where connections would be |
17 | * claimed from a pool, and then added back to the pool for re-use only after |
18 | * the calling code's variable for this object went out of scope (a __destruct |
19 | * got called when the calling function returns or throws). This is no longer |
20 | * needed today as LoadBalancer now permits re-use internally even for |
21 | * overlapping callers, where two pieces of code may both obtain their own |
22 | * DBConnRef object and where both are used alternatingly, and yet still share |
23 | * the same connection. |
24 | * |
25 | * @par Example: |
26 | * @code |
27 | * function getRowData() { |
28 | * $conn = $this->lb->getConnection( DB_REPLICA ); |
29 | * $row = $conn->select( ... ); |
30 | * return $row ? (array)$row : false; |
31 | * } |
32 | * @endcode |
33 | * |
34 | * @ingroup Database |
35 | * @since 1.22 |
36 | */ |
37 | class DBConnRef implements Stringable, IMaintainableDatabase, IDatabaseForOwner { |
38 | /** @var ILoadBalancer */ |
39 | private $lb; |
40 | /** @var Database|null Live connection handle */ |
41 | private $conn; |
42 | /** |
43 | * @var array Map of (DBConnRef::FLD_* constant => connection parameter) |
44 | * @phan-var array{0:int,1:array|string|false,2:DatabaseDomain,3:int} |
45 | */ |
46 | private $params; |
47 | /** @var int One of DB_PRIMARY/DB_REPLICA */ |
48 | private $role; |
49 | |
50 | /** |
51 | * @var int Reference to the $modcount passed to the constructor. |
52 | * $conn is valid if $modCountRef and $modCountFix are the same. |
53 | */ |
54 | private $modCountRef; |
55 | |
56 | /** |
57 | * @var int Last known good value of $modCountRef |
58 | * $conn is valid if $modCountRef and $modCountFix are the same. |
59 | */ |
60 | private $modCountFix; |
61 | |
62 | private const FLD_INDEX = 0; |
63 | private const FLD_GROUPS = 1; |
64 | private const FLD_DOMAIN = 2; |
65 | private const FLD_FLAGS = 3; |
66 | |
67 | /** |
68 | * @internal May not be used outside Rdbms LoadBalancer |
69 | * @param ILoadBalancer $lb Connection manager for $conn |
70 | * @param array $params [server index, query groups, domain, flags] |
71 | * @param int $role The type of connection asked for; one of DB_PRIMARY/DB_REPLICA |
72 | * @param null|int &$modcount Reference to a modification counter. This is for |
73 | * LoadBalancer::reconfigure to indicate that a new connection should be acquired. |
74 | */ |
75 | public function __construct( ILoadBalancer $lb, $params, $role, &$modcount = 0 ) { |
76 | if ( !is_array( $params ) || count( $params ) < 4 || $params[self::FLD_DOMAIN] === false ) { |
77 | throw new InvalidArgumentException( "Missing lazy connection arguments." ); |
78 | } |
79 | |
80 | $params[self::FLD_DOMAIN] = DatabaseDomain::newFromId( $params[self::FLD_DOMAIN] ); |
81 | |
82 | $this->lb = $lb; |
83 | $this->params = $params; |
84 | $this->role = $role; |
85 | |
86 | // $this->conn is valid as long as $this->modCountRef and $this->modCountFix are the same. |
87 | $this->modCountRef = &$modcount; // remember reference |
88 | $this->modCountFix = $modcount; // remember current value |
89 | } |
90 | |
91 | /** |
92 | * Connect to the database if we are not already connected. |
93 | */ |
94 | public function ensureConnection() { |
95 | if ( $this->modCountFix !== $this->modCountRef ) { |
96 | // Discard existing connection, unless we are in an ongoing transaction. |
97 | // This is triggered by LoadBalancer::reconfigure(), to allow changed settings |
98 | // to take effect. The primary use case are replica servers being taken out of |
99 | // rotation, or the primary database changing. |
100 | if ( $this->conn && !$this->conn->trxLevel() ) { |
101 | $this->conn->close(); |
102 | $this->conn = null; |
103 | } |
104 | } |
105 | |
106 | if ( $this->conn === null ) { |
107 | $this->conn = $this->lb->getConnectionInternal( |
108 | $this->params[self::FLD_INDEX], |
109 | $this->params[self::FLD_GROUPS], |
110 | $this->params[self::FLD_DOMAIN]->getId(), |
111 | $this->params[self::FLD_FLAGS] |
112 | ); |
113 | $this->modCountFix = $this->modCountRef; |
114 | } |
115 | |
116 | if ( !$this->params[self::FLD_DOMAIN]->equals( $this->conn->getDomainID() ) ) { |
117 | // The underlying connection handle is likely being shared by other DBConnRef |
118 | // instances in a load balancer. Make sure that each one routes queries by their |
119 | // owner function to the domain that the owner expects. |
120 | $this->conn->selectDomain( $this->params[self::FLD_DOMAIN] ); |
121 | } |
122 | } |
123 | |
124 | public function __call( $name, array $arguments ) { |
125 | $this->ensureConnection(); |
126 | |
127 | return $this->conn->$name( ...$arguments ); |
128 | } |
129 | |
130 | /** |
131 | * @return int DB_PRIMARY when this *requires* the primary DB, otherwise DB_REPLICA |
132 | * @since 1.33 |
133 | */ |
134 | public function getReferenceRole() { |
135 | return $this->role; |
136 | } |
137 | |
138 | public function getServerInfo() { |
139 | return $this->__call( __FUNCTION__, func_get_args() ); |
140 | } |
141 | |
142 | public function trxLevel() { |
143 | return $this->__call( __FUNCTION__, func_get_args() ); |
144 | } |
145 | |
146 | public function trxTimestamp() { |
147 | return $this->__call( __FUNCTION__, func_get_args() ); |
148 | } |
149 | |
150 | public function explicitTrxActive() { |
151 | return $this->__call( __FUNCTION__, func_get_args() ); |
152 | } |
153 | |
154 | public function tablePrefix( $prefix = null ) { |
155 | if ( $prefix !== null ) { |
156 | // Disallow things that might confuse the LoadBalancer tracking |
157 | throw $this->getDomainChangeException(); |
158 | } |
159 | |
160 | if ( $this->conn === null ) { |
161 | // Avoid triggering a database connection |
162 | $prefix = $this->params[self::FLD_DOMAIN]->getTablePrefix(); |
163 | } else { |
164 | // This will just return the prefix |
165 | $prefix = $this->__call( __FUNCTION__, func_get_args() ); |
166 | } |
167 | |
168 | return $prefix; |
169 | } |
170 | |
171 | public function dbSchema( $schema = null ) { |
172 | if ( $schema !== null ) { |
173 | // Disallow things that might confuse the LoadBalancer tracking |
174 | throw $this->getDomainChangeException(); |
175 | } |
176 | |
177 | if ( $this->conn === null ) { |
178 | // Avoid triggering a database connection |
179 | $schema = (string)( $this->params[self::FLD_DOMAIN]->getSchema() ); |
180 | } else { |
181 | // This will just return the schema |
182 | $schema = $this->__call( __FUNCTION__, func_get_args() ); |
183 | } |
184 | |
185 | return $schema; |
186 | } |
187 | |
188 | public function getLBInfo( $name = null ) { |
189 | return $this->__call( __FUNCTION__, func_get_args() ); |
190 | } |
191 | |
192 | public function setLBInfo( $nameOrArray, $value = null ) { |
193 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
194 | // Disallow things that might confuse the LoadBalancer tracking |
195 | throw $this->getDomainChangeException(); |
196 | } |
197 | |
198 | public function implicitOrderby() { |
199 | return $this->__call( __FUNCTION__, func_get_args() ); |
200 | } |
201 | |
202 | public function lastDoneWrites() { |
203 | return $this->__call( __FUNCTION__, func_get_args() ); |
204 | } |
205 | |
206 | public function writesPending() { |
207 | return $this->__call( __FUNCTION__, func_get_args() ); |
208 | } |
209 | |
210 | public function writesOrCallbacksPending() { |
211 | return $this->__call( __FUNCTION__, func_get_args() ); |
212 | } |
213 | |
214 | public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) { |
215 | return $this->__call( __FUNCTION__, func_get_args() ); |
216 | } |
217 | |
218 | public function pendingWriteCallers() { |
219 | return $this->__call( __FUNCTION__, func_get_args() ); |
220 | } |
221 | |
222 | public function isOpen() { |
223 | return $this->__call( __FUNCTION__, func_get_args() ); |
224 | } |
225 | |
226 | public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) { |
227 | return $this->__call( __FUNCTION__, func_get_args() ); |
228 | } |
229 | |
230 | public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) { |
231 | return $this->__call( __FUNCTION__, func_get_args() ); |
232 | } |
233 | |
234 | public function restoreFlags( $state = self::RESTORE_PRIOR ) { |
235 | return $this->__call( __FUNCTION__, func_get_args() ); |
236 | } |
237 | |
238 | public function getFlag( $flag ) { |
239 | return $this->__call( __FUNCTION__, func_get_args() ); |
240 | } |
241 | |
242 | public function getProperty( $name ) { |
243 | return $this->__call( __FUNCTION__, func_get_args() ); |
244 | } |
245 | |
246 | public function getDomainID() { |
247 | if ( $this->conn === null ) { |
248 | // Avoid triggering a database connection |
249 | return $this->params[self::FLD_DOMAIN]->getId(); |
250 | } |
251 | |
252 | return $this->__call( __FUNCTION__, func_get_args() ); |
253 | } |
254 | |
255 | public function getType() { |
256 | if ( $this->conn === null ) { |
257 | // Avoid triggering a database connection |
258 | $index = $this->normalizeServerIndex( $this->params[self::FLD_INDEX] ); |
259 | if ( $index >= 0 ) { |
260 | // In theory, if $index is DB_REPLICA, the type could vary |
261 | return $this->lb->getServerType( $index ); |
262 | } |
263 | } |
264 | |
265 | return $this->__call( __FUNCTION__, func_get_args() ); |
266 | } |
267 | |
268 | public function insertId() { |
269 | return $this->__call( __FUNCTION__, func_get_args() ); |
270 | } |
271 | |
272 | public function lastErrno() { |
273 | return $this->__call( __FUNCTION__, func_get_args() ); |
274 | } |
275 | |
276 | public function lastError() { |
277 | return $this->__call( __FUNCTION__, func_get_args() ); |
278 | } |
279 | |
280 | public function affectedRows() { |
281 | return $this->__call( __FUNCTION__, func_get_args() ); |
282 | } |
283 | |
284 | public function getSoftwareLink() { |
285 | return $this->__call( __FUNCTION__, func_get_args() ); |
286 | } |
287 | |
288 | public function getServerVersion() { |
289 | return $this->__call( __FUNCTION__, func_get_args() ); |
290 | } |
291 | |
292 | public function close( $fname = __METHOD__ ) { |
293 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
294 | throw new DBUnexpectedError( $this->conn, 'Cannot close shared connection.' ); |
295 | } |
296 | |
297 | public function query( $sql, $fname = __METHOD__, $flags = 0 ) { |
298 | if ( $this->role !== ILoadBalancer::DB_PRIMARY ) { |
299 | $flags |= IDatabase::QUERY_REPLICA_ROLE; |
300 | } |
301 | |
302 | return $this->__call( __FUNCTION__, [ $sql, $fname, $flags ] ); |
303 | } |
304 | |
305 | public function newSelectQueryBuilder(): SelectQueryBuilder { |
306 | // Use $this not $this->conn so that the domain is preserved (T326377) |
307 | return new SelectQueryBuilder( $this ); |
308 | } |
309 | |
310 | public function newUnionQueryBuilder(): UnionQueryBuilder { |
311 | // Use $this not $this->conn so that the domain is preserved (T326377) |
312 | return new UnionQueryBuilder( $this ); |
313 | } |
314 | |
315 | public function newUpdateQueryBuilder(): UpdateQueryBuilder { |
316 | // Use $this not $this->conn so that the domain is preserved (T326377) |
317 | return new UpdateQueryBuilder( $this ); |
318 | } |
319 | |
320 | public function newDeleteQueryBuilder(): DeleteQueryBuilder { |
321 | // Use $this not $this->conn so that the domain is preserved (T326377) |
322 | return new DeleteQueryBuilder( $this ); |
323 | } |
324 | |
325 | public function newInsertQueryBuilder(): InsertQueryBuilder { |
326 | // Use $this not $this->conn so that the domain is preserved (T326377) |
327 | return new InsertQueryBuilder( $this ); |
328 | } |
329 | |
330 | public function newReplaceQueryBuilder(): ReplaceQueryBuilder { |
331 | // Use $this not $this->conn so that the domain is preserved (T326377) |
332 | return new ReplaceQueryBuilder( $this ); |
333 | } |
334 | |
335 | public function selectField( |
336 | $tables, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = [] |
337 | ) { |
338 | return $this->__call( __FUNCTION__, func_get_args() ); |
339 | } |
340 | |
341 | public function selectFieldValues( |
342 | $tables, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = [] |
343 | ): array { |
344 | return $this->__call( __FUNCTION__, func_get_args() ); |
345 | } |
346 | |
347 | public function select( |
348 | $tables, $vars, $conds = '', $fname = __METHOD__, |
349 | $options = [], $join_conds = [] |
350 | ) { |
351 | return $this->__call( __FUNCTION__, func_get_args() ); |
352 | } |
353 | |
354 | public function selectSQLText( |
355 | $tables, $vars, $conds = '', $fname = __METHOD__, |
356 | $options = [], $join_conds = [] |
357 | ) { |
358 | return $this->__call( __FUNCTION__, func_get_args() ); |
359 | } |
360 | |
361 | public function limitResult( $sql, $limit, $offset = false ) { |
362 | return $this->__call( __FUNCTION__, func_get_args() ); |
363 | } |
364 | |
365 | public function selectRow( |
366 | $tables, $vars, $conds, $fname = __METHOD__, |
367 | $options = [], $join_conds = [] |
368 | ) { |
369 | return $this->__call( __FUNCTION__, func_get_args() ); |
370 | } |
371 | |
372 | public function estimateRowCount( |
373 | $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] |
374 | ): int { |
375 | return $this->__call( __FUNCTION__, func_get_args() ); |
376 | } |
377 | |
378 | public function selectRowCount( |
379 | $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] |
380 | ): int { |
381 | return $this->__call( __FUNCTION__, func_get_args() ); |
382 | } |
383 | |
384 | public function lockForUpdate( |
385 | $table, $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] |
386 | ) { |
387 | $this->assertRoleAllowsWrites(); |
388 | |
389 | return $this->__call( __FUNCTION__, func_get_args() ); |
390 | } |
391 | |
392 | public function fieldExists( $table, $field, $fname = __METHOD__ ) { |
393 | return $this->__call( __FUNCTION__, func_get_args() ); |
394 | } |
395 | |
396 | public function indexExists( $table, $index, $fname = __METHOD__ ) { |
397 | return $this->__call( __FUNCTION__, func_get_args() ); |
398 | } |
399 | |
400 | public function tableExists( $table, $fname = __METHOD__ ) { |
401 | return $this->__call( __FUNCTION__, func_get_args() ); |
402 | } |
403 | |
404 | public function insert( $table, $rows, $fname = __METHOD__, $options = [] ) { |
405 | $this->assertRoleAllowsWrites(); |
406 | |
407 | return $this->__call( __FUNCTION__, func_get_args() ); |
408 | } |
409 | |
410 | public function update( $table, $set, $conds, $fname = __METHOD__, $options = [] ) { |
411 | $this->assertRoleAllowsWrites(); |
412 | |
413 | return $this->__call( __FUNCTION__, func_get_args() ); |
414 | } |
415 | |
416 | public function buildComparison( string $op, array $conds ): string { |
417 | return $this->__call( __FUNCTION__, func_get_args() ); |
418 | } |
419 | |
420 | public function makeList( array $a, $mode = self::LIST_COMMA ) { |
421 | return $this->__call( __FUNCTION__, func_get_args() ); |
422 | } |
423 | |
424 | public function makeWhereFrom2d( $data, $baseKey, $subKey ) { |
425 | return $this->__call( __FUNCTION__, func_get_args() ); |
426 | } |
427 | |
428 | public function factorConds( $condsArray ) { |
429 | return $this->__call( __FUNCTION__, func_get_args() ); |
430 | } |
431 | |
432 | public function bitNot( $field ) { |
433 | return $this->__call( __FUNCTION__, func_get_args() ); |
434 | } |
435 | |
436 | public function bitAnd( $fieldLeft, $fieldRight ) { |
437 | return $this->__call( __FUNCTION__, func_get_args() ); |
438 | } |
439 | |
440 | public function bitOr( $fieldLeft, $fieldRight ) { |
441 | return $this->__call( __FUNCTION__, func_get_args() ); |
442 | } |
443 | |
444 | public function buildConcat( $stringList ) { |
445 | return $this->__call( __FUNCTION__, func_get_args() ); |
446 | } |
447 | |
448 | public function buildGroupConcatField( |
449 | $delim, $tables, $field, $conds = '', $join_conds = [] |
450 | ) { |
451 | return $this->__call( __FUNCTION__, func_get_args() ); |
452 | } |
453 | |
454 | public function buildGreatest( $fields, $values ) { |
455 | return $this->__call( __FUNCTION__, func_get_args() ); |
456 | } |
457 | |
458 | public function buildLeast( $fields, $values ) { |
459 | return $this->__call( __FUNCTION__, func_get_args() ); |
460 | } |
461 | |
462 | public function buildSubstring( $input, $startPosition, $length = null ) { |
463 | return $this->__call( __FUNCTION__, func_get_args() ); |
464 | } |
465 | |
466 | public function buildStringCast( $field ) { |
467 | return $this->__call( __FUNCTION__, func_get_args() ); |
468 | } |
469 | |
470 | public function buildIntegerCast( $field ) { |
471 | return $this->__call( __FUNCTION__, func_get_args() ); |
472 | } |
473 | |
474 | public function buildExcludedValue( $column ) { |
475 | return $this->__call( __FUNCTION__, func_get_args() ); |
476 | } |
477 | |
478 | public function buildSelectSubquery( |
479 | $tables, $vars, $conds = '', $fname = __METHOD__, |
480 | $options = [], $join_conds = [] |
481 | ) { |
482 | return $this->__call( __FUNCTION__, func_get_args() ); |
483 | } |
484 | |
485 | public function databasesAreIndependent() { |
486 | return $this->__call( __FUNCTION__, func_get_args() ); |
487 | } |
488 | |
489 | public function selectDomain( $domain ) { |
490 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
491 | // Disallow things that might confuse the LoadBalancer tracking |
492 | throw $this->getDomainChangeException(); |
493 | } |
494 | |
495 | public function getDBname() { |
496 | if ( $this->conn === null ) { |
497 | // Avoid triggering a database connection |
498 | return $this->params[self::FLD_DOMAIN]->getDatabase(); |
499 | } |
500 | |
501 | return $this->__call( __FUNCTION__, func_get_args() ); |
502 | } |
503 | |
504 | public function getServer() { |
505 | return $this->__call( __FUNCTION__, func_get_args() ); |
506 | } |
507 | |
508 | public function getServerName() { |
509 | if ( $this->conn === null ) { |
510 | // Avoid triggering a database connection |
511 | $index = $this->normalizeServerIndex( $this->params[self::FLD_INDEX] ); |
512 | if ( $index >= 0 ) { |
513 | // If $index is DB_REPLICA, the server name could vary |
514 | return $this->lb->getServerName( $index ); |
515 | } |
516 | } |
517 | |
518 | return $this->__call( __FUNCTION__, func_get_args() ); |
519 | } |
520 | |
521 | public function addQuotes( $s ) { |
522 | return $this->__call( __FUNCTION__, func_get_args() ); |
523 | } |
524 | |
525 | public function expr( string $field, string $op, $value ): Expression { |
526 | // Does not use __call here to delay creating the db connection |
527 | return new Expression( $field, $op, $value ); |
528 | } |
529 | |
530 | public function andExpr( array $conds ): AndExpressionGroup { |
531 | // Does not use __call here to delay creating the db connection |
532 | return AndExpressionGroup::newFromArray( $conds ); |
533 | } |
534 | |
535 | public function orExpr( array $conds ): OrExpressionGroup { |
536 | // Does not use __call here to delay creating the db connection |
537 | return OrExpressionGroup::newFromArray( $conds ); |
538 | } |
539 | |
540 | public function addIdentifierQuotes( $s ) { |
541 | return $this->__call( __FUNCTION__, func_get_args() ); |
542 | } |
543 | |
544 | public function buildLike( $param, ...$params ) { |
545 | return $this->__call( __FUNCTION__, func_get_args() ); |
546 | } |
547 | |
548 | public function anyChar() { |
549 | return $this->__call( __FUNCTION__, func_get_args() ); |
550 | } |
551 | |
552 | public function anyString() { |
553 | return $this->__call( __FUNCTION__, func_get_args() ); |
554 | } |
555 | |
556 | public function replace( $table, $uniqueKeys, $rows, $fname = __METHOD__ ) { |
557 | $this->assertRoleAllowsWrites(); |
558 | |
559 | return $this->__call( __FUNCTION__, func_get_args() ); |
560 | } |
561 | |
562 | public function upsert( |
563 | $table, array $rows, $uniqueKeys, array $set, $fname = __METHOD__ |
564 | ) { |
565 | $this->assertRoleAllowsWrites(); |
566 | |
567 | return $this->__call( __FUNCTION__, func_get_args() ); |
568 | } |
569 | |
570 | public function deleteJoin( |
571 | $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__ |
572 | ) { |
573 | $this->assertRoleAllowsWrites(); |
574 | |
575 | $this->__call( __FUNCTION__, func_get_args() ); |
576 | } |
577 | |
578 | public function delete( $table, $conds, $fname = __METHOD__ ) { |
579 | $this->assertRoleAllowsWrites(); |
580 | |
581 | return $this->__call( __FUNCTION__, func_get_args() ); |
582 | } |
583 | |
584 | public function insertSelect( |
585 | $destTable, $srcTable, $varMap, $conds, |
586 | $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = [] |
587 | ) { |
588 | $this->assertRoleAllowsWrites(); |
589 | |
590 | return $this->__call( __FUNCTION__, func_get_args() ); |
591 | } |
592 | |
593 | public function unionSupportsOrderAndLimit() { |
594 | return $this->__call( __FUNCTION__, func_get_args() ); |
595 | } |
596 | |
597 | public function unionQueries( $sqls, $all, $options = [] ) { |
598 | return $this->__call( __FUNCTION__, func_get_args() ); |
599 | } |
600 | |
601 | public function conditional( $cond, $caseTrueExpression, $caseFalseExpression ) { |
602 | return $this->__call( __FUNCTION__, func_get_args() ); |
603 | } |
604 | |
605 | public function strreplace( $orig, $old, $new ) { |
606 | return $this->__call( __FUNCTION__, func_get_args() ); |
607 | } |
608 | |
609 | public function primaryPosWait( DBPrimaryPos $pos, $timeout ) { |
610 | return $this->__call( __FUNCTION__, func_get_args() ); |
611 | } |
612 | |
613 | public function getPrimaryPos() { |
614 | return $this->__call( __FUNCTION__, func_get_args() ); |
615 | } |
616 | |
617 | public function serverIsReadOnly() { |
618 | return $this->__call( __FUNCTION__, func_get_args() ); |
619 | } |
620 | |
621 | public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) { |
622 | // DB_REPLICA role: caller might want to refresh cache after a REPEATABLE-READ snapshot |
623 | return $this->__call( __FUNCTION__, func_get_args() ); |
624 | } |
625 | |
626 | public function onTransactionCommitOrIdle( callable $callback, $fname = __METHOD__ ) { |
627 | // DB_REPLICA role: caller might want to refresh cache after a REPEATABLE-READ snapshot |
628 | return $this->__call( __FUNCTION__, func_get_args() ); |
629 | } |
630 | |
631 | public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) { |
632 | // DB_REPLICA role: caller might want to refresh cache after a cache mutex is released |
633 | return $this->__call( __FUNCTION__, func_get_args() ); |
634 | } |
635 | |
636 | public function setTransactionListener( $name, ?callable $callback = null ) { |
637 | return $this->__call( __FUNCTION__, func_get_args() ); |
638 | } |
639 | |
640 | public function startAtomic( |
641 | $fname = __METHOD__, $cancelable = IDatabase::ATOMIC_NOT_CANCELABLE |
642 | ) { |
643 | // Don't call assertRoleAllowsWrites(); caller might want a REPEATABLE-READ snapshot |
644 | return $this->__call( __FUNCTION__, func_get_args() ); |
645 | } |
646 | |
647 | public function endAtomic( $fname = __METHOD__ ) { |
648 | // Don't call assertRoleAllowsWrites(); caller might want a REPEATABLE-READ snapshot |
649 | return $this->__call( __FUNCTION__, func_get_args() ); |
650 | } |
651 | |
652 | public function cancelAtomic( $fname = __METHOD__, ?AtomicSectionIdentifier $sectionId = null ) { |
653 | // Don't call assertRoleAllowsWrites(); caller might want a REPEATABLE-READ snapshot |
654 | return $this->__call( __FUNCTION__, func_get_args() ); |
655 | } |
656 | |
657 | public function doAtomicSection( |
658 | $fname, callable $callback, $cancelable = self::ATOMIC_NOT_CANCELABLE |
659 | ) { |
660 | // Don't call assertRoleAllowsWrites(); caller might want a REPEATABLE-READ snapshot |
661 | return $this->__call( __FUNCTION__, func_get_args() ); |
662 | } |
663 | |
664 | public function begin( $fname = __METHOD__, $mode = IDatabase::TRANSACTION_EXPLICIT ) { |
665 | return $this->__call( __FUNCTION__, func_get_args() ); |
666 | } |
667 | |
668 | public function commit( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { |
669 | return $this->__call( __FUNCTION__, func_get_args() ); |
670 | } |
671 | |
672 | public function rollback( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { |
673 | return $this->__call( __FUNCTION__, func_get_args() ); |
674 | } |
675 | |
676 | public function flushSession( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { |
677 | return $this->__call( __FUNCTION__, func_get_args() ); |
678 | } |
679 | |
680 | public function flushSnapshot( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { |
681 | return $this->__call( __FUNCTION__, func_get_args() ); |
682 | } |
683 | |
684 | public function timestamp( $ts = 0 ) { |
685 | return $this->__call( __FUNCTION__, func_get_args() ); |
686 | } |
687 | |
688 | public function timestampOrNull( $ts = null ) { |
689 | return $this->__call( __FUNCTION__, func_get_args() ); |
690 | } |
691 | |
692 | public function ping( &$rtt = null ) { |
693 | return func_num_args() |
694 | ? $this->__call( __FUNCTION__, [ &$rtt ] ) |
695 | : $this->__call( __FUNCTION__, [] ); // method cares about null vs missing |
696 | } |
697 | |
698 | public function getLag() { |
699 | return $this->__call( __FUNCTION__, func_get_args() ); |
700 | } |
701 | |
702 | public function getSessionLagStatus() { |
703 | return $this->__call( __FUNCTION__, func_get_args() ); |
704 | } |
705 | |
706 | public function encodeBlob( $b ) { |
707 | return $this->__call( __FUNCTION__, func_get_args() ); |
708 | } |
709 | |
710 | public function decodeBlob( $b ) { |
711 | return $this->__call( __FUNCTION__, func_get_args() ); |
712 | } |
713 | |
714 | public function setSessionOptions( array $options ) { |
715 | $this->__call( __FUNCTION__, func_get_args() ); |
716 | } |
717 | |
718 | public function setSchemaVars( $vars ) { |
719 | return $this->__call( __FUNCTION__, func_get_args() ); |
720 | } |
721 | |
722 | public function lockIsFree( $lockName, $method ) { |
723 | $this->assertRoleAllowsWrites(); |
724 | |
725 | return $this->__call( __FUNCTION__, func_get_args() ); |
726 | } |
727 | |
728 | public function lock( $lockName, $method, $timeout = 5, $flags = 0 ) { |
729 | $this->assertRoleAllowsWrites(); |
730 | |
731 | return $this->__call( __FUNCTION__, func_get_args() ); |
732 | } |
733 | |
734 | public function unlock( $lockName, $method ) { |
735 | $this->assertRoleAllowsWrites(); |
736 | |
737 | return $this->__call( __FUNCTION__, func_get_args() ); |
738 | } |
739 | |
740 | public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) { |
741 | $this->assertRoleAllowsWrites(); |
742 | |
743 | return $this->__call( __FUNCTION__, func_get_args() ); |
744 | } |
745 | |
746 | public function getInfinity() { |
747 | return $this->__call( __FUNCTION__, func_get_args() ); |
748 | } |
749 | |
750 | public function encodeExpiry( $expiry ) { |
751 | return $this->__call( __FUNCTION__, func_get_args() ); |
752 | } |
753 | |
754 | public function decodeExpiry( $expiry, $format = TS_MW ) { |
755 | return $this->__call( __FUNCTION__, func_get_args() ); |
756 | } |
757 | |
758 | public function isReadOnly() { |
759 | return $this->__call( __FUNCTION__, func_get_args() ); |
760 | } |
761 | |
762 | public function setTableAliases( array $aliases ) { |
763 | return $this->__call( __FUNCTION__, func_get_args() ); |
764 | } |
765 | |
766 | public function getTableAliases() { |
767 | return $this->__call( __FUNCTION__, func_get_args() ); |
768 | } |
769 | |
770 | public function setIndexAliases( array $aliases ) { |
771 | return $this->__call( __FUNCTION__, func_get_args() ); |
772 | } |
773 | |
774 | public function tableName( $name, $format = 'quoted' ) { |
775 | return $this->__call( __FUNCTION__, func_get_args() ); |
776 | } |
777 | |
778 | public function tableNames( ...$tables ) { |
779 | return $this->__call( __FUNCTION__, func_get_args() ); |
780 | } |
781 | |
782 | public function tableNamesN( ...$tables ) { |
783 | return $this->__call( __FUNCTION__, func_get_args() ); |
784 | } |
785 | |
786 | public function sourceFile( |
787 | $filename, |
788 | ?callable $lineCallback = null, |
789 | ?callable $resultCallback = null, |
790 | $fname = false, |
791 | ?callable $inputCallback = null |
792 | ) { |
793 | $this->assertRoleAllowsWrites(); |
794 | |
795 | return $this->__call( __FUNCTION__, func_get_args() ); |
796 | } |
797 | |
798 | public function sourceStream( |
799 | $fp, |
800 | ?callable $lineCallback = null, |
801 | ?callable $resultCallback = null, |
802 | $fname = __METHOD__, |
803 | ?callable $inputCallback = null |
804 | ) { |
805 | $this->assertRoleAllowsWrites(); |
806 | |
807 | return $this->__call( __FUNCTION__, func_get_args() ); |
808 | } |
809 | |
810 | public function dropTable( $table, $fname = __METHOD__ ) { |
811 | $this->assertRoleAllowsWrites(); |
812 | |
813 | return $this->__call( __FUNCTION__, func_get_args() ); |
814 | } |
815 | |
816 | public function truncateTable( $table, $fname = __METHOD__ ) { |
817 | $this->assertRoleAllowsWrites(); |
818 | |
819 | return $this->__call( __FUNCTION__, func_get_args() ); |
820 | } |
821 | |
822 | public function streamStatementEnd( &$sql, &$newLine ) { |
823 | return $this->__call( __FUNCTION__, [ &$sql, &$newLine ] ); |
824 | } |
825 | |
826 | public function duplicateTableStructure( |
827 | $oldName, $newName, $temporary = false, $fname = __METHOD__ |
828 | ) { |
829 | $this->assertRoleAllowsWrites(); |
830 | |
831 | return $this->__call( __FUNCTION__, func_get_args() ); |
832 | } |
833 | |
834 | public function indexUnique( $table, $index, $fname = __METHOD__ ) { |
835 | return $this->__call( __FUNCTION__, func_get_args() ); |
836 | } |
837 | |
838 | public function listTables( $prefix = null, $fname = __METHOD__ ) { |
839 | return $this->__call( __FUNCTION__, func_get_args() ); |
840 | } |
841 | |
842 | public function fieldInfo( $table, $field ) { |
843 | return $this->__call( __FUNCTION__, func_get_args() ); |
844 | } |
845 | |
846 | public function __toString() { |
847 | if ( $this->conn === null ) { |
848 | return $this->getType() . ' object #' . spl_object_id( $this ); |
849 | } |
850 | |
851 | return $this->__call( __FUNCTION__, func_get_args() ); |
852 | } |
853 | |
854 | /** |
855 | * Error out if the role is not DB_PRIMARY |
856 | * |
857 | * Note that the underlying connection may or may not itself be read-only. |
858 | * It could even be to a writable primary (both server-side and to the application). |
859 | * This error is meant for the case when a DB_REPLICA handle was requested but a |
860 | * a write was attempted on that handle regardless. |
861 | * |
862 | * In configurations where the primary DB has some generic read load or is the only server, |
863 | * DB_PRIMARY/DB_REPLICA will sometimes (or always) use the same connection to the primary DB. |
864 | * This does not effect the role of DBConnRef instances. |
865 | * @throws DBReadOnlyRoleError |
866 | */ |
867 | protected function assertRoleAllowsWrites() { |
868 | // DB_PRIMARY is "prima facie" writable |
869 | if ( $this->role !== ILoadBalancer::DB_PRIMARY ) { |
870 | throw new DBReadOnlyRoleError( $this->conn, "Cannot write with role DB_REPLICA" ); |
871 | } |
872 | } |
873 | |
874 | /** |
875 | * @return DBUnexpectedError |
876 | */ |
877 | protected function getDomainChangeException() { |
878 | return new DBUnexpectedError( |
879 | $this, |
880 | "Cannot directly change the selected DB domain; any underlying connection handle " . |
881 | "is owned by a LoadBalancer instance and possibly shared with other callers. " . |
882 | "LoadBalancer automatically manages DB domain re-selection of unused handles." |
883 | ); |
884 | } |
885 | |
886 | /** |
887 | * @param int $i Specific or virtual (DB_PRIMARY/DB_REPLICA) server index |
888 | * @return int|mixed |
889 | */ |
890 | protected function normalizeServerIndex( $i ) { |
891 | return ( $i === ILoadBalancer::DB_PRIMARY ) ? ServerInfo::WRITER_INDEX : $i; |
892 | } |
893 | } |