* @ingroup Database
*/
+use Wikimedia\AtEase\AtEase;
use Wikimedia\Timestamp\ConvertibleTimestamp;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\DatabaseDomain;
use Wikimedia\Rdbms\Blob;
use Wikimedia\Rdbms\ResultWrapper;
+use Wikimedia\Rdbms\IResultWrapper;
use Wikimedia\Rdbms\DBConnectionError;
use Wikimedia\Rdbms\DBUnexpectedError;
use Wikimedia\Rdbms\DBExpectedError;
/** @var array */
private $mFieldInfoCache = [];
- function __construct( array $p ) {
- $p['tablePrefix'] = strtoupper( $p['tablePrefix'] );
- parent::__construct( $p );
+ /** @var string[] Map of (reserved table name => alternate table name) */
+ private $keywordTableMap = [];
- // @TODO: dependency inject
- Hooks::run( 'DatabaseOraclePostInit', [ $this ] );
+ /**
+ * @see Database::__construct()
+ * @param array $params Additional parameters include:
+ * - keywordTableMap : Map of reserved table names to alternative table names to use
+ */
+ function __construct( array $params ) {
+ $this->keywordTableMap = $params['keywordTableMap'] ?? [];
+ $params['tablePrefix'] = strtoupper( $params['tablePrefix'] );
+ parent::__construct( $params );
}
function __destruct() {
- if ( $this->opened ) {
- Wikimedia\suppressWarnings();
+ if ( $this->conn ) {
+ AtEase::suppressWarnings();
$this->close();
- Wikimedia\restoreWarnings();
+ AtEase::restoreWarnings();
}
}
throw new DBConnectionError( $this, $this->lastError() );
}
- $this->opened = true;
-
# removed putenv calls because they interfere with the system globaly
$this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
$this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
}
function execFlags() {
- return $this->trxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
+ return $this->trxLevel() ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
}
/**
* @return bool|mixed|ORAResult
*/
protected function doQuery( $sql ) {
- wfDebug( "SQL: [$sql]\n" );
if ( !mb_check_encoding( (string)$sql, 'UTF-8' ) ) {
throw new DBUnexpectedError( $this, "SQL encoding is invalid\n$sql" );
}
/**
* Frees resources associated with the LOB descriptor
- * @param ResultWrapper|ORAResult $res
+ * @param IResultWrapper|ORAResult $res
*/
function freeResult( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- $res->free();
+ ResultWrapper::unwrap( $res )->free();
}
/**
- * @param ResultWrapper|ORAResult $res
- * @return mixed
+ * @param IResultWrapper|ORAResult $res
+ * @return stdClass|bool
*/
function fetchObject( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return $res->fetchObject();
+ return ResultWrapper::unwrap( $res )->fetchObject();
}
/**
- * @param ResultWrapper|ORAResult $res
- * @return mixed
+ * @param IResultWrapper|ORAResult $res
+ * @return stdClass|bool
*/
function fetchRow( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return $res->fetchRow();
+ return ResultWrapper::unwrap( $res )->fetchRow();
}
/**
- * @param ResultWrapper|ORAResult $res
+ * @param IResultWrapper|ORAResult $res
* @return int
*/
function numRows( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return $res->numRows();
+ return ResultWrapper::unwrap( $res )->numRows();
}
/**
- * @param ResultWrapper|ORAResult $res
+ * @param IResultWrapper|ORAResult $res
* @return int
*/
function numFields( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return $res->numFields();
+ return ResultWrapper::unwrap( $res )->numFields();
}
function fieldName( $stmt, $n ) {
if ( $res instanceof ORAResult ) {
$res->seek( $row );
} else {
- $res->result->seek( $row );
+ ResultWrapper::unwrap( $res )->seek( $row );
}
}
$this->mLastResult = $stmt = oci_parse( $this->conn, $sql );
if ( $stmt === false ) {
$e = oci_error( $this->conn );
- $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
+ $this->reportQueryError( $e['message'], $e['code'], $sql, $fname );
return false;
}
$val = $this->getVerifiedUTF8( $val );
if ( oci_bind_by_name( $stmt, ":$col", $val, -1, SQLT_CHR ) === false ) {
$e = oci_error( $stmt );
- $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
+ $this->reportQueryError( $e['message'], $e['code'], $sql, $fname );
return false;
}
if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
$e = oci_error( $stmt );
if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
- $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
+ $this->reportQueryError( $e['message'], $e['code'], $sql, $fname );
return false;
} else {
}
}
- if ( !$this->trxLevel ) {
+ if ( !$this->trxLevel() ) {
oci_commit( $this->conn );
}
return parent::upsert( $table, $rows, $uniqueIndexes, $set, $fname );
}
- function tableName( $name, $format = 'quoted' ) {
- /*
- Replace reserved words with better ones
- Using uppercase because that's the only way Oracle can handle
- quoted tablenames
- */
- switch ( $name ) {
- case 'user':
- $name = 'MWUSER';
- break;
- case 'text':
- $name = 'PAGECONTENT';
- break;
- }
+ public function tableName( $name, $format = 'quoted' ) {
+ // Replace reserved words with better ones
+ $name = $this->remappedTableName( $name );
return strtoupper( parent::tableName( $name, $format ) );
}
+ /**
+ * @param string $name
+ * @return string Value of $name or remapped name if $name is a reserved keyword
+ */
+ public function remappedTableName( $name ) {
+ return $this->keywordTableMap[$name] ?? $name;
+ }
+
function tableNameInternal( $name ) {
$name = $this->tableName( $name );
* Return sequence_name if table has a sequence
*
* @param string $table
- * @return bool
+ * @return string[]|bool
*/
private function getSequenceData( $table ) {
if ( $this->sequenceData == null ) {
- $result = $this->doQuery( "SELECT lower(asq.sequence_name),
- lower(atc.table_name),
- lower(atc.column_name)
- FROM all_sequences asq, all_tab_columns atc
- WHERE decode(
- atc.table_name,
- '{$this->tablePrefix}MWUSER',
- '{$this->tablePrefix}USER',
- atc.table_name
- ) || '_' ||
- atc.column_name || '_SEQ' = '{$this->tablePrefix}' || asq.sequence_name
- AND asq.sequence_owner = upper('{$this->getDBname()}')
- AND atc.owner = upper('{$this->getDBname()}')" );
+ $dbname = $this->currentDomain->getDatabase();
+ $prefix = $this->currentDomain->getTablePrefix();
+ // See https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions040.htm
+ $decodeArgs = [ 'atc.table_name' ]; // the switch
+ foreach ( $this->keywordTableMap as $reserved => $alternative ) {
+ $search = strtoupper( $prefix . $alternative ); // case
+ $replace = strtoupper( $prefix . $reserved ); // result
+ $decodeArgs[] = $this->addQuotes( $search );
+ $decodeArgs[] = $this->addQuotes( $replace );
+ }
+ $decodeArgs[] = [ 'atc.table_name' ]; // default
+ $decodeArgs = implode( ', ', $decodeArgs );
+
+ $result = $this->doQuery(
+ "SELECT lower(asq.sequence_name), lower(atc.table_name), lower(atc.column_name)
+ FROM all_sequences asq, all_tab_columns atc
+ WHERE decode({$decodeArgs}) || '_' ||
+ atc.column_name || '_SEQ' = '{$prefix}' || asq.sequence_name
+ AND asq.sequence_owner = upper('{$dbname}')
+ AND atc.owner = upper('{$dbname}')"
+ );
while ( ( $row = $result->fetchRow() ) !== false ) {
$this->sequenceData[$row[1]] = [
$fname = __METHOD__
) {
$temporary = $temporary ? 'TRUE' : 'FALSE';
+ $tablePrefix = $this->currentDomain->getTablePrefix();
$newName = strtoupper( $newName );
$oldName = strtoupper( $oldName );
- $tabName = substr( $newName, strlen( $this->tablePrefix ) );
+ $tabName = substr( $newName, strlen( $tablePrefix ) );
$oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) );
- $newPrefix = strtoupper( $this->tablePrefix );
+ $newPrefix = strtoupper( $tablePrefix );
return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', " .
"'$oldPrefix', '$newPrefix', $temporary ); END;" );
}
protected function doBegin( $fname = __METHOD__ ) {
- $this->trxLevel = 1;
- $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' );
+ $this->query( 'SET CONSTRAINTS ALL DEFERRED' );
}
protected function doCommit( $fname = __METHOD__ ) {
- if ( $this->trxLevel ) {
+ if ( $this->trxLevel() ) {
$ret = oci_commit( $this->conn );
if ( !$ret ) {
throw new DBUnexpectedError( $this, $this->lastError() );
}
- $this->trxLevel = 0;
- $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
+ $this->query( 'SET CONSTRAINTS ALL IMMEDIATE' );
}
}
protected function doRollback( $fname = __METHOD__ ) {
- if ( $this->trxLevel ) {
+ if ( $this->trxLevel() ) {
oci_rollback( $this->conn );
- $this->trxLevel = 0;
- $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
+ $ignoreErrors = true;
+ $this->query( 'SET CONSTRAINTS ALL IMMEDIATE', $fname, $ignoreErrors );
}
}
}
}
- if ( !$this->trxLevel ) {
+ if ( !$this->trxLevel() ) {
oci_commit( $this->conn );
}