From d818748c5ede18b6af7697f16ca4bc162a48b271 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kucha=C5=99?= <honza.kuchar@grifart.cz>
Date: Wed, 11 May 2016 23:09:06 +0200
Subject: [PATCH] Added ICursorDriver + ArrayCursorDriver implementation

---
 src/PostgresDriver/ArrayCursorDriver.php      | 174 ++++++++++++++++++
 src/PostgresDriver/Cursor.php                 | 107 ++---------
 src/PostgresDriver/CursorFactory.php          |   2 +-
 src/PostgresDriver/ICursorDriver.php          |  53 ++++++
 src/PostgresDriver/PostgresCursorDriver.php   | 139 ++++++++++++++
 src/PostgresDriver/SemanticCursor.php         |  10 +-
 src/PostgresDriver/TrackedCursor.php          |  12 +-
 tests/Store/PostgresDriver/CursorTest.phpt    |  22 ++-
 .../SemanticCursorIntegration.phpt            |  20 +-
 .../PostgresDriver/TrackedCursorTest.phpt     |  22 ++-
 10 files changed, 435 insertions(+), 126 deletions(-)
 create mode 100644 src/PostgresDriver/ArrayCursorDriver.php
 create mode 100644 src/PostgresDriver/ICursorDriver.php
 create mode 100644 src/PostgresDriver/PostgresCursorDriver.php

