namespace Wikimedia\Rdbms;
use Exception;
+use RuntimeException;
use stdClass;
use Wikimedia\AtEase\AtEase;
/** @var string[] */
protected $ignoreErrors = [];
- public function implicitGroupby() {
- return false;
- }
-
public function implicitOrderby() {
return false;
}
}
protected function open( $server, $user, $password, $dbName, $schema, $tablePrefix ) {
- // Test for driver support, to avoid suppressed fatal error
if ( !function_exists( 'sqlsrv_connect' ) ) {
throw new DBConnectionError(
$this,
- "Microsoft SQL Server Native (sqlsrv) functions missing.
- You can download the driver from: http://go.microsoft.com/fwlink/?LinkId=123470\n"
+ "Microsoft SQL Server Native (sqlsrv) functions missing.\n
+ You can download the driver from: http://go.microsoft.com/fwlink/?LinkId=123470"
);
}
$this->close();
+
+ if ( $schema !== null ) {
+ throw $this->newExceptionAfterConnectError( "Got schema '$schema'; not supported." );
+ }
+
$this->server = $server;
$this->user = $user;
$this->password = $password;
$connectionInfo = [];
-
- if ( $dbName != '' ) {
+ if ( strlen( $dbName ) ) {
$connectionInfo['Database'] = $dbName;
}
-
- // Decide which auth scenerio to use
- // if we are using Windows auth, then don't add credentials to $connectionInfo
if ( !$this->useWindowsAuth ) {
$connectionInfo['UID'] = $user;
$connectionInfo['PWD'] = $password;
}
AtEase::suppressWarnings();
- $this->conn = sqlsrv_connect( $server, $connectionInfo );
+ $this->conn = sqlsrv_connect( $server, $connectionInfo ) ?: null;
AtEase::restoreWarnings();
- if ( $this->conn === false ) {
- $error = $this->lastError();
- $this->connLogger->error(
- "Error connecting to {db_server}: {error}",
- $this->getLogContext( [ 'method' => __METHOD__, 'error' => $error ] )
- );
- throw new DBConnectionError( $this, $error );
+ if ( !$this->conn ) {
+ throw $this->newExceptionAfterConnectError( $this->lastError() );
}
- $this->currentDomain = new DatabaseDomain(
- ( $dbName != '' ) ? $dbName : null,
- null,
- $tablePrefix
- );
-
- return (bool)$this->conn;
+ try {
+ $this->currentDomain = new DatabaseDomain(
+ strlen( $dbName ) ? $dbName : null,
+ null,
+ $tablePrefix
+ );
+ } catch ( Exception $e ) {
+ throw $this->newExceptionAfterConnectError( $e->getMessage() );
+ }
}
/**
}
public function freeResult( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- sqlsrv_free_stmt( $res );
+ sqlsrv_free_stmt( ResultWrapper::unwrap( $res ) );
}
/**
* @return int
*/
public function numRows( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
+ $res = ResultWrapper::unwrap( $res );
$ret = sqlsrv_num_rows( $res );
-
if ( $ret === false ) {
// we cannot get an amount of rows from this cursor type
// has_rows returns bool true/false if the result has rows
* @return int
*/
public function numFields( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return sqlsrv_num_fields( $res );
+ return sqlsrv_num_fields( ResultWrapper::unwrap( $res ) );
}
/**
* @return int
*/
public function fieldName( $res, $n ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return sqlsrv_field_metadata( $res )[$n]['Name'];
+ return sqlsrv_field_metadata( ResultWrapper::unwrap( $res ) )[$n]['Name'];
}
/**
return $statementOnly;
}
+ public function serverIsReadOnly() {
+ $encDatabase = $this->addQuotes( $this->getDBname() );
+ $res = $this->query(
+ "SELECT IS_READ_ONLY FROM SYS.DATABASES WHERE NAME = $encDatabase",
+ __METHOD__
+ );
+ $row = $this->fetchObject( $res );
+
+ return $row ? (bool)$row->IS_READ_ONLY : false;
+ }
+
/**
* @return int
*/
}
$this->scrollableCursor = true;
- if ( $ret instanceof ResultWrapper && !is_null( $identity ) ) {
+ if ( $ret instanceof IResultWrapper && !is_null( $identity ) ) {
// Then we want to get the identity column value we were assigned and save it off
$row = $ret->fetchObject();
if ( is_object( $row ) ) {
$this->query( 'ROLLBACK TRANSACTION ' . $this->addIdentifierQuotes( $identifier ), $fname );
}
- /**
- * Begin a transaction, committing any previously open transaction
- * @param string $fname
- */
protected function doBegin( $fname = __METHOD__ ) {
- sqlsrv_begin_transaction( $this->conn );
- $this->trxLevel = 1;
+ if ( !sqlsrv_begin_transaction( $this->conn ) ) {
+ $this->reportQueryError( $this->lastError(), $this->lastErrno(), 'BEGIN', $fname );
+ }
}
/**
* @param string $fname
*/
protected function doCommit( $fname = __METHOD__ ) {
- sqlsrv_commit( $this->conn );
- $this->trxLevel = 0;
+ if ( !sqlsrv_commit( $this->conn ) ) {
+ $this->reportQueryError( $this->lastError(), $this->lastErrno(), 'COMMIT', $fname );
+ }
}
/**
* @param string $fname
*/
protected function doRollback( $fname = __METHOD__ ) {
- sqlsrv_rollback( $this->conn );
- $this->trxLevel = 0;
+ if ( !sqlsrv_rollback( $this->conn ) ) {
+ $this->queryLogger->error(
+ "{fname}\t{db_server}\t{errno}\t{error}\t",
+ $this->getLogContext( [
+ 'errno' => $this->lastErrno(),
+ 'error' => $this->lastError(),
+ 'fname' => $fname,
+ 'trace' => ( new RuntimeException() )->getTraceAsString()
+ ] )
+ );
+ }
}
/**
protected function doSelectDomain( DatabaseDomain $domain ) {
if ( $domain->getSchema() !== null ) {
- throw new DBExpectedError( $this, __CLASS__ . ": domain schemas are not supported." );
+ throw new DBExpectedError(
+ $this,
+ __CLASS__ . ": domain '{$domain->getId()}' has a schema component"
+ );
}
$database = $domain->getDatabase();