*/
/**
- * Redis-based caching module for redis server >= 2.6.12
+ * Redis-based caching module for redis server >= 2.6.12 and phpredis >= 2.2.4
*
+ * @see https://github.com/phpredis/phpredis/blob/d310ed7c8/Changelog.md
* @note Avoid use of Redis::MULTI transactions for twemproxy support
*
* @ingroup Cache
$e = null;
try {
// Note that redis does not return false if the key was not there
- $result = ( $conn->delete( $key ) !== false );
+ $result = ( $conn->del( $key ) !== false );
} catch ( RedisException $e ) {
$result = false;
$this->handleException( $conn, $e );
// Avoid delete() with array to reduce CPU hogging from a single request
$conn->multi( Redis::PIPELINE );
foreach ( $batchKeys as $key ) {
- $conn->delete( $key );
+ $conn->del( $key );
}
$batchResult = $conn->exec();
if ( $batchResult === false ) {
$this->doSelectDomain( DatabaseDomain::newFromId( $domain ) );
}
+ /**
+ * @param DatabaseDomain $domain
+ * @throws DBConnectionError
+ * @throws DBError
+ * @since 1.32
+ */
protected function doSelectDomain( DatabaseDomain $domain ) {
$this->currentDomain = $domain;
}
throw $this->newExceptionAfterConnectError( "DB path or directory required" );
}
- if ( !self::isProcessMemoryPath( $path ) && !is_readable( $path ) ) {
+ // Check if the database file already exists but is non-readable
+ if (
+ !self::isProcessMemoryPath( $path ) &&
+ file_exists( $path ) &&
+ !is_readable( $path )
+ ) {
throw $this->newExceptionAfterConnectError( 'SQLite database file is not readable' );
} elseif ( !in_array( $this->trxMode, self::$VALID_TRX_MODES, true ) ) {
throw $this->newExceptionAfterConnectError( "Got mode '{$this->trxMode}' for BEGIN" );
}
try {
+ // Open the database file, creating it if it does not yet exist
$this->conn = new PDO( "sqlite:$path", null, null, $attributes );
} catch ( PDOException $e ) {
throw $this->newExceptionAfterConnectError( $e->getMessage() );
return false;
}
+ protected function doSelectDomain( DatabaseDomain $domain ) {
+ if ( $domain->getSchema() !== null ) {
+ throw new DBExpectedError(
+ $this,
+ __CLASS__ . ": domain '{$domain->getId()}' has a schema component"
+ );
+ }
+
+ $database = $domain->getDatabase();
+ // A null database means "don't care" so leave it as is and update the table prefix
+ if ( $database === null ) {
+ $this->currentDomain = new DatabaseDomain(
+ $this->currentDomain->getDatabase(),
+ null,
+ $domain->getTablePrefix()
+ );
+
+ return true;
+ }
+
+ if ( $database !== $this->getDBname() ) {
+ throw new DBExpectedError(
+ $this,
+ __CLASS__ . ": cannot change database (got '$database')"
+ );
+ }
+
+ return true;
+ }
+
/**
* Use MySQL's naming (accounts for prefix etc) but remove surrounding backticks
*
}
public function serverIsReadOnly() {
+ $this->assertHasConnectionHandle();
+
$path = $this->getDbFilePath();
return ( !self::isProcessMemoryPath( $path ) && !is_writable( $path ) );
*
* @param string $db
* @return bool True unless an exception was thrown
- * @throws DBConnectionError If databasesAreIndependent() is true and an error occurs
- * @throws DBError
+ * @throws DBConnectionError If databasesAreIndependent() is true and connection change fails
+ * @throws DBError On query error or if database changes are disallowed
* @deprecated Since 1.32 Use selectDomain() instead
*/
public function selectDB( $db );
* This should only be called by a load balancer or if the handle is not attached to one
*
* @param string|DatabaseDomain $domain
+ * @throws DBConnectionError If databasesAreIndependent() is true and connection change fails
+ * @throws DBError On query error, if domain changes are disallowed, or the domain is invalid
* @since 1.32
- * @throws DBConnectionError
*/
public function selectDomain( $domain );
$opts->add( 'feed', '' );
$opts->add( 'tagfilter', '' );
$opts->add( 'invert', false );
+ $opts->add( 'associated', false );
$opts->add( 'size-mode', 'max' );
$opts->add( 'size', 0 );
$username = $this->opts->consumeValue( 'username' );
$tagFilterVal = $this->opts->consumeValue( 'tagfilter' );
$nsinvert = $this->opts->consumeValue( 'invert' );
+ $nsassociated = $this->opts->consumeValue( 'associated' );
$size = $this->opts->consumeValue( 'size' );
$max = $this->opts->consumeValue( 'size-mode' ) === 'max';
'default' => $nsinvert,
'tooltip' => 'invert',
],
+ 'nsassociated' => [
+ 'type' => 'check',
+ 'name' => 'associated',
+ 'label-message' => 'namespace_association',
+ 'default' => $nsassociated,
+ 'tooltip' => 'namespace_association',
+ ],
'tagFilter' => [
'type' => 'tagfilter',
'name' => 'tagfilter',
/**
* @ingroup Pager
*/
+use MediaWiki\MediaWikiServices;
+
class NewPagesPager extends ReverseChronologicalPager {
/**
$conds = [];
$conds['rc_new'] = 1;
- $namespace = $this->opts->getValue( 'namespace' );
- $namespace = ( $namespace === 'all' ) ? false : intval( $namespace );
-
$username = $this->opts->getValue( 'username' );
$user = Title::makeTitleSafe( NS_USER, $username );
}
}
- if ( $namespace !== false ) {
- if ( $this->opts->getValue( 'invert' ) ) {
- $conds[] = 'rc_namespace != ' . $this->mDb->addQuotes( $namespace );
- } else {
- $conds['rc_namespace'] = $namespace;
- }
- }
-
if ( $user ) {
$conds[] = ActorMigration::newMigration()->getWhere(
$this->mDb, 'rc_user', User::newFromName( $user->getText(), false ), false
$conds[] = ActorMigration::newMigration()->isAnon( $rcQuery['fields']['rc_user'] );
}
+ $conds = array_merge( $conds, $this->getNamespaceCond() );
+
# If this user cannot see patrolled edits or they are off, don't do dumb queries!
if ( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) {
$conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
return $info;
}
+ // Based on ContribsPager.php
+ function getNamespaceCond() {
+ $namespace = $this->opts->getValue( 'namespace' );
+ if ( $namespace === 'all' || $namespace === '' ) {
+ return [];
+ }
+
+ $namespace = intval( $namespace );
+ $invert = $this->opts->getValue( 'invert' );
+ $associated = $this->opts->getValue( 'associated' );
+
+ $eq_op = $invert ? '!=' : '=';
+ $bool_op = $invert ? 'AND' : 'OR';
+
+ if ( !$associated ) {
+ return [ "rc_namespace $eq_op " . $this->mDb->addQuotes( $namespace ) ];
+ }
+
+ $associatedNS = MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociated( $namespace );
+ return [
+ "rc_namespace $eq_op " . $this->mDb->addQuotes( $namespace ) .
+ $bool_op .
+ " rc_namespace $eq_op " . $this->mDb->addQuotes( $associatedNS )
+ ];
+ }
+
function getIndexField() {
return 'rc_timestamp';
}
* @covers \Wikimedia\Rdbms\DatabasePostgres::selectDB
* @expectedException \Wikimedia\Rdbms\DBConnectionError
*/
- public function testInvalidSelectDBIndependant() {
+ public function testInvalidSelectDBIndependent() {
$dbname = 'unittest-domain'; // explodes if DB is selected
$factory = $this->newLBFactoryMulti(
[ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
[
- 'dbname' => 'do_not_select_me' // explodes if DB is selected
+ // Explodes with SQLite and Postgres during open/USE
+ 'dbname' => 'bad_dir/do_not_select_me'
]
);
$lb = $factory->getMainLB();
- if ( !wfGetDB( DB_MASTER )->databasesAreIndependent() ) {
+ if ( !$lb->getConnection( DB_MASTER )->databasesAreIndependent() ) {
$this->markTestSkipped( "Not applicable per databasesAreIndependent()" );
}
/**
* @covers \Wikimedia\Rdbms\DatabaseSqlite::selectDB
* @covers \Wikimedia\Rdbms\DatabasePostgres::selectDB
- * @expectedException \Wikimedia\Rdbms\DBConnectionError
+ * @expectedException \Wikimedia\Rdbms\DBExpectedError
*/
- public function testInvalidSelectDBIndependant2() {
+ public function testInvalidSelectDBIndependent2() {
$dbname = 'unittest-domain'; // explodes if DB is selected
$factory = $this->newLBFactoryMulti(
[ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
[
- 'dbname' => 'do_not_select_me' // explodes if DB is selected
+ // Explodes with SQLite and Postgres during open/USE
+ 'dbname' => 'bad_dir/do_not_select_me'
]
);
$lb = $factory->getMainLB();
- if ( !wfGetDB( DB_MASTER )->databasesAreIndependent() ) {
+ if ( !$lb->getConnection( DB_MASTER )->databasesAreIndependent() ) {
$this->markTestSkipped( "Not applicable per databasesAreIndependent()" );
}
$db = $lb->getConnection( DB_MASTER );
- \Wikimedia\suppressWarnings();
$db->selectDB( 'garbage-db' );
- \Wikimedia\restoreWarnings();
}
/**