1
0
mirror of https://github.com/dg/dibi.git synced 2025-07-31 19:30:30 +02:00

* seek() or rowCount() in unbuffered mode throws exceptions

* out of range seek() throws exception
* deprecated DibiDriver::errorInfo
* fixed seek(0) on first iteration
* added DibiDatabaseException::catchError() & restore() for converting errors to exceptions
This commit is contained in:
David Grudl
2007-11-12 01:39:26 +00:00
parent 5ee6a19f93
commit ea00d5d37d
14 changed files with 184 additions and 223 deletions

View File

@@ -123,7 +123,7 @@ class DibiMsSqlDriver extends DibiDriver
throw new DibiDatabaseException('Query error', 0, $sql);
}
return is_resource($res) ? new DibiMSSqlResult($res) : NULL;
return is_resource($res) ? new DibiMSSqlResult($res, TRUE) : NULL;
}
@@ -189,21 +189,6 @@ class DibiMsSqlDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
return array(
'message' => NULL,
'code' => NULL,
);
}
/**
* Escapes the string
*
@@ -293,7 +278,7 @@ class DibiMSSqlResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
return mssql_num_rows($this->resource);
}
@@ -317,11 +302,14 @@ class DibiMSSqlResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
return mssql_data_seek($this->resource, $row);
if (!mssql_data_seek($this->resource, $row)) {
throw new DibiDriverException('Unable to seek to row ' . $row);
}
}
@@ -331,7 +319,7 @@ class DibiMSSqlResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
mssql_free_result($this->resource);
}

View File

@@ -103,27 +103,16 @@ class DibiMySqlDriver extends DibiDriver
$host = ':' . $config['socket'];
}
// some errors aren't handled. Must use $php_errormsg
if (function_exists('ini_set')) {
$save = ini_set('track_errors', TRUE);
}
$php_errormsg = '';
DibiDatabaseException::catchError();
if (empty($config['persistent'])) {
$connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['options']);
} else {
$connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['options']);
}
if (function_exists('ini_set')) {
ini_set('track_errors', $save);
}
DibiDatabaseException::restore();
if (!is_resource($connection)) {
$msg = mysql_error();
if (!$msg) $msg = $php_errormsg;
throw new DibiDatabaseException($msg, mysql_errno());
throw new DibiDatabaseException(mysql_error(), mysql_errno());
}
if (isset($config['charset'])) {
@@ -163,17 +152,18 @@ class DibiMySqlDriver extends DibiDriver
{
$connection = $this->getConnection();
if ($this->getConfig('unbuffered')) {
$res = @mysql_unbuffered_query($sql, $connection);
} else {
$buffered = !$this->getConfig('unbuffered');
if ($buffered) {
$res = @mysql_query($sql, $connection);
} else {
$res = @mysql_unbuffered_query($sql, $connection);
}
if ($errno = mysql_errno($connection)) {
throw new DibiDatabaseException(mysql_error($connection), $errno, $sql);
}
return is_resource($res) ? new DibiMySqlResult($res) : NULL;
return is_resource($res) ? new DibiMySqlResult($res, $buffered) : NULL;
}
@@ -240,22 +230,6 @@ class DibiMySqlDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
$connection = $this->getConnection();
return array(
'message' => mysql_error($connection),
'code' => mysql_errno($connection),
);
}
/**
* Escapes the string
*
@@ -342,7 +316,7 @@ class DibiMySqlResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
return mysql_num_rows($this->resource);
}
@@ -366,11 +340,14 @@ class DibiMySqlResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
return mysql_data_seek($this->resource, $row);
if (!mysql_data_seek($this->resource, $row)) {
throw new DibiDriverException('Unable to seek to row ' . $row);
}
}
@@ -380,7 +357,7 @@ class DibiMySqlResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
mysql_free_result($this->resource);
}

View File

@@ -132,13 +132,14 @@ class DibiMySqliDriver extends DibiDriver
protected function doQuery($sql)
{
$connection = $this->getConnection();
$res = @mysqli_query($connection, $sql, $this->getConfig('unbuffered') ? MYSQLI_USE_RESULT : MYSQLI_STORE_RESULT);
$buffered = !$this->getConfig('unbuffered');
$res = @mysqli_query($connection, $sql, $buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
if ($errno = mysqli_errno($connection)) {
throw new DibiDatabaseException(mysqli_error($connection), $errno, $sql);
}
return is_object($res) ? new DibiMySqliResult($res) : NULL;
return is_object($res) ? new DibiMySqliResult($res, $buffered) : NULL;
}
@@ -216,22 +217,6 @@ class DibiMySqliDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
$connection = $this->getConnection();
return array(
'message' => mysqli_error($connection),
'code' => mysqli_errno($connection),
);
}
/**
* Escapes the string
*
@@ -318,7 +303,7 @@ class DibiMySqliResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
return mysqli_num_rows($this->resource);
}
@@ -342,11 +327,14 @@ class DibiMySqliResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
return mysqli_data_seek($this->resource, $row);
if (!mysqli_data_seek($this->resource, $row)) {
throw new DibiDriverException('Unable to seek to row ' . $row);
}
}
@@ -356,7 +344,7 @@ class DibiMySqliResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
mysqli_free_result($this->resource);
}

View File

@@ -135,7 +135,7 @@ class DibiOdbcDriver extends DibiDriver
if (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res);
if ($this->affectedRows < 0) $this->affectedRows = FALSE;
return new DibiOdbcResult($res);
return new DibiOdbcResult($res, TRUE);
}
}
@@ -212,22 +212,6 @@ class DibiOdbcDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
$connection = $this->getConnection();
return array(
'message' => odbc_errormsg($connection),
'code' => odbc_error($connection),
);
}
/**
* Escapes the string
*
@@ -316,7 +300,7 @@ class DibiOdbcResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
// will return -1 with many drivers :-(
return odbc_num_rows($this->resource);
@@ -341,9 +325,10 @@ class DibiOdbcResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
$this->row = $row;
}
@@ -355,7 +340,7 @@ class DibiOdbcResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
odbc_free_result($this->resource);
}

View File

@@ -126,8 +126,7 @@ class DibiOracleDriver extends DibiDriver
throw new DibiDatabaseException($err['message'], $err['code'], $sql);
}
// TODO!
return is_resource($res) ? new DibiOracleResult($statement) : TRUE;
return is_resource($res) ? new DibiOracleResult($statement, TRUE) : TRUE;
}
@@ -201,18 +200,6 @@ class DibiOracleDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
return oci_error($this->getConnection());
}
/**
* Escapes the string
*
@@ -294,7 +281,7 @@ class DibiOracleResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
return oci_num_rows($this->resource);
}
@@ -309,6 +296,7 @@ class DibiOracleResult extends DibiResult
*/
protected function doFetch()
{
$this->fetched = TRUE;
return oci_fetch_assoc($this->resource);
}
@@ -318,11 +306,13 @@ class DibiOracleResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
//throw new BadMethodCallException(__METHOD__ . ' is not implemented');
if ($row === 0 && !$this->fetched) return TRUE;
throw new BadMethodCallException(__METHOD__ . ' is not implemented');
}
@@ -332,7 +322,7 @@ class DibiOracleResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
oci_free_statement($this->resource);
}

