/**
* This is the IBM DB2 database abstraction layer.
* See maintenance/ibm_db2/README for development notes
- * and other specific information
+ * and other specific information.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Database
* This represents a column in a DB2 database
* @ingroup Database
*/
-class IBM_DB2Field {
+class IBM_DB2Field implements Field {
private $name = '';
private $tablename = '';
private $type = '';
* Can column be null?
* @return bool true or false
*/
- function nullable() { return $this->nullable; }
+ function isNullable() { return $this->nullable; }
/**
* How much can you fit in the column per row?
* @return int length
}
}
+/**
+ * Wrapper to address lack of certain operations in the DB2 driver
+ * ( seek, num_rows )
+ * @ingroup Database
+ * @since 1.19
+ */
+class IBM_DB2Result{
+ private $db;
+ private $result;
+ private $num_rows;
+ private $current_pos;
+ private $columns = array();
+ private $sql;
+
+ private $resultSet = array();
+ private $loadedLines = 0;
+
+ /**
+ * Construct and initialize a wrapper for DB2 query results
+ * @param $db DatabaseBase
+ * @param $result Object
+ * @param $num_rows Integer
+ * @param $sql String
+ * @param $columns Array
+ */
+ public function __construct( $db, $result, $num_rows, $sql, $columns ){
+ $this->db = $db;
+
+ if( $result instanceof ResultWrapper ){
+ $this->result = $result->result;
+ }
+ else{
+ $this->result = $result;
+ }
+
+ $this->num_rows = $num_rows;
+ $this->current_pos = 0;
+ if ( $this->num_rows > 0 ) {
+ // Make a lower-case list of the column names
+ // By default, DB2 column names are capitalized
+ // while MySQL column names are lowercase
+
+ // Is there a reasonable maximum value for $i?
+ // Setting to 2048 to prevent an infinite loop
+ for( $i = 0; $i < 2048; $i++ ) {
+ $name = db2_field_name( $this->result, $i );
+ if ( $name != false ) {
+ continue;
+ }
+ else {
+ return false;
+ }
+
+ $this->columns[$i] = strtolower( $name );
+ }
+ }
+
+ $this->sql = $sql;
+ }
+
+ /**
+ * Unwrap the DB2 query results
+ * @return mixed Object on success, false on failure
+ */
+ public function getResult() {
+ if ( $this->result ) {
+ return $this->result;
+ }
+ else return false;
+ }
+
+ /**
+ * Get the number of rows in the result set
+ * @return integer
+ */
+ public function getNum_rows() {
+ return $this->num_rows;
+ }
+
+ /**
+ * Return a row from the result set in object format
+ * @return mixed Object on success, false on failure.
+ */
+ public function fetchObject() {
+ if ( $this->result
+ && $this->num_rows > 0
+ && $this->current_pos >= 0
+ && $this->current_pos < $this->num_rows )
+ {
+ $row = $this->fetchRow();
+ $ret = new stdClass();
+
+ foreach ( $row as $k => $v ) {
+ $lc = $this->columns[$k];
+ $ret->$lc = $v;
+ }
+ return $ret;
+ }
+ return false;
+ }
+
+ /**
+ * Return a row form the result set in array format
+ * @return mixed Array on success, false on failure
+ * @throws DBUnexpectedError
+ */
+ public function fetchRow(){
+ if ( $this->result
+ && $this->num_rows > 0
+ && $this->current_pos >= 0
+ && $this->current_pos < $this->num_rows )
+ {
+ if ( $this->loadedLines <= $this->current_pos ) {
+ $row = db2_fetch_array( $this->result );
+ $this->resultSet[$this->loadedLines++] = $row;
+ if ( $this->db->lastErrno() ) {
+ throw new DBUnexpectedError( $this->db, 'Error in fetchRow(): '
+ . htmlspecialchars( $this->db->lastError() ) );
+ }
+ }
+
+ if ( $this->loadedLines > $this->current_pos ){
+ return $this->resultSet[$this->current_pos++];
+ }
+
+ }
+ return false;
+ }
+
+ /**
+ * Free a DB2 result object
+ * @throws DBUnexpectedError
+ */
+ public function freeResult(){
+ unset( $this->resultSet );
+ if ( !@db2_free_result( $this->result ) ) {
+ throw new DBUnexpectedError( $this, "Unable to free DB2 result\n" );
+ }
+ }
+}
+
/**
* Primary database interface
* @ingroup Database
protected $mPHPError = false;
protected $mServer, $mUser, $mPassword, $mConn = null, $mDBname;
- protected $mOut, $mOpened = false;
+ protected $mOpened = false;
- protected $mFailFunction;
protected $mTablePrefix;
protected $mFlags;
protected $mTrxLevel = 0;
protected $mAffectedRows = null;
/** Number of rows returned by last SELECT */
protected $mNumRows = null;
+ /** Current row number on the cursor of the last SELECT */
+ protected $currentRow = 0;
/** Connection config options - see constructor */
public $mConnOptions = array();
public $mStmtOptions = array();
/** Default schema */
- const USE_GLOBAL = 'mediawiki';
+ const USE_GLOBAL = 'get from global';
/** Option that applies to nothing */
const NONE_OPTION = 0x00;
/**
* Returns true if this database supports (and uses) cascading deletes
+ * @return bool
*/
function cascadingDeletes() {
return true;
/**
* Returns true if this database supports (and uses) triggers (e.g. on the
* page table)
+ * @return bool
*/
function cleanupTriggers() {
return true;
* Returns true if this database is strict about what can be put into an
* IP field.
* Specifically, it uses a NULL value instead of an empty string.
+ * @return bool
*/
function strictIPs() {
return true;
/**
* Returns true if this database uses timestamps rather than integers
- */
+ * @return bool
+ */
function realTimestamps() {
return true;
}
/**
* Returns true if this database does an implicit sort when doing GROUP BY
+ * @return bool
*/
function implicitGroupby() {
return false;
* Returns true if this database does an implicit order by when the column
* has an index
* For example: SELECT page_title FROM page LIMIT 1
+ * @return bool
*/
function implicitOrderby() {
return false;
/**
* Returns true if this database can do a native search on IP columns
* e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
+ * @return bool
*/
function searchableIPs() {
return true;
/**
* Returns true if this database can use functional indexes
+ * @return bool
*/
function functionalIndexes() {
return true;
/**
* Returns a unique string representing the wiki on the server
+ * @return string
*/
- function getWikiID() {
+ public function getWikiID() {
if( $this->mSchema ) {
return "{$this->mDBname}-{$this->mSchema}";
} else {
}
}
- function getType() {
+ /**
+ * Returns the database software identifieir
+ * @return string
+ */
+ public function getType() {
return 'ibm_db2';
}
- ######################################
- # Setup
- ######################################
-
+ /**
+ * Returns the database connection object
+ * @return Object
+ */
+ public function getDb(){
+ return $this->mConn;
+ }
/**
*
* @param $user String: username
* @param $password String: password
* @param $dbName String: database name on the server
- * @param $failFunction Callback (optional)
* @param $flags Integer: database behaviour flags (optional, unused)
* @param $schema String
*/
- public function DatabaseIbm_db2( $server = false, $user = false,
+ public function __construct( $server = false, $user = false,
$password = false,
- $dbName = false, $failFunction = false, $flags = 0,
+ $dbName = false, $flags = 0,
$schema = self::USE_GLOBAL )
{
-
- global $wgOut, $wgDBmwschema;
- # Can't get a reference if it hasn't been set yet
- if ( !isset( $wgOut ) ) {
- $wgOut = null;
- }
- $this->mOut =& $wgOut;
- $this->mFailFunction = $failFunction;
- $this->mFlags = DBO_TRX | $flags;
+ global $wgDBmwschema;
if ( $schema == self::USE_GLOBAL ) {
$this->mSchema = $wgDBmwschema;
self::STMT_OPTION );
$this->setDB2Option( 'rowcount', 'DB2_ROWCOUNT_PREFETCH_ON',
self::STMT_OPTION );
-
- $this->open( $server, $user, $password, $dbName );
+ parent::__construct( $server, $user, $password, $dbName, DBO_TRX | $flags );
}
/**
* @param $user String
* @param $password String
* @param $dbName String: database name
- * @return a fresh connection
+ * @return DatabaseBase a fresh connection
*/
public function open( $server, $user, $password, $dbName ) {
- // Load the port number
- global $wgDBport;
wfProfileIn( __METHOD__ );
- // Load IBM DB2 driver if missing
+ # Load IBM DB2 driver if missing
wfDl( 'ibm_db2' );
- // Test for IBM DB2 support, to avoid suppressed fatal error
+ # Test for IBM DB2 support, to avoid suppressed fatal error
if ( !function_exists( 'db2_connect' ) ) {
- $error = <<<ERROR
-DB2 functions missing, have you enabled the ibm_db2 extension for PHP?
-
-ERROR;
- $this->installPrint( $error );
- $this->reportConnectionError( $error );
+ throw new DBConnectionError( $this, "DB2 functions missing, have you enabled the ibm_db2 extension for PHP?" );
}
- if ( strlen( $user ) < 1 ) {
- return null;
- }
+ global $wgDBport;
// Close existing connection
$this->close();
$this->openUncataloged( $dbName, $user, $password, $server, $port );
- // Apply connection config
- db2_set_option( $this->mConn, $this->mConnOptions, 1 );
- // Some MediaWiki code is still transaction-less (?).
- // The strategy is to keep AutoCommit on for that code
- // but switch it off whenever a transaction is begun.
- db2_autocommit( $this->mConn, DB2_AUTOCOMMIT_ON );
-
if ( !$this->mConn ) {
$this->installPrint( "DB connection error\n" );
$this->installPrint(
"Server: $server, Database: $dbName, User: $user, Password: "
. substr( $password, 0, 3 ) . "...\n" );
$this->installPrint( $this->lastError() . "\n" );
- return null;
+ wfProfileOut( __METHOD__ );
+ wfDebug( "DB connection error\n" );
+ wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
+ wfDebug( $this->lastError() . "\n" );
+ throw new DBConnectionError( $this, $this->lastError() );
}
+ // Some MediaWiki code is still transaction-less (?).
+ // The strategy is to keep AutoCommit on for that code
+ // but switch it off whenever a transaction is begun.
+ db2_autocommit( $this->mConn, DB2_AUTOCOMMIT_ON );
+
$this->mOpened = true;
$this->applySchema();
* Opens a cataloged database connection, sets mConn
*/
protected function openCataloged( $dbName, $user, $password ) {
- @$this->mConn = db2_pconnect( $dbName, $user, $password );
+ wfSuppressWarnings();
+ $this->mConn = db2_pconnect( $dbName, $user, $password );
+ wfRestoreWarnings();
}
/**
*/
protected function openUncataloged( $dbName, $user, $password, $server, $port )
{
- $str = "DRIVER={IBM DB2 ODBC DRIVER};";
- $str .= "DATABASE=$dbName;";
- $str .= "HOSTNAME=$server;";
- // port was formerly validated to not be 0
- $str .= "PORT=$port;";
- $str .= "PROTOCOL=TCPIP;";
- $str .= "UID=$user;";
- $str .= "PWD=$password;";
-
- @$this->mConn = db2_pconnect( $str, $user, $password );
+ $dsn = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=$dbName;CHARSET=UTF-8;HOSTNAME=$server;PORT=$port;PROTOCOL=TCPIP;UID=$user;PWD=$password;";
+ wfSuppressWarnings();
+ $this->mConn = db2_pconnect( $dsn, "", "", array() );
+ wfRestoreWarnings();
}
/**
* Closes a database connection, if it is open
* Returns success, true if already closed
+ * @return bool
*/
- public function close() {
- $this->mOpened = false;
- if ( $this->mConn ) {
- if ( $this->trxLevel() > 0 ) {
- $this->commit();
- }
- return db2_close( $this->mConn );
- } else {
- return true;
- }
- }
-
- /**
- * Returns a fresh instance of this class
- *
- * @param $server String: hostname of database server
- * @param $user String: username
- * @param $password String
- * @param $dbName String: database name on the server
- * @param $failFunction Callback (optional)
- * @param $flags Integer: database behaviour flags (optional, unused)
- * @return DatabaseIbm_db2 object
- */
- static function newFromParams( $server, $user, $password, $dbName,
- $failFunction = false, $flags = 0 )
- {
- return new DatabaseIbm_db2( $server, $user, $password, $dbName,
- $failFunction, $flags );
+ protected function closeConnection() {
+ return db2_close( $this->mConn );
}
/**
* Retrieves the most current database error
* Forces a database rollback
+ * @return bool|string
*/
public function lastError() {
$connerr = db2_conn_errormsg();
if ( $connerr ) {
- //$this->rollback();
+ //$this->rollback( __METHOD__ );
return $connerr;
}
$stmterr = db2_stmt_errormsg();
if ( $stmterr ) {
- //$this->rollback();
+ //$this->rollback( __METHOD__ );
return $stmterr;
}
* The DBMS-dependent part of query()
* @param $sql String: SQL query.
* @return object Result object for fetch functions or false on failure
- * @access private
*/
- /*private*/
- public function doQuery( $sql ) {
+ protected function doQuery( $sql ) {
$this->applySchema();
+ // Needed to handle any UTF-8 encoding issues in the raw sql
+ // Note that we fully support prepared statements for DB2
+ // prepare() and execute() should be used instead of doQuery() whenever possible
+ $sql = utf8_decode( $sql );
+
$ret = db2_exec( $this->mConn, $sql, $this->mStmtOptions );
if( $ret == false ) {
$error = db2_stmt_errormsg();
+
$this->installPrint( "<pre>$sql</pre>" );
$this->installPrint( $error );
throw new DBUnexpectedError( $this, 'SQL error: '
* Queries whether a given table exists
* @return boolean
*/
- public function tableExists( $table ) {
+ public function tableExists( $table, $fname = __METHOD__ ) {
$schema = $this->mSchema;
- $sql = <<< EOF
-SELECT COUNT( * ) FROM SYSIBM.SYSTABLES ST
-WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema'
-EOF;
+
+ $sql = "SELECT COUNT( * ) FROM SYSIBM.SYSTABLES ST WHERE ST.NAME = '" .
+ strtoupper( $table ) .
+ "' AND ST.CREATOR = '" .
+ strtoupper( $schema ) . "'";
$res = $this->query( $sql );
if ( !$res ) {
return false;
}
// If the table exists, there should be one of it
- @$row = $this->fetchRow( $res );
+ $row = $this->fetchRow( $res );
$count = $row[0];
if ( $count == '1' || $count == 1 ) {
return true;
* Fields can be retrieved with $row->fieldname, with fields acting like
* member variables.
*
- * @param $res SQL result object as returned from Database::query(), etc.
+ * @param $res array|ResultWrapper SQL result object as returned from Database::query(), etc.
* @return DB2 row object
* @throws DBUnexpectedError Thrown if the database returns an error
*/
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
- @$row = db2_fetch_object( $res );
+ wfSuppressWarnings();
+ $row = db2_fetch_object( $res );
+ wfRestoreWarnings();
if( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in fetchObject(): '
. htmlspecialchars( $this->lastError() ) );
* Fetch the next row from the given result object, in associative array
* form. Fields are retrieved with $row['fieldname'].
*
- * @param $res SQL result object as returned from Database::query(), etc.
- * @return DB2 row object
+ * @param $res array|ResultWrapper SQL result object as returned from Database::query(), etc.
+ * @return ResultWrapper row object
* @throws DBUnexpectedError Thrown if the database returns an error
*/
public function fetchRow( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
- @$row = db2_fetch_array( $res );
- if ( $this->lastErrno() ) {
- throw new DBUnexpectedError( $this, 'Error in fetchRow(): '
- . htmlspecialchars( $this->lastError() ) );
- }
- return $row;
- }
-
- /**
- * Create tables, stored procedures, and so on
- */
- public function setup_database() {
- try {
- // TODO: switch to root login if available
-
- // Switch into the correct namespace
- $this->applySchema();
- $this->begin();
-
- $res = $this->sourceFile( "../maintenance/ibm_db2/tables.sql" );
- if ( $res !== true ) {
- print ' <b>FAILED</b>: ' . htmlspecialchars( $res ) . '</li>';
- } else {
- print ' done</li>';
- }
- $res = $this->sourceFile( "../maintenance/ibm_db2/foreignkeys.sql" );
- if ( $res !== true ) {
- print ' <b>FAILED</b>: ' . htmlspecialchars( $res ) . '</li>';
- } else {
- print '<li>Foreign keys done</li>';
- }
- $res = null;
-
- // TODO: populate interwiki links
-
- if ( $this->lastError() ) {
- $this->installPrint(
- 'Errors encountered during table creation -- rolled back' );
- $this->installPrint( 'Please install again' );
- $this->rollback();
- } else {
- $this->commit();
+ if ( db2_num_rows( $res ) > 0) {
+ wfSuppressWarnings();
+ $row = db2_fetch_array( $res );
+ wfRestoreWarnings();
+ if ( $this->lastErrno() ) {
+ throw new DBUnexpectedError( $this, 'Error in fetchRow(): '
+ . htmlspecialchars( $this->lastError() ) );
}
- } catch ( MWException $mwe ) {
- print "<br><pre>$mwe</pre><br>";
+ return $row;
}
+ return false;
}
/**
* Doesn't escape numbers
*
* @param $s String: string to escape
- * @return escaped string
+ * @return string escaped string
*/
public function addQuotes( $s ) {
//$this->installPrint( "DB2::addQuotes( $s )\n" );
/**
* Alias for addQuotes()
* @param $s String: string to escape
- * @return escaped string
+ * @return string escaped string
*/
public function strencode( $s ) {
// Bloody useless function
protected function applySchema() {
if ( !( $this->mSchemaSet ) ) {
$this->mSchemaSet = true;
- $this->begin();
+ $this->begin( __METHOD__ );
$this->doQuery( "SET SCHEMA = $this->mSchema" );
- $this->commit();
+ $this->commit( __METHOD__ );
}
}
* LIST_SET - comma separated with field names, like a SET clause
* LIST_NAMES - comma separated field names
* LIST_SET_PREPARED - like LIST_SET, except with ? tokens as values
+ * @return string
*/
function makeList( $a, $mode = LIST_COMMA ) {
if ( !is_array( $a ) ) {
throw new DBUnexpectedError( $this,
- 'DatabaseBase::makeList called with incorrect parameters' );
+ 'DatabaseIbm_db2::makeList called with incorrect parameters' );
}
// if this is for a prepared UPDATE statement
* @param $sql string SQL query we will append the limit too
* @param $limit integer the SQL limit
* @param $offset integer the SQL offset (default false)
+ * @return string
*/
public function limitResult( $sql, $limit, $offset=false ) {
if( !is_numeric( $limit ) ) {
* Handle reserved keyword replacement in table names
*
* @param $name Object
+ * @param $format String Ignored parameter Default 'quoted'Boolean
* @return String
*/
- public function tableName( $name ) {
+ public function tableName( $name, $format = 'quoted' ) {
// we want maximum compatibility with MySQL schema
return $name;
}
/**
* Generates a timestamp in an insertable format
*
- * @param $ts timestamp
+ * @param $ts string timestamp
* @return String: timestamp value
*/
public function timestamp( $ts = 0 ) {
/**
* Return the next in a sequence, save the value for retrieval via insertId()
* @param $seqName String: name of a defined sequence in the database
- * @return next value in that sequence
+ * @return int next value in that sequence
*/
public function nextSequenceValue( $seqName ) {
// Not using sequences in the primary schema to allow for easier migration
/**
* This must be called after nextSequenceVal
- * @return Last sequence value used as a primary key
+ * @return int Last sequence value used as a primary key
*/
public function insertId() {
return $this->mInsertId;
$res = true;
// If we are not in a transaction, we need to be for savepoint trickery
if ( !$this->mTrxLevel ) {
- $this->begin();
+ $this->begin( __METHOD__ );
}
$sql = "INSERT INTO $table ( " . implode( ',', $keys ) . ' ) VALUES ';
} else {
$sql .= '( ?' . str_repeat( ',?', $key_count-1 ) . ' )';
}
- //$this->installPrint( "Preparing the following SQL:" );
- //$this->installPrint( "$sql" );
- //$this->installPrint( print_r( $args, true ));
+ $this->installPrint( "Preparing the following SQL:" );
+ $this->installPrint( "$sql" );
+ $this->installPrint( print_r( $args, true ));
$stmt = $this->prepare( $sql );
// start a transaction/enter transaction mode
- $this->begin();
+ $this->begin( __METHOD__ );
if ( !$ignore ) {
//$first = true;
$overhead = "SAVEPOINT $ignore ON ROLLBACK RETAIN CURSORS";
db2_exec( $this->mConn, $overhead, $this->mStmtOptions );
- $this->execute( $stmt, $row );
+ $res2 = $this->execute( $stmt, $row );
if ( !$res2 ) {
$this->installPrint( 'Last error:' );
$this->mAffectedRows = $numrowsinserted;
}
// commit either way
- $this->commit();
+ $this->commit( __METHOD__ );
$this->freePrepared( $stmt );
return $res;
*/
private function removeNullPrimaryKeys( $table, $args ) {
$schema = $this->mSchema;
+
// find out the primary keys
- $keyres = db2_primary_keys( $this->mConn, null, strtoupper( $schema ),
- strtoupper( $table )
- );
+ $keyres = $this->doQuery( "SELECT NAME FROM SYSIBM.SYSCOLUMNS WHERE TBNAME = '"
+ . strtoupper( $table )
+ . "' AND TBCREATOR = '"
+ . strtoupper( $schema )
+ . "' AND KEYSEQ > 0" );
+
$keys = array();
for (
- $row = $this->fetchObject( $keyres );
+ $row = $this->fetchRow( $keyres );
$row != null;
- $row = $this->fetchObject( $keyres )
+ $row = $this->fetchRow( $keyres )
)
{
- $keys[] = strtolower( $row->column_name );
+ $keys[] = strtolower( $row[0] );
}
// remove primary keys
foreach ( $args as $ai => $row ) {
- foreach ( $keys as $ki => $key ) {
+ foreach ( $keys as $key ) {
if ( $row[$key] == null ) {
unset( $row[$key] );
}
* UPDATE wrapper, takes a condition array and a SET array
*
* @param $table String: The table to UPDATE
- * @param $values An array of values to SET
- * @param $conds An array of conditions ( WHERE ). Use '*' to update all rows.
+ * @param $values array An array of values to SET
+ * @param $conds array An array of conditions ( WHERE ). Use '*' to update all rows.
* @param $fname String: The Class::Function calling this function
* ( for the log )
- * @param $options An array of UPDATE options, can be one or
+ * @param $options array An array of UPDATE options, can be one or
* more of IGNORE, LOW_PRIORITY
* @return Boolean
*/
- public function update( $table, $values, $conds, $fname = 'Database::update',
+ public function update( $table, $values, $conds, $fname = 'DatabaseIbm_db2::update',
$options = array() )
{
$table = $this->tableName( $table );
* DELETE query wrapper
*
* Use $conds == "*" to delete all rows
+ * @return bool|\ResultWrapper
*/
- public function delete( $table, $conds, $fname = 'Database::delete' ) {
+ public function delete( $table, $conds, $fname = 'DatabaseIbm_db2::delete' ) {
if ( !$conds ) {
throw new DBUnexpectedError( $this,
- 'Database::delete() called with no conditions' );
+ 'DatabaseIbm_db2::delete() called with no conditions' );
}
$table = $this->tableName( $table );
$sql = "DELETE FROM $table";
return db2_num_rows( $this->mLastResult );
}
- /**
- * Simulates REPLACE with a DELETE followed by INSERT
- * @param $table Object
- * @param $uniqueIndexes Array consisting of indexes and arrays of indexes
- * @param $rows Array: rows to insert
- * @param $fname String: name of the function for profiling
- * @return nothing
- */
- function replace( $table, $uniqueIndexes, $rows,
- $fname = 'DatabaseIbm_db2::replace' )
- {
- $table = $this->tableName( $table );
-
- if ( count( $rows )==0 ) {
- return;
- }
-
- # Single row case
- if ( !is_array( reset( $rows ) ) ) {
- $rows = array( $rows );
- }
-
- foreach( $rows as $row ) {
- # Delete rows which collide
- if ( $uniqueIndexes ) {
- $sql = "DELETE FROM $table WHERE ";
- $first = true;
- foreach ( $uniqueIndexes as $index ) {
- if ( $first ) {
- $first = false;
- $sql .= '( ';
- } else {
- $sql .= ' ) OR ( ';
- }
- if ( is_array( $index ) ) {
- $first2 = true;
- foreach ( $index as $col ) {
- if ( $first2 ) {
- $first2 = false;
- } else {
- $sql .= ' AND ';
- }
- $sql .= $col . '=' . $this->addQuotes( $row[$col] );
- }
- } else {
- $sql .= $index . '=' . $this->addQuotes( $row[$index] );
- }
- }
- $sql .= ' )';
- $this->query( $sql, $fname );
- }
-
- # Now insert the row
- $sql = "INSERT INTO $table ( "
- . $this->makeList( array_keys( $row ), LIST_NAMES )
- .' ) VALUES ( ' . $this->makeList( $row, LIST_COMMA ) . ' )';
- $this->query( $sql, $fname );
- }
- }
-
/**
* Returns the number of rows in the result set
* Has to be called right after the corresponding select query
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
+
if ( $this->mNumRows ) {
return $this->mNumRows;
} else {
* Moves the row pointer of the result set
* @param $res Object: result set
* @param $row Integer: row number
- * @return success or failure
+ * @return bool success or failure
*/
public function dataSeek( $res, $row ) {
if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
+ return $res = $res->result;
}
- return db2_fetch_row( $res, $row );
+ if ( $res instanceof IBM_DB2Result ) {
+ return $res->dataSeek( $row );
+ }
+ wfDebug( "dataSeek operation in DB2 database\n" );
+ return false;
}
###
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
- if ( !@db2_free_result( $res ) ) {
+ wfSuppressWarnings();
+ $ok = db2_free_result( $res );
+ wfRestoreWarnings();
+ if ( !$ok ) {
throw new DBUnexpectedError( $this, "Unable to free DB2 result\n" );
}
}
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
+ if ( $res instanceof IBM_DB2Result ) {
+ $res = $res->getResult();
+ }
return db2_num_fields( $res );
}
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
+ if ( $res instanceof IBM_DB2Result ) {
+ $res = $res->getResult();
+ }
return db2_field_name( $res, $n );
}
* @param $conds Array or string, condition(s) for WHERE
* @param $fname String: calling function name (use __METHOD__)
* for logs/profiling
- * @param $options Associative array of options
- * (e.g. array('GROUP BY' => 'page_title')),
+ * @param $options array Associative array of options
+ * (e.g. array( 'GROUP BY' => 'page_title' )),
* see Database::makeSelectOptions code for list of
* supported stuff
- * @param $join_conds Associative array of table join conditions (optional)
+ * @param $join_conds array Associative array of table join conditions (optional)
* (e.g. array( 'page' => array('LEFT JOIN',
* 'page_latest=rev_id') )
* @return Mixed: database result resource for fetch functions or false
{
$res = parent::select( $table, $vars, $conds, $fname, $options,
$join_conds );
+ $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
// We must adjust for offset
- if ( isset( $options['LIMIT'] ) ) {
- if ( isset ( $options['OFFSET'] ) ) {
- $limit = $options['LIMIT'];
- $offset = $options['OFFSET'];
- }
+ if ( isset( $options['LIMIT'] ) && isset ( $options['OFFSET'] ) ) {
+ $limit = $options['LIMIT'];
+ $offset = $options['OFFSET'];
}
-
// DB2 does not have a proper num_rows() function yet, so we must emulate
// DB2 9.5.4 and the corresponding ibm_db2 driver will introduce
// a working one
$res2 = parent::select( $table, $vars2, $conds, $fname, $options2,
$join_conds );
+
$obj = $this->fetchObject( $res2 );
$this->mNumRows = $obj->num_rows;
-
- return $res;
+
+ return new ResultWrapper( $this, new IBM_DB2Result( $this, $res, $obj->num_rows, $vars, $sql ) );
}
/**
*
* @private
*
- * @param $options Associative array of options to be turned into
+ * @param $options array Associative array of options to be turned into
* an SQL query, valid keys are listed in the function.
* @return Array
*/
// db2_ping() doesn't exist
// Emulate
$this->close();
- $this->mConn = $this->openUncataloged( $this->mDBName, $this->mUser,
+ $this->openUncataloged( $this->mDBName, $this->mUser,
$this->mPassword, $this->mServer, $this->mPort );
return false;
######################################
# Unimplemented and not applicable
######################################
- /**
- * Not implemented
- * @return string ''
- */
- public function getStatus( $which = '%' ) {
- $this->installPrint( 'Not implemented for DB2: getStatus()' );
- return '';
- }
/**
* Not implemented
* @return string $sql
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
+ if ( $res instanceof IBM_DB2Result ) {
+ $res = $res->getResult();
+ }
return db2_field_type( $res, $index );
}
* Verifies that an index was created as unique
* @param $table String: table name
* @param $index String: index name
- * @param $fname function name for profiling
+ * @param $fname string function name for profiling
* @return Bool
*/
public function indexUnique ( $table, $index,
- $fname = 'Database::indexUnique' )
+ $fname = 'DatabaseIbm_db2::indexUnique' )
{
$table = $this->tableName( $table );
$sql = <<<SQL
return $size;
}
- /**
- * DELETE where the condition is a join
- * @param $delTable String: deleting from this table
- * @param $joinTable String: using data from this table
- * @param $delVar String: variable in deleteable table
- * @param $joinVar String: variable in data table
- * @param $conds Array: conditionals for join table
- * @param $fname String: function name for profiling
- */
- public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar,
- $conds, $fname = "DatabaseIbm_db2::deleteJoin" )
- {
- if ( !$conds ) {
- throw new DBUnexpectedError( $this,
- 'Database::deleteJoin() called with empty $conds' );
- }
-
- $delTable = $this->tableName( $delTable );
- $joinTable = $this->tableName( $joinTable );
- $sql = <<<SQL
-DELETE FROM $delTable
-WHERE $delVar IN (
- SELECT $joinVar FROM $joinTable
-
-SQL;
- if ( $conds != '*' ) {
- $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
- }
- $sql .= ' )';
-
- $this->query( $sql, $fname );
- }
-
/**
* Description is left as an exercise for the reader
* @param $b Mixed: data to be encoded
* in the appropriate places.
* @param $query String
* @param $args ...
+ * @return Resource
*/
public function safeQuery( $query, $args = null ) {
// copied verbatim from Database.php
/**
* Switches module between regular and install modes
+ * @return string
*/
public function setMode( $mode ) {
$old = $this->mMode;