diff --git a/src/PostgresDriver/ArrayCursorDriver.php b/src/PostgresDriver/ArrayCursorDriver.php
new file mode 100644
index 0000000..41ad9ea
--- /dev/null
+++ b/src/PostgresDriver/ArrayCursorDriver.php
@@ -0,0 +1,174 @@
+<?php declare(strict_types = 1);
+/**
+ * This file is part of mappi/store.
+ */
+
+namespace Grifart\Mappi\Store\PostgresDriver;
+
+use Dibi\Connection;
+use Dibi\Row;
+
+/**
+ * Array Cursor simulates scrolling cursor as implemented in PostgreSQL 9.5.
+ *
+ * For more information see ICursor.
+ * @see ICursor
+ * @package Grifart\Mappi\Store\PostgresDriver
+ */
+class ArrayCursorDriver implements ICursorDriver
+{
+	private $cursors = [];
+	private $indexes = [];
+
+	public function createTestCursor(string $name, int $length)
+	{
+		if(isset($this->cursors[$name])) {
+			throw new CursorException("Cursor with name $name already exists");
+		}
+
+		$this->cursors[$name] = range(0, $length+1);
+		$this->cursors[$name][0] = null;
+		$this->cursors[$name][$length+1] = null;
+
+		$this->indexes[$name] = 0;
+	}
+
+	public function close(string $name) : bool
+	{
+		unset($this->cursors[$name]);
+		return TRUE;
+	}
+
+	private function getMaxIndex(string $name) {
+		return count($this->cursors[$name])-1;
+	}
+
+	private function normalizeIndex(string $name, int &$index)
+	{
+		if($index < 0) {
+			$index = 0;
+		}
+		$max = $this->getMaxIndex($name);
+		if($index > $max) {
+			$index = $max;
+		}
+	}
+
+	private function pointerMoveTo(string $name, int $index)
+	{
+		$this->normalizeIndex($name, $index);
+		$this->indexes[$name] = $index;
+	}
+
+	private function getCurrentIndex(string $name) : int
+	{
+		return $this->indexes[$name];
+	}
+
+	private function getCurrentValue(string $name) {
+		return $this->cursors[$name][$this->getCurrentIndex($name)];
+	}
+
+	public function moveTo(string $name, int $index) : bool
+	{
+		if ($index < 0) {
+			throw new \InvalidArgumentException("Negative index not supported. Use moveFromEndTo() instead.");
+		}
+		$this->pointerMoveTo($name, $index);
+		return !!$this->getCurrentValue($name);
+	}
+
+	private function cursorArrayMoveFromEndTo(string $name, int $index)
+	{
+		$max = $this->getMaxIndex($name);
+
+		$indexFromLeft = $max - $index;
+		$this->normalizeIndex($name, $indexFromLeft);
+
+		$this->moveTo($name, $indexFromLeft);
+	}
+
+	public function moveFromEndTo(string $name, int $index) : bool
+	{
+		if ($index < 0) {
+			throw new \InvalidArgumentException("Negative index not supported. Use moveTo() instead.");
+		}
+		$this->cursorArrayMoveFromEndTo($name, $index);
+		return !!$this->getCurrentValue($name);
+	}
+
+	public function moveBy(string $name, int $rows) : bool
+	{
+		$i = $this->getCurrentIndex($name);
+		$indexFromLeft = $i + $rows;
+		$this->normalizeIndex($name, $indexFromLeft);
+		$this->moveTo($name, $indexFromLeft);
+		return !!$this->getCurrentValue($name);
+	}
+
+	// ----------------- FETCH ---------------------
+
+	public function fetchRange(string $name, int $rows) : array
+	{
+		$currentKey = $this->getCurrentIndex($name);
+		if($rows === self::FETCH_FOREGOING) {
+			$finalKey = PHP_INT_MIN;
+		} elseif ($rows === self::FETCH_REMAINING) {
+			$finalKey = PHP_INT_MAX;
+		} else {
+			$finalKey = $currentKey + $rows;
+		}
+		$this->normalizeIndex($name, $finalKey);
+
+		$data = [];
+		if($rows === 0) {
+			$v = $this->getCurrentValue($name);
+			if($v !== NULL) {
+				$data[] = ["n" => $this->getCurrentValue($name)];
+			}
+			return $data;
+		}
+
+		if($finalKey < $currentKey) { // backwards
+			for($i = $currentKey-1; $i >= $finalKey; $i--) {
+				$this->moveTo($name, $i);
+				$v = $this->getCurrentValue($name);
+				if($v === NULL) {break;}
+				$data[] = ["n" => $this->getCurrentValue($name)];
+			}
+		} else {
+			for($i = $currentKey+1; $i <= $finalKey; $i++) {
+				$this->moveTo($name, $i);
+				$v = $this->getCurrentValue($name);
+				if($v === NULL) {break;}
+				$data[] = ["n" => $this->getCurrentValue($name)];
+			}
+		}
+		return $data;
+	}
+
+	public function fetchOneAt(string $name, int $index)
+	{
+		if($index < 0) {
+			$this->moveFromEndTo($name, abs($index));
+		} else {
+			$this->moveTo($name, $index);
+		}
+		$data = $this->fetchRange($name, 0);
+		if(count($data) === 0) {
+			return NULL;
+		}
+		return $data[0];
+	}
+
+	public function fetchOneBy(string $name, int $rows)
+	{
+		$this->moveBy($name, $rows);
+		$data = $this->fetchRange($name, 0);
+		if(count($data) === 0) {
+			return NULL;
+		}
+		return $data[0];
+	}
+
+}
\ No newline at end of file
diff --git a/src/PostgresDriver/Cursor.php b/src/PostgresDriver/Cursor.php
index 0685104..8de0357 100644
--- a/src/PostgresDriver/Cursor.php
+++ b/src/PostgresDriver/Cursor.php
@@ -20,8 +20,8 @@ use Dibi\Row;
  */
 final class Cursor implements ICursor
 {
-	/** @var Connection */
-	private $connection;
+	/** @var ICursorDriver */
+	private $driver;
 
 	/** @var string */
 	private $name;
@@ -33,23 +33,16 @@ final class Cursor implements ICursor
 	 * Warning: this class itself does not check if cursor is really valid in
 	 * constructor.
 	 *
-	 * @param Connection $connection  Connection with defined cursor with
-	 *                                name given in next parameter
-	 * @param string     $name        Name of cursor defined in
-	 *                                transaction (if not valid all later
-	 *                                call wil fail)
+	 * @param ICursorDriver $driver
+	 * @param string        $name Name of cursor (if not valid all later call will fail)
 	 */
-	public function __construct(Connection $connection, string $name)
+	public function __construct(ICursorDriver $driver, string $name)
 	{
-		$this->connection = $connection;
+		$this->driver = $driver;
 		$this->name = $name;
 		// todo: close; for now automatically closed on transaction end; maybe in destructor? Ignore errors?
 	}
 
-	public function getConnection() : Connection
-	{
-		return $this->connection;
-	}
 
 	public function getName() : string
 	{
@@ -66,11 +59,7 @@ final class Cursor implements ICursor
 		if ($index < 0) {
 			throw new \InvalidArgumentException("Negative index not supported. Use moveFromEndTo() instead.");
 		}
-		$this->headOnRecord = !!$this->getConnection()->query(
-			"MOVE ABSOLUTE %i IN %n",
-			$index,
-			$this->getName()
-		);
+		$this->headOnRecord = $this->driver->moveTo($this->name, $index);
 	}
 
 	public function moveFromEndTo(int $index)
@@ -78,102 +67,42 @@ final class Cursor implements ICursor
 		if ($index < 0) {
 			throw new \InvalidArgumentException("Negative index not supported. Use moveTo() instead.");
 		}
-		if ($index === 0) { // END position
-			$this->headOnRecord = !!$this->getConnection()->query(
-				"MOVE ABSOLUTE -1 IN %n; MOVE NEXT IN %n;",
-				$this->getName(),
-				$this->getName()
-			);
-			return;
-		}
 
-		$negativeIndex = -$index;
-		$this->headOnRecord = !!$this->getConnection()->query(
-			"MOVE ABSOLUTE %i IN %n",
-			$negativeIndex,
-			$this->getName()
-		);
+		$this->headOnRecord = $this->driver->moveFromEndTo($this->name, $index);
 	}
 
 	public function moveBy(int $rows)
 	{
-		$this->headOnRecord = !!$this->getConnection()->query(
-			"MOVE RELATIVE %i IN %n",
-			$rows,
-			$this->getName()
-		);
+		$this->headOnRecord = $this->driver->moveBy($this->name, $rows);
 	}
 
 	// ----------------- FETCH ---------------------
 
 	public function fetchRange(int $rows) : array
 	{
-		$forward = $rows >= 0;
-		$numberOfRowsSQLClause = ($rows === self::FETCH_FOREGOING || $rows === self::FETCH_REMAINING) ?
-			"ALL" : abs($rows);
-
-		$recs = $this->connection->query(
-			$forward ? "FETCH FORWARD %sql FROM %n" : "FETCH BACKWARD %sql FROM %n",
-			$numberOfRowsSQLClause,
-			$this->getName()
-		)->fetchAll();
+		$recs = $this->driver->fetchRange($this->name, $rows);
 
-		if ($rows === 0) {
+		if ($rows === 0) { // current row
 			$this->headOnRecord = count($recs) === 1;
 		} else {
 			// todo: test this properly!!
 			$this->headOnRecord = count($recs) === abs($rows);
 		}
-		// todo: DibiRecord to array
-		return $this->convertRows($recs);
+		return $recs;
 	}
 
 	public function fetchOneAt(int $index)
 	{
-		$row = $this->connection->query(
-			"FETCH ABSOLUTE %i FROM %n",
-			$index,
-			$this->getName()
-		)->fetch();
-		$this->headOnRecord = $row !== FALSE;
-		if ($row === FALSE) {
-			return NULL;
-		}
-		return $this->convertRow($row);
+		$row = $this->driver->fetchOneAt($this->name, $index);
+		$this->headOnRecord = $row !== NULL;
+		return $row;
 	}
 
 	public function fetchOneBy(int $rows)
 	{
-		$row = $this->connection->query(
-			"FETCH RELATIVE %i FROM %n",
-			$rows,
-			$this->getName()
-		)->fetch();
-		$this->headOnRecord = $row !== FALSE;
-		if ($row === FALSE) {
-			return NULL;
-		}
-		return $this->convertRow($row);
+		$row = $this->driver->fetchOneBy($this->name, $rows);
+		$this->headOnRecord = $row !== NULL;
+		return $row;
 	}
 
-	// ----------- DATA CONVERSION -----------
-
-	private function convertRow(Row $row) : array
-	{
-		return $row->toArray();
-	}
-
-	/**
-	 * @param Row[] $rows
-	 * @return array
-	 */
-	private function convertRows(array $rows) : array
-	{
-		// TODO: TrackedCursor should return correct index values in array indexes
-		$new = [];
-		foreach ($rows as $row) {
-			$new[] = $this->convertRow($row);
-		}
-		return $new;
-	}
 }