View File

@@ -104,7 +104,7 @@ class DibiPdoDriver extends DibiDriver
protected function doQuery($sql)
{
$res = $this->getConnection()->query($sql);
return $res instanceof PDOStatement ? new DibiPdoResult($res) : NULL;
return $res instanceof PDOStatement ? new DibiPdoResult($res, TRUE) : NULL;
}
@@ -169,23 +169,6 @@ class DibiPdoDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
$error = $this->getConnection()->errorInfo();
return array(
'message' => $error[2],
'code' => $error[1],
'SQLSTATE '=> $error[0],
);
}
/**
* Escapes the string
*
@@ -270,7 +253,7 @@ class DibiPdoResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
return $this->resource->rowCount();
}
@@ -294,9 +277,10 @@ class DibiPdoResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
$this->row = $row;
}
@@ -308,7 +292,7 @@ class DibiPdoResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
}

View File

@@ -81,25 +81,16 @@ class DibiPostgreDriver extends DibiDriver
$config = $this->getConfig();
// some errors aren't handled. Must use $php_errormsg
if (function_exists('ini_set')) {
$save = ini_set('track_errors', TRUE);
}
$php_errormsg = '';
DibiDatabaseException::catchError();
if (isset($config['persistent'])) {
$connection = @pg_connect($config['database'], PGSQL_CONNECT_FORCE_NEW);
} else {
$connection = @pg_pconnect($config['database'], PGSQL_CONNECT_FORCE_NEW);
}
if (function_exists('ini_set')) {
ini_set('track_errors', $save);
}
DibiDatabaseException::restore();
if (!is_resource($connection)) {
throw new DibiDatabaseException($php_errormsg);
throw new DibiDatabaseException('unknown error');
}
if (isset($config['charset'])) {
@@ -146,7 +137,7 @@ class DibiPostgreDriver extends DibiDriver
$this->affectedRows = pg_affected_rows($res);
if ($this->affectedRows < 0) $this->affectedRows = FALSE;
}
return new DibiPostgreResult($res);
return new DibiPostgreResult($res, TRUE);
}
}
@@ -225,21 +216,6 @@ class DibiPostgreDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
return array(
'message' => pg_last_error($this->getConnection()),
'code' => NULL,
);
}
/**
* Escapes the string
*
@@ -326,7 +302,7 @@ class DibiPostgreResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
return pg_num_rows($this->resource);
}
@@ -350,11 +326,14 @@ class DibiPostgreResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
return pg_result_seek($this->resource, $row);
if (!pg_result_seek($this->resource, $row)) {
throw new DibiDriverException('Unable to seek to row ' . $row);
}
}
@@ -364,7 +343,7 @@ class DibiPostgreResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
pg_free_result($this->resource);
}

View File

@@ -113,10 +113,11 @@ class DibiSqliteDriver extends DibiDriver
$connection = $this->getConnection();
$errorMsg = NULL;
if ($this->getConfig('unbuffered')) {
$res = @sqlite_unbuffered_query($connection, $sql, SQLITE_ASSOC, $errorMsg);
} else {
$buffered = !$this->getConfig('unbuffered');
if ($buffered) {
$res = @sqlite_query($connection, $sql, SQLITE_ASSOC, $errorMsg);
} else {
$res = @sqlite_unbuffered_query($connection, $sql, SQLITE_ASSOC, $errorMsg);
}
@@ -124,7 +125,7 @@ class DibiSqliteDriver extends DibiDriver
throw new DibiDatabaseException($errorMsg, sqlite_last_error($connection), $sql);
}
return is_resource($res) ? new DibiSqliteResult($res) : NULL;
return is_resource($res) ? new DibiSqliteResult($res, $buffered) : NULL;
}
@@ -191,22 +192,6 @@ class DibiSqliteDriver extends DibiDriver
/**
* Returns last error
*
* @return array with items 'message' and 'code'
*/
public function errorInfo()
{
$code = sqlite_last_error($this->getConnection());
return array(
'message' => sqlite_error_string($code),
'code' => $code,
);
}
/**
* Escapes the string
*
@@ -288,7 +273,7 @@ class DibiSqliteResult extends DibiResult
*
* @return int
*/
public function rowCount()
protected function doRowCount()
{
return sqlite_num_rows($this->resource);
}
@@ -312,11 +297,14 @@ class DibiSqliteResult extends DibiResult
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
public function seek($row)
protected function doSeek($row)
{
return sqlite_seek($this->resource, $row);
DibiDatabaseException::catchError();
sqlite_seek($this->resource, $row);
DibiDatabaseException::restore();
}
@@ -326,7 +314,7 @@ class DibiSqliteResult extends DibiResult
*
* @return void
*/
protected function free()
protected function doFree()
{
}

