Return a ResultWrapper from Database::query() and query builder functions, instead of a raw DB result resource. Backwards compatibility is maintained, except with naughty code that was calling database driver functions directly on result objects.

This commit is contained in:
Tim Starling 2007-07-05 19:42:18 +00:00
parent 1e1f1b3dc4
commit 9382bc7a85
2 changed files with 122 additions and 28 deletions

View file

@ -678,9 +678,12 @@ class Database {
* Usually aborts on failure. If errors are explicitly ignored, returns success.
*
* @param $sql String: SQL query
* @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST comment (you can use __METHOD__ or add some extra info)
* @param $tempIgnore Bool: Whether to avoid throwing an exception on errors... maybe best to catch the exception instead?
* @return Result object to feed to fetchObject, fetchRow, ...; or false on failure if $tempIgnore set
* @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST
* comment (you can use __METHOD__ or add some extra info)
* @param $tempIgnore Bool: Whether to avoid throwing an exception on errors...
* maybe best to catch the exception instead?
* @return true for a successful write query, ResultWrapper object for a successful read query,
* or false on failure if $tempIgnore set
* @throws DBQueryError Thrown when the database returns an error of any kind
*/
public function query( $sql, $fname = '', $tempIgnore = false ) {
@ -765,7 +768,7 @@ class Database {
wfProfileOut( $queryProf );
wfProfileOut( $totalProf );
}
return $ret;
return $this->resultObject( $ret );
}
/**
@ -909,6 +912,9 @@ class Database {
* Free a result object
*/
function freeResult( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
if ( !@/**/mysql_free_result( $res ) ) {
throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
}
@ -924,6 +930,9 @@ class Database {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
@/**/$row = mysql_fetch_object( $res );
if( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
@ -940,6 +949,9 @@ class Database {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
@/**/$row = mysql_fetch_array( $res );
if ( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
@ -951,6 +963,9 @@ class Database {
* Get the number of rows in a result object
*/
function numRows( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
@/**/$n = mysql_num_rows( $res );
if( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) );
@ -962,14 +977,24 @@ class Database {
* Get the number of fields in a result object
* See documentation for mysql_num_fields()
*/
function numFields( $res ) { return mysql_num_fields( $res ); }
function numFields( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
return mysql_num_fields( $res );
}
/**
* Get a field name in a result object
* See documentation for mysql_field_name():
* http://www.php.net/mysql_field_name
*/
function fieldName( $res, $n ) { return mysql_field_name( $res, $n ); }
function fieldName( $res, $n ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
return mysql_field_name( $res, $n );
}
/**
* Get the inserted value of an auto-increment row
@ -987,7 +1012,12 @@ class Database {
* Change the position of the cursor in a result object
* See mysql_data_seek()
*/
function dataSeek( $res, $row ) { return mysql_data_seek( $res, $row ); }
function dataSeek( $res, $row ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
return mysql_data_seek( $res, $row );
}
/**
* Get the last error number
@ -1352,9 +1382,9 @@ class Database {
function fieldInfo( $table, $field ) {
$table = $this->tableName( $table );
$res = $this->query( "SELECT * FROM $table LIMIT 1" );
$n = mysql_num_fields( $res );
$n = mysql_num_fields( $res->result );
for( $i = 0; $i < $n; $i++ ) {
$meta = mysql_fetch_field( $res, $i );
$meta = mysql_fetch_field( $res->result, $i );
if( $field == $meta->name ) {
return new MySQLField($meta);
}
@ -1366,6 +1396,9 @@ class Database {
* mysql_field_type() wrapper
*/
function fieldType( $res, $index ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
return mysql_field_type( $res, $index );
}
@ -2001,7 +2034,12 @@ class Database {
*/
function resultObject( $result ) {
if( empty( $result ) ) {
return NULL;
return false;
} elseif ( $result instanceof ResultWrapper ) {
return $result;
} elseif ( $result === true ) {
// Successful write query
return $result;
} else {
return new ResultWrapper( $this, $result );
}
@ -2176,7 +2214,7 @@ class Database {
$cmd = $this->replaceVars( $cmd );
$res = $this->query( $cmd, __METHOD__, true );
if ( $resultCallback ) {
call_user_func( $resultCallback, $this->resultObject( $res ) );
call_user_func( $resultCallback, $res );
}
if ( false === $res ) {
@ -2248,36 +2286,51 @@ class ResultWrapper {
var $db, $result;
/**
* @todo document
* Create a new result object from a result resource and a Database object
*/
function ResultWrapper( &$database, $result ) {
$this->db =& $database;
$this->result =& $result;
function ResultWrapper( $database, $result ) {
$this->db = $database;
if ( $result instanceof ResultWrapper ) {
$this->result = $result->result;
} else {
$this->result = $result;
}
}
/**
* @todo document
* Get the number of rows in a result object
*/
function numRows() {
return $this->db->numRows( $this->result );
}
/**
* @todo document
* Fetch the next row from the given result object, in object form.
* Fields can be retrieved with $row->fieldname, with fields acting like
* member variables.
*
* @param $res SQL result object as returned from Database::query(), etc.
* @return MySQL row object
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject() {
return $this->db->fetchObject( $this->result );
}
/**
* @todo document
* 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 MySQL row object
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow() {
return $this->db->fetchRow( $this->result );
}
/**
* @todo document
* Free a result object
*/
function free() {
$this->db->freeResult( $this->result );
@ -2285,10 +2338,17 @@ class ResultWrapper {
unset( $this->db );
}
/**
* Change the position of the cursor in a result object
* See mysql_data_seek()
*/
function seek( $row ) {
$this->db->dataSeek( $this->result, $row );
}
/**
* Reset the cursor to the start of the result set
*/
function rewind() {
if ($this->numRows()) {
$this->db->dataSeek($this->result, 0);

View file

@ -505,12 +505,18 @@ class DatabasePostgres extends Database {
}
function freeResult( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
if ( !@pg_free_result( $res ) ) {
throw new DBUnexpectedError($this, "Unable to free Postgres result\n" );
}
}
function fetchObject( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
@$row = pg_fetch_object( $res );
# FIXME: HACK HACK HACK HACK debug
@ -524,6 +530,9 @@ class DatabasePostgres extends Database {
}
function fetchRow( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
@$row = pg_fetch_array( $res );
if( pg_last_error($this->mConn) ) {
throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
@ -532,14 +541,27 @@ class DatabasePostgres extends Database {
}
function numRows( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
@$n = pg_num_rows( $res );
if( pg_last_error($this->mConn) ) {
throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
}
return $n;
}
function numFields( $res ) { return pg_num_fields( $res ); }
function fieldName( $res, $n ) { return pg_field_name( $res, $n ); }
function numFields( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
return pg_num_fields( $res );
}
function fieldName( $res, $n ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
return pg_field_name( $res, $n );
}
/**
* This must be called after nextSequenceVal
@ -548,7 +570,13 @@ class DatabasePostgres extends Database {
return $this->mInsertId;
}
function dataSeek( $res, $row ) { return pg_result_seek( $res, $row ); }
function dataSeek( $res, $row ) {
if ( $res instanceof ResultWrapper ) {
$res = $res->result;
}
return pg_result_seek( $res, $row );
}
function lastError() {
if ( $this->mConn ) {
return pg_last_error();
@ -917,7 +945,7 @@ class DatabasePostgres extends Database {
. "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
. "AND c.relkind IN ('" . implode("','", $types) . "')";
$res = $this->query( $SQL );
$count = $res ? pg_num_rows($res) : 0;
$count = $res ? $res->numRows() : 0;
if ($res)
$this->freeResult( $res );
return $count ? true : false;
@ -950,7 +978,7 @@ END;
$this->addQuotes($trigger)));
if (!$res)
return NULL;
$rows = pg_num_rows($res);
$rows = $res->numRows();
$this->freeResult($res);
return $rows;
}
@ -974,7 +1002,7 @@ END;
$res = $this->query($SQL);
if (!$res)
return NULL;
$rows = pg_num_rows($res);
$rows = $res->numRows();
$this->freeResult($res);
return $rows;
}
@ -987,7 +1015,12 @@ END;
$SQL = "SELECT rolname FROM pg_catalog.pg_namespace n, pg_catalog.pg_roles r "
."WHERE n.nspowner=r.oid AND n.nspname = '$eschema'";
$res = $this->query( $SQL );
$owner = $res ? pg_num_rows($res) ? pg_fetch_result($res, 0, 0) : false : false;
if ( $res && $res->numRows() ) {
$row = $res->fetchRow();
$owner = $row->rolname;
} else {
$owner = false;
}
if ($res)
$this->freeResult($res);
return $owner;
@ -1005,7 +1038,7 @@ END;
. "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
. "AND a.attrelid = c.oid AND a.attname = '$ecol'";
$res = $this->query( $SQL, $fname );
$count = $res ? pg_num_rows($res) : 0;
$count = $res ? $res->numRows() : 0;
if ($res)
$this->freeResult( $res );
return $count;
@ -1071,7 +1104,8 @@ END;
$tss = $this->addQuotes($wgDBts2schema);
$pgp = $this->addQuotes($wgDBport);
$dbn = $this->addQuotes($this->mDBname);
$ctype = pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0);
$ctypeRow = $this->doQuery("SHOW lc_ctype")->fetchArray();
$ctype = $ctypeRow[0];
$SQL = "UPDATE mediawiki_version SET mw_version=$mwv, pg_version=$pgv, pg_user=$pgu, ".
"mw_schema = $mws, ts2_schema = $tss, pg_port=$pgp, pg_dbname=$dbn, ".