* @file
* @ingroup Database
*/
+namespace Wikimedia\Rdbms;
+
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Wikimedia\ScopedCallback;
-use Wikimedia\Rdbms\TransactionProfiler;
-use Wikimedia\Rdbms\LikeMatch;
-use Wikimedia\Rdbms\DatabaseDomain;
-use Wikimedia\Rdbms\ResultWrapper;
-use Wikimedia\Rdbms\DBMasterPos;
-use Wikimedia\Rdbms\Blob;
use Wikimedia\Timestamp\ConvertibleTimestamp;
-use Wikimedia\Rdbms\IDatabase;
-use Wikimedia\Rdbms\IMaintainableDatabase;
+use MediaWiki;
+use BagOStuff;
+use HashBagOStuff;
+use InvalidArgumentException;
+use Exception;
+use RuntimeException;
/**
* Relational database abstraction object
'oracle' => [],
'mssql' => [],
];
+ static $classAliases = [
+ 'DatabaseMssql' => DatabaseMssql::class,
+ 'DatabaseMysql' => DatabaseMysql::class,
+ 'DatabaseMysqli' => DatabaseMysqli::class,
+ 'DatabaseSqlite' => DatabaseSqlite::class,
+ 'DatabasePostgres' => DatabasePostgres::class
+ ];
$driver = false;
$dbType = strtolower( $dbType );
} else {
$driver = $dbType;
}
+
if ( $driver === false || $driver === '' ) {
throw new InvalidArgumentException( __METHOD__ .
" no viable database extension found for type '$dbType'" );
}
$class = 'Database' . ucfirst( $driver );
+ if ( isset( $classAliases[$class] ) ) {
+ $class = $classAliases[$class];
+ }
+
if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
// Resolve some defaults for b/c
$p['host'] = isset( $p['host'] ) ? $p['host'] : false;
$sql,
$matches
) ) {
+ $isTemp = isset( $this->mSessionTempTables[$matches[1]] );
unset( $this->mSessionTempTables[$matches[1]] );
- return true;
+ return $isTemp;
} elseif ( preg_match(
'/^(?:INSERT\s+(?:\w+\s+)?INTO|UPDATE|DELETE\s+FROM)\s+[`"\']?(\w+)[`"\']?/i',
$sql,
* @param string $s
* @return string
*/
- protected function escapeLikeInternal( $s ) {
- return addcslashes( $s, '\%_' );
+ protected function escapeLikeInternal( $s, $escapeChar = '`' ) {
+ return str_replace( [ $escapeChar, '%', '_' ],
+ [ "{$escapeChar}{$escapeChar}", "{$escapeChar}%", "{$escapeChar}_" ],
+ $s );
}
public function buildLike() {
$s = '';
+ // We use ` instead of \ as the default LIKE escape character, since addQuotes()
+ // may escape backslashes, creating problems of double escaping. The `
+ // character has good cross-DBMS compatibility, avoiding special operators
+ // in MS SQL like ^ and %
+ $escapeChar = '`';
+
foreach ( $params as $value ) {
if ( $value instanceof LikeMatch ) {
$s .= $value->toString();
} else {
- $s .= $this->escapeLikeInternal( $value );
+ $s .= $this->escapeLikeInternal( $value, $escapeChar );
}
}
- return " LIKE {$this->addQuotes( $s )} ";
+ return ' LIKE ' . $this->addQuotes( $s ) . ' ESCAPE ' . $this->addQuotes( $escapeChar ) . ' ';
}
public function anyChar() {
return false;
}
- /**
- * Lock specific tables
- *
- * @param array $read Array of tables to lock for read access
- * @param array $write Array of tables to lock for write access
- * @param string $method Name of caller
- * @param bool $lowPriority Whether to indicate writes to be LOW PRIORITY
- * @return bool
- */
- public function lockTables( $read, $write, $method, $lowPriority = true ) {
+ public function tableLocksHaveTransactionScope() {
return true;
}
- /**
- * Unlock specific tables
- *
- * @param string $method The caller
- * @return bool
- */
- public function unlockTables( $method ) {
+ final public function lockTables( array $read, array $write, $method ) {
+ if ( $this->writesOrCallbacksPending() ) {
+ throw new DBUnexpectedError( $this, "Transaction writes or callbacks still pending." );
+ }
+
+ if ( $this->tableLocksHaveTransactionScope() ) {
+ $this->startAtomic( $method );
+ }
+
+ return $this->doLockTables( $read, $write, $method );
+ }
+
+ protected function doLockTables( array $read, array $write, $method ) {
+ return true;
+ }
+
+ final public function unlockTables( $method ) {
+ if ( $this->tableLocksHaveTransactionScope() ) {
+ $this->endAtomic( $method );
+
+ return true; // locks released on COMMIT/ROLLBACK
+ }
+
+ return $this->doUnlockTables( $method );
+ }
+
+ protected function doUnlockTables( $method ) {
return true;
}
}
}
-class_alias( Database::class, 'DatabaseBase' );
+class_alias( Database::class, 'DatabaseBase' ); // b/c for old name
+class_alias( Database::class, 'Database' ); // b/c global alias