35use InvalidArgumentException;
39use Wikimedia\Assert\Assert;
40use Wikimedia\AtEase\AtEase;
205 return $lb->getConnectionRef( $index, [], $this->dbDomain );
222 # Write to external storage if required
223 if ( $this->useExternalStore ) {
225 $data = $this->extStoreAccess->insert( $data, [
'domain' => $this->dbDomain ] );
232 $flags .=
'external';
241 $old_id = $dbw->nextSequenceValue(
'text_old_id_seq' );
247 'old_flags' => $flags,
252 $textId = $dbw->insertId();
272 public function getBlob( $blobAddress, $queryFlags = 0 ) {
273 Assert::parameterType(
'string', $blobAddress,
'$blobAddress' );
276 $blob = $this->cache->getWithSetCallback(
279 function ( $unused, &$ttl, &$setOpts ) use ( $blobAddress, $queryFlags, &$error ) {
281 list( $result, $errors ) = $this->
fetchBlobs( [ $blobAddress ], $queryFlags );
283 $error = $errors[$blobAddress] ??
null;
284 return $result[$blobAddress];
293 Assert::postcondition( is_string(
$blob ),
'Blob must not be null' );
313 list( $blobsByAddress, $errors ) = $this->
fetchBlobs( $blobAddresses, $queryFlags );
315 $blobsByAddress = array_map(
static function (
$blob ) {
317 }, $blobsByAddress );
319 $result = StatusValue::newGood( $blobsByAddress );
321 foreach ( $errors as $error ) {
322 $result->warning(
'internalerror', $error );
339 $textIdToBlobAddress = [];
342 foreach ( $blobAddresses as $blobAddress ) {
345 }
catch ( InvalidArgumentException $ex ) {
347 $ex->getMessage() .
'. Use findBadBlobs.php to remedy.',
354 if ( $schema ===
'bad' ) {
358 .
": loading known-bad content ($blobAddress), returning empty string"
360 $result[$blobAddress] =
'';
362 } elseif ( $schema ===
'tt' ) {
363 $textId = intval( $id );
365 if ( $textId < 1 || $id !== (
string)$textId ) {
366 $errors[$blobAddress] =
"Bad blob address: $blobAddress."
367 .
' Use findBadBlobs.php to remedy.';
368 $result[$blobAddress] =
false;
371 $textIdToBlobAddress[$textId] = $blobAddress;
373 $errors[$blobAddress] =
"Unknown blob address schema: $schema."
374 .
' Use findBadBlobs.php to remedy.';
375 $result[$blobAddress] =
false;
379 $textIds = array_keys( $textIdToBlobAddress );
381 return [ $result, $errors ];
385 $queryFlags |= DBAccessObjectUtils::hasFlags( $queryFlags, self::READ_LATEST )
386 ? self::READ_LATEST_IMMUTABLE
388 list( $index, $options, $fallbackIndex, $fallbackOptions ) =
389 DBAccessObjectUtils::getDBOptions( $queryFlags );
392 $rows = $dbConnection->select(
394 [
'old_id',
'old_text',
'old_flags' ],
395 [
'old_id' => $textIds ],
402 if ( $dbConnection->numRows( $rows ) !== count( $textIds ) && $fallbackIndex !==
null ) {
403 $fetchedTextIds = [];
404 foreach ( $rows as $row ) {
405 $fetchedTextIds[] = $row->old_id;
407 $missingTextIds = array_diff( $textIds, $fetchedTextIds );
409 $rowsFromFallback = $dbConnection->select(
411 [
'old_id',
'old_text',
'old_flags' ],
412 [
'old_id' => $missingTextIds ],
416 $appendIterator =
new AppendIterator();
417 $appendIterator->append( $rows );
418 $appendIterator->append( $rowsFromFallback );
419 $rows = $appendIterator;
422 foreach ( $rows as $row ) {
423 $blobAddress = $textIdToBlobAddress[$row->old_id];
425 if ( $row->old_text !==
null ) {
426 $blob = $this->
expandBlob( $row->old_text, $row->old_flags, $blobAddress );
428 if (
$blob ===
false ) {
429 $errors[$blobAddress] =
"Bad data in text row {$row->old_id}."
430 .
' Use findBadBlobs.php to remedy.';
432 $result[$blobAddress] =
$blob;
436 if ( count( $result ) !== count( $blobAddresses ) ) {
437 foreach ( $blobAddresses as $blobAddress ) {
438 if ( !isset( $result[$blobAddress ] ) ) {
439 $errors[$blobAddress] =
"Unable to fetch blob at $blobAddress."
440 .
' Use findBadBlobs.php to remedy.';
441 $result[$blobAddress] =
false;
445 return [ $result, $errors ];
459 return $this->cache->makeGlobalKey(
461 $this->dbLoadBalancer->resolveDomainID( $this->dbDomain ),
485 public function expandBlob( $raw, $flags, $cacheKey =
null ) {
486 if ( is_string( $flags ) ) {
487 $flags = explode(
',', $flags );
491 if ( in_array(
'external', $flags ) ) {
493 $parts = explode(
'://', $url, 2 );
494 if ( count( $parts ) == 1 || $parts[1] ==
'' ) {
500 return $this->cache->getWithSetCallback(
503 function () use ( $url, $flags ) {
505 $blob = $this->extStoreAccess
506 ->fetchFromURL( $url, [
'domain' => $this->dbDomain ] );
513 $blob = $this->extStoreAccess->fetchFromURL( $url, [
'domain' => $this->dbDomain ] );
543 $blobFlags[] =
'utf-8';
545 if ( $this->compressBlobs ) {
546 if ( function_exists(
'gzdeflate' ) ) {
547 $deflated = gzdeflate(
$blob );
549 if ( $deflated ===
false ) {
553 $blobFlags[] =
'gzip';
556 wfDebug( __METHOD__ .
" -- no zlib support, not compressing" );
559 return implode(
',', $blobFlags );
578 if ( in_array(
'error', $blobFlags ) ) {
583 if ( in_array(
'gzip', $blobFlags ) ) {
584 # Deal with optional compression of archived pages.
585 # This can be done periodically via maintenance/compressOld.php, and
586 # as pages are saved if $wgCompressRevisions is set.
589 if (
$blob ===
false ) {
590 wfWarn( __METHOD__ .
': gzinflate() failed' );
595 if ( in_array(
'object', $blobFlags ) ) {
596 # Generic compressed storage
598 if ( !is_object( $obj ) ) {
602 $blob = $obj->getText();
606 if (
$blob !==
false && $this->legacyEncoding
607 && !in_array(
'utf-8', $blobFlags ) && !in_array(
'utf8', $blobFlags )
609 # Old revisions kept around in a legacy encoding?
610 # Upconvert on demand.
611 # ("utf8" checked for compatibility with some broken
612 # conversion scripts 2008-12-30)
614 # *input* string. We just ignore those too.
617 AtEase::suppressWarnings();
618 $blob = iconv( $this->legacyEncoding,
'UTF-8//IGNORE',
$blob );
619 AtEase::restoreWarnings();
635 if (
$cache->
getQoS( $cache::ATTR_DURABILITY ) >= $cache::QOS_DURABILITY_RDBMS ) {
637 $ttl = $cache::TTL_UNCACHEABLE;
639 $ttl = $this->cacheExpiry ?: $cache::TTL_UNCACHEABLE;
668 if ( $schema !==
'tt' ) {
672 $textId = intval( $id );
674 if ( !$textId || $id !== (
string)$textId ) {
675 throw new InvalidArgumentException(
"Malformed text_id: $id" );
709 if ( !preg_match(
'/^([-+.\w]+):([^\s?]+)(\?([^\s]*))?$/', $address, $m ) ) {
710 throw new InvalidArgumentException(
"Bad blob address: $address" );
713 $schema = strtolower( $m[1] );
715 $parameters = isset( $m[4] ) ?
wfCgiToArray( $m[4] ) : [];
717 return [ $schema, $id, $parameters ];
721 if ( $this->useExternalStore && $this->extStoreAccess->isReadOnly() ) {
unserialize( $serialized)
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Helper class for DAO classes.
Key/value blob storage for a collection of storage medium types (e.g.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Multi-datacenter aware caching interface.
Interface for database access objects.
Generic interface providing TTL constants for lightweight expiring object stores.