1<?php 2 3namespace Wikimedia\Rdbms; 4 5use InvalidArgumentException; 6 7/** 8 * Helper class used for automatically marking an IDatabase connection as reusable (once it no 9 * longer matters which DB domain is selected) and for deferring the actual network connection 10 * 11 * This uses an RAII-style pattern where calling code is expected to keep the returned reference 12 * handle as a function variable that falls out of scope when no longer needed. This avoids the 13 * need for matching reuseConnection() calls for every "return" statement as well as the tedious 14 * use of try/finally. 15 * 16 * @par Example: 17 * @code 18 * function getRowData() { 19 * $conn = $this->lb->getConnectedRef( DB_REPLICA ); 20 * $row = $conn->select( ... ); 21 * return $row ? (array)$row : false; 22 * // $conn falls out of scope and $this->lb->reuseConnection() gets called 23 * } 24 * @endcode 25 * 26 * @ingroup Database 27 * @since 1.22 28 */ 29class DBConnRef implements IDatabase { 30 /** @var ILoadBalancer */ 31 private $lb; 32 /** @var Database|null Live connection handle */ 33 private $conn; 34 /** @var array N-tuple of (server index, group, DatabaseDomain|string) */ 35 private $params; 36 /** @var int One of DB_PRIMARY/DB_REPLICA */ 37 private $role; 38 39 private const FLD_INDEX = 0; 40 private const FLD_GROUP = 1; 41 private const FLD_DOMAIN = 2; 42 private const FLD_FLAGS = 3; 43 44 /** 45 * @param ILoadBalancer $lb Connection manager for $conn 46 * @param IDatabase|array $conn Database or (server index, query groups, domain, flags) 47 * @param int $role The type of connection asked for; one of DB_PRIMARY/DB_REPLICA 48 * @internal This method should not be called outside of LoadBalancer 49 */ 50 public function __construct( ILoadBalancer $lb, $conn, $role ) { 51 $this->lb = $lb; 52 $this->role = $role; 53 if ( $conn instanceof IDatabase && !( $conn instanceof DBConnRef ) ) { 54 $this->conn = $conn; // live handle 55 } elseif ( is_array( $conn ) && count( $conn ) >= 4 && $conn[self::FLD_DOMAIN] !== false ) { 56 $this->params = $conn; 57 } else { 58 throw new InvalidArgumentException( "Missing lazy connection arguments." ); 59 } 60 } 61 62 public function __call( $name, array $arguments ) { 63 if ( $this->conn === null ) { 64 list( $index, $groups, $wiki, $flags ) = $this->params; 65 $this->conn = $this->lb->getConnection( $index, $groups, $wiki, $flags ); 66 } 67 68 return $this->conn->$name( ...$arguments ); 69 } 70 71 /** 72 * @return int DB_PRIMARY when this *requires* the primary DB, otherwise DB_REPLICA 73 * @since 1.33 74 */ 75 public function getReferenceRole() { 76 return $this->role; 77 } 78 79 public function getServerInfo() { 80 return $this->__call( __FUNCTION__, func_get_args() ); 81 } 82 83 public function getTopologyBasedServerId() { 84 return $this->__call( __FUNCTION__, func_get_args() ); 85 } 86 87 public function getTopologyRole() { 88 return $this->__call( __FUNCTION__, func_get_args() ); 89 } 90 91 public function getTopologyRootPrimary() { 92 return $this->__call( __FUNCTION__, func_get_args() ); 93 } 94 95 public function getTopologyRootMaster() { 96 wfDeprecated( __METHOD__, '1.37' ); 97 return $this->__call( __FUNCTION__, func_get_args() ); 98 } 99 100 public function trxLevel() { 101 return $this->__call( __FUNCTION__, func_get_args() ); 102 } 103 104 public function trxTimestamp() { 105 return $this->__call( __FUNCTION__, func_get_args() ); 106 } 107 108 public function explicitTrxActive() { 109 return $this->__call( __FUNCTION__, func_get_args() ); 110 } 111 112 public function assertNoOpenTransactions() { 113 return $this->__call( __FUNCTION__, func_get_args() ); 114 } 115 116 public function tablePrefix( $prefix = null ) { 117 if ( $this->conn === null && $prefix === null ) { 118 $domain = DatabaseDomain::newFromId( $this->params[self::FLD_DOMAIN] ); 119 // Avoid triggering a database connection 120 return $domain->getTablePrefix(); 121 } elseif ( $this->conn !== null && $prefix === null ) { 122 // This will just return the prefix 123 return $this->__call( __FUNCTION__, func_get_args() ); 124 } 125 // Disallow things that might confuse the LoadBalancer tracking 126 throw $this->getDomainChangeException(); 127 } 128 129 public function dbSchema( $schema = null ) { 130 if ( $this->conn === null && $schema === null ) { 131 $domain = DatabaseDomain::newFromId( $this->params[self::FLD_DOMAIN] ); 132 // Avoid triggering a database connection 133 return $domain->getSchema(); 134 } elseif ( $this->conn !== null && $schema === null ) { 135 // This will just return the schema 136 return $this->__call( __FUNCTION__, func_get_args() ); 137 } 138 // Disallow things that might confuse the LoadBalancer tracking 139 throw $this->getDomainChangeException(); 140 } 141 142 public function getLBInfo( $name = null ) { 143 return $this->__call( __FUNCTION__, func_get_args() ); 144 } 145 146 public function setLBInfo( $nameOrArray, $value = null ) { 147 // @phan-suppress-previous-line PhanPluginNeverReturnMethod 148 // Disallow things that might confuse the LoadBalancer tracking 149 throw $this->getDomainChangeException(); 150 } 151 152 public function implicitOrderby() { 153 return $this->__call( __FUNCTION__, func_get_args() ); 154 } 155 156 public function lastQuery() { 157 return $this->__call( __FUNCTION__, func_get_args() ); 158 } 159 160 public function lastDoneWrites() { 161 return $this->__call( __FUNCTION__, func_get_args() ); 162 } 163 164 public function writesPending() { 165 return $this->__call( __FUNCTION__, func_get_args() ); 166 } 167 168 public function preCommitCallbacksPending() { 169 return $this->__call( __FUNCTION__, func_get_args() ); 170 } 171 172 public function writesOrCallbacksPending() { 173 return $this->__call( __FUNCTION__, func_get_args() ); 174 } 175 176 public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) { 177 return $this->__call( __FUNCTION__, func_get_args() ); 178 } 179 180 public function pendingWriteCallers() { 181 return $this->__call( __FUNCTION__, func_get_args() ); 182 } 183 184 public function pendingWriteRowsAffected() { 185 return $this->__call( __FUNCTION__, func_get_args() ); 186 } 187 188 public function isOpen() { 189 return $this->__call( __FUNCTION__, func_get_args() ); 190 } 191 192 public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) { 193 return $this->__call( __FUNCTION__, func_get_args() ); 194 } 195 196 public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) { 197 return $this->__call( __FUNCTION__, func_get_args() ); 198 } 199 200 public function restoreFlags( $state = self::RESTORE_PRIOR ) { 201 return $this->__call( __FUNCTION__, func_get_args() ); 202 } 203 204 public function getFlag( $flag ) { 205 return $this->__call( __FUNCTION__, func_get_args() ); 206 } 207 208 public function getProperty( $name ) { 209 return $this->__call( __FUNCTION__, func_get_args() ); 210 } 211 212 public function getDomainID() { 213 if ( $this->conn === null ) { 214 $domain = $this->params[self::FLD_DOMAIN]; 215 // Avoid triggering a database connection 216 return $domain instanceof DatabaseDomain ? $domain->getId() : $domain; 217 } 218 219 return $this->__call( __FUNCTION__, func_get_args() ); 220 } 221 222 public function getType() { 223 if ( $this->conn === null ) { 224 // Avoid triggering a database connection 225 if ( $this->params[self::FLD_INDEX] === ILoadBalancer::DB_PRIMARY ) { 226 $index = $this->lb->getWriterIndex(); 227 } else { 228 $index = $this->params[self::FLD_INDEX]; 229 } 230 if ( $index >= 0 ) { 231 // In theory, if $index is DB_REPLICA, the type could vary 232 return $this->lb->getServerType( $index ); 233 } 234 } 235 236 return $this->__call( __FUNCTION__, func_get_args() ); 237 } 238 239 public function fetchObject( $res ) { 240 return $this->__call( __FUNCTION__, func_get_args() ); 241 } 242 243 public function fetchRow( $res ) { 244 return $this->__call( __FUNCTION__, func_get_args() ); 245 } 246 247 public function numRows( $res ) { 248 return $this->__call( __FUNCTION__, func_get_args() ); 249 } 250 251 public function numFields( $res ) { 252 return $this->__call( __FUNCTION__, func_get_args() ); 253 } 254 255 public function fieldName( $res, $n ) { 256 return $this->__call( __FUNCTION__, func_get_args() ); 257 } 258 259 public function insertId() { 260 return $this->__call( __FUNCTION__, func_get_args() ); 261 } 262 263 public function dataSeek( $res, $row ) { 264 return $this->__call( __FUNCTION__, func_get_args() ); 265 } 266 267 public function lastErrno() { 268 return $this->__call( __FUNCTION__, func_get_args() ); 269 } 270 271 public function lastError() { 272 return $this->__call( __FUNCTION__, func_get_args() ); 273 } 274 275 public function affectedRows() { 276 return $this->__call( __FUNCTION__, func_get_args() ); 277 } 278 279 public function getSoftwareLink() { 280 return $this->__call( __FUNCTION__, func_get_args() ); 281 } 282 283 public function getServerVersion() { 284 return $this->__call( __FUNCTION__, func_get_args() ); 285 } 286 287 public function close( $fname = __METHOD__, $owner = null ) { 288 // @phan-suppress-previous-line PhanPluginNeverReturnMethod 289 throw new DBUnexpectedError( $this->conn, 'Cannot close shared connection.' ); 290 } 291 292 public function query( $sql, $fname = __METHOD__, $flags = 0 ) { 293 if ( $this->role !== ILoadBalancer::DB_PRIMARY ) { 294 $flags |= IDatabase::QUERY_REPLICA_ROLE; 295 } 296 297 return $this->__call( __FUNCTION__, [ $sql, $fname, $flags ] ); 298 } 299 300 public function freeResult( $res ) { 301 return $this->__call( __FUNCTION__, func_get_args() ); 302 } 303 304 public function newSelectQueryBuilder() { 305 return $this->__call( __FUNCTION__, func_get_args() ); 306 } 307 308 public function selectField( 309 $table, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = [] 310 ) { 311 return $this->__call( __FUNCTION__, func_get_args() ); 312 } 313 314 public function selectFieldValues( 315 $table, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = [] 316 ): array { 317 return $this->__call( __FUNCTION__, func_get_args() ); 318 } 319 320 public function select( 321 $table, $vars, $conds = '', $fname = __METHOD__, 322 $options = [], $join_conds = [] 323 ) { 324 return $this->__call( __FUNCTION__, func_get_args() ); 325 } 326 327 public function selectSQLText( 328 $table, $vars, $conds = '', $fname = __METHOD__, 329 $options = [], $join_conds = [] 330 ) { 331 return $this->__call( __FUNCTION__, func_get_args() ); 332 } 333 334 public function limitResult( $sql, $limit, $offset = false ) { 335 return $this->__call( __FUNCTION__, func_get_args() ); 336 } 337 338 public function selectRow( 339 $table, $vars, $conds, $fname = __METHOD__, 340 $options = [], $join_conds = [] 341 ) { 342 return $this->__call( __FUNCTION__, func_get_args() ); 343 } 344 345 public function estimateRowCount( 346 $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] 347 ) { 348 return $this->__call( __FUNCTION__, func_get_args() ); 349 } 350 351 public function selectRowCount( 352 $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] 353 ) { 354 return $this->__call( __FUNCTION__, func_get_args() ); 355 } 356 357 public function lockForUpdate( 358 $table, $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] 359 ) { 360 $this->assertRoleAllowsWrites(); 361 362 return $this->__call( __FUNCTION__, func_get_args() ); 363 } 364 365 public function fieldExists( $table, $field, $fname = __METHOD__ ) { 366 return $this->__call( __FUNCTION__, func_get_args() ); 367 } 368 369 public function indexExists( $table, $index, $fname = __METHOD__ ) { 370 return $this->__call( __FUNCTION__, func_get_args() ); 371 } 372 373 public function tableExists( $table, $fname = __METHOD__ ) { 374 return $this->__call( __FUNCTION__, func_get_args() ); 375 } 376 377 public function insert( $table, $rows, $fname = __METHOD__, $options = [] ) { 378 $this->assertRoleAllowsWrites(); 379 380 return $this->__call( __FUNCTION__, func_get_args() ); 381 } 382 383 public function update( $table, $set, $conds, $fname = __METHOD__, $options = [] ) { 384 $this->assertRoleAllowsWrites(); 385 386 return $this->__call( __FUNCTION__, func_get_args() ); 387 } 388 389 public function makeList( array $a, $mode = self::LIST_COMMA ) { 390 return $this->__call( __FUNCTION__, func_get_args() ); 391 } 392 393 public function makeWhereFrom2d( $data, $baseKey, $subKey ) { 394 return $this->__call( __FUNCTION__, func_get_args() ); 395 } 396 397 public function aggregateValue( $valuedata, $valuename = 'value' ) { 398 return $this->__call( __FUNCTION__, func_get_args() ); 399 } 400 401 public function bitNot( $field ) { 402 return $this->__call( __FUNCTION__, func_get_args() ); 403 } 404 405 public function bitAnd( $fieldLeft, $fieldRight ) { 406 return $this->__call( __FUNCTION__, func_get_args() ); 407 } 408 409 public function bitOr( $fieldLeft, $fieldRight ) { 410 return $this->__call( __FUNCTION__, func_get_args() ); 411 } 412 413 public function buildConcat( $stringList ) { 414 return $this->__call( __FUNCTION__, func_get_args() ); 415 } 416 417 public function buildGroupConcatField( 418 $delim, $table, $field, $conds = '', $join_conds = [] 419 ) { 420 return $this->__call( __FUNCTION__, func_get_args() ); 421 } 422 423 public function buildGreatest( $fields, $values ) { 424 return $this->__call( __FUNCTION__, func_get_args() ); 425 } 426 427 public function buildLeast( $fields, $values ) { 428 return $this->__call( __FUNCTION__, func_get_args() ); 429 } 430 431 public function buildSubstring( $input, $startPosition, $length = null ) { 432 return $this->__call( __FUNCTION__, func_get_args() ); 433 } 434 435 public function buildStringCast( $field ) { 436 return $this->__call( __FUNCTION__, func_get_args() ); 437 } 438 439 public function buildIntegerCast( $field ) { 440 return $this->__call( __FUNCTION__, func_get_args() ); 441 } 442 443 public function buildSelectSubquery( 444 $table, $vars, $conds = '', $fname = __METHOD__, 445 $options = [], $join_conds = [] 446 ) { 447 return $this->__call( __FUNCTION__, func_get_args() ); 448 } 449 450 public function databasesAreIndependent() { 451 return $this->__call( __FUNCTION__, func_get_args() ); 452 } 453 454 public function selectDB( $db ) { 455 // @phan-suppress-previous-line PhanPluginNeverReturnMethod 456 // Disallow things that might confuse the LoadBalancer tracking 457 throw $this->getDomainChangeException(); 458 } 459 460 public function selectDomain( $domain ) { 461 // @phan-suppress-previous-line PhanPluginNeverReturnMethod 462 // Disallow things that might confuse the LoadBalancer tracking 463 throw $this->getDomainChangeException(); 464 } 465 466 public function getDBname() { 467 if ( $this->conn === null ) { 468 $domain = DatabaseDomain::newFromId( $this->params[self::FLD_DOMAIN] ); 469 // Avoid triggering a database connection 470 return $domain->getDatabase(); 471 } 472 473 return $this->__call( __FUNCTION__, func_get_args() ); 474 } 475 476 public function getServer() { 477 return $this->__call( __FUNCTION__, func_get_args() ); 478 } 479 480 public function getServerName() { 481 return $this->__call( __FUNCTION__, func_get_args() ); 482 } 483 484 public function addQuotes( $s ) { 485 return $this->__call( __FUNCTION__, func_get_args() ); 486 } 487 488 public function addIdentifierQuotes( $s ) { 489 return $this->__call( __FUNCTION__, func_get_args() ); 490 } 491 492 public function buildLike( $param, ...$params ) { 493 return $this->__call( __FUNCTION__, func_get_args() ); 494 } 495 496 public function anyChar() { 497 return $this->__call( __FUNCTION__, func_get_args() ); 498 } 499 500 public function anyString() { 501 return $this->__call( __FUNCTION__, func_get_args() ); 502 } 503 504 public function nextSequenceValue( $seqName ) { 505 $this->assertRoleAllowsWrites(); 506 507 return $this->__call( __FUNCTION__, func_get_args() ); 508 } 509 510 public function replace( $table, $uniqueKeys, $rows, $fname = __METHOD__ ) { 511 $this->assertRoleAllowsWrites(); 512 513 return $this->__call( __FUNCTION__, func_get_args() ); 514 } 515 516 public function upsert( 517 $table, array $rows, $uniqueKeys, array $set, $fname = __METHOD__ 518 ) { 519 $this->assertRoleAllowsWrites(); 520 521 return $this->__call( __FUNCTION__, func_get_args() ); 522 } 523 524 public function deleteJoin( 525 $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__ 526 ) { 527 $this->assertRoleAllowsWrites(); 528 529 return $this->__call( __FUNCTION__, func_get_args() ); 530 } 531 532 public function delete( $table, $conds, $fname = __METHOD__ ) { 533 $this->assertRoleAllowsWrites(); 534 535 return $this->__call( __FUNCTION__, func_get_args() ); 536 } 537 538 public function insertSelect( 539 $destTable, $srcTable, $varMap, $conds, 540 $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = [] 541 ) { 542 $this->assertRoleAllowsWrites(); 543 544 return $this->__call( __FUNCTION__, func_get_args() ); 545 } 546 547 public function unionSupportsOrderAndLimit() { 548 return $this->__call( __FUNCTION__, func_get_args() ); 549 } 550 551 public function unionQueries( $sqls, $all ) { 552 return $this->__call( __FUNCTION__, func_get_args() ); 553 } 554 555 public function unionConditionPermutations( 556 $table, $vars, array $permute_conds, $extra_conds = '', $fname = __METHOD__, 557 $options = [], $join_conds = [] 558 ) { 559 return $this->__call( __FUNCTION__, func_get_args() ); 560 } 561 562 public function conditional( $cond, $caseTrueExpression, $caseFalseExpression ) { 563 return $this->__call( __FUNCTION__, func_get_args() ); 564 } 565 566 public function strreplace( $orig, $old, $new ) { 567 return $this->__call( __FUNCTION__, func_get_args() ); 568 } 569 570 public function getServerUptime() { 571 return $this->__call( __FUNCTION__, func_get_args() ); 572 } 573 574 public function wasDeadlock() { 575 return $this->__call( __FUNCTION__, func_get_args() ); 576 } 577 578 public function wasLockTimeout() { 579 return $this->__call( __FUNCTION__, func_get_args() ); 580 } 581 582 public function wasConnectionLoss() { 583 return $this->__call( __FUNCTION__, func_get_args() ); 584 } 585 586 public function wasReadOnlyError() { 587 return $this->__call( __FUNCTION__, func_get_args() ); 588 } 589 590 public function wasErrorReissuable() { 591 return $this->__call( __FUNCTION__, func_get_args() ); 592 } 593 594 public function primaryPosWait( DBPrimaryPos $pos, $timeout ) { 595 return $this->__call( __FUNCTION__, func_get_args() ); 596 } 597 598 public function masterPosWait( DBPrimaryPos $pos, $timeout ) { 599 wfDeprecated( __METHOD__, '1.37' ); 600 return $this->__call( __FUNCTION__, func_get_args() ); 601 } 602 603 public function getReplicaPos() { 604 return $this->__call( __FUNCTION__, func_get_args() ); 605 } 606 607 public function getPrimaryPos() { 608 return $this->__call( __FUNCTION__, func_get_args() ); 609 } 610 611 public function getMasterPos() { 612 wfDeprecated( __METHOD__, '1.37' ); 613 return $this->getPrimaryPos(); 614 } 615 616 public function serverIsReadOnly() { 617 return $this->__call( __FUNCTION__, func_get_args() ); 618 } 619 620 public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) { 621 // DB_REPLICA role: caller might want to refresh cache after a REPEATABLE-READ snapshot 622 return $this->__call( __FUNCTION__, func_get_args() ); 623 } 624 625 public function onTransactionCommitOrIdle( callable $callback, $fname = __METHOD__ ) { 626 // DB_REPLICA role: caller might want to refresh cache after a REPEATABLE-READ snapshot 627 return $this->__call( __FUNCTION__, func_get_args() ); 628 } 629 630 public function onTransactionIdle( callable $callback, $fname = __METHOD__ ) { 631 return $this->onTransactionCommitOrIdle( $callback, $fname ); 632 } 633 634 public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) { 635 // DB_REPLICA role: caller might want to refresh cache after a cache mutex is released 636 return $this->__call( __FUNCTION__, func_get_args() ); 637 } 638 639 public function onAtomicSectionCancel( callable $callback, $fname = __METHOD__ ) { 640 return $this->__call( __FUNCTION__, func_get_args() ); 641 } 642 643 public function setTransactionListener( $name, callable $callback = null ) { 644 return $this->__call( __FUNCTION__, func_get_args() ); 645 } 646 647 public function startAtomic( 648 $fname = __METHOD__, $cancelable = IDatabase::ATOMIC_NOT_CANCELABLE 649 ) { 650 // Don't call assertRoleAllowsWrites(); caller might want a REPEATABLE-READ snapshot 651 return $this->__call( __FUNCTION__, func_get_args() ); 652 } 653 654 public function endAtomic( $fname = __METHOD__ ) { 655 // Don't call assertRoleAllowsWrites(); caller might want a REPEATABLE-READ snapshot 656 return $this->__call( __FUNCTION__, func_get_args() ); 657 } 658 659 public function cancelAtomic( $fname = __METHOD__, AtomicSectionIdentifier $sectionId = null ) { 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 doAtomicSection( 665 $fname, callable $callback, $cancelable = self::ATOMIC_NOT_CANCELABLE 666 ) { 667 // Don't call assertRoleAllowsWrites(); caller might want a REPEATABLE-READ snapshot 668 return $this->__call( __FUNCTION__, func_get_args() ); 669 } 670 671 public function begin( $fname = __METHOD__, $mode = IDatabase::TRANSACTION_EXPLICIT ) { 672 return $this->__call( __FUNCTION__, func_get_args() ); 673 } 674 675 public function commit( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { 676 return $this->__call( __FUNCTION__, func_get_args() ); 677 } 678 679 public function rollback( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { 680 return $this->__call( __FUNCTION__, func_get_args() ); 681 } 682 683 public function flushSnapshot( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { 684 return $this->__call( __FUNCTION__, func_get_args() ); 685 } 686 687 public function timestamp( $ts = 0 ) { 688 return $this->__call( __FUNCTION__, func_get_args() ); 689 } 690 691 public function timestampOrNull( $ts = null ) { 692 return $this->__call( __FUNCTION__, func_get_args() ); 693 } 694 695 public function ping( &$rtt = null ) { 696 return func_num_args() 697 ? $this->__call( __FUNCTION__, [ &$rtt ] ) 698 : $this->__call( __FUNCTION__, [] ); // method cares about null vs missing 699 } 700 701 public function getLag() { 702 return $this->__call( __FUNCTION__, func_get_args() ); 703 } 704 705 public function getSessionLagStatus() { 706 return $this->__call( __FUNCTION__, func_get_args() ); 707 } 708 709 public function maxListLen() { 710 return $this->__call( __FUNCTION__, func_get_args() ); 711 } 712 713 public function encodeBlob( $b ) { 714 return $this->__call( __FUNCTION__, func_get_args() ); 715 } 716 717 public function decodeBlob( $b ) { 718 return $this->__call( __FUNCTION__, func_get_args() ); 719 } 720 721 public function setSessionOptions( array $options ) { 722 $this->__call( __FUNCTION__, func_get_args() ); 723 } 724 725 public function setSchemaVars( $vars ) { 726 return $this->__call( __FUNCTION__, func_get_args() ); 727 } 728 729 public function lockIsFree( $lockName, $method ) { 730 $this->assertRoleAllowsWrites(); 731 732 return $this->__call( __FUNCTION__, func_get_args() ); 733 } 734 735 public function lock( $lockName, $method, $timeout = 5, $flags = 0 ) { 736 $this->assertRoleAllowsWrites(); 737 738 return $this->__call( __FUNCTION__, func_get_args() ); 739 } 740 741 public function unlock( $lockName, $method ) { 742 $this->assertRoleAllowsWrites(); 743 744 return $this->__call( __FUNCTION__, func_get_args() ); 745 } 746 747 public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) { 748 $this->assertRoleAllowsWrites(); 749 750 return $this->__call( __FUNCTION__, func_get_args() ); 751 } 752 753 public function namedLocksEnqueue() { 754 return $this->__call( __FUNCTION__, func_get_args() ); 755 } 756 757 public function getInfinity() { 758 return $this->__call( __FUNCTION__, func_get_args() ); 759 } 760 761 public function encodeExpiry( $expiry ) { 762 return $this->__call( __FUNCTION__, func_get_args() ); 763 } 764 765 public function decodeExpiry( $expiry, $format = TS_MW ) { 766 return $this->__call( __FUNCTION__, func_get_args() ); 767 } 768 769 public function setBigSelects( $value = true ) { 770 return $this->__call( __FUNCTION__, func_get_args() ); 771 } 772 773 public function isReadOnly() { 774 return $this->__call( __FUNCTION__, func_get_args() ); 775 } 776 777 public function setTableAliases( array $aliases ) { 778 return $this->__call( __FUNCTION__, func_get_args() ); 779 } 780 781 public function setIndexAliases( array $aliases ) { 782 return $this->__call( __FUNCTION__, func_get_args() ); 783 } 784 785 public function __toString() { 786 if ( $this->conn === null ) { 787 return $this->getType() . ' object #' . spl_object_id( $this ); 788 } 789 790 return $this->__call( __FUNCTION__, func_get_args() ); 791 } 792 793 /** 794 * Error out if the role is not DB_PRIMARY 795 * 796 * Note that the underlying connection may or may not itself be read-only. 797 * It could even be to a writable primary (both server-side and to the application). 798 * This error is meant for the case when a DB_REPLICA handle was requested but a 799 * a write was attempted on that handle regardless. 800 * 801 * In configurations where the primary DB has some generic read load or is the only server, 802 * DB_PRIMARY/DB_REPLICA will sometimes (or always) use the same connection to the primary DB. 803 * This does not effect the role of DBConnRef instances. 804 * @throws DBReadOnlyRoleError 805 */ 806 protected function assertRoleAllowsWrites() { 807 // DB_PRIMARY is "prima facie" writable 808 if ( $this->role !== ILoadBalancer::DB_PRIMARY ) { 809 throw new DBReadOnlyRoleError( $this->conn, "Cannot write with role DB_REPLICA" ); 810 } 811 } 812 813 /** 814 * @return DBUnexpectedError 815 */ 816 protected function getDomainChangeException() { 817 return new DBUnexpectedError( 818 $this, 819 "Cannot directly change the selected DB domain; any underlying connection handle " . 820 "is owned by a LoadBalancer instance and possibly shared with other callers. " . 821 "LoadBalancer automatically manages DB domain re-selection of unused handles." 822 ); 823 } 824 825 /** 826 * Clean up the connection when out of scope 827 */ 828 public function __destruct() { 829 if ( $this->conn ) { 830 $this->lb->reuseConnection( $this->conn ); 831 } 832 } 833} 834 835/** 836 * @since 1.22 837 * @deprecated since 1.29 838 */ 839class_alias( DBConnRef::class, 'DBConnRef' ); 840