diff --git a/dibi/dibi.php b/dibi/dibi.php index b520584a..85b01c70 100644 --- a/dibi/dibi.php +++ b/dibi/dibi.php @@ -44,6 +44,7 @@ require_once dirname(__FILE__) . '/libs/DibiTranslator.php'; require_once dirname(__FILE__) . '/libs/DibiLogger.php'; require_once dirname(__FILE__) . '/libs/DibiVariable.php'; require_once dirname(__FILE__) . '/libs/DibiTable.php'; +require_once dirname(__FILE__) . '/libs/DibiDataSource.php'; @@ -396,7 +397,7 @@ class dibi /** * Import SQL dump from file - extreme fast! * - * @param filename + * @param string filename * @return int count of sql commands */ public static function loadFile($file) diff --git a/dibi/libs/DibiConnection.php b/dibi/libs/DibiConnection.php index e856b88a..e6087483 100644 --- a/dibi/libs/DibiConnection.php +++ b/dibi/libs/DibiConnection.php @@ -391,7 +391,7 @@ class DibiConnection extends NObject /** * Import SQL dump from file - extreme fast! * - * @param filename + * @param string filename * @return int count of sql commands */ public function loadFile($file) diff --git a/dibi/libs/DibiDataSource.php b/dibi/libs/DibiDataSource.php new file mode 100644 index 00000000..5fff71a3 --- /dev/null +++ b/dibi/libs/DibiDataSource.php @@ -0,0 +1,93 @@ +sql = $sql; + } else { + // SQL command + $this->sql = '(' . $sql . ') AS [source]'; + } + + $this->connection = $connection === NULL ? dibi::getConnection() : $connection; + } + + + + /** + * @param int offset + * @param int limit + * @param array columns + * @return ArrayIterator + */ + public function getIterator($offset = NULL, $limit = NULL, $cols = NULL) + { + return $this->connection->query(' + SELECT %n', ($cols === NULL ? '*' : $cols), ' + FROM %sql', $this->sql, ' + %ofs %lmt', $offset, $limit + ); + } + + + + /** + * @return int + */ + public function count() + { + if ($this->count === NULL) { + $this->count = $this->connection->query(' + SELECT COUNT(*) + FROM %sql', $this->sql + )->fetchSingle(); + } + return $this->count; + } + +} diff --git a/dibi/libs/DibiResult.php b/dibi/libs/DibiResult.php index 5bc344de..7ee4b1c5 100644 --- a/dibi/libs/DibiResult.php +++ b/dibi/libs/DibiResult.php @@ -41,7 +41,7 @@ * @package dibi * @version $Revision$ $Date$ */ -class DibiResult extends NObject implements IteratorAggregate, Countable +class DibiResult extends NObject implements IDataSource { /** * IDibiDriver diff --git a/dibi/libs/DibiTable.php b/dibi/libs/DibiTable.php index 18688203..c48a956d 100644 --- a/dibi/libs/DibiTable.php +++ b/dibi/libs/DibiTable.php @@ -141,14 +141,8 @@ abstract class DibiTable extends NObject */ public function insert($data) { - if (is_object($data)) { - $data = (array) $data; - } elseif (!is_array($data)) { - throw new DibiException('Dataset must be array or anonymous object'); - } - $this->connection->query( - 'INSERT INTO %n', $this->name, '%v', $data + 'INSERT INTO %n', $this->name, '%v', $this->prepare($data) ); return $this->connection->insertId(); } @@ -163,15 +157,9 @@ abstract class DibiTable extends NObject */ public function update($where, $data) { - if (is_object($data)) { - $data = (array) $data; - } elseif (!is_array($data)) { - throw new DibiException('Dataset must be array or anonymous object'); - } - $this->connection->query( 'UPDATE %n', $this->name, - 'SET %a', $data, + 'SET %a', $this->prepare($data), 'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')' ); return $this->connection->affectedRows(); @@ -266,6 +254,24 @@ abstract class DibiTable extends NObject + /** + * User data pre-processing + * @param array|object + * @return array + */ + protected function prepare($data) + { + if (is_object($data)) { + return (array) $data; + } elseif (is_array($data)) { + return $data; + } + + throw new DibiException('Dataset must be array or anonymous object'); + } + + + /** * User DibiResult post-processing * @param DibiResult diff --git a/dibi/libs/DibiTranslator.php b/dibi/libs/DibiTranslator.php index 5a6942d0..e16b6229 100644 --- a/dibi/libs/DibiTranslator.php +++ b/dibi/libs/DibiTranslator.php @@ -53,6 +53,12 @@ final class DibiTranslator extends NObject /** @var int */ private $ifLevelStart; + /** @var int */ + public $limit; + + /** @var int */ + public $offset; + public function __construct(IDibiDriver $driver) @@ -70,6 +76,8 @@ final class DibiTranslator extends NObject */ public function translate(array $args) { + $this->limit = -1; + $this->offset = 0; $this->hasError = FALSE; $commandIns = NULL; $lastArr = NULL; @@ -140,6 +148,11 @@ final class DibiTranslator extends NObject // TODO: check !!! $this->sql = preg_replace('#\x00.*?\x00#s', '', $this->sql); + // apply limit + if ($this->limit > -1 || $this->offset > 0) { + $this->driver->applyLimit($this->sql, $this->limit, $this->offset); + } + return !$this->hasError; } @@ -172,6 +185,7 @@ final class DibiTranslator extends NObject case 'l': // LIST $kx = NULL; + // break intentionally omitted case 'v': // VALUES foreach ($value as $k => $v) { // split into identifier & modifier @@ -251,7 +265,6 @@ final class DibiTranslator extends NObject return $this->delimite($value); case 'sql':// preserve as SQL - case 'p': // back compatibility $value = (string) $value; // speed-up - is regexp required? @@ -281,6 +294,14 @@ final class DibiTranslator extends NObject substr($value, $toSkip) ); + case 'lmt': // apply limit + if ($value !== NULL) $this->limit = (int) $value; + return ''; + + case 'ofs': // apply offset + if ($value !== NULL) $this->offset = (int) $value; + return ''; + case 'a': case 'v': $this->hasError = TRUE; diff --git a/dibi/libs/interfaces.php b/dibi/libs/interfaces.php index a41504f4..be1aaab0 100644 --- a/dibi/libs/interfaces.php +++ b/dibi/libs/interfaces.php @@ -39,6 +39,20 @@ interface IDibiVariable +/** + * Provides an interface between a dataset and data-aware components + * @package dibi + */ +interface IDataSource extends Countable, IteratorAggregate +{ + //function IteratorAggregate::getIterator(); + //function Countable::count(); +} + + + + + /** * dibi driver interface * diff --git a/examples/apply-limit.php b/examples/apply-limit.php new file mode 100644 index 00000000..739f6520 --- /dev/null +++ b/examples/apply-limit.php @@ -0,0 +1,34 @@ +

dibi apply limit/offset example

+
+ 'sqlite',
+    'database' => 'sample.sdb',
+));
+
+
+// no limit
+$res = dibi::query('SELECT * FROM [products]');
+foreach ($res as $n => $row) {
+    print_r($row);
+}
+
+echo '
'; + +// with limit = 2 +$res = dibi::query('SELECT * FROM [products] %lmt', 2); +foreach ($res as $n => $row) { + print_r($row); +} + +echo '
'; + +// with limit = 2, offset = 1 +$res = dibi::query('SELECT * FROM [products] %lmt %ofs', 2, 1); +foreach ($res as $n => $row) { + print_r($row); +} diff --git a/examples/log.sql b/examples/log.sql index 1c4f65da..c4143ead 100644 --- a/examples/log.sql +++ b/examples/log.sql @@ -15,3 +15,20 @@ ERROR: [1] near "FROM": syntax error -- driver: ; -- 2007-11-15 01:19:00 +OK: SELECT * FROM [customers] WHERE [customer_id] = 1; +-- rows: 1 +-- takes: 0.319 ms +-- driver: sqlite +-- 2008-01-18 03:57:19 + +OK: SELECT * FROM [customers] WHERE [customer_id] < 5; +-- rows: 4 +-- takes: 0.384 ms +-- driver: sqlite +-- 2008-01-18 03:57:19 + +ERROR: [2] sqlite_query(): near "FROM": syntax error +-- SQL: SELECT FROM [customers] WHERE [customer_id] < 38 +-- driver: ; +-- 2008-01-18 03:57:19 +