diff --git a/src/PostgresDriver/ArrayCursorDriver.php b/src/PostgresDriver/ArrayCursorDriver.php index 41ad9ead48b9bf1030ae5e7c247aed389776d7a1..b2760515b562cf5c73cba0a779befdf1ef2ef57a 100644 --- a/src/PostgresDriver/ArrayCursorDriver.php +++ b/src/PostgresDriver/ArrayCursorDriver.php @@ -5,9 +5,6 @@ namespace Grifart\Mappi\Store\PostgresDriver; -use Dibi\Connection; -use Dibi\Row; - /** * Array Cursor simulates scrolling cursor as implemented in PostgreSQL 9.5. * @@ -97,10 +94,10 @@ class ArrayCursorDriver implements ICursorDriver return !!$this->getCurrentValue($name); } - public function moveBy(string $name, int $rows) : bool + public function moveBy(string $name, int $offset) : bool { $i = $this->getCurrentIndex($name); - $indexFromLeft = $i + $rows; + $indexFromLeft = $i + $offset; $this->normalizeIndex($name, $indexFromLeft); $this->moveTo($name, $indexFromLeft); return !!$this->getCurrentValue($name); @@ -108,20 +105,20 @@ class ArrayCursorDriver implements ICursorDriver // ----------------- FETCH --------------------- - public function fetchRange(string $name, int $rows) : array + public function fetchRange(string $name, int $offset) : array { $currentKey = $this->getCurrentIndex($name); - if($rows === self::FETCH_FOREGOING) { + if($offset === self::FETCH_FOREGOING) { $finalKey = PHP_INT_MIN; - } elseif ($rows === self::FETCH_REMAINING) { + } elseif ($offset === self::FETCH_REMAINING) { $finalKey = PHP_INT_MAX; } else { - $finalKey = $currentKey + $rows; + $finalKey = $currentKey + $offset; } $this->normalizeIndex($name, $finalKey); $data = []; - if($rows === 0) { + if($offset === 0) { $v = $this->getCurrentValue($name); if($v !== NULL) { $data[] = ["n" => $this->getCurrentValue($name)]; @@ -161,9 +158,9 @@ class ArrayCursorDriver implements ICursorDriver return $data[0]; } - public function fetchOneBy(string $name, int $rows) + public function fetchOneBy(string $name, int $offset) { - $this->moveBy($name, $rows); + $this->moveBy($name, $offset); $data = $this->fetchRange($name, 0); if(count($data) === 0) { return NULL; diff --git a/src/PostgresDriver/Cursor.php b/src/PostgresDriver/Cursor.php index 8de0357c7e4dba2c24793ab2e5f1e0050c681cad..5d849c83e22fed2aeda7eebb2bcadd7c2e5e9b85 100644 --- a/src/PostgresDriver/Cursor.php +++ b/src/PostgresDriver/Cursor.php @@ -5,9 +5,6 @@ namespace Grifart\Mappi\Store\PostgresDriver; -use Dibi\Connection; -use Dibi\Row; - /** * PostgreSQL cursor driver * @@ -71,22 +68,22 @@ final class Cursor implements ICursor $this->headOnRecord = $this->driver->moveFromEndTo($this->name, $index); } - public function moveBy(int $rows) + public function moveBy(int $offset) { - $this->headOnRecord = $this->driver->moveBy($this->name, $rows); + $this->headOnRecord = $this->driver->moveBy($this->name, $offset); } // ----------------- FETCH --------------------- - public function fetchRange(int $rows) : array + public function fetchRange(int $offset) : array { - $recs = $this->driver->fetchRange($this->name, $rows); + $recs = $this->driver->fetchRange($this->name, $offset); - if ($rows === 0) { // current row + if ($offset === 0) { // current row $this->headOnRecord = count($recs) === 1; } else { // todo: test this properly!! - $this->headOnRecord = count($recs) === abs($rows); + $this->headOnRecord = count($recs) === abs($offset); } return $recs; } @@ -98,9 +95,9 @@ final class Cursor implements ICursor return $row; } - public function fetchOneBy(int $rows) + public function fetchOneBy(int $offset) { - $row = $this->driver->fetchOneBy($this->name, $rows); + $row = $this->driver->fetchOneBy($this->name, $offset); $this->headOnRecord = $row !== NULL; return $row; } diff --git a/src/PostgresDriver/CursorPosition.php b/src/PostgresDriver/CursorPosition.php index b1defb1df505a50876695cc2db0a5a1b74a4cea5..f682c483a38dfa77b0bc7abaf9c2f2eced0a20fe 100644 --- a/src/PostgresDriver/CursorPosition.php +++ b/src/PostgresDriver/CursorPosition.php @@ -5,6 +5,8 @@ namespace Grifart\Mappi\Store\PostgresDriver; +// todo: make this immutable as VOs should be + /** * Value object for TrackedCursor position * diff --git a/src/PostgresDriver/ICursor.php b/src/PostgresDriver/ICursor.php index efda5b2deb71de5bb4f362964a4e3c2366e9483f..6c11a89518c5b24033cc5f615f51bb695e023d92 100644 --- a/src/PostgresDriver/ICursor.php +++ b/src/PostgresDriver/ICursor.php @@ -5,7 +5,7 @@ namespace Grifart\Mappi\Store\PostgresDriver; /** - * Represents PostgreSQL cursor + * Represents cursor * * Cursor works same as tape. When you have 3 items on it it has these indexes. * index: 0 1 2 3 4 @@ -35,12 +35,14 @@ interface ICursor public function isOnRecord() : bool; /** - * @param int $rows + * Move cursor by offset + * @param int $offset * @return void */ - public function moveBy(int $rows); + public function moveBy(int $offset); /** + * Move cursor to absolute position from the beginning * @param int $index * @throws CursorException Negative indexes not supported * @return void @@ -48,6 +50,7 @@ interface ICursor public function moveTo(int $index); /** + * Move cursor to absolute position from the end * @param int $index * @throws CursorException Negative indexes not supported * @return mixed @@ -55,24 +58,28 @@ interface ICursor public function moveFromEndTo(int $index); /** - * Fetch the next/previous count rows. FORWARD 0 re-fetches the current row. - * @param int $rows - * @return array + * Fetch the next/previous count of rows. If zero given current row is fetched. + * Cursor position will be on the last fetched row. + * @param int $offset the last row offset + * @return array fetched rows */ - public function fetchRange(int $rows) : array; + public function fetchRange(int $offset) : array; /** - * Fetch the count'th row of the query, or the abs(count)'th row from the end if count is negative. Position before first row or after last row if count is out of range; in particular, ABSOLUTE 0 positions before the first row. - * @param int $index Positive means from left; negative from right + * Fetch one row at given position + * Cursor position will be on the fetched row. + * @param int $index Positive means from the beginning; negative from the end * @return array|NULL */ public function fetchOneAt(int $index); /** - * Fetch the count'th succeeding row, or the abs(count)'th prior row if count is negative. RELATIVE 0 re-fetches the current row, if any. - * @param int $rows + * Fetch one row at relative position from current position + * Has the same effect like calling moveBy() and then fetch current row + * Cursor position will be on the fetched row. + * @param int $offset * @return array|NULL */ - public function fetchOneBy(int $rows); + public function fetchOneBy(int $offset); } \ No newline at end of file diff --git a/src/PostgresDriver/ICursorDriver.php b/src/PostgresDriver/ICursorDriver.php index 6d3ab8464d0e2abc10de1d4555b67f8feaa81048..6e645a9dcb9da6b38057fc8322805623a800167f 100644 --- a/src/PostgresDriver/ICursorDriver.php +++ b/src/PostgresDriver/ICursorDriver.php @@ -4,50 +4,74 @@ */ namespace Grifart\Mappi\Store\PostgresDriver; +/** + * Represents cursor + * + * Cursor works same as tape. When you have 3 items on it it has these indexes. + * index: 0 1 2 3 4 + * value: - 1 2 3 - + * head: ^ + * + * - You have HEAD which you can move around and read records before, after or current. + * - You cannot ask for current position. + * - You can move to absolute position from left or from right + * - You can more relatively to any side + * - BEGINNING (0) and END (4) has no value + * - Cursor starts at BEGINNING + * - When you move too far HEAD will be parked in BEGINNING position or in the END position + * + * @package Grifart\Mappi\Store\PostgresDriver + */ interface ICursorDriver { const FETCH_REMAINING = PHP_INT_MAX; const FETCH_FOREGOING = PHP_INT_MIN; /** - * @param string $name Cursor name + * Move cursor from the BEGINNING to given position + * @param string $name The cursor name * @param int $index * @return bool Head on record? */ public function moveTo(string $name, int $index) : bool; /** - * @param string $name Cursor name + * Move cursor from the END to given position + * @param string $name The cursor name * @param int $index * @return bool Head on record? */ public function moveFromEndTo(string $name, int $index) : bool; /** - * @param string $name Cursor name - * @param int $rows + * Move cursor from the current position by given offset + * @param string $name The cursor name + * @param int $offset * @return bool Head on record? */ - public function moveBy(string $name, int $rows) : bool; + public function moveBy(string $name, int $offset) : bool; /** - * @param string $name Cursor name - * @param int $rows + * Fetch all rows from current position to given offset + * @param string $name The cursor name + * @param int $offset * @return array */ - public function fetchRange(string $name, int $rows) : array; + public function fetchRange(string $name, int $offset) : array; /** - * @param string $name - * @param int $index + * Fetch one row at given position + * @param string $name The cursor name + * @param int $index from the BEGINNING; if negative from the END * @return array|null */ public function fetchOneAt(string $name, int $index); /** - * @param string $name - * @param int $rows + * Fetch one row at relative position from current row + * @param string $name The cursor name + * @param int $offset * @return array|null */ - public function fetchOneBy(string $name, int $rows); + public function fetchOneBy(string $name, int $offset); } \ No newline at end of file diff --git a/src/PostgresDriver/ICursorFactory.php b/src/PostgresDriver/ICursorFactory.php deleted file mode 100644 index d4f7276755648a77f2da190dc1d8e8a9ced1121d..0000000000000000000000000000000000000000 --- a/src/PostgresDriver/ICursorFactory.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php declare(strict_types = 1); -/** - * This file is part of mappi/store. - */ - -namespace Grifart\Mappi\Store\PostgresDriver; - -interface ICursorFactory -{ - public function create(string $sql, bool $scroll) : ICursor; -} \ No newline at end of file diff --git a/src/PostgresDriver/PostgresCursorDriver.php b/src/PostgresDriver/PostgresCursorDriver.php index 76fea60afca536b00f4430dd12fbf53fe900418f..5413c4867b5e6685aef928bdeac4cc3e73098de7 100644 --- a/src/PostgresDriver/PostgresCursorDriver.php +++ b/src/PostgresDriver/PostgresCursorDriver.php @@ -64,22 +64,22 @@ class PostgresCursorDriver implements ICursorDriver ); } - public function moveBy(string $name, int $rows) : bool + public function moveBy(string $name, int $offset) : bool { return !!$this->getConnection()->query( "MOVE RELATIVE %i IN %n", - $rows, + $offset, $name ); } // ----------------- FETCH --------------------- - public function fetchRange(string $name, int $rows) : array + public function fetchRange(string $name, int $offset) : array { - $forward = $rows >= 0; - $numberOfRowsSQLClause = ($rows === self::FETCH_FOREGOING || $rows === self::FETCH_REMAINING) ? - "ALL" : abs($rows); + $forward = $offset >= 0; + $numberOfRowsSQLClause = ($offset === self::FETCH_FOREGOING || $offset === self::FETCH_REMAINING) ? + "ALL" : abs($offset); $recs = $this->connection->query( $forward ? "FETCH FORWARD %sql FROM %n" : "FETCH BACKWARD %sql FROM %n", @@ -103,11 +103,11 @@ class PostgresCursorDriver implements ICursorDriver return $this->convertRow($row); } - public function fetchOneBy(string $name, int $rows) + public function fetchOneBy(string $name, int $offset) { $row = $this->connection->query( "FETCH RELATIVE %i FROM %n", - $rows, + $offset, $name )->fetch(); if($row === FALSE) { diff --git a/src/PostgresDriver/CursorFactory.php b/src/PostgresDriver/PostgresCursorFactory.php similarity index 85% rename from src/PostgresDriver/CursorFactory.php rename to src/PostgresDriver/PostgresCursorFactory.php index 63a50f2209628f044f1d8bdd6c4561621ed6538a..b7a1c7c32cc65b587df8c647abb314e9e260137c 100644 --- a/src/PostgresDriver/CursorFactory.php +++ b/src/PostgresDriver/PostgresCursorFactory.php @@ -7,13 +7,13 @@ namespace Grifart\Mappi\Store\PostgresDriver; use Dibi\Connection; -final class CursorFactory +final class PostgresCursorFactory { /** @var Connection */ private $connection; /** - * CursorFactory constructor. + * PostgresCursorFactory constructor. * @param Connection $connection */ public function __construct(Connection $connection) @@ -46,6 +46,10 @@ final class CursorFactory ); } + /** + * Generate cursor unique name in current transaction + * @return string + */ private function generateCursorName() : string { return uniqid(); diff --git a/src/PostgresDriver/SemanticCursor.php b/src/PostgresDriver/SemanticCursor.php index 695c6782349063bcd13c04979b10e5767d8650cb..9fafb781536a48444f8ce6f42d21126984bb6c27 100644 --- a/src/PostgresDriver/SemanticCursor.php +++ b/src/PostgresDriver/SemanticCursor.php @@ -29,50 +29,6 @@ class SemanticCursor implements ICursor $this->cursor = $cursor; } - // ICursor proxy: - - public function getName() : string - { - return $this->cursor->getName(); - } - - public function isOnRecord() : bool - { - return $this->cursor->isOnRecord(); - } - - public function moveTo(int $index) - { - $this->cursor->moveTo($index); - } - - public function moveFromEndTo(int $index) - { - return $this->cursor->moveFromEndTo($index); - } - - public function moveBy(int $rows) - { - $this->cursor->moveBy($rows); - } - - public function fetchRange(int $rows) : array - { - return $this->cursor->fetchRange($rows); - } - - public function fetchOneAt(int $index) - { - return $this->cursor->fetchOneAt($index); - } - - public function fetchOneBy(int $rows) - { - return $this->cursor->fetchOneBy($rows); - } - - // The semantic extension: - /** * Move cursor on the last row * @see fetchCurrent() @@ -230,11 +186,62 @@ class SemanticCursor implements ICursor /** * @param array $row - * @return mixed The value + * @return mixed The extracted value */ private function extractFirstColumnFromRow(array $row) { return reset($row); } + + + // The ICursor proxy: + + /** {@inheritdoc} */ + public function getName() : string + { + return $this->cursor->getName(); + } + + /** {@inheritdoc} */ + public function isOnRecord() : bool + { + return $this->cursor->isOnRecord(); + } + + /** {@inheritdoc} */ + public function moveTo(int $index) + { + $this->cursor->moveTo($index); + } + + /** {@inheritdoc} */ + public function moveFromEndTo(int $index) + { + return $this->cursor->moveFromEndTo($index); + } + + /** {@inheritdoc} */ + public function moveBy(int $offset) + { + $this->cursor->moveBy($offset); + } + + /** {@inheritdoc} */ + public function fetchRange(int $offset) : array + { + return $this->cursor->fetchRange($offset); + } + + /** {@inheritdoc} */ + public function fetchOneAt(int $index) + { + return $this->cursor->fetchOneAt($index); + } + + /** {@inheritdoc} */ + public function fetchOneBy(int $offset) + { + return $this->cursor->fetchOneBy($offset); + } } \ No newline at end of file diff --git a/src/PostgresDriver/TrackedCursor.php b/src/PostgresDriver/TrackedCursor.php index 5e160a6c97da8052a70da8da9124456033a5e87d..d086671b95aacd944825c4286d35e1d422578d2f 100644 --- a/src/PostgresDriver/TrackedCursor.php +++ b/src/PostgresDriver/TrackedCursor.php @@ -87,38 +87,38 @@ class TrackedCursor implements ICursor throw CursorException::cursorPosition_unknownError(); } - public function moveBy(int $rows) + public function moveBy(int $offset) { // todo: use MOVE FORWARD n IN ... which returns number of rows read - $this->cursor->moveBy($rows); + $this->cursor->moveBy($offset); if ($this->cursor->isOnRecord()) { - $this->position->movePositionBy($rows); + $this->position->movePositionBy($offset); return; } - if ($rows < 0) { + if ($offset < 0) { $this->position->setPositionFromLeft(0); // BEGINNING return; } - if ($rows > 0) { + if ($offset > 0) { $this->position->setPositionFromRight(0); // END return; } - if ($rows === 0) { + if ($offset === 0) { return; // already on one of ends } throw CursorException::cursorPosition_unknownError(); } - public function fetchRange(int $rows): array + public function fetchRange(int $offset): array { - $result = $this->cursor->fetchRange($rows); - if($rows === 0) { // zero does not move cursor + $result = $this->cursor->fetchRange($offset); + if($offset === 0) { // zero does not move cursor return $result; } - $forward = $rows > 0; + $forward = $offset > 0; $count = count($result); - $reachedEnd = $count < abs($rows); + $reachedEnd = $count < abs($offset); if(!$reachedEnd) { $this->position->movePositionBy( $count * ($forward ? 1 : -1) @@ -164,23 +164,23 @@ class TrackedCursor implements ICursor throw CursorException::cursorPosition_unknownError(); } - public function fetchOneBy(int $rows) + public function fetchOneBy(int $offset) { // todo: content tests - $row = $this->cursor->fetchOneBy($rows); + $row = $this->cursor->fetchOneBy($offset); if ($row !== NULL) { - $this->position->movePositionBy($rows); + $this->position->movePositionBy($offset); return $row; } - if ($rows < 0) { + if ($offset < 0) { $this->position->setPositionFromLeft(0); // BEGINNING return NULL; } - if ($rows > 0) { + if ($offset > 0) { $this->position->setPositionFromRight(0); // END return NULL; } - if ($rows === 0) { + if ($offset === 0) { return NULL; // already on one of ends } diff --git a/tests/Store/PostgresDriver/CursorTest.phpt b/tests/Store/PostgresDriver/CursorTest.phpt index 5ab1fb8a4abce83957cf3128eda3cd401a594626..2a1b9f1304d8cf4f7360554d100a47b2a3c24c44 100644 --- a/tests/Store/PostgresDriver/CursorTest.phpt +++ b/tests/Store/PostgresDriver/CursorTest.phpt @@ -7,7 +7,7 @@ namespace Grifart\Mappi\Tests\Store\Store\PostgresDriver; use Grifart\Mappi\Store\PostgresDriver\ArrayCursorDriver; use Grifart\Mappi\Store\PostgresDriver\Cursor; -use Grifart\Mappi\Store\PostgresDriver\CursorFactory; +use Grifart\Mappi\Store\PostgresDriver\PostgresCursorFactory; use Grifart\Mappi\Store\PostgresDriver\ICursor; require_once __DIR__ . "/../../bootstrap.php"; @@ -23,7 +23,7 @@ class CursorTest extends ICursorTest //global $connection, $SQL_thousandRowsAscending; //$connection->begin(); - //$factory = new CursorFactory($connection); + //$factory = new PostgresCursorFactory($connection); //$this->uut = $factory->create($SQL_thousandRowsAscending, true); $driver = new ArrayCursorDriver(); $driver->createTestCursor("test", 1000); diff --git a/tests/Store/PostgresDriver/PostgresCursorTest.phpt b/tests/Store/PostgresDriver/PostgresCursorTest.phpt index ada9cac791f4e3ef565a7d6ba9b5325edd150431..851939f4aad19b12661b7b2f59e61c01ce919442 100644 --- a/tests/Store/PostgresDriver/PostgresCursorTest.phpt +++ b/tests/Store/PostgresDriver/PostgresCursorTest.phpt @@ -6,11 +6,8 @@ namespace Grifart\Mappi\Tests\Store\Store\PostgresDriver; use Dibi\DriverException; -use Grifart\Mappi\Store\PostgresDriver\ArrayCursorDriver; -use Grifart\Mappi\Store\PostgresDriver\Cursor; -use Grifart\Mappi\Store\PostgresDriver\CursorFactory; +use Grifart\Mappi\Store\PostgresDriver\PostgresCursorFactory; use Grifart\Mappi\Store\PostgresDriver\ICursor; -use Grifart\Mappi\Store\PostgresDriver\ICursorDriver; use Tester\Environment; require_once __DIR__ . "/../../bootstrap.php"; @@ -27,7 +24,7 @@ class PostgresCursorTest extends ICursorTest try{ $connection->begin(); - $factory = new CursorFactory($connection); + $factory = new PostgresCursorFactory($connection); $this->uut = $factory->create($SQL_thousandRowsAscending, true); } catch (DriverException $e) { Environment::skip("It looks like you haven't properly set-up dibi connection to PostgreSQL. Check tests/bootstrap.php"); diff --git a/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt b/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt index 137534facb93987e0df9399f66edcc7b6dcfba78..690877e80f6bf20606535eab1fba38a1113d5572 100644 --- a/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt +++ b/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt @@ -7,7 +7,7 @@ namespace Grifart\Mappi\Tests\Store\Store\PostgresDriver; use Grifart\Mappi\Store\PostgresDriver\ArrayCursorDriver; use Grifart\Mappi\Store\PostgresDriver\Cursor; -use Grifart\Mappi\Store\PostgresDriver\CursorFactory; +use Grifart\Mappi\Store\PostgresDriver\PostgresCursorFactory; use Grifart\Mappi\Store\PostgresDriver\SemanticCursor; use Mockery; diff --git a/tests/Store/PostgresDriver/TrackedCursorTest.phpt b/tests/Store/PostgresDriver/TrackedCursorTest.phpt index 0574378e85d4d029e2f0dbeefae32394bd1985be..d9b8d04280ef1bd4ea468557c28fa29872ac1677 100644 --- a/tests/Store/PostgresDriver/TrackedCursorTest.phpt +++ b/tests/Store/PostgresDriver/TrackedCursorTest.phpt @@ -10,7 +10,7 @@ namespace Grifart\Mappi\Tests\Store\Store\PostgresDriver; use Grifart\Mappi\Store\PostgresDriver\ArrayCursorDriver; use Grifart\Mappi\Store\PostgresDriver\Cursor; -use Grifart\Mappi\Store\PostgresDriver\CursorFactory; +use Grifart\Mappi\Store\PostgresDriver\PostgresCursorFactory; use Grifart\Mappi\Store\PostgresDriver\CursorPosition; use Grifart\Mappi\Store\PostgresDriver\ICursor; use Grifart\Mappi\Store\PostgresDriver\TrackedCursor; @@ -33,7 +33,7 @@ class TrackedCursorTest extends ICursorTest // global $connection, $SQL_thousandRowsAscending; // $connection->begin(); -// $factory = new CursorFactory($connection); +// $factory = new PostgresCursorFactory($connection); $driver = new ArrayCursorDriver(); $driver->createTestCursor("test", 1000); $cursor = new Cursor(