\ No newline at end of file
diff --git a/src/PostgresDriver/CursorFactory.php b/src/PostgresDriver/CursorFactory.php
index b317d2e..63a50f2 100644
--- a/src/PostgresDriver/CursorFactory.php
+++ b/src/PostgresDriver/CursorFactory.php
@@ -41,7 +41,7 @@ final class CursorFactory
 		);
 		
 		return new Cursor(
-			$this->connection,
+			new PostgresCursorDriver($this->connection),
 			$name
 		);
 	}
diff --git a/src/PostgresDriver/ICursorDriver.php b/src/PostgresDriver/ICursorDriver.php
new file mode 100644
index 0000000..6d3ab84
--- /dev/null
+++ b/src/PostgresDriver/ICursorDriver.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * This file is part of mappi/store.
+ */
+namespace Grifart\Mappi\Store\PostgresDriver;
+
+interface ICursorDriver
+{
+	const FETCH_REMAINING = PHP_INT_MAX;
+	const FETCH_FOREGOING = PHP_INT_MIN;
+
+	/**
+	 * @param string $name Cursor name
+	 * @param int    $index
+	 * @return bool Head on record?
+	 */
+	public function moveTo(string $name, int $index) : bool;
+
+	/**
+	 * @param string $name 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
+	 * @return bool  Head on record?
+	 */
+	public function moveBy(string $name, int $rows) : bool;
+
+	/**
+	 * @param string $name Cursor name
+	 * @param int    $rows
+	 * @return array
+	 */
+	public function fetchRange(string $name, int $rows) : array;
+
+	/**
+	 * @param string $name
+	 * @param int    $index
+	 * @return array|null
+	 */
+	public function fetchOneAt(string $name, int $index);
+
+	/**
+	 * @param string $name
+	 * @param int    $rows
+	 * @return array|null
+	 */
+	public function fetchOneBy(string $name, int $rows);
+}
\ No newline at end of file
diff --git a/src/PostgresDriver/PostgresCursorDriver.php b/src/PostgresDriver/PostgresCursorDriver.php
new file mode 100644
index 0000000..76fea60
--- /dev/null
+++ b/src/PostgresDriver/PostgresCursorDriver.php
@@ -0,0 +1,139 @@
+<?php declare(strict_types = 1);
+/**
+ * This file is part of mappi/store.
+ */
+
+namespace Grifart\Mappi\Store\PostgresDriver;
+
+use Dibi\Connection;
+use Dibi\Row;
+
+class PostgresCursorDriver implements ICursorDriver
+{
+
+	/** @var  Connection */
+	private $connection;
+
+	/**
+	 * PostgresCursorDriver constructor.
+	 * @param Connection $connection
+	 */
+	public function __construct(Connection $connection)
+	{
+		$this->connection = $connection;
+	}
+
+	/**
+	 * @return Connection
+	 */
+	private function getConnection()
+	{
+		return $this->connection;
+	}
+
+	public function moveTo(string $name, int $index) : bool
+	{
+		if ($index < 0) {
+			throw new \InvalidArgumentException("Negative index not supported. Use moveFromEndTo() instead.");
+		}
+		return !!$this->getConnection()->query(
+			"MOVE ABSOLUTE %i IN %n",
+			$index,
+			$name
+		);
+	}
+
+	public function moveFromEndTo(string $name, int $index) : bool
+	{
+		if ($index < 0) {
+			throw new \InvalidArgumentException("Negative index not supported. Use moveTo() instead.");
+		}
+		if ($index === 0) { // END position
+			return !!$this->getConnection()->query(
+				"MOVE ABSOLUTE -1 IN %n; MOVE NEXT IN %n;",
+				$name,
+				$name
+			);
+		}
+
+		$negativeIndex = -$index;
+		return !!$this->getConnection()->query(
+			"MOVE ABSOLUTE %i IN %n",
+			$negativeIndex,
+			$name
+		);
+	}
+
+	public function moveBy(string $name, int $rows) : bool
+	{
+		return !!$this->getConnection()->query(
+			"MOVE RELATIVE %i IN %n",
+			$rows,
+			$name
+		);
+	}
+
+	// ----------------- FETCH ---------------------
+
+	public function fetchRange(string $name, int $rows) : array
+	{
+		$forward = $rows >= 0;
+		$numberOfRowsSQLClause = ($rows === self::FETCH_FOREGOING || $rows === self::FETCH_REMAINING) ?
+			"ALL" : abs($rows);
+
+		$recs = $this->connection->query(
+			$forward ? "FETCH FORWARD %sql FROM %n" : "FETCH BACKWARD %sql FROM %n",
+			$numberOfRowsSQLClause,
+			$name
+		)->fetchAll();
+
+		return $this->convertRows($recs);
+	}
+
+	public function fetchOneAt(string $name, int $index)
+	{
+		$row = $this->connection->query(
+			"FETCH ABSOLUTE %i FROM %n",
+			$index,
+			$name
+		)->fetch();
+		if($row === FALSE) {
+			return NULL;
+		}
+		return $this->convertRow($row);
+	}
+
+	public function fetchOneBy(string $name, int $rows)
+	{
+		$row = $this->connection->query(
+			"FETCH RELATIVE %i FROM %n",
+			$rows,
+			$name
+		)->fetch();
+		if($row === FALSE) {
+			return NULL;
+		}
+		return $this->convertRow($row);
+	}
+
+	// ----------- DATA CONVERSION -----------
+
+	private function convertRow(Row $row) : array
+	{
+		return $row->toArray();
+	}
+
+	/**
+	 * @param Row[] $rows
+	 * @return array
+	 */
+	private function convertRows(array $rows) : array
+	{
+		$new = [];
+		foreach ($rows as $row) {
+			$new[] = $this->convertRow($row);
+		}
+		return $new;
+	}
+
+}
\ No newline at end of file
diff --git a/src/PostgresDriver/SemanticCursor.php b/src/PostgresDriver/SemanticCursor.php
index 5d3ce73..695c678 100644
--- a/src/PostgresDriver/SemanticCursor.php
+++ b/src/PostgresDriver/SemanticCursor.php
@@ -6,9 +6,9 @@
 namespace Grifart\Mappi\Store\PostgresDriver;
 
 /**
- * Adds more semantics into cursor API.
+ * Adds semantic API into ICursor.
  *
- * Use this terminology:
+ * Terminology:
  * - BEGINNING: the position before first row (initial)
  * - FIRST: the first row position
  * - LAST: the last row position
@@ -18,7 +18,7 @@ namespace Grifart\Mappi\Store\PostgresDriver;
  */
 class SemanticCursor implements ICursor
 {
-	/** @var  ICursor */
+	/** @var ICursor */
 	private $cursor;
 
 	/**
@@ -29,7 +29,7 @@ class SemanticCursor implements ICursor
 		$this->cursor = $cursor;
 	}
 
-	// Classic driver part:
+	// ICursor proxy:
 
 	public function getName() : string
 	{
@@ -71,7 +71,7 @@ class SemanticCursor implements ICursor
 		return $this->cursor->fetchOneBy($rows);
 	}
 
-	// The extension:
+	// The semantic extension:
 
 	/**
 	 * Move cursor on the last row
diff --git a/src/PostgresDriver/TrackedCursor.php b/src/PostgresDriver/TrackedCursor.php
index d4b820a..5e160a6 100644
--- a/src/PostgresDriver/TrackedCursor.php
+++ b/src/PostgresDriver/TrackedCursor.php
@@ -15,20 +15,15 @@ class TrackedCursor implements ICursor
 	/** @var ICursor */
 	private $cursor;
 
-	/** @var Connection */
-	private $connection;
-
 	/**
 	 * Tip: if you are not sure that cursor will be in initial state, call
 	 * ->moveToBeginning() after initialization.
-	 * @param Connection     $connection
 	 * @param ICursor        $cursor Cursor in initial state (index=0)
 	 * @param CursorPosition $initialPosition
 	 */
-	public function __construct(Connection $connection, ICursor $cursor, CursorPosition $initialPosition)
+	public function __construct(ICursor $cursor, CursorPosition $initialPosition)
 	{
 		$this->cursor = $cursor;
-		$this->connection = $connection;
 		$this->position = $initialPosition;
 	}
 
@@ -40,11 +35,6 @@ class TrackedCursor implements ICursor
 		return clone $this->position;
 	}
 
