Merge "rdbms: fix table prefixing in "FOR UPDATE" clause generation in Postgres" into REL1_43
This commit is contained in:
commit
787b2e4603
9 changed files with 55 additions and 26 deletions
|
|
@ -235,7 +235,7 @@ class ApiQueryBlocks extends ApiQueryBase {
|
|||
|
||||
if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
|
||||
$this->addWhere(
|
||||
$this->hideUserUtils->getExpression( $db, $db->tableName( 'block_target' ) . '.bt_user' )
|
||||
$this->hideUserUtils->getExpression( $db, 'block_target.bt_user' )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ class PostgresInstaller extends DatabaseInstaller {
|
|||
$conn = $status->getDB();
|
||||
$superuser = $this->getVar( '_InstallUser' );
|
||||
|
||||
$row = $conn->selectRow( '"pg_catalog"."pg_roles"', '*',
|
||||
$row = $conn->selectRow( 'pg_catalog.pg_roles', '*',
|
||||
[ 'rolname' => $superuser ], __METHOD__ );
|
||||
|
||||
return $row;
|
||||
|
|
@ -247,9 +247,9 @@ class PostgresInstaller extends DatabaseInstaller {
|
|||
return false;
|
||||
}
|
||||
$conn = $status->getDB();
|
||||
$installerId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid',
|
||||
$installerId = $conn->selectField( 'pg_catalog.pg_roles', 'oid',
|
||||
[ 'rolname' => $this->getVar( '_InstallUser' ) ], __METHOD__ );
|
||||
$webId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid',
|
||||
$webId = $conn->selectField( 'pg_catalog.pg_roles', 'oid',
|
||||
[ 'rolname' => $this->getVar( 'wgDBuser' ) ], __METHOD__ );
|
||||
|
||||
return $this->isRoleMember( $conn, $installerId, $webId, $this->maxRoleSearchDepth );
|
||||
|
|
@ -269,7 +269,7 @@ class PostgresInstaller extends DatabaseInstaller {
|
|||
return true;
|
||||
}
|
||||
// Get all members of the given group
|
||||
$res = $conn->select( '"pg_catalog"."pg_auth_members"', [ 'member' ],
|
||||
$res = $conn->select( 'pg_catalog.pg_auth_members', [ 'member' ],
|
||||
[ 'roleid' => $group ], __METHOD__ );
|
||||
foreach ( $res as $row ) {
|
||||
if ( $row->member == $targetMember ) {
|
||||
|
|
@ -319,7 +319,7 @@ class PostgresInstaller extends DatabaseInstaller {
|
|||
$conn = $status->getDB();
|
||||
$dbName = $this->getVar( 'wgDBname' );
|
||||
|
||||
$exists = (bool)$conn->selectField( '"pg_catalog"."pg_database"', '1',
|
||||
$exists = (bool)$conn->selectField( 'pg_catalog.pg_database', '1',
|
||||
[ 'datname' => $dbName ], __METHOD__ );
|
||||
if ( !$exists ) {
|
||||
$safedb = $conn->addIdentifierQuotes( $dbName );
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ EOT;
|
|||
$module = DatabaseSqlite::getFulltextSearchModule();
|
||||
$searchIndexSql = (string)$this->db->newSelectQueryBuilder()
|
||||
->select( 'sql' )
|
||||
->from( $this->db->addIdentifierQuotes( 'sqlite_master' ) )
|
||||
->from( 'sqlite_master' )
|
||||
->where( [ 'tbl_name' => $this->db->tableName( 'searchindex', 'raw' ) ] )
|
||||
->caller( __METHOD__ )->fetchField();
|
||||
$fts3tTable = ( stristr( $searchIndexSql, 'fts' ) !== false );
|
||||
|
|
|
|||
|
|
@ -85,24 +85,23 @@ class PostgresPlatform extends SQLPlatform {
|
|||
reset( $toCheck );
|
||||
while ( $toCheck ) {
|
||||
$alias = key( $toCheck );
|
||||
$name = $toCheck[$alias];
|
||||
$table = $toCheck[$alias];
|
||||
unset( $toCheck[$alias] );
|
||||
|
||||
$hasAlias = !is_numeric( $alias );
|
||||
if ( !$hasAlias && is_string( $name ) ) {
|
||||
$alias = $name;
|
||||
if ( !is_string( $alias ) ) {
|
||||
// No alias? Set it equal to the table name
|
||||
$alias = $table;
|
||||
}
|
||||
|
||||
if ( !isset( $join_conds[$alias] ) ||
|
||||
!preg_match( '/^(?:LEFT|RIGHT|FULL)(?: OUTER)? JOIN$/i', $join_conds[$alias][0] )
|
||||
) {
|
||||
if ( is_array( $name ) ) {
|
||||
if ( is_array( $table ) ) {
|
||||
// It's a parenthesized group, process all the tables inside the group.
|
||||
$toCheck = array_merge( $toCheck, $name );
|
||||
$toCheck = array_merge( $toCheck, $table );
|
||||
} else {
|
||||
// Quote alias names so $this->tableName() won't mangle them
|
||||
$options['FOR UPDATE'][] = $hasAlias ?
|
||||
$this->addIdentifierQuotes( $alias ) : $alias;
|
||||
// If an alias is declared, then any FOR UPDATE FOR must use it
|
||||
$options['FOR UPDATE'][] = $alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -135,8 +134,10 @@ class PostgresPlatform extends SQLPlatform {
|
|||
$preLimitTail .= $this->makeOrderBy( $options );
|
||||
|
||||
if ( isset( $options['FOR UPDATE'] ) ) {
|
||||
$postLimitTail .= ' FOR UPDATE OF ' .
|
||||
implode( ', ', array_map( [ $this, 'tableName' ], $options['FOR UPDATE'] ) );
|
||||
$postLimitTail .= ' FOR UPDATE OF ' . implode(
|
||||
', ',
|
||||
array_map( [ $this, 'addIdentifierQuotes' ], $options['FOR UPDATE'] )
|
||||
);
|
||||
} elseif ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
|
||||
$postLimitTail .= ' FOR UPDATE';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class SQLPlatform implements ISQLPlatform {
|
|||
public function addIdentifierQuotes( $s ) {
|
||||
if ( strcspn( $s, "\0\"`'." ) !== strlen( $s ) ) {
|
||||
throw new DBLanguageError(
|
||||
"Identifier must not contain quote, dot or null characters"
|
||||
"Identifier must not contain quote, dot or null characters: got '$s'"
|
||||
);
|
||||
}
|
||||
$quoteChar = $this->getIdentifierQuoteChar();
|
||||
|
|
@ -1027,15 +1027,24 @@ class SQLPlatform implements ISQLPlatform {
|
|||
throw new InvalidArgumentException( "Table must be a string or Subquery" );
|
||||
}
|
||||
|
||||
if ( $alias === false || $alias === $table ) {
|
||||
if ( $alias === false ) {
|
||||
if ( $table instanceof Subquery ) {
|
||||
throw new InvalidArgumentException( "Subquery table missing alias" );
|
||||
}
|
||||
|
||||
return $quotedTable;
|
||||
$quotedTableWithAnyAlias = $quotedTable;
|
||||
} elseif (
|
||||
$alias === $table &&
|
||||
(
|
||||
str_contains( $alias, '.' ) ||
|
||||
$this->tableName( $alias, 'raw' ) === $table
|
||||
)
|
||||
) {
|
||||
$quotedTableWithAnyAlias = $quotedTable;
|
||||
} else {
|
||||
return $quotedTable . ' ' . $this->addIdentifierQuotes( $alias );
|
||||
$quotedTableWithAnyAlias = $quotedTable . ' ' . $this->addIdentifierQuotes( $alias );
|
||||
}
|
||||
|
||||
return $quotedTableWithAnyAlias;
|
||||
}
|
||||
|
||||
public function tableName( string $name, $format = 'quoted' ) {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class SearchSqlite extends SearchDatabase {
|
|||
$dbr = $this->dbProvider->getReplicaDatabase();
|
||||
$sql = (string)$dbr->newSelectQueryBuilder()
|
||||
->select( 'sql' )
|
||||
->from( $dbr->addIdentifierQuotes( 'sqlite_master' ) )
|
||||
->from( 'sqlite_master' )
|
||||
->where( [ 'tbl_name' => $dbr->tableName( 'searchindex', 'raw' ) ] )
|
||||
->caller( __METHOD__ )->fetchField();
|
||||
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@ class BlockListPager extends TablePager {
|
|||
# With multiblocks we can't just rely on bl_deleted in the row being formatted
|
||||
$info['fields']['hu_deleted'] = $this->hideUserUtils->getExpression(
|
||||
$db,
|
||||
$db->tableName( 'block_target' ) . '.bt_user',
|
||||
'block_target.bt_user',
|
||||
HideUserUtils::HIDDEN_USERS );
|
||||
return $info;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -987,6 +987,11 @@ class RevisionStoreDbTest extends MediaWikiIntegrationTestCase {
|
|||
$this->assertSame( $revRecord->getId(), $storeRecord->getId() );
|
||||
$this->assertTrue( $storeRecord->getSlot( SlotRecord::MAIN )->getContent()->equals( $content ) );
|
||||
$this->assertSame( __METHOD__, $storeRecord->getComment()->text );
|
||||
|
||||
$storeRecord = $store->getRevisionByPageId( $page->getId(), 0, IDBAccessObject::READ_LOCKING );
|
||||
$this->assertSame( $revRecord->getId(), $storeRecord->getId() );
|
||||
$this->assertTrue( $storeRecord->getSlot( SlotRecord::MAIN )->getContent()->equals( $content ) );
|
||||
$this->assertSame( __METHOD__, $storeRecord->getComment()->text );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -541,6 +541,20 @@ class DatabaseMySQLTest extends TestCase {
|
|||
$wdb->platform = new MySQLPlatform( new AddQuoterMock() );
|
||||
|
||||
/** @var IDatabase $db */
|
||||
$db->setTableAliases( [
|
||||
'meow' => [ 'dbname' => 'feline', 'schema' => null, 'prefix' => '' ]
|
||||
] );
|
||||
$sql = $db->newSelectQueryBuilder()
|
||||
->select( 'field' )
|
||||
->from( 'meow' )
|
||||
->where( [ 'a' => 'x' ] )
|
||||
->caller( __METHOD__ )->getSQL();
|
||||
|
||||
$this->assertSameSql(
|
||||
"SELECT field FROM `feline`.`meow` `meow` WHERE a = 'x' ",
|
||||
$sql
|
||||
);
|
||||
|
||||
$db->setTableAliases( [
|
||||
'meow' => [ 'dbname' => 'feline', 'schema' => null, 'prefix' => 'cat_' ]
|
||||
] );
|
||||
|
|
@ -551,7 +565,7 @@ class DatabaseMySQLTest extends TestCase {
|
|||
->caller( __METHOD__ )->getSQL();
|
||||
|
||||
$this->assertSameSql(
|
||||
"SELECT field FROM `feline`.`cat_meow` WHERE a = 'x' ",
|
||||
"SELECT field FROM `feline`.`cat_meow` `meow` WHERE a = 'x' ",
|
||||
$sql
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue