. */ require_once 'creole/ResultSet.php'; require_once 'creole/common/ResultSetCommon.php'; /** * MSSQL implementation of ResultSet. * * MS SQL does not support LIMIT or OFFSET natively so the methods * in here need to perform some adjustments and extra checking to make sure * that this behaves the same as RDBMS drivers using native OFFSET/LIMIT. * * @author Hans Lellelid * @version $Revision: 1.21 $ * @package creole.drivers.mssql */ class MSSQLResultSet extends ResultSetCommon implements ResultSet { /** * Offset at which to start reading rows. * @var int */ private $offset = 0; /** * Maximum rows to retrieve, or 0 if all. * @var int */ private $limit = 0; /** * This MSSQL-only function exists to set offset after ResultSet is instantiated. * This function should be "protected" in Java sense: only available to classes in package. * THIS METHOD SHOULD NOT BE CALLED BY ANYTHING EXCEPTION DRIVER CLASSES. * @param int $offset New offset. If great than 0, then seek(0) will be called to move cursor. * @access protected */ public function _setOffset($offset) { $this->offset = $offset; if ($offset > 0) { $this->seek(0); // 0 becomes $offset by seek() method } } /** * This MSSQL-only function exists to set limit after ResultSet is instantiated. * This function should be "protected" in Java sense: only available to classes in package. * THIS METHOD SHOULD NOT BE CALLED BY ANYTHING EXCEPTION DRIVER CLASSES. * @param int $limit New limit. * @access protected */ public function _setLimit($limit) { $this->limit = $limit; } /** * @see ResultSet::seek() */ function seek($rownum) { // support emulated OFFSET $actual = $rownum + $this->offset; if (($this->limit > 0 && $rownum >= $this->limit) || $rownum < 0) { // have to check for rownum < 0, because mssql_seek() won't // complain if the $actual is valid. return false; } // MSSQL rows start w/ 0, but this works, because we are // looking to move the position _before_ the next desired position if (!@mssql_data_seek($this->result, $actual)) { return false; } $this->cursorPos = $rownum; return true; } /** * @see ResultSet::next() */ function next() { // support emulated LIMIT if ($this->limit > 0 && ($this->cursorPos >= $this->limit)) { $this->afterLast(); return false; } if ($this->result === true) { return false; } if (extension_loaded('sqlsrv')) { // The modes for fetching are different when using sqlsrv. So we'll translate the mode // as needed $mode = SQLSRV_FETCH_BOTH; if ($this->fetchmode == ResultSet::FETCHMODE_ASSOC) { $mode = SQLSRV_FETCH_ASSOC; } elseif ($this->fetchmode == ResultSet::FETCHMODE_NUM) { $mode = SQLSRV_FETCH_NUMERIC; } $this->fields = sqlsrv_fetch_array($this->result, $mode); } else { $this->fields = mssql_fetch_array($this->result, $this->fetchmode); } if (!$this->fields) { if ($errmsg = mssql_get_last_message()) { throw new SQLException("Error fetching result", $errmsg); } else { // We've advanced beyond end of recordset. $this->afterLast(); return false; } } if ($this->fetchmode === ResultSet::FETCHMODE_ASSOC && $this->lowerAssocCase) { $this->fields = array_change_key_case($this->fields, CASE_LOWER); } // Advance cursor position $this->cursorPos++; return true; } /** * @see ResultSet::getRecordCount() */ function getRecordCount() { if (extension_loaded('sqlsrv')) { $rows = @sqlsrv_num_rows($this->result); } else { $rows = @mssql_num_rows($this->result); } if ($rows === null) { throw new SQLException('Error getting record count', mssql_get_last_message()); } // adjust count based on emulated LIMIT/OFFSET $rows -= $this->offset; return ($this->limit > 0 && $rows > $this->limit ? $this->limit : $rows); } /** * @see ResultSet::close() */ function close() { if (extension_loaded('sqlsrv')) { $ret = @sqlsrv_free_stmt($this->result); } else { $ret = @mssql_free_result($this->result); } $this->result = false; $this->fields = array(); $this->limit = 0; $this->offset = 0; } }