-	public function getConnection() : Connection
-	{
-		return $this->connection;
-	}
-
 	public function getName() : string
 	{
 		return $this->cursor->getName();
diff --git a/tests/Store/PostgresDriver/CursorTest.phpt b/tests/Store/PostgresDriver/CursorTest.phpt
index bb08846..5ab1fb8 100644
--- a/tests/Store/PostgresDriver/CursorTest.phpt
+++ b/tests/Store/PostgresDriver/CursorTest.phpt
@@ -5,6 +5,8 @@
 
 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\ICursor;
 
@@ -18,19 +20,25 @@ class CursorTest extends ICursorTest
 
 	protected function setUp()
 	{
-		global $connection, $SQL_thousandRowsAscending;
-		$connection->begin();
-
-		$factory = new CursorFactory($connection);
-		$this->uut = $factory->create($SQL_thousandRowsAscending, true);
+		//global $connection, $SQL_thousandRowsAscending;
+		//$connection->begin();
+
+		//$factory = new CursorFactory($connection);
+		//$this->uut = $factory->create($SQL_thousandRowsAscending, true);
+		$driver = new ArrayCursorDriver();
+		$driver->createTestCursor("test", 1000);
+		$this->uut = new Cursor(
+			$driver,
+			"test"
+		);
 
 		parent::setUp();
 	}
 
 	public function tearDown()
 	{
-		global $connection;
-		$connection->rollback();
+//		global $connection;
+//		$connection->rollback();
 
 		parent::tearDown();
 	}
diff --git a/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt b/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt
index 89b1fea..137534f 100644
--- a/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt
+++ b/tests/Store/PostgresDriver/SemanticCursorIntegration.phpt
@@ -5,6 +5,8 @@
 
 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\SemanticCursor;
 use Mockery;
@@ -26,12 +28,18 @@ class SemanticCursorIntegrationTest extends ICursorTest
 
 	protected function setUp()
 	{
-		global $connection, $SQL_thousandRowsAscending;
-		$connection->begin();
+//		global $connection, $SQL_thousandRowsAscending;
+//		$connection->begin();
 
+
+		$driver = new ArrayCursorDriver();
+		$driver->createTestCursor("test", 1000);
+		$cursor = new Cursor(
+			$driver,
+			"test"
+		);
 		$this->uut = new SemanticCursor(
-			(new CursorFactory($connection))
-				->create($SQL_thousandRowsAscending, true)
+			$cursor
 		);
 
 		parent::setUp();
@@ -39,8 +47,8 @@ class SemanticCursorIntegrationTest extends ICursorTest
 
 	public function tearDown()
 	{
-		global $connection;
-		$connection->rollback();
+//		global $connection;
+//		$connection->rollback();
 
 		parent::tearDown();
 	}
diff --git a/tests/Store/PostgresDriver/TrackedCursorTest.phpt b/tests/Store/PostgresDriver/TrackedCursorTest.phpt
index b7183d0..0574378 100644
--- a/tests/Store/PostgresDriver/TrackedCursorTest.phpt
+++ b/tests/Store/PostgresDriver/TrackedCursorTest.phpt
@@ -8,6 +8,8 @@
 
 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\CursorPosition;
 use Grifart\Mappi\Store\PostgresDriver\ICursor;
@@ -28,13 +30,19 @@ class TrackedCursorTest extends ICursorTest
 
 	protected function setUp()
 	{
-		global $connection, $SQL_thousandRowsAscending;
-		$connection->begin();
+//		global $connection, $SQL_thousandRowsAscending;
+//		$connection->begin();
+
+//		$factory = new CursorFactory($connection);
+		$driver = new ArrayCursorDriver();
+		$driver->createTestCursor("test", 1000);
+		$cursor = new Cursor(
+			$driver,
+			"test"
+		);
 
-		$factory = new CursorFactory($connection);
 		$this->uut = new TrackedCursor(
-			$connection,
-			$factory->create($SQL_thousandRowsAscending, TRUE),
+			$cursor,
 			CursorPosition::fromLeft(0)
 		);
 		parent::setUp();
@@ -42,8 +50,8 @@ class TrackedCursorTest extends ICursorTest
 
 	public function tearDown()
 	{
-		global $connection;
-		$connection->rollback();
+//		global $connection;
+//		$connection->rollback();
 
 		parent::tearDown();
 	}
-- 
GitLab