View File

@@ -303,10 +303,12 @@ abstract class DibiDriver extends NObject
/**
* Returns last error
*
* @return array with items 'message' and 'code'
* @deprecated
*/
abstract public function errorInfo();
public function errorInfo()
{
throw new BadMethodCallException(__METHOD__ . ' has been deprecated');
}

View File

@@ -45,6 +45,9 @@ class DibiDatabaseException extends DibiException
/** @var string */
private $sql;
/** @var callback */
private static $oldHandler;
public function __construct($message = NULL, $code = 0, $sql = NULL)
{
@@ -71,4 +74,31 @@ class DibiDatabaseException extends DibiException
return $s;
}
}
public static function _catchErrorHandler($errno, $errstr)
{
self::restore();
throw new self($errstr, $errno);
}
public static function catchError()
{
self::$oldHandler = set_error_handler(array(__CLASS__, '_catchErrorHandler'), E_ALL);
}
public static function restore()
{
if (self::$oldHandler) {
set_error_handler(self::$oldHandler);
self::$oldHandler = NULL;
} else {
restore_error_handler();
}
}
}

View File

@@ -54,13 +54,25 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
*/
protected $meta;
/**
* Resultset resource
* @var resource
*/
protected $resource;
/**
* Is buffered (seekable and countable)?
* @var bool
*/
protected $buffered;
/**
* Already fetched? Used for allowance for first seek(0)
* @var bool
*/
protected $fetched = FALSE;
private static $types = array(
dibi::FIELD_TEXT => 'string',
@@ -74,9 +86,10 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
public function __construct($resource)
public function __construct($resource, $buffered)
{
$this->resource = $resource;
$this->buffered = $buffered;
}
@@ -96,9 +109,21 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
* Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @return void
* @throws DibiException
*/
abstract public function seek($row);
final public function seek($row)
{
if ($row === 0 && !$this->fetched) {
return TRUE;
}
if (!$this->buffered) {
throw new BadMethodCallException(__METHOD__ . ' is not allowed for unbuffered queries');
}
return $this->doSeek($row);
}
@@ -107,16 +132,43 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
*
* @return int
*/
abstract public function rowCount();
final public function rowCount()
{
if (!$this->buffered) {
throw new BadMethodCallException(__METHOD__ . ' is not allowed for unbuffered queries');
}
return $this->doRowCount();
}
/**
* Frees the resources allocated for this result set
* Internal: Moves cursor position without fetching row
*
* @param int the 0-based cursor pos to seek to
* @return void
* @throws DibiException
*/
abstract protected function doSeek($row);
/**
* Internal: Returns the number of rows in a result set
*
* @return int
*/
abstract protected function doRowCount();
/**
* Internal: Frees the resources allocated for this result set
*
* @return void
*/
abstract protected function free();
abstract protected function doFree();
@@ -140,6 +192,7 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
{
$row = $this->doFetch();
if (!is_array($row)) return FALSE;
$this->fetched = TRUE;
// types-converting?
if ($t = $this->convert) { // little speed-up
@@ -164,6 +217,7 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
{
$row = $this->doFetch();
if (!is_array($row)) return FALSE;
$this->fetched = TRUE;
// types-converting?
if ($t = $this->convert) { // little speed-up
@@ -186,7 +240,7 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
*/
final function fetchAll()
{
@$this->seek(0);
$this->seek(0);
$row = $this->fetch();
if (!$row) return array(); // empty resultset
@@ -220,7 +274,7 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
*/
final function fetchAssoc($assoc)
{
@$this->seek(0);
$this->seek(0);
$row = $this->fetch();
if (!$row) return array(); // empty resultset
@@ -288,7 +342,7 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
*/
final function fetchPairs($key = NULL, $value = NULL)
{
@$this->seek(0);
$this->seek(0);
$row = $this->fetch();
if (!$row) return array(); // empty resultset
@@ -341,7 +395,7 @@ abstract class DibiResult extends NObject implements IteratorAggregate, Countabl
*/
public function __destruct()
{
@$this->free();
@$this->doFree();
}

View File

@@ -74,7 +74,7 @@ final class DibiResultIterator implements Iterator
public function rewind()
{
$this->pointer = 0;
@$this->result->seek($this->offset);
$this->result->seek($this->offset);
$this->row = $this->result->fetch();
}

View File

@@ -25,8 +25,6 @@ product_id | title
// fetch a single value
$res = dibi::query('SELECT [title] FROM [products]');
if (!$res) die('SQL error');
$value = $res->fetchSingle();
print_r($value); // Chair
echo '<hr>';

View File

@@ -12,8 +12,6 @@ dibi::connect(array(
$res = dibi::query('SELECT * FROM [customers]');
if (!$res) die('SQL error');
// auto-convert this field to integer
$res->setType('customer_id', Dibi::FIELD_INTEGER);