MediaWiki  REL1_24
DatabaseOracle.php
Go to the documentation of this file.
00001 <?php
00030 class ORAResult {
00031     private $rows;
00032     private $cursor;
00033     private $nrows;
00034 
00035     private $columns = array();
00036 
00037     private function array_unique_md( $array_in ) {
00038         $array_out = array();
00039         $array_hashes = array();
00040 
00041         foreach ( $array_in as $item ) {
00042             $hash = md5( serialize( $item ) );
00043             if ( !isset( $array_hashes[$hash] ) ) {
00044                 $array_hashes[$hash] = $hash;
00045                 $array_out[] = $item;
00046             }
00047         }
00048 
00049         return $array_out;
00050     }
00051 
00057     function __construct( &$db, $stmt, $unique = false ) {
00058         $this->db =& $db;
00059 
00060         $this->nrows = oci_fetch_all( $stmt, $this->rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM );
00061         if ( $this->nrows === false ) {
00062             $e = oci_error( $stmt );
00063             $db->reportQueryError( $e['message'], $e['code'], '', __METHOD__ );
00064             $this->free();
00065 
00066             return;
00067         }
00068 
00069         if ( $unique ) {
00070             $this->rows = $this->array_unique_md( $this->rows );
00071             $this->nrows = count( $this->rows );
00072         }
00073 
00074         if ( $this->nrows > 0 ) {
00075             foreach ( $this->rows[0] as $k => $v ) {
00076                 $this->columns[$k] = strtolower( oci_field_name( $stmt, $k + 1 ) );
00077             }
00078         }
00079 
00080         $this->cursor = 0;
00081         oci_free_statement( $stmt );
00082     }
00083 
00084     public function free() {
00085         unset( $this->db );
00086     }
00087 
00088     public function seek( $row ) {
00089         $this->cursor = min( $row, $this->nrows );
00090     }
00091 
00092     public function numRows() {
00093         return $this->nrows;
00094     }
00095 
00096     public function numFields() {
00097         return count( $this->columns );
00098     }
00099 
00100     public function fetchObject() {
00101         if ( $this->cursor >= $this->nrows ) {
00102             return false;
00103         }
00104         $row = $this->rows[$this->cursor++];
00105         $ret = new stdClass();
00106         foreach ( $row as $k => $v ) {
00107             $lc = $this->columns[$k];
00108             $ret->$lc = $v;
00109         }
00110 
00111         return $ret;
00112     }
00113 
00114     public function fetchRow() {
00115         if ( $this->cursor >= $this->nrows ) {
00116             return false;
00117         }
00118 
00119         $row = $this->rows[$this->cursor++];
00120         $ret = array();
00121         foreach ( $row as $k => $v ) {
00122             $lc = $this->columns[$k];
00123             $ret[$lc] = $v;
00124             $ret[$k] = $v;
00125         }
00126 
00127         return $ret;
00128     }
00129 }
00130 
00135 class ORAField implements Field {
00136     private $name, $tablename, $default, $max_length, $nullable,
00137         $is_pk, $is_unique, $is_multiple, $is_key, $type;
00138 
00139     function __construct( $info ) {
00140         $this->name = $info['column_name'];
00141         $this->tablename = $info['table_name'];
00142         $this->default = $info['data_default'];
00143         $this->max_length = $info['data_length'];
00144         $this->nullable = $info['not_null'];
00145         $this->is_pk = isset( $info['prim'] ) && $info['prim'] == 1 ? 1 : 0;
00146         $this->is_unique = isset( $info['uniq'] ) && $info['uniq'] == 1 ? 1 : 0;
00147         $this->is_multiple = isset( $info['nonuniq'] ) && $info['nonuniq'] == 1 ? 1 : 0;
00148         $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
00149         $this->type = $info['data_type'];
00150     }
00151 
00152     function name() {
00153         return $this->name;
00154     }
00155 
00156     function tableName() {
00157         return $this->tablename;
00158     }
00159 
00160     function defaultValue() {
00161         return $this->default;
00162     }
00163 
00164     function maxLength() {
00165         return $this->max_length;
00166     }
00167 
00168     function isNullable() {
00169         return $this->nullable;
00170     }
00171 
00172     function isKey() {
00173         return $this->is_key;
00174     }
00175 
00176     function isMultipleKey() {
00177         return $this->is_multiple;
00178     }
00179 
00180     function type() {
00181         return $this->type;
00182     }
00183 }
00184 
00188 class DatabaseOracle extends DatabaseBase {
00190     protected $mLastResult = null;
00191 
00193     protected $mAffectedRows;
00194 
00196     private $mInsertId = null;
00197 
00199     private $ignoreDupValOnIndex = false;
00200 
00202     private $sequenceData = null;
00203 
00205     private $defaultCharset = 'AL32UTF8';
00206 
00208     private $mFieldInfoCache = array();
00209 
00210     function __construct( $p = null ) {
00211         global $wgDBprefix;
00212 
00213         if ( !is_array( $p ) ) { // legacy calling pattern
00214             wfDeprecated( __METHOD__ . " method called without parameter array.", "1.22" );
00215             $args = func_get_args();
00216             $p = array(
00217                 'host' => isset( $args[0] ) ? $args[0] : false,
00218                 'user' => isset( $args[1] ) ? $args[1] : false,
00219                 'password' => isset( $args[2] ) ? $args[2] : false,
00220                 'dbname' => isset( $args[3] ) ? $args[3] : false,
00221                 'flags' => isset( $args[4] ) ? $args[4] : 0,
00222                 'tablePrefix' => isset( $args[5] ) ? $args[5] : 'get from global',
00223                 'schema' => 'get from global',
00224                 'foreign' => isset( $args[6] ) ? $args[6] : false
00225             );
00226         }
00227         if ( $p['tablePrefix'] == 'get from global' ) {
00228             $p['tablePrefix'] = $wgDBprefix;
00229         }
00230         $p['tablePrefix'] = strtoupper( $p['tablePrefix'] );
00231         parent::__construct( $p );
00232         wfRunHooks( 'DatabaseOraclePostInit', array( $this ) );
00233     }
00234 
00235     function __destruct() {
00236         if ( $this->mOpened ) {
00237             wfSuppressWarnings();
00238             $this->close();
00239             wfRestoreWarnings();
00240         }
00241     }
00242 
00243     function getType() {
00244         return 'oracle';
00245     }
00246 
00247     function cascadingDeletes() {
00248         return true;
00249     }
00250 
00251     function cleanupTriggers() {
00252         return true;
00253     }
00254 
00255     function strictIPs() {
00256         return true;
00257     }
00258 
00259     function realTimestamps() {
00260         return true;
00261     }
00262 
00263     function implicitGroupby() {
00264         return false;
00265     }
00266 
00267     function implicitOrderby() {
00268         return false;
00269     }
00270 
00271     function searchableIPs() {
00272         return true;
00273     }
00274 
00284     function open( $server, $user, $password, $dbName ) {
00285         global $wgDBOracleDRCP;
00286         if ( !function_exists( 'oci_connect' ) ) {
00287             throw new DBConnectionError(
00288                 $this,
00289                 "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n " .
00290                     "(Note: if you recently installed PHP, you may need to restart your webserver\n " .
00291                     "and database)\n" );
00292         }
00293 
00294         $this->close();
00295         $this->mUser = $user;
00296         $this->mPassword = $password;
00297         // changed internal variables functions
00298         // mServer now holds the TNS endpoint
00299         // mDBname is schema name if different from username
00300         if ( !$server ) {
00301             // backward compatibillity (server used to be null and TNS was supplied in dbname)
00302             $this->mServer = $dbName;
00303             $this->mDBname = $user;
00304         } else {
00305             $this->mServer = $server;
00306             if ( !$dbName ) {
00307                 $this->mDBname = $user;
00308             } else {
00309                 $this->mDBname = $dbName;
00310             }
00311         }
00312 
00313         if ( !strlen( $user ) ) { # e.g. the class is being loaded
00314             return null;
00315         }
00316 
00317         if ( $wgDBOracleDRCP ) {
00318             $this->setFlag( DBO_PERSISTENT );
00319         }
00320 
00321         $session_mode = $this->mFlags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT;
00322 
00323         wfSuppressWarnings();
00324         if ( $this->mFlags & DBO_PERSISTENT ) {
00325             $this->mConn = oci_pconnect(
00326                 $this->mUser,
00327                 $this->mPassword,
00328                 $this->mServer,
00329                 $this->defaultCharset,
00330                 $session_mode
00331             );
00332         } elseif ( $this->mFlags & DBO_DEFAULT ) {
00333             $this->mConn = oci_new_connect(
00334                 $this->mUser,
00335                 $this->mPassword,
00336                 $this->mServer,
00337                 $this->defaultCharset,
00338                 $session_mode
00339             );
00340         } else {
00341             $this->mConn = oci_connect(
00342                 $this->mUser,
00343                 $this->mPassword,
00344                 $this->mServer,
00345                 $this->defaultCharset,
00346                 $session_mode
00347             );
00348         }
00349         wfRestoreWarnings();
00350 
00351         if ( $this->mUser != $this->mDBname ) {
00352             //change current schema in session
00353             $this->selectDB( $this->mDBname );
00354         }
00355 
00356         if ( !$this->mConn ) {
00357             throw new DBConnectionError( $this, $this->lastError() );
00358         }
00359 
00360         $this->mOpened = true;
00361 
00362         # removed putenv calls because they interfere with the system globaly
00363         $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
00364         $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
00365         $this->doQuery( 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=\'.,\'' );
00366 
00367         return $this->mConn;
00368     }
00369 
00375     protected function closeConnection() {
00376         return oci_close( $this->mConn );
00377     }
00378 
00379     function execFlags() {
00380         return $this->mTrxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
00381     }
00382 
00383     protected function doQuery( $sql ) {
00384         wfDebug( "SQL: [$sql]\n" );
00385         if ( !StringUtils::isUtf8( $sql ) ) {
00386             throw new MWException( "SQL encoding is invalid\n$sql" );
00387         }
00388 
00389         // handle some oracle specifics
00390         // remove AS column/table/subquery namings
00391         if ( !$this->getFlag( DBO_DDLMODE ) ) {
00392             $sql = preg_replace( '/ as /i', ' ', $sql );
00393         }
00394 
00395         // Oracle has issues with UNION clause if the statement includes LOB fields
00396         // So we do a UNION ALL and then filter the results array with array_unique
00397         $union_unique = ( preg_match( '/\/\* UNION_UNIQUE \*\/ /', $sql ) != 0 );
00398         // EXPLAIN syntax in Oracle is EXPLAIN PLAN FOR and it return nothing
00399         // you have to select data from plan table after explain
00400         $explain_id = MWTimestamp::getLocalInstance()->format( 'dmYHis' );
00401 
00402         $sql = preg_replace(
00403             '/^EXPLAIN /',
00404             'EXPLAIN PLAN SET STATEMENT_ID = \'' . $explain_id . '\' FOR',
00405             $sql,
00406             1,
00407             $explain_count
00408         );
00409 
00410         wfSuppressWarnings();
00411 
00412         if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
00413             $e = oci_error( $this->mConn );
00414             $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00415 
00416             return false;
00417         }
00418 
00419         if ( !oci_execute( $stmt, $this->execFlags() ) ) {
00420             $e = oci_error( $stmt );
00421             if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
00422                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00423 
00424                 return false;
00425             }
00426         }
00427 
00428         wfRestoreWarnings();
00429 
00430         if ( $explain_count > 0 ) {
00431             return $this->doQuery( 'SELECT id, cardinality "ROWS" FROM plan_table ' .
00432                 'WHERE statement_id = \'' . $explain_id . '\'' );
00433         } elseif ( oci_statement_type( $stmt ) == 'SELECT' ) {
00434             return new ORAResult( $this, $stmt, $union_unique );
00435         } else {
00436             $this->mAffectedRows = oci_num_rows( $stmt );
00437 
00438             return true;
00439         }
00440     }
00441 
00442     function queryIgnore( $sql, $fname = '' ) {
00443         return $this->query( $sql, $fname, true );
00444     }
00445 
00450     function freeResult( $res ) {
00451         if ( $res instanceof ResultWrapper ) {
00452             $res = $res->result;
00453         }
00454 
00455         $res->free();
00456     }
00457 
00462     function fetchObject( $res ) {
00463         if ( $res instanceof ResultWrapper ) {
00464             $res = $res->result;
00465         }
00466 
00467         return $res->fetchObject();
00468     }
00469 
00470     function fetchRow( $res ) {
00471         if ( $res instanceof ResultWrapper ) {
00472             $res = $res->result;
00473         }
00474 
00475         return $res->fetchRow();
00476     }
00477 
00478     function numRows( $res ) {
00479         if ( $res instanceof ResultWrapper ) {
00480             $res = $res->result;
00481         }
00482 
00483         return $res->numRows();
00484     }
00485 
00486     function numFields( $res ) {
00487         if ( $res instanceof ResultWrapper ) {
00488             $res = $res->result;
00489         }
00490 
00491         return $res->numFields();
00492     }
00493 
00494     function fieldName( $stmt, $n ) {
00495         return oci_field_name( $stmt, $n );
00496     }
00497 
00502     function insertId() {
00503         return $this->mInsertId;
00504     }
00505 
00510     function dataSeek( $res, $row ) {
00511         if ( $res instanceof ORAResult ) {
00512             $res->seek( $row );
00513         } else {
00514             $res->result->seek( $row );
00515         }
00516     }
00517 
00518     function lastError() {
00519         if ( $this->mConn === false ) {
00520             $e = oci_error();
00521         } else {
00522             $e = oci_error( $this->mConn );
00523         }
00524 
00525         return $e['message'];
00526     }
00527 
00528     function lastErrno() {
00529         if ( $this->mConn === false ) {
00530             $e = oci_error();
00531         } else {
00532             $e = oci_error( $this->mConn );
00533         }
00534 
00535         return $e['code'];
00536     }
00537 
00538     function affectedRows() {
00539         return $this->mAffectedRows;
00540     }
00541 
00550     function indexInfo( $table, $index, $fname = __METHOD__ ) {
00551         return false;
00552     }
00553 
00554     function indexUnique( $table, $index, $fname = __METHOD__ ) {
00555         return false;
00556     }
00557 
00558     function insert( $table, $a, $fname = __METHOD__, $options = array() ) {
00559         if ( !count( $a ) ) {
00560             return true;
00561         }
00562 
00563         if ( !is_array( $options ) ) {
00564             $options = array( $options );
00565         }
00566 
00567         if ( in_array( 'IGNORE', $options ) ) {
00568             $this->ignoreDupValOnIndex = true;
00569         }
00570 
00571         if ( !is_array( reset( $a ) ) ) {
00572             $a = array( $a );
00573         }
00574 
00575         foreach ( $a as &$row ) {
00576             $this->insertOneRow( $table, $row, $fname );
00577         }
00578         $retVal = true;
00579 
00580         if ( in_array( 'IGNORE', $options ) ) {
00581             $this->ignoreDupValOnIndex = false;
00582         }
00583 
00584         return $retVal;
00585     }
00586 
00587     private function fieldBindStatement( $table, $col, &$val, $includeCol = false ) {
00588         $col_info = $this->fieldInfoMulti( $table, $col );
00589         $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
00590 
00591         $bind = '';
00592         if ( is_numeric( $col ) ) {
00593             $bind = $val;
00594             $val = null;
00595 
00596             return $bind;
00597         } elseif ( $includeCol ) {
00598             $bind = "$col = ";
00599         }
00600 
00601         if ( $val == '' && $val !== 0 && $col_type != 'BLOB' && $col_type != 'CLOB' ) {
00602             $val = null;
00603         }
00604 
00605         if ( $val === 'NULL' ) {
00606             $val = null;
00607         }
00608 
00609         if ( $val === null ) {
00610             if ( $col_info != false && $col_info->isNullable() == 0 && $col_info->defaultValue() != null ) {
00611                 $bind .= 'DEFAULT';
00612             } else {
00613                 $bind .= 'NULL';
00614             }
00615         } else {
00616             $bind .= ':' . $col;
00617         }
00618 
00619         return $bind;
00620     }
00621 
00629     private function insertOneRow( $table, $row, $fname ) {
00630         global $wgContLang;
00631 
00632         $table = $this->tableName( $table );
00633         // "INSERT INTO tables (a, b, c)"
00634         $sql = "INSERT INTO " . $table . " (" . join( ',', array_keys( $row ) ) . ')';
00635         $sql .= " VALUES (";
00636 
00637         // for each value, append ":key"
00638         $first = true;
00639         foreach ( $row as $col => &$val ) {
00640             if ( !$first ) {
00641                 $sql .= ', ';
00642             } else {
00643                 $first = false;
00644             }
00645             if ( $this->isQuotedIdentifier( $val ) ) {
00646                 $sql .= $this->removeIdentifierQuotes( $val );
00647                 unset( $row[$col] );
00648             } else {
00649                 $sql .= $this->fieldBindStatement( $table, $col, $val );
00650             }
00651         }
00652         $sql .= ')';
00653 
00654         if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
00655             $e = oci_error( $this->mConn );
00656             $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00657 
00658             return false;
00659         }
00660         foreach ( $row as $col => &$val ) {
00661             $col_info = $this->fieldInfoMulti( $table, $col );
00662             $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
00663 
00664             if ( $val === null ) {
00665                 // do nothing ... null was inserted in statement creation
00666             } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
00667                 if ( is_object( $val ) ) {
00668                     $val = $val->fetch();
00669                 }
00670 
00671                 // backward compatibility
00672                 if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) {
00673                     $val = $this->getInfinity();
00674                 }
00675 
00676                 $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val;
00677                 if ( oci_bind_by_name( $stmt, ":$col", $val, -1, SQLT_CHR ) === false ) {
00678                     $e = oci_error( $stmt );
00679                     $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00680 
00681                     return false;
00682                 }
00683             } else {
00685                 if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) {
00686                     $e = oci_error( $stmt );
00687                     throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
00688                 }
00689 
00690                 if ( is_object( $val ) ) {
00691                     $val = $val->fetch();
00692                 }
00693 
00694                 if ( $col_type == 'BLOB' ) {
00695                     $lob[$col]->writeTemporary( $val, OCI_TEMP_BLOB );
00696                     oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_BLOB );
00697                 } else {
00698                     $lob[$col]->writeTemporary( $val, OCI_TEMP_CLOB );
00699                     oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB );
00700                 }
00701             }
00702         }
00703 
00704         wfSuppressWarnings();
00705 
00706         if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
00707             $e = oci_error( $stmt );
00708             if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
00709                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
00710 
00711                 return false;
00712             } else {
00713                 $this->mAffectedRows = oci_num_rows( $stmt );
00714             }
00715         } else {
00716             $this->mAffectedRows = oci_num_rows( $stmt );
00717         }
00718 
00719         wfRestoreWarnings();
00720 
00721         if ( isset( $lob ) ) {
00722             foreach ( $lob as $lob_v ) {
00723                 $lob_v->free();
00724             }
00725         }
00726 
00727         if ( !$this->mTrxLevel ) {
00728             oci_commit( $this->mConn );
00729         }
00730 
00731         return oci_free_statement( $stmt );
00732     }
00733 
00734     function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
00735         $insertOptions = array(), $selectOptions = array()
00736     ) {
00737         $destTable = $this->tableName( $destTable );
00738         if ( !is_array( $selectOptions ) ) {
00739             $selectOptions = array( $selectOptions );
00740         }
00741         list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
00742         if ( is_array( $srcTable ) ) {
00743             $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
00744         } else {
00745             $srcTable = $this->tableName( $srcTable );
00746         }
00747 
00748         if ( ( $sequenceData = $this->getSequenceData( $destTable ) ) !== false &&
00749             !isset( $varMap[$sequenceData['column']] )
00750         ) {
00751             $varMap[$sequenceData['column']] = 'GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')';
00752         }
00753 
00754         // count-alias subselect fields to avoid abigious definition errors
00755         $i = 0;
00756         foreach ( $varMap as &$val ) {
00757             $val = $val . ' field' . ( $i++ );
00758         }
00759 
00760         $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
00761             " SELECT $startOpts " . implode( ',', $varMap ) .
00762             " FROM $srcTable $useIndex ";
00763         if ( $conds != '*' ) {
00764             $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
00765         }
00766         $sql .= " $tailOpts";
00767 
00768         if ( in_array( 'IGNORE', $insertOptions ) ) {
00769             $this->ignoreDupValOnIndex = true;
00770         }
00771 
00772         $retval = $this->query( $sql, $fname );
00773 
00774         if ( in_array( 'IGNORE', $insertOptions ) ) {
00775             $this->ignoreDupValOnIndex = false;
00776         }
00777 
00778         return $retval;
00779     }
00780 
00781     public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
00782         $fname = __METHOD__
00783     ) {
00784         if ( !count( $rows ) ) {
00785             return true; // nothing to do
00786         }
00787 
00788         if ( !is_array( reset( $rows ) ) ) {
00789             $rows = array( $rows );
00790         }
00791 
00792         $sequenceData = $this->getSequenceData( $table );
00793         if ( $sequenceData !== false ) {
00794             // add sequence column to each list of columns, when not set
00795             foreach ( $rows as &$row ) {
00796                 if ( !isset( $row[$sequenceData['column']] ) ) {
00797                     $row[$sequenceData['column']] =
00798                         $this->addIdentifierQuotes( 'GET_SEQUENCE_VALUE(\'' .
00799                             $sequenceData['sequence'] . '\')' );
00800                 }
00801             }
00802         }
00803 
00804         return parent::upsert( $table, $rows, $uniqueIndexes, $set, $fname );
00805     }
00806 
00807     function tableName( $name, $format = 'quoted' ) {
00808         /*
00809         Replace reserved words with better ones
00810         Using uppercase because that's the only way Oracle can handle
00811         quoted tablenames
00812         */
00813         switch ( $name ) {
00814             case 'user':
00815                 $name = 'MWUSER';
00816                 break;
00817             case 'text':
00818                 $name = 'PAGECONTENT';
00819                 break;
00820         }
00821 
00822         return strtoupper( parent::tableName( $name, $format ) );
00823     }
00824 
00825     function tableNameInternal( $name ) {
00826         $name = $this->tableName( $name );
00827 
00828         return preg_replace( '/.*\.(.*)/', '$1', $name );
00829     }
00830 
00837     function nextSequenceValue( $seqName ) {
00838         $res = $this->query( "SELECT $seqName.nextval FROM dual" );
00839         $row = $this->fetchRow( $res );
00840         $this->mInsertId = $row[0];
00841 
00842         return $this->mInsertId;
00843     }
00844 
00851     private function getSequenceData( $table ) {
00852         if ( $this->sequenceData == null ) {
00853             $result = $this->doQuery( "SELECT lower(asq.sequence_name),
00854                 lower(atc.table_name),
00855                 lower(atc.column_name)
00856             FROM all_sequences asq, all_tab_columns atc
00857             WHERE decode(
00858                     atc.table_name,
00859                     '{$this->mTablePrefix}MWUSER',
00860                     '{$this->mTablePrefix}USER',
00861                     atc.table_name
00862                 ) || '_' ||
00863                 atc.column_name || '_SEQ' = '{$this->mTablePrefix}' || asq.sequence_name
00864                 AND asq.sequence_owner = upper('{$this->mDBname}')
00865                 AND atc.owner = upper('{$this->mDBname}')" );
00866 
00867             while ( ( $row = $result->fetchRow() ) !== false ) {
00868                 $this->sequenceData[$row[1]] = array(
00869                     'sequence' => $row[0],
00870                     'column' => $row[2]
00871                 );
00872             }
00873         }
00874         $table = strtolower( $this->removeIdentifierQuotes( $this->tableName( $table ) ) );
00875 
00876         return ( isset( $this->sequenceData[$table] ) ) ? $this->sequenceData[$table] : false;
00877     }
00878 
00886     function textFieldSize( $table, $field ) {
00887         $fieldInfoData = $this->fieldInfo( $table, $field );
00888 
00889         return $fieldInfoData->maxLength();
00890     }
00891 
00892     function limitResult( $sql, $limit, $offset = false ) {
00893         if ( $offset === false ) {
00894             $offset = 0;
00895         }
00896 
00897         return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < (1 + $limit + $offset)";
00898     }
00899 
00900     function encodeBlob( $b ) {
00901         return new Blob( $b );
00902     }
00903 
00904     function decodeBlob( $b ) {
00905         if ( $b instanceof Blob ) {
00906             $b = $b->fetch();
00907         }
00908 
00909         return $b;
00910     }
00911 
00912     function unionQueries( $sqls, $all ) {
00913         $glue = ' UNION ALL ';
00914 
00915         return 'SELECT * ' . ( $all ? '' : '/* UNION_UNIQUE */ ' ) .
00916             'FROM (' . implode( $glue, $sqls ) . ')';
00917     }
00918 
00919     function wasDeadlock() {
00920         return $this->lastErrno() == 'OCI-00060';
00921     }
00922 
00923     function duplicateTableStructure( $oldName, $newName, $temporary = false,
00924         $fname = __METHOD__
00925     ) {
00926         $temporary = $temporary ? 'TRUE' : 'FALSE';
00927 
00928         $newName = strtoupper( $newName );
00929         $oldName = strtoupper( $oldName );
00930 
00931         $tabName = substr( $newName, strlen( $this->mTablePrefix ) );
00932         $oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) );
00933         $newPrefix = strtoupper( $this->mTablePrefix );
00934 
00935         return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', " .
00936             "'$oldPrefix', '$newPrefix', $temporary ); END;" );
00937     }
00938 
00939     function listTables( $prefix = null, $fname = __METHOD__ ) {
00940         $listWhere = '';
00941         if ( !empty( $prefix ) ) {
00942             $listWhere = ' AND table_name LIKE \'' . strtoupper( $prefix ) . '%\'';
00943         }
00944 
00945         $owner = strtoupper( $this->mDBname );
00946         $result = $this->doQuery( "SELECT table_name FROM all_tables " .
00947             "WHERE owner='$owner' AND table_name NOT LIKE '%!_IDX\$_' ESCAPE '!' $listWhere" );
00948 
00949         // dirty code ... i know
00950         $endArray = array();
00951         $endArray[] = strtoupper( $prefix . 'MWUSER' );
00952         $endArray[] = strtoupper( $prefix . 'PAGE' );
00953         $endArray[] = strtoupper( $prefix . 'IMAGE' );
00954         $fixedOrderTabs = $endArray;
00955         while ( ( $row = $result->fetchRow() ) !== false ) {
00956             if ( !in_array( $row['table_name'], $fixedOrderTabs ) ) {
00957                 $endArray[] = $row['table_name'];
00958             }
00959         }
00960 
00961         return $endArray;
00962     }
00963 
00964     public function dropTable( $tableName, $fName = __METHOD__ ) {
00965         $tableName = $this->tableName( $tableName );
00966         if ( !$this->tableExists( $tableName ) ) {
00967             return false;
00968         }
00969 
00970         return $this->doQuery( "DROP TABLE $tableName CASCADE CONSTRAINTS PURGE" );
00971     }
00972 
00973     function timestamp( $ts = 0 ) {
00974         return wfTimestamp( TS_ORACLE, $ts );
00975     }
00976 
00984     public function aggregateValue( $valuedata, $valuename = 'value' ) {
00985         return $valuedata;
00986     }
00987 
00988     function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
00989         # Ignore errors during error handling to avoid infinite
00990         # recursion
00991         $ignore = $this->ignoreErrors( true );
00992         ++$this->mErrorCount;
00993 
00994         if ( $ignore || $tempIgnore ) {
00995             wfDebug( "SQL ERROR (ignored): $error\n" );
00996             $this->ignoreErrors( $ignore );
00997         } else {
00998             throw new DBQueryError( $this, $error, $errno, $sql, $fname );
00999         }
01000     }
01001 
01005     public function getSoftwareLink() {
01006         return '[{{int:version-db-oracle-url}} Oracle]';
01007     }
01008 
01012     function getServerVersion() {
01013         //better version number, fallback on driver
01014         $rset = $this->doQuery(
01015             'SELECT version FROM product_component_version ' .
01016                 'WHERE UPPER(product) LIKE \'ORACLE DATABASE%\''
01017         );
01018         if ( !( $row = $rset->fetchRow() ) ) {
01019             return oci_server_version( $this->mConn );
01020         }
01021 
01022         return $row['version'];
01023     }
01024 
01032     function indexExists( $table, $index, $fname = __METHOD__ ) {
01033         $table = $this->tableName( $table );
01034         $table = strtoupper( $this->removeIdentifierQuotes( $table ) );
01035         $index = strtoupper( $index );
01036         $owner = strtoupper( $this->mDBname );
01037         $sql = "SELECT 1 FROM all_indexes WHERE owner='$owner' AND index_name='{$table}_{$index}'";
01038         $res = $this->doQuery( $sql );
01039         if ( $res ) {
01040             $count = $res->numRows();
01041             $res->free();
01042         } else {
01043             $count = 0;
01044         }
01045 
01046         return $count != 0;
01047     }
01048 
01055     function tableExists( $table, $fname = __METHOD__ ) {
01056         $table = $this->tableName( $table );
01057         $table = $this->addQuotes( strtoupper( $this->removeIdentifierQuotes( $table ) ) );
01058         $owner = $this->addQuotes( strtoupper( $this->mDBname ) );
01059         $sql = "SELECT 1 FROM all_tables WHERE owner=$owner AND table_name=$table";
01060         $res = $this->doQuery( $sql );
01061         if ( $res && $res->numRows() > 0 ) {
01062             $exists = true;
01063         } else {
01064             $exists = false;
01065         }
01066 
01067         $res->free();
01068 
01069         return $exists;
01070     }
01071 
01082     private function fieldInfoMulti( $table, $field ) {
01083         $field = strtoupper( $field );
01084         if ( is_array( $table ) ) {
01085             $table = array_map( array( &$this, 'tableNameInternal' ), $table );
01086             $tableWhere = 'IN (';
01087             foreach ( $table as &$singleTable ) {
01088                 $singleTable = $this->removeIdentifierQuotes( $singleTable );
01089                 if ( isset( $this->mFieldInfoCache["$singleTable.$field"] ) ) {
01090                     return $this->mFieldInfoCache["$singleTable.$field"];
01091                 }
01092                 $tableWhere .= '\'' . $singleTable . '\',';
01093             }
01094             $tableWhere = rtrim( $tableWhere, ',' ) . ')';
01095         } else {
01096             $table = $this->removeIdentifierQuotes( $this->tableNameInternal( $table ) );
01097             if ( isset( $this->mFieldInfoCache["$table.$field"] ) ) {
01098                 return $this->mFieldInfoCache["$table.$field"];
01099             }
01100             $tableWhere = '= \'' . $table . '\'';
01101         }
01102 
01103         $fieldInfoStmt = oci_parse(
01104             $this->mConn,
01105             'SELECT * FROM wiki_field_info_full WHERE table_name ' .
01106                 $tableWhere . ' and column_name = \'' . $field . '\''
01107         );
01108         if ( oci_execute( $fieldInfoStmt, $this->execFlags() ) === false ) {
01109             $e = oci_error( $fieldInfoStmt );
01110             $this->reportQueryError( $e['message'], $e['code'], 'fieldInfo QUERY', __METHOD__ );
01111 
01112             return false;
01113         }
01114         $res = new ORAResult( $this, $fieldInfoStmt );
01115         if ( $res->numRows() == 0 ) {
01116             if ( is_array( $table ) ) {
01117                 foreach ( $table as &$singleTable ) {
01118                     $this->mFieldInfoCache["$singleTable.$field"] = false;
01119                 }
01120             } else {
01121                 $this->mFieldInfoCache["$table.$field"] = false;
01122             }
01123             $fieldInfoTemp = null;
01124         } else {
01125             $fieldInfoTemp = new ORAField( $res->fetchRow() );
01126             $table = $fieldInfoTemp->tableName();
01127             $this->mFieldInfoCache["$table.$field"] = $fieldInfoTemp;
01128         }
01129         $res->free();
01130 
01131         return $fieldInfoTemp;
01132     }
01133 
01140     function fieldInfo( $table, $field ) {
01141         if ( is_array( $table ) ) {
01142             throw new DBUnexpectedError( $this, 'DatabaseOracle::fieldInfo called with table array!' );
01143         }
01144 
01145         return $this->fieldInfoMulti( $table, $field );
01146     }
01147 
01148     protected function doBegin( $fname = __METHOD__ ) {
01149         $this->mTrxLevel = 1;
01150         $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' );
01151     }
01152 
01153     protected function doCommit( $fname = __METHOD__ ) {
01154         if ( $this->mTrxLevel ) {
01155             $ret = oci_commit( $this->mConn );
01156             if ( !$ret ) {
01157                 throw new DBUnexpectedError( $this, $this->lastError() );
01158             }
01159             $this->mTrxLevel = 0;
01160             $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
01161         }
01162     }
01163 
01164     protected function doRollback( $fname = __METHOD__ ) {
01165         if ( $this->mTrxLevel ) {
01166             oci_rollback( $this->mConn );
01167             $this->mTrxLevel = 0;
01168             $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
01169         }
01170     }
01171 
01182     function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
01183         $fname = __METHOD__, $inputCallback = false ) {
01184         $cmd = '';
01185         $done = false;
01186         $dollarquote = false;
01187 
01188         $replacements = array();
01189 
01190         while ( !feof( $fp ) ) {
01191             if ( $lineCallback ) {
01192                 call_user_func( $lineCallback );
01193             }
01194             $line = trim( fgets( $fp, 1024 ) );
01195             $sl = strlen( $line ) - 1;
01196 
01197             if ( $sl < 0 ) {
01198                 continue;
01199             }
01200             if ( '-' == $line[0] && '-' == $line[1] ) {
01201                 continue;
01202             }
01203 
01204             // Allow dollar quoting for function declarations
01205             if ( substr( $line, 0, 8 ) == '/*$mw$*/' ) {
01206                 if ( $dollarquote ) {
01207                     $dollarquote = false;
01208                     $line = str_replace( '/*$mw$*/', '', $line ); // remove dollarquotes
01209                     $done = true;
01210                 } else {
01211                     $dollarquote = true;
01212                 }
01213             } elseif ( !$dollarquote ) {
01214                 if ( ';' == $line[$sl] && ( $sl < 2 || ';' != $line[$sl - 1] ) ) {
01215                     $done = true;
01216                     $line = substr( $line, 0, $sl );
01217                 }
01218             }
01219 
01220             if ( $cmd != '' ) {
01221                 $cmd .= ' ';
01222             }
01223             $cmd .= "$line\n";
01224 
01225             if ( $done ) {
01226                 $cmd = str_replace( ';;', ";", $cmd );
01227                 if ( strtolower( substr( $cmd, 0, 6 ) ) == 'define' ) {
01228                     if ( preg_match( '/^define\s*([^\s=]*)\s*=\s*\'\{\$([^\}]*)\}\'/', $cmd, $defines ) ) {
01229                         $replacements[$defines[2]] = $defines[1];
01230                     }
01231                 } else {
01232                     foreach ( $replacements as $mwVar => $scVar ) {
01233                         $cmd = str_replace( '&' . $scVar . '.', '`{$' . $mwVar . '}`', $cmd );
01234                     }
01235 
01236                     $cmd = $this->replaceVars( $cmd );
01237                     if ( $inputCallback ) {
01238                         call_user_func( $inputCallback, $cmd );
01239                     }
01240                     $res = $this->doQuery( $cmd );
01241                     if ( $resultCallback ) {
01242                         call_user_func( $resultCallback, $res, $this );
01243                     }
01244 
01245                     if ( false === $res ) {
01246                         $err = $this->lastError();
01247 
01248                         return "Query \"{$cmd}\" failed with error code \"$err\".\n";
01249                     }
01250                 }
01251 
01252                 $cmd = '';
01253                 $done = false;
01254             }
01255         }
01256 
01257         return true;
01258     }
01259 
01260     function selectDB( $db ) {
01261         $this->mDBname = $db;
01262         if ( $db == null || $db == $this->mUser ) {
01263             return true;
01264         }
01265         $sql = 'ALTER SESSION SET CURRENT_SCHEMA=' . strtoupper( $db );
01266         $stmt = oci_parse( $this->mConn, $sql );
01267         wfSuppressWarnings();
01268         $success = oci_execute( $stmt );
01269         wfRestoreWarnings();
01270         if ( !$success ) {
01271             $e = oci_error( $stmt );
01272             if ( $e['code'] != '1435' ) {
01273                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01274             }
01275 
01276             return false;
01277         }
01278 
01279         return true;
01280     }
01281 
01282     function strencode( $s ) {
01283         return str_replace( "'", "''", $s );
01284     }
01285 
01286     function addQuotes( $s ) {
01287         global $wgContLang;
01288         if ( isset( $wgContLang->mLoaded ) && $wgContLang->mLoaded ) {
01289             $s = $wgContLang->checkTitleEncoding( $s );
01290         }
01291 
01292         return "'" . $this->strencode( $s ) . "'";
01293     }
01294 
01295     public function addIdentifierQuotes( $s ) {
01296         if ( !$this->getFlag( DBO_DDLMODE ) ) {
01297             $s = '/*Q*/' . $s;
01298         }
01299 
01300         return $s;
01301     }
01302 
01303     public function removeIdentifierQuotes( $s ) {
01304         return strpos( $s, '/*Q*/' ) === false ? $s : substr( $s, 5 );
01305     }
01306 
01307     public function isQuotedIdentifier( $s ) {
01308         return strpos( $s, '/*Q*/' ) !== false;
01309     }
01310 
01311     private function wrapFieldForWhere( $table, &$col, &$val ) {
01312         global $wgContLang;
01313 
01314         $col_info = $this->fieldInfoMulti( $table, $col );
01315         $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
01316         if ( $col_type == 'CLOB' ) {
01317             $col = 'TO_CHAR(' . $col . ')';
01318             $val = $wgContLang->checkTitleEncoding( $val );
01319         } elseif ( $col_type == 'VARCHAR2' ) {
01320             $val = $wgContLang->checkTitleEncoding( $val );
01321         }
01322     }
01323 
01324     private function wrapConditionsForWhere( $table, $conds, $parentCol = null ) {
01325         $conds2 = array();
01326         foreach ( $conds as $col => $val ) {
01327             if ( is_array( $val ) ) {
01328                 $conds2[$col] = $this->wrapConditionsForWhere( $table, $val, $col );
01329             } else {
01330                 if ( is_numeric( $col ) && $parentCol != null ) {
01331                     $this->wrapFieldForWhere( $table, $parentCol, $val );
01332                 } else {
01333                     $this->wrapFieldForWhere( $table, $col, $val );
01334                 }
01335                 $conds2[$col] = $val;
01336             }
01337         }
01338 
01339         return $conds2;
01340     }
01341 
01342     function selectRow( $table, $vars, $conds, $fname = __METHOD__,
01343         $options = array(), $join_conds = array()
01344     ) {
01345         if ( is_array( $conds ) ) {
01346             $conds = $this->wrapConditionsForWhere( $table, $conds );
01347         }
01348 
01349         return parent::selectRow( $table, $vars, $conds, $fname, $options, $join_conds );
01350     }
01351 
01360     function makeSelectOptions( $options ) {
01361         $preLimitTail = $postLimitTail = '';
01362         $startOpts = '';
01363 
01364         $noKeyOptions = array();
01365         foreach ( $options as $key => $option ) {
01366             if ( is_numeric( $key ) ) {
01367                 $noKeyOptions[$option] = true;
01368             }
01369         }
01370 
01371         $preLimitTail .= $this->makeGroupByWithHaving( $options );
01372 
01373         $preLimitTail .= $this->makeOrderBy( $options );
01374 
01375         if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
01376             $postLimitTail .= ' FOR UPDATE';
01377         }
01378 
01379         if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
01380             $startOpts .= 'DISTINCT';
01381         }
01382 
01383         if ( isset( $options['USE INDEX'] ) && !is_array( $options['USE INDEX'] ) ) {
01384             $useIndex = $this->useIndexClause( $options['USE INDEX'] );
01385         } else {
01386             $useIndex = '';
01387         }
01388 
01389         return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
01390     }
01391 
01392     public function delete( $table, $conds, $fname = __METHOD__ ) {
01393         if ( is_array( $conds ) ) {
01394             $conds = $this->wrapConditionsForWhere( $table, $conds );
01395         }
01396         // a hack for deleting pages, users and images (which have non-nullable FKs)
01397         // all deletions on these tables have transactions so final failure rollbacks these updates
01398         $table = $this->tableName( $table );
01399         if ( $table == $this->tableName( 'user' ) ) {
01400             $this->update( 'archive', array( 'ar_user' => 0 ),
01401                 array( 'ar_user' => $conds['user_id'] ), $fname );
01402             $this->update( 'ipblocks', array( 'ipb_user' => 0 ),
01403                 array( 'ipb_user' => $conds['user_id'] ), $fname );
01404             $this->update( 'image', array( 'img_user' => 0 ),
01405                 array( 'img_user' => $conds['user_id'] ), $fname );
01406             $this->update( 'oldimage', array( 'oi_user' => 0 ),
01407                 array( 'oi_user' => $conds['user_id'] ), $fname );
01408             $this->update( 'filearchive', array( 'fa_deleted_user' => 0 ),
01409                 array( 'fa_deleted_user' => $conds['user_id'] ), $fname );
01410             $this->update( 'filearchive', array( 'fa_user' => 0 ),
01411                 array( 'fa_user' => $conds['user_id'] ), $fname );
01412             $this->update( 'uploadstash', array( 'us_user' => 0 ),
01413                 array( 'us_user' => $conds['user_id'] ), $fname );
01414             $this->update( 'recentchanges', array( 'rc_user' => 0 ),
01415                 array( 'rc_user' => $conds['user_id'] ), $fname );
01416             $this->update( 'logging', array( 'log_user' => 0 ),
01417                 array( 'log_user' => $conds['user_id'] ), $fname );
01418         } elseif ( $table == $this->tableName( 'image' ) ) {
01419             $this->update( 'oldimage', array( 'oi_name' => 0 ),
01420                 array( 'oi_name' => $conds['img_name'] ), $fname );
01421         }
01422 
01423         return parent::delete( $table, $conds, $fname );
01424     }
01425 
01435     function update( $table, $values, $conds, $fname = __METHOD__, $options = array() ) {
01436         global $wgContLang;
01437 
01438         $table = $this->tableName( $table );
01439         $opts = $this->makeUpdateOptions( $options );
01440         $sql = "UPDATE $opts $table SET ";
01441 
01442         $first = true;
01443         foreach ( $values as $col => &$val ) {
01444             $sqlSet = $this->fieldBindStatement( $table, $col, $val, true );
01445 
01446             if ( !$first ) {
01447                 $sqlSet = ', ' . $sqlSet;
01448             } else {
01449                 $first = false;
01450             }
01451             $sql .= $sqlSet;
01452         }
01453 
01454         if ( $conds !== array() && $conds !== '*' ) {
01455             $conds = $this->wrapConditionsForWhere( $table, $conds );
01456             $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
01457         }
01458 
01459         if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) {
01460             $e = oci_error( $this->mConn );
01461             $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01462 
01463             return false;
01464         }
01465         foreach ( $values as $col => &$val ) {
01466             $col_info = $this->fieldInfoMulti( $table, $col );
01467             $col_type = $col_info != false ? $col_info->type() : 'CONSTANT';
01468 
01469             if ( $val === null ) {
01470                 // do nothing ... null was inserted in statement creation
01471             } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) {
01472                 if ( is_object( $val ) ) {
01473                     $val = $val->getData();
01474                 }
01475 
01476                 if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) {
01477                     $val = '31-12-2030 12:00:00.000000';
01478                 }
01479 
01480                 $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val;
01481                 if ( oci_bind_by_name( $stmt, ":$col", $val ) === false ) {
01482                     $e = oci_error( $stmt );
01483                     $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01484 
01485                     return false;
01486                 }
01487             } else {
01489                 if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) {
01490                     $e = oci_error( $stmt );
01491                     throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
01492                 }
01493 
01494                 if ( is_object( $val ) ) {
01495                     $val = $val->getData();
01496                 }
01497 
01498                 if ( $col_type == 'BLOB' ) {
01499                     $lob[$col]->writeTemporary( $val );
01500                     oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, SQLT_BLOB );
01501                 } else {
01502                     $lob[$col]->writeTemporary( $val );
01503                     oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB );
01504                 }
01505             }
01506         }
01507 
01508         wfSuppressWarnings();
01509 
01510         if ( oci_execute( $stmt, $this->execFlags() ) === false ) {
01511             $e = oci_error( $stmt );
01512             if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) {
01513                 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
01514 
01515                 return false;
01516             } else {
01517                 $this->mAffectedRows = oci_num_rows( $stmt );
01518             }
01519         } else {
01520             $this->mAffectedRows = oci_num_rows( $stmt );
01521         }
01522 
01523         wfRestoreWarnings();
01524 
01525         if ( isset( $lob ) ) {
01526             foreach ( $lob as $lob_v ) {
01527                 $lob_v->free();
01528             }
01529         }
01530 
01531         if ( !$this->mTrxLevel ) {
01532             oci_commit( $this->mConn );
01533         }
01534 
01535         return oci_free_statement( $stmt );
01536     }
01537 
01538     function bitNot( $field ) {
01539         // expecting bit-fields smaller than 4bytes
01540         return 'BITNOT(' . $field . ')';
01541     }
01542 
01543     function bitAnd( $fieldLeft, $fieldRight ) {
01544         return 'BITAND(' . $fieldLeft . ', ' . $fieldRight . ')';
01545     }
01546 
01547     function bitOr( $fieldLeft, $fieldRight ) {
01548         return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')';
01549     }
01550 
01551     function getDBname() {
01552         return $this->mDBname;
01553     }
01554 
01555     function getServer() {
01556         return $this->mServer;
01557     }
01558 
01559     public function buildGroupConcatField(
01560         $delim, $table, $field, $conds = '', $join_conds = array()
01561     ) {
01562         $fld = "LISTAGG($field," . $this->addQuotes( $delim ) . ") WITHIN GROUP (ORDER BY $field)";
01563 
01564         return '(' . $this->selectSQLText( $table, $fld, $conds, null, array(), $join_conds ) . ')';
01565     }
01566 
01567     public function getSearchEngine() {
01568         return 'SearchOracle';
01569     }
01570 
01571     public function getInfinity() {
01572         return '31-12-2030 12:00:00.000000';
01573     }
01574 }