1
0
mirror of https://github.com/dg/dibi.git synced 2025-08-30 17:29:53 +02:00

Compare commits

..

19 Commits

Author SHA1 Message Date
David Grudl
12bebf8049 Released version 2.1.2 2014-05-13 17:24:59 +02:00
David Grudl
8a2f09033a updated examples, SQlite3 is used instead of SQLite2 2014-05-13 17:24:59 +02:00
David Grudl
ca839c5fbf bridges: changed file structure 2014-05-13 17:01:38 +02:00
David Grudl
2890690133 typos 2014-05-13 17:01:37 +02:00
David Grudl
3c8ceebe27 updated test support files 2014-05-13 17:01:37 +02:00
Emmanuel еΜanwʬĔbdƎv
8ed72797e3 readme.md: typos 2014-05-13 17:01:36 +02:00
Caspern
8ddbb41a1f .gitattributes: ignoring some paths when downloading from github 2014-05-13 17:01:36 +02:00
Ciki
1dc57bad85 fix casting to float
Now, when sql returns float from (0,1) interval, e.g. '.842' - the decimal part is missing & it won't get cast to float.
This is fix for that situation
2014-05-13 17:01:35 +02:00
David Grudl
6016f21ad4 DibiRow: null time checking consistent with DibiResult 2014-05-13 17:01:34 +02:00
Ondrej Brablc
d75e605645 Avoid error handler invocation 2014-05-13 17:01:34 +02:00
David Grudl
fa5b7678f4 phpDoc simplified & typos 2014-05-13 17:01:23 +02:00
David Grudl
2c3fe68c6c Released version 2.1.1 2013-10-16 21:39:29 +02:00
Jan Tvrdik
008371f3e7 composer.json: fix compatibility with dg/dibi requirements 2013-10-16 21:37:29 +02:00
David Grudl
105327f8e6 typos 2013-10-16 21:37:29 +02:00
David Grudl
d04eb76abb typos & whitespace 2013-10-16 21:37:26 +02:00
David Grudl
0b49f7da94 added examples to readme.md 2013-10-16 21:37:11 +02:00
Rossler Jan
0884cd4007 DibiResult: fixed illegal offset type in fetchPairs. 2013-10-16 21:34:09 +02:00
Miloslav Hůla
acc6ecb0d2 DibiResult: fixed normalization of time when begins by 00: 2013-10-16 21:34:08 +02:00
Jáchym Toušek
b21d592e07 Fixed usage of deprecated parameter 2013-10-16 21:34:08 +02:00
147 changed files with 6298 additions and 9155 deletions

2
.gitattributes vendored
View File

@@ -1,6 +1,4 @@
.gitattributes export-ignore
.gitignore export-ignore
.github export-ignore
.travis.yml export-ignore
appveyor.yml export-ignore
tests/ export-ignore

View File

@@ -1,16 +0,0 @@
- bug report? yes/no
- feature request? yes/no
- version: ?.?.? <!-- exact release version, for bug reports -->
### Description
...
### Steps To Reproduce
... If possible a minimal demo of the problem ...
<!--
REMEMBER, AN ISSUE IS NOT THE PLACE TO ASK QUESTIONS. We will be happy to help you on Gitter https://gitter.im/nette/nette
A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report.
Feature requests are welcome. Explain your intentions. It's up to you to make a strong case to convince the project's developers of the merits of this feature.
-->

View File

@@ -1,11 +0,0 @@
- bug fix? yes/no <!-- #issue numbers, if any -->
- new feature? yes/no
- BC break? yes/no
<!--
Describe your changes here to communicate to the maintainers why we should accept this pull request.
Please add new tests to show the fix or feature works.
Thanks for contributing!
-->

View File

@@ -1,41 +0,0 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
script:
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini $COVERAGE
- php temp/code-checker/src/code-checker.php --short-arrays
after_failure:
# Print *.actual content
- for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done
before_script:
# Install Nette Tester & Code Checker
- travis_retry composer install --no-interaction
- travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction
- if [ $TRAVIS_PHP_VERSION == "7.0" ]; then COVERAGE="-p phpdbg --coverage ./coverage.xml --coverage-src ./src"; fi
# Create databases.ini
- cp ./tests/databases.travis.ini ./tests/databases.ini
# Create Postgre database
- psql -c 'CREATE DATABASE dibi_test' -U postgres
after_script:
# Report Code Coverage
- >
if [ "$COVERAGE" != "" ]; then
wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar
&& php coveralls.phar --verbose --config tests/.coveralls.yml
|| true; fi
sudo: false
cache:
directories:
- $HOME/.composer/cache

View File

@@ -1,59 +0,0 @@
build: off
cache:
- c:\php5 -> appveyor.yml
- c:\php7 -> appveyor.yml
- '%LOCALAPPDATA%\Composer\files -> appveyor.yml'
clone_folder: c:\projects\dibi
services:
- mssql2012sp1
# - mssql2014
- mysql
init:
- SET PATH=c:\php5;%PATH%
- SET ANSICON=121x90 (121x90)
install:
# Install PHP 5
- IF EXIST c:\php5 (SET PHP=0) ELSE (SET PHP=1)
- IF %PHP%==1 mkdir c:\php5
- IF %PHP%==1 cd c:\php5
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip
- IF %PHP%==1 7z x php-5.6.14-Win32-VC11-x86.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_56_ts.dll ext\php_sqlsrv_ts.dll
- IF %PHP%==1 copy SQLSRV\php_pdo_sqlsrv_56_ts.dll ext\php_pdo_sqlsrv_ts.dll
- IF %PHP%==1 del /Q *.zip
# Install PHP 7
- IF EXIST c:\php7 (SET PHP=0) ELSE (SET PHP=1)
- IF %PHP%==1 mkdir c:\php7
- IF %PHP%==1 cd c:\php7
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-7.0.3-Win32-VC14-x86.zip
- IF %PHP%==1 7z x php-7.0.3-Win32-VC14-x86.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_7_ts.dll ext\php_sqlsrv_ts.dll
- IF %PHP%==1 del /Q *.zip
# Install Nette Tester
- cd c:\projects\dibi
- appveyor DownloadFile https://getcomposer.org/composer.phar
- php composer.phar install --prefer-dist --no-interaction --no-progress
# Create databases.ini
- copy tests\databases.appveyor.ini tests\databases.ini
test_script:
- vendor\bin\tester tests -s -p c:\php5\php -c tests\php5-win.ini
- vendor\bin\tester tests -s -p c:\php7\php -c tests\php7-win.ini
on_failure:
# Print *.actual content
- type tests\dibi\output\*.actual

View File

@@ -1,32 +1,25 @@
{
"name": "dibi/dibi",
"description": "Dibi is Database Abstraction Library for PHP",
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"],
"homepage": "https://dibiphp.com",
"description": "Dibi is Database Abstraction Library for PHP 5.",
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "oracle", "access", "pdo", "odbc"],
"homepage": "http://dibiphp.com/",
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
"homepage": "http://davidgrudl.com"
}
],
"require": {
"php": ">=5.4.4"
"php": ">=5.2.0"
},
"require-dev": {
"tracy/tracy": "~2.2",
"nette/tester": "~1.7"
"nette/tester": "~1.1"
},
"replace": {
"dg/dibi": "*"
"dg/dibi": "self.version"
},
"autoload": {
"classmap": ["src/"],
"files": ["src/loader.php"]
},
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
"classmap": ["dibi/"]
}
}

View File

@@ -1,31 +0,0 @@
How to contribute & use the issue tracker
=========================================
Dibi welcomes your contributions. There are several ways to help out:
* Create an issue on GitHub, if you have found a bug
* Write test cases for open bug issues
* Write fixes for open bug/feature issues, preferably with test cases included
* Contribute to the documentation
Issues
------
Please **do not use the issue tracker to ask questions**. We will be happy to help you
on [Dibi forum](https://forum.dibiphp.com).
A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report.
**Feature requests** are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature.
Contributing
------------
The best way to propose a feature is to discuss your ideas on [Dibi forum](https://forum.dibiphp.com) before implementing them.
Please do not fix whitespace, format code, or make a purely cosmetic patch.
Thanks! :heart:

View File

@@ -0,0 +1,55 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* Dibi extension for Nette Framework 2.0. Creates 'connection' service.
*
* @author David Grudl
* @package dibi\nette
* @phpversion 5.3
*/
class DibiNette20Extension extends Nette\Config\CompilerExtension
{
public function loadConfiguration()
{
$container = $this->getContainerBuilder();
$config = $this->getConfig();
$useProfiler = isset($config['profiler'])
? $config['profiler']
: !$container->parameters['productionMode'];
unset($config['profiler']);
if (isset($config['flags'])) {
$flags = 0;
foreach ((array) $config['flags'] as $flag) {
$flags |= constant($flag);
}
$config['flags'] = $flags;
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('DibiConnection', array($config));
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('DibiNettePanel')
->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array('@self'))
->addSetup('Nette\Diagnostics\Debugger::$blueScreen->addPanel(?)', array('DibiNettePanel::renderException'));
$connection->addSetup('$service->onEvent[] = ?', array(array($panel, 'logEvent')));
}
}
}

View File

@@ -0,0 +1,17 @@
# Requires Nette Framework 2.0 for PHP 5.3
#
# In bootstrap.php append these lines after line $configurator = new Nette\Config\Configurator;
#
# $configurator->onCompile[] = function($configurator, $compiler) {
# $compiler->addExtension('dibi', new DibiNette20Extension);
# };
#
# This will create service named 'dibi.connection'.
common:
dibi:
host: localhost
username: root
password: ***
database: foo
lazy: TRUE

View File

@@ -2,18 +2,18 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Bridges\Nette;
use Nette;
/**
* Dibi extension for Nette Framework 2.1. Creates 'connection' service.
*
* @author David Grudl
* @package dibi\nette
* @phpversion 5.3
*/
class DibiExtension21 extends Nette\DI\CompilerExtension
class DibiNette21Extension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
@@ -36,16 +36,15 @@ class DibiExtension21 extends Nette\DI\CompilerExtension
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
->setClass('DibiConnection', array($config));
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Nette\Panel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', ['@self'])
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', ['Dibi\Bridges\Nette\Panel::renderException']);
->setClass('DibiNettePanel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', array('@self'))
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', array('DibiNettePanel::renderException'));
$connection->addSetup('$service->onEvent[] = ?', [[$panel, 'logEvent']]);
$connection->addSetup('$service->onEvent[] = ?', array(array($panel, 'logEvent')));
}
}

View File

@@ -2,27 +2,25 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Bridges\Nette;
use Dibi;
use Dibi\Event;
use Dibi\Helpers;
use Nette;
use Nette\Diagnostics\Debugger;
if (interface_exists('Nette\Diagnostics\IBarPanel')) {
class_alias('Nette\Diagnostics\IBarPanel', 'IBarPanel');
}
/**
* Dibi panel for Nette\Diagnostics.
*
* @author David Grudl
* @package dibi\nette
*/
class Panel implements Nette\Diagnostics\IBarPanel
class DibiNettePanel extends DibiObject implements IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
public static $maxLength = 1000;
static public $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
@@ -31,21 +29,36 @@ class Panel implements Nette\Diagnostics\IBarPanel
public $filter;
/** @var array */
private $events = [];
private $events = array();
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->explain = $explain;
}
public function register(Dibi\Connection $connection)
public function register(DibiConnection $connection)
{
Debugger::getBar()->addPanel($this);
Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
if (is_callable('Nette\Diagnostics\Debugger::enable') && !class_exists('NDebugger')) {
class_alias('Nette\Diagnostics\Debugger', 'NDebugger'); // PHP 5.2 code compatibility
}
if (is_callable('NDebugger::enable') && is_callable('NDebugger::getBlueScreen')) { // Nette Framework 2.1
NDebugger::getBar()->addPanel($this);
NDebugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
$connection->onEvent[] = array($this, 'logEvent');
} elseif (is_callable('NDebugger::enable')) { // Nette Framework 2.0 (for PHP 5.3 or PHP 5.2 prefixed)
NDebugger::$bar && NDebugger::$bar->addPanel($this);
NDebugger::$blueScreen && NDebugger::$blueScreen->addPanel(array(__CLASS__, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
} elseif (is_callable('Debugger::enable') && !is_callable('Debugger::getBlueScreen')) { // Nette Framework 2.0 for PHP 5.2 non-prefixed
Debugger::$bar && Debugger::$bar->addPanel($this);
Debugger::$blueScreen && Debugger::$blueScreen->addPanel(array(__CLASS__, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
}
}
@@ -53,7 +66,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
* After event notification.
* @return void
*/
public function logEvent(Event $event)
public function logEvent(DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -68,11 +81,11 @@ class Panel implements Nette\Diagnostics\IBarPanel
*/
public static function renderException($e)
{
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
if ($e instanceof DibiException && $e->getSql()) {
return array(
'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE),
];
'panel' => dibi::dump($e->getSql(), TRUE),
);
}
}
@@ -105,15 +118,14 @@ class Panel implements Nette\Diagnostics\IBarPanel
foreach ($this->events as $event) {
$totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === Event::SELECT) {
if ($this->explain && $event->type === DibiEvent::SELECT) {
try {
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) {
}
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN' : 'EXPLAIN');
$explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (DibiException $e) {}
list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
}
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
@@ -123,7 +135,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
$s .= "<br /><a href='#nette-debug-DibiProfiler-row-$counter' class='nette-toggler nette-toggle-collapsed' rel='#nette-debug-DibiProfiler-row-$counter'>explain</a>";
}
$s .= '</td><td class="nette-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
$s .= '</td><td class="nette-DibiProfiler-sql">' . dibi::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) {
$s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>";
}
@@ -132,7 +144,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
if (!class_exists($helpers)) {
$helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers';
}
$s .= call_user_func([$helpers, 'editorLink'], $event->source[0], $event->source[1])->class('nette-DibiProfiler-source');
$s .= call_user_func(array($helpers, 'editorLink'), $event->source[0], $event->source[1])->class('nette-DibiProfiler-source');
}
$s .= "</td><td>{$event->count}</td><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";

View File

@@ -1,8 +1,8 @@
# This will create service named 'dibi.connection'.
# Requires Nette Framework 2.2
# Requires Nette Framework 2.1
extensions:
dibi: Dibi\Bridges\Nette\DibiExtension22
dibi: DibiNette21Extension
dibi:
host: localhost

View File

@@ -1,3 +1,629 @@
<?php
trigger_error('Dibi was moved to /src/loader.php', E_USER_WARNING);
require __DIR__ . '/../src/loader.php';
/**
* dibi - smart database abstraction layer (http://dibiphp.com)
*
* Copyright (c) 2005, 2012 David Grudl (http://davidgrudl.com)
*/
/**
* Check PHP configuration.
*/
if (version_compare(PHP_VERSION, '5.2.0', '<')) {
throw new Exception('dibi needs PHP 5.2.0 or newer.');
}
require_once dirname(__FILE__) . '/libs/interfaces.php';
require_once dirname(__FILE__) . '/libs/DibiDateTime.php';
require_once dirname(__FILE__) . '/libs/DibiObject.php';
require_once dirname(__FILE__) . '/libs/DibiLiteral.php';
require_once dirname(__FILE__) . '/libs/DibiHashMap.php';
require_once dirname(__FILE__) . '/libs/DibiException.php';
require_once dirname(__FILE__) . '/libs/DibiConnection.php';
require_once dirname(__FILE__) . '/libs/DibiResult.php';
require_once dirname(__FILE__) . '/libs/DibiResultIterator.php';
require_once dirname(__FILE__) . '/libs/DibiRow.php';
require_once dirname(__FILE__) . '/libs/DibiTranslator.php';
require_once dirname(__FILE__) . '/libs/DibiDataSource.php';
require_once dirname(__FILE__) . '/libs/DibiFluent.php';
require_once dirname(__FILE__) . '/libs/DibiDatabaseInfo.php';
require_once dirname(__FILE__) . '/libs/DibiEvent.php';
require_once dirname(__FILE__) . '/libs/DibiFileLogger.php';
require_once dirname(__FILE__) . '/libs/DibiFirePhpLogger.php';
if (interface_exists('Nette\Diagnostics\IBarPanel') || interface_exists('IBarPanel')) {
require_once dirname(__FILE__) . '/bridges/Nette-2.1/DibiNettePanel.php';
}
/**
* Interface for database drivers.
*
* This class is static container class for creating DB objects and
* store connections info.
*
* @author David Grudl
* @package dibi
*/
class dibi
{
/** column type */
const TEXT = 's', // as 'string'
BINARY = 'bin',
BOOL = 'b',
INTEGER = 'i',
FLOAT = 'f',
DATE = 'd',
DATETIME = 't',
TIME = 't';
const IDENTIFIER = 'n';
/** @deprecated */
const FIELD_TEXT = dibi::TEXT,
FIELD_BINARY = dibi::BINARY,
FIELD_BOOL = dibi::BOOL,
FIELD_INTEGER = dibi::INTEGER,
FIELD_FLOAT = dibi::FLOAT,
FIELD_DATE = dibi::DATE,
FIELD_DATETIME = dibi::DATETIME,
FIELD_TIME = dibi::TIME;
/** version */
const VERSION = '2.1.2',
REVISION = '$WCREV$ released on $WCDATE$';
/** sorting order */
const ASC = 'ASC',
DESC = 'DESC';
/** @var DibiConnection[] Connection registry storage for DibiConnection objects */
private static $registry = array();
/** @var DibiConnection Current connection */
private static $connection;
/** @var array @see addHandler */
private static $handlers = array();
/** @var string Last SQL command @see dibi::query() */
public static $sql;
/** @var int Elapsed time for last query */
public static $elapsedTime;
/** @var int Elapsed time for all queries */
public static $totalTime;
/** @var int Number or queries */
public static $numOfQueries = 0;
/** @var string Default dibi driver */
public static $defaultDriver = 'mysql';
/**
* Static class - cannot be instantiated.
*/
final public function __construct()
{
throw new LogicException("Cannot instantiate static class " . get_class($this));
}
/********************* connections handling ****************d*g**/
/**
* Creates a new DibiConnection object and connects it to specified database.
* @param mixed connection parameters
* @param string connection name
* @return DibiConnection
* @throws DibiException
*/
public static function connect($config = array(), $name = 0)
{
return self::$connection = self::$registry[$name] = new DibiConnection($config, $name);
}
/**
* Disconnects from database (doesn't destroy DibiConnection object).
* @return void
*/
public static function disconnect()
{
self::getConnection()->disconnect();
}
/**
* Returns TRUE when connection was established.
* @return bool
*/
public static function isConnected()
{
return (self::$connection !== NULL) && self::$connection->isConnected();
}
/**
* Retrieve active connection.
* @param string connection registy name
* @return DibiConnection
* @throws DibiException
*/
public static function getConnection($name = NULL)
{
if ($name === NULL) {
if (self::$connection === NULL) {
throw new DibiException('Dibi is not connected to database.');
}
return self::$connection;
}
if (!isset(self::$registry[$name])) {
throw new DibiException("There is no connection named '$name'.");
}
return self::$registry[$name];
}
/**
* Sets connection.
* @param DibiConnection
* @return DibiConnection
*/
public static function setConnection(DibiConnection $connection)
{
return self::$connection = $connection;
}
/**
* Change active connection.
* @param string connection registy name
* @return void
* @throws DibiException
*/
public static function activate($name)
{
self::$connection = self::getConnection($name);
}
/********************* monostate for active connection ****************d*g**/
/**
* Generates and executes SQL query - Monostate for DibiConnection::query().
* @param array|mixed one or more arguments
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
public static function query($args)
{
$args = func_get_args();
return self::getConnection()->query($args);
}
/**
* Executes the SQL query - Monostate for DibiConnection::nativeQuery().
* @param string SQL statement.
* @return DibiResult|int result set object (if any)
*/
public static function nativeQuery($sql)
{
return self::getConnection()->nativeQuery($sql);
}
/**
* Generates and prints SQL query - Monostate for DibiConnection::test().
* @param array|mixed one or more arguments
* @return bool
*/
public static function test($args)
{
$args = func_get_args();
return self::getConnection()->test($args);
}
/**
* Generates and returns SQL query as DibiDataSource - Monostate for DibiConnection::test().
* @param array|mixed one or more arguments
* @return DibiDataSource
*/
public static function dataSource($args)
{
$args = func_get_args();
return self::getConnection()->dataSource($args);
}
/**
* Executes SQL query and fetch result - Monostate for DibiConnection::query() & fetch().
* @param array|mixed one or more arguments
* @return DibiRow
* @throws DibiException
*/
public static function fetch($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetch();
}
/**
* Executes SQL query and fetch results - Monostate for DibiConnection::query() & fetchAll().
* @param array|mixed one or more arguments
* @return DibiRow[]
* @throws DibiException
*/
public static function fetchAll($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchAll();
}
/**
* Executes SQL query and fetch first column - Monostate for DibiConnection::query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
public static function fetchSingle($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchSingle();
}
/**
* Executes SQL query and fetch pairs - Monostate for DibiConnection::query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
public static function fetchPairs($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchPairs();
}
/**
* Gets the number of affected rows.
* Monostate for DibiConnection::getAffectedRows()
* @return int number of rows
* @throws DibiException
*/
public static function getAffectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws DibiException
*/
public static function affectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* Monostate for DibiConnection::getInsertId()
* @param string optional sequence name
* @return int
* @throws DibiException
*/
public static function getInsertId($sequence=NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws DibiException
*/
public static function insertId($sequence=NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Begins a transaction - Monostate for DibiConnection::begin().
* @param string optional savepoint name
* @return void
* @throws DibiException
*/
public static function begin($savepoint = NULL)
{
self::getConnection()->begin($savepoint);
}
/**
* Commits statements in a transaction - Monostate for DibiConnection::commit($savepoint = NULL).
* @param string optional savepoint name
* @return void
* @throws DibiException
*/
public static function commit($savepoint = NULL)
{
self::getConnection()->commit($savepoint);
}
/**
* Rollback changes in a transaction - Monostate for DibiConnection::rollback().
* @param string optional savepoint name
* @return void
* @throws DibiException
*/
public static function rollback($savepoint = NULL)
{
self::getConnection()->rollback($savepoint);
}
/**
* Gets a information about the current database - Monostate for DibiConnection::getDatabaseInfo().
* @return DibiDatabaseInfo
*/
public static function getDatabaseInfo()
{
return self::getConnection()->getDatabaseInfo();
}
/**
* Import SQL dump from file - extreme fast!
* @param string filename
* @return int count of sql commands
*/
public static function loadFile($file)
{
return self::getConnection()->loadFile($file);
}
/**
* Replacement for majority of dibi::methods() in future.
*/
public static function __callStatic($name, $args)
{
//if ($name = 'select', 'update', ...') {
// return self::command()->$name($args);
//}
return call_user_func_array(array(self::getConnection(), $name), $args);
}
/********************* fluent SQL builders ****************d*g**/
/**
* @return DibiFluent
*/
public static function command()
{
return self::getConnection()->command();
}
/**
* @param string column name
* @return DibiFluent
*/
public static function select($args)
{
$args = func_get_args();
return call_user_func_array(array(self::getConnection(), 'select'), $args);
}
/**
* @param string table
* @param array
* @return DibiFluent
*/
public static function update($table, $args)
{
return self::getConnection()->update($table, $args);
}
/**
* @param string table
* @param array
* @return DibiFluent
*/
public static function insert($table, $args)
{
return self::getConnection()->insert($table, $args);
}
/**
* @param string table
* @return DibiFluent
*/
public static function delete($table)
{
return self::getConnection()->delete($table);
}
/********************* data types ****************d*g**/
/**
* @deprecated
*/
public static function datetime($time = NULL)
{
trigger_error(__METHOD__ . '() is deprecated; create DateTime object instead.', E_USER_WARNING);
return new DibiDateTime($time);
}
/**
* @deprecated
*/
public static function date($date = NULL)
{
trigger_error(__METHOD__ . '() is deprecated; create DateTime object instead.', E_USER_WARNING);
return new DibiDateTime($date);
}
/********************* substitutions ****************d*g**/
/**
* Returns substitution hashmap - Monostate for DibiConnection::getSubstitutes().
* @return DibiHashMap
*/
public static function getSubstitutes()
{
return self::getConnection()->getSubstitutes();
}
/** @deprecated */
public static function addSubst($expr, $subst)
{
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->expr = val; instead.', E_USER_WARNING);
self::getSubstitutes()->$expr = $subst;
}
/** @deprecated */
public static function removeSubst($expr)
{
trigger_error(__METHOD__ . '() is deprecated; use unset(dibi::getSubstitutes()->expr) instead.', E_USER_WARNING);
$substitutes = self::getSubstitutes();
if ($expr === TRUE) {
foreach ($substitutes as $expr => $foo) {
unset($substitutes->$expr);
}
} else {
unset($substitutes->$expr);
}
}
/** @deprecated */
public static function setSubstFallback($callback)
{
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->setCallback() instead.', E_USER_WARNING);
self::getSubstitutes()->setCallback($callback);
}
/********************* misc tools ****************d*g**/
/**
* Prints out a syntax highlighted version of the SQL command or DibiResult.
* @param string|DibiResult
* @param bool return output instead of printing it?
* @return string
*/
public static function dump($sql = NULL, $return = FALSE)
{
ob_start();
if ($sql instanceof DibiResult) {
$sql->dump();
} else {
if ($sql === NULL) {
$sql = self::$sql;
}
static $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE';
// insert new lines
$sql = " $sql ";
$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
// reduce spaces
$sql = preg_replace('#[ \t]{2,}#', " ", $sql);
$sql = wordwrap($sql, 100);
$sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql);
// syntax highlight
$highlighter = "#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is";
if (PHP_SAPI === 'cli') {
if (substr(getenv('TERM'), 0, 5) === 'xterm') {
$sql = preg_replace_callback($highlighter, array('dibi', 'cliHighlightCallback'), $sql);
}
echo trim($sql) . "\n\n";
} else {
$sql = htmlSpecialChars($sql);
$sql = preg_replace_callback($highlighter, array('dibi', 'highlightCallback'), $sql);
echo '<pre class="dump">', trim($sql), "</pre>\n\n";
}
}
if ($return) {
return ob_get_clean();
} else {
ob_end_flush();
}
}
private static function highlightCallback($matches)
{
if (!empty($matches[1])) { // comment
return '<em style="color:gray">' . $matches[1] . '</em>';
} elseif (!empty($matches[2])) { // error
return '<strong style="color:red">' . $matches[2] . '</strong>';
} elseif (!empty($matches[3])) { // most important keywords
return '<strong style="color:blue">' . $matches[3] . '</strong>';
} elseif (!empty($matches[4])) { // other keywords
return '<strong style="color:green">' . $matches[4] . '</strong>';
}
}
private static function cliHighlightCallback($matches)
{
if (!empty($matches[1])) { // comment
return "\033[1;30m" . $matches[1] . "\033[0m";
} elseif (!empty($matches[2])) { // error
return "\033[1;31m" . $matches[2] . "\033[0m";
} elseif (!empty($matches[3])) { // most important keywords
return "\033[1;34m" . $matches[3] . "\033[0m";
} elseif (!empty($matches[4])) { // other keywords
return "\033[1;32m" . $matches[4] . "\033[0m";
}
}
}

View File

@@ -2,13 +2,9 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Firebird/InterBase database.
@@ -20,24 +16,25 @@ use Dibi;
* - charset => character encoding to set
* - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author Tomáš Kraina, Roman Sklenář
* @package dibi\drivers
*/
class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
use Dibi\Strict;
const ERROR_EXCEPTION_THROWN = -836;
/** @var resource|NULL */
/** @var resource Connection resource */
private $connection;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $transaction;
/** @var bool */
@@ -45,12 +42,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('interbase')) {
throw new Dibi\NotSupportedException("PHP extension 'interbase' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'interbase' is not loaded.");
}
}
@@ -58,35 +55,40 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
Dibi\Helpers::alias($config, 'database', 'db');
DibiConnection::alias($config, 'database', 'db');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$config += [
$config += array(
'username' => ini_get('ibase.default_password'),
'password' => ini_get('ibase.default_user'),
'database' => ini_get('ibase.default_db'),
'charset' => ini_get('ibase.default_charset'),
'buffers' => 0,
];
);
DibiDriverException::tryError();
if (empty($config['persistent'])) {
$this->connection = @ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
$this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else {
$this->connection = @ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
$this->connection = ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, ibase_errcode());
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
throw new DibiDriverException(ibase_errmsg(), ibase_errcode());
}
}
}
@@ -96,34 +98,38 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function disconnect()
{
@ibase_close($this->connection); // @ - connection can be already disconnected
ibase_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException|Dibi\Exception
* @return IDibiResultDriver|NULL
* @throws DibiDriverException|DibiException
*/
public function query($sql)
{
DibiDriverException::tryError();
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql);
if ($res === FALSE) {
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
} else {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode(), $sql);
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), dibi::$sql);
}
}
if ($res === FALSE) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
return NULL;
}
@@ -152,14 +158,14 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
$this->transaction = ibase_trans($this->getResource());
$this->transaction = ibase_trans($this->resource);
$this->inTransaction = TRUE;
}
@@ -168,16 +174,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_commit($this->transaction)) {
throw new Dibi\DriverException('Unable to handle operation - failure when commiting transaction.');
throw new DibiDriverException('Unable to handle operation - failure when commiting transaction.');
}
$this->inTransaction = FALSE;
@@ -188,16 +194,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_rollback($this->transaction)) {
throw new Dibi\DriverException('Unable to handle operation - failure when rolbacking transaction.');
throw new DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
}
$this->inTransaction = FALSE;
@@ -216,7 +222,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection resource.
* @return resource|NULL
* @return resource
*/
public function getResource()
{
@@ -226,7 +232,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -237,7 +243,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -252,68 +258,33 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Encodes data for use in a SQL statement.
* @param string
* @return string
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
return "'" . str_replace("'", "''", $value) . "'";
}
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
return $value;
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '"' . str_replace('"', '""', $value). '"';
}
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
@@ -325,41 +296,35 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function escapeLike($value, $pos)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit > 0 || $offset > 0) {
// http://www.firebirdsql.org/refdocs/langrefupd20-select.html
$sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . (int) $limit : '') . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
if ($limit >= 0 && $offset > 0) {
// see http://scott.yang.id.au/2004/01/limit-in-select-statements-in-firebird/
$sql = 'SELECT FIRST ' . (int) $limit . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
}
}
@@ -383,7 +348,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Firebird/Interbase do not support returning number of rows in result set.');
throw new DibiNotSupportedException("Firebird/Interbase do not support returning number of rows in result set.");
}
@@ -394,15 +359,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function fetch($assoc)
{
$result = $assoc ? @ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
DibiDriverException::tryError();
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (ibase_errcode()) {
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
} else {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
throw new DibiDriverException($msg, ibase_errcode(), dibi::$sql);
}
}
@@ -412,13 +378,13 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Firebird/Interbase do not support seek in result set.');
throw new DibiNotSupportedException("Firebird/Interbase do not support seek in result set.");
}
@@ -435,7 +401,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the result set resource.
* @return resource|NULL
* @return mysqli_result
*/
public function getResultResource()
{
@@ -451,21 +417,21 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns()
{
$count = ibase_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => $row['name'],
'fullname' => $row['name'],
'table' => $row['relation'],
'nativetype' => $row['type'],
];
);
}
return $columns;
}
/********************* Dibi\Reflector ********************/
/********************* IDibiReflector ********************/
/**
@@ -480,12 +446,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$tables = [];
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'TRUE',
];
);
}
return $tables;
}
@@ -529,10 +495,10 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY r.RDB\$FIELD_POSITION;"
);
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME'];
$columns[$key] = [
$columns[$key] = array(
'name' => $key,
'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']),
@@ -540,7 +506,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
'nullable' => $row['NULLABLE'] === 'TRUE',
'default' => $row['DEFAULT_VALUE'],
'autoincrement' => FALSE,
];
);
}
return $columns;
}
@@ -567,7 +533,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
$indexes = [];
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key;
@@ -597,14 +563,14 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
$keys = [];
$keys = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$keys[$key] = [
$keys[$key] = array(
'name' => $key,
'column' => $row['FIELD_NAME'],
'table' => $table,
];
);
}
return $keys;
}
@@ -624,7 +590,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
$indices = [];
$indices = array();
while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0];
}
@@ -648,7 +614,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
$constraints = [];
$constraints = array();
while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0];
}
@@ -689,17 +655,17 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
END AS TRIGGER_ENABLED
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
. ($table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table');")
);
$triggers = [];
$triggers = array();
while ($row = $res->fetch(TRUE)) {
$triggers[$row['TRIGGER_NAME']] = [
$triggers[$row['TRIGGER_NAME']] = array(
'name' => $row['TRIGGER_NAME'],
'table' => $row['TABLE_NAME'],
'type' => trim($row['TRIGGER_TYPE']),
'event' => trim($row['TRIGGER_EVENT']),
'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE',
];
);
}
return $triggers;
}
@@ -716,10 +682,10 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$q = "SELECT TRIM(RDB\$TRIGGER_NAME)
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0";
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$q .= $table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q);
$triggers = [];
$triggers = array();
while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0];
}
@@ -766,7 +732,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
$procedures = [];
$procedures = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']);
@@ -790,7 +756,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;"
);
$procedures = [];
$procedures = array();
while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0];
}
@@ -809,7 +775,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$generators = [];
$generators = array();
while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0];
}
@@ -828,7 +794,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$functions = [];
$functions = array();
while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0];
}
@@ -836,3 +802,41 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
}
/**
* Database procedure exception.
*
* @author Roman Sklenář
* @copyright Copyright (c) 2010
* @package dibi\drivers
*/
class DibiProcedureException extends DibiException
{
/** @var string */
protected $severity;
/**
* Construct the exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL)
{
parent::__construct($message, (int) $code, $sql);
$this->severity = $severity;
}
/**
* Gets the exception severity.
* @return string
*/
public function getSeverity()
{
$this->severity;
}
}

View File

@@ -2,37 +2,35 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use Dibi\Connection;
use Dibi\Helpers;
require_once dirname(__FILE__) . '/DibiMsSql2005Reflector.php';
/**
* The dibi driver for Microsoft SQL Server and SQL Azure databases.
* The dibi driver for MS SQL Driver 2005 database.
*
* Driver options:
* - host => the MS SQL server host name. It can also include a port number (hostname:port)
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - options (array) => connection options {@link https://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - options (array) => connection options {@link http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
/** @var resource|NULL */
/** @var resource Connection resource */
private $connection;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
@@ -41,17 +39,14 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $version = '';
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlsrv')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlsrv' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
}
}
@@ -59,37 +54,31 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
Helpers::alias($config, 'options|UID', 'username');
Helpers::alias($config, 'options|PWD', 'password');
Helpers::alias($config, 'options|Database', 'database');
Helpers::alias($config, 'options|CharacterSet', 'charset');
DibiConnection::alias($config, 'options|UID', 'username');
DibiConnection::alias($config, 'options|PWD', 'password');
DibiConnection::alias($config, 'options|Database', 'database');
DibiConnection::alias($config, 'options|CharacterSet', 'charset');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$options = $config['options'];
// Default values
if (!isset($options['CharacterSet'])) {
$options['CharacterSet'] = 'UTF-8';
if (!isset($config['options']['CharacterSet'])) {
$config['options']['CharacterSet'] = 'UTF-8';
}
$options['PWD'] = (string) $options['PWD'];
$options['UID'] = (string) $options['UID'];
$options['Database'] = (string) $options['Database'];
$this->connection = sqlsrv_connect($config['host'], $options);
$this->connection = sqlsrv_connect($config['host'], (array) $config['options']);
}
if (!is_resource($this->connection)) {
$info = sqlsrv_errors();
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
throw new DibiDriverException($info[0]['message'], $info[0]['code']);
}
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
}
@@ -99,15 +88,15 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function disconnect()
{
@sqlsrv_close($this->connection); // @ - connection can be already disconnected
sqlsrv_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -116,13 +105,12 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
if ($res === FALSE) {
$info = sqlsrv_errors();
throw new Dibi\DriverException($info[0]['message'], $info[0]['code'], $sql);
throw new DibiDriverException($info[0]['message'], $info[0]['code'], $sql);
} elseif (is_resource($res)) {
$this->affectedRows = sqlsrv_rows_affected($res);
return $this->createResultDriver($res);
}
return NULL;
}
@@ -142,7 +130,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getInsertId($sequence)
{
$res = sqlsrv_query($this->connection, 'SELECT SCOPE_IDENTITY()');
$res = sqlsrv_query($this->connection, 'SELECT @@IDENTITY');
if (is_resource($res)) {
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
return $row[0];
@@ -155,7 +143,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -167,7 +155,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -179,7 +167,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -189,7 +177,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection resource.
* @return resource|NULL
* @return mixed
*/
public function getResource()
{
@@ -199,18 +187,18 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new SqlsrvReflector($this);
return new DibiMssql2005Reflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -225,69 +213,34 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
return "'" . str_replace("'", "''", $value) . "'";
}
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
// @see http://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(']', ']]', $value) . ']';
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(']', ']]', $value) . ']';
}
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
@@ -299,56 +252,40 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') AS T ';
}
} elseif (version_compare($this->version, '11', '<')) { // 11 == SQL Server 2012
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = sprintf('SELECT TOP (%d) * FROM (%s) t', $limit, $sql);
}
} elseif ($limit !== NULL) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
} elseif ($offset) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
}
}
@@ -372,7 +309,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -389,12 +326,12 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -415,13 +352,13 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getResultColumns()
{
$columns = [];
$columns = array();
foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) {
$columns[] = [
$columns[] = array(
'name' => $fieldMetadata['Name'],
'fullname' => $fieldMetadata['Name'],
'nativetype' => $fieldMetadata['Type'],
];
);
}
return $columns;
}
@@ -429,7 +366,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the result set resource.
* @return resource|NULL
* @return mixed
*/
public function getResultResource()
{

View File

@@ -2,27 +2,24 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for Microsoft SQL Server and SQL Azure databases.
* The dibi reflector for MSSQL2005 databases.
*
* @author Daniel Kouba
* @package dibi\drivers
* @internal
*/
class SqlsrvReflector implements Dibi\Reflector
class DibiMsSql2005Reflector extends DibiObject implements IDibiReflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
/** @var IDibiDriver */
private $driver;
public function __construct(Dibi\Driver $driver)
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
@@ -34,13 +31,13 @@ class SqlsrvReflector implements Dibi\Reflector
*/
public function getTables()
{
$res = $this->driver->query("SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE [TABLE_SCHEMA] = 'dbo'");
$tables = [];
$res = $this->driver->query('SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES');
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
);
}
return $tables;
}
@@ -57,10 +54,10 @@ class SqlsrvReflector implements Dibi\Reflector
SELECT c.name as COLUMN_NAME, c.is_identity AS AUTO_INCREMENT
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.name = {$this->driver->escapeText($table)}
WHERE t.name = {$this->driver->escape($table, dibi::TEXT)}
");
$autoIncrements = [];
$autoIncrements = array();
while ($row = $res->fetch(TRUE)) {
$autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT'];
}
@@ -78,11 +75,11 @@ class SqlsrvReflector implements Dibi\Reflector
And TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
And CCU.COLUMN_NAME = C.COLUMN_NAME
) As Z
WHERE C.TABLE_NAME = {$this->driver->escapeText($table)}
WHERE C.TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
");
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$columns[] = [
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => strtoupper($row['DATA_TYPE']),
@@ -92,7 +89,7 @@ class SqlsrvReflector implements Dibi\Reflector
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => $autoIncrements[$row['COLUMN_NAME']],
'vendor' => $row,
];
);
}
return $columns;
}
@@ -105,19 +102,19 @@ class SqlsrvReflector implements Dibi\Reflector
*/
public function getIndexes($table)
{
$keyUsagesRes = $this->driver->query(sprintf("EXEC [sys].[sp_helpindex] @objname = N%s", $this->driver->escapeText($table)));
$keyUsages = [];
while ($row = $keyUsagesRes->fetch(TRUE)) {
$keyUsages[$row['index_name']] = explode(',', $row['index_keys']);
$keyUsagesRes = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}");
$keyUsages = array();
while( $row = $keyUsagesRes->fetch(TRUE) ) {
$keyUsages[$row['CONSTRAINT_NAME']][(int) $row['ORDINAL_POSITION'] - 1] = $row['COLUMN_NAME'];
}
$res = $this->driver->query("SELECT [i].* FROM [sys].[indexes] [i] INNER JOIN [sys].[tables] [t] ON [i].[object_id] = [t].[object_id] WHERE [t].[name] = {$this->driver->escapeText($table)}");
$indexes = [];
$res = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = $row['is_unique'] === 1;
$indexes[$row['name']]['primary'] = $row['is_primary_key'] === 1;
$indexes[$row['name']]['columns'] = isset($keyUsages[$row['name']]) ? $keyUsages[$row['name']] : [];
$indexes[$row['CONSTRAINT_NAME']]['name'] = $row['CONSTRAINT_NAME'];
$indexes[$row['CONSTRAINT_NAME']]['unique'] = $row['CONSTRAINT_TYPE'] === 'UNIQUE';
$indexes[$row['CONSTRAINT_NAME']]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$indexes[$row['CONSTRAINT_NAME']]['columns'] = isset($keyUsages[$row['CONSTRAINT_NAME']]) ? $keyUsages[$row['CONSTRAINT_NAME']] : array();
}
return array_values($indexes);
}
@@ -130,7 +127,7 @@ class SqlsrvReflector implements Dibi\Reflector
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
}

View File

@@ -2,13 +2,10 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
require_once dirname(__FILE__) . '/DibiMsSqlReflector.php';
/**
* The dibi driver for MS SQL database.
@@ -20,16 +17,17 @@ use Dibi;
* - database => the database name to select
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
/** @var resource|NULL */
/** @var resource Connection resource */
private $connection;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
@@ -37,12 +35,12 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mssql')) {
throw new Dibi\NotSupportedException("PHP extension 'mssql' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'mssql' is not loaded.");
}
}
@@ -50,9 +48,9 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
@@ -63,11 +61,11 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException("Can't connect to DB.");
throw new DibiDriverException("Can't connect to DB.");
}
if (isset($config['database']) && !@mssql_select_db($this->escapeIdentifier($config['database']), $this->connection)) { // intentionally @
throw new Dibi\DriverException("Can't select DB '$config[database]'.");
if (isset($config['database']) && !@mssql_select_db($this->escape($config['database'], dibi::IDENTIFIER), $this->connection)) { // intentionally @
throw new DibiDriverException("Can't select DB '$config[database]'.");
}
}
@@ -78,27 +76,26 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function disconnect()
{
@mssql_close($this->connection); // @ - connection can be already disconnected
mssql_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$res = @mssql_query($sql, $this->connection); // intentionally @
if ($res === FALSE) {
throw new Dibi\DriverException(mssql_get_last_message(), 0, $sql);
throw new DibiDriverException(mssql_get_last_message(), 0, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
return NULL;
}
@@ -131,7 +128,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -143,7 +140,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -155,7 +152,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -165,7 +162,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection resource.
* @return resource|NULL
* @return mixed
*/
public function getResource()
{
@@ -175,18 +172,18 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new MsSqlReflector($this);
return new DibiMsSqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -201,69 +198,34 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
return "'" . str_replace("'", "''", $value) . "'";
}
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
// @see http://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
@@ -275,48 +237,41 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) {
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
}
}
@@ -383,15 +338,15 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
$count = mssql_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mssql_fetch_field($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => $row['name'],
'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'],
'table' => $row['column_source'],
'nativetype' => $row['type'],
];
);
}
return $columns;
}
@@ -399,7 +354,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the result set resource.
* @return resource|NULL
* @return mixed
*/
public function getResultResource()
{
@@ -407,4 +362,5 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
}

View File

@@ -2,27 +2,27 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*
* Copyright (c) 2005, 2010 David Grudl (http://davidgrudl.com)
*
* @package dibi\drivers
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for MS SQL databases.
* The dibi reflector for MsSQL databases.
*
* @author Steven Bredenberg
* @package dibi\drivers
* @internal
*/
class MsSqlReflector implements Dibi\Reflector
class DibiMsSqlReflector extends DibiObject implements IDibiReflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
/** @var IDibiDriver */
private $driver;
public function __construct(Dibi\Driver $driver)
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
@@ -34,16 +34,16 @@ class MsSqlReflector implements Dibi\Reflector
*/
public function getTables()
{
$res = $this->driver->query('
$res = $this->driver->query("
SELECT TABLE_NAME, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
');
$tables = [];
");
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
);
}
return $tables;
}
@@ -52,26 +52,26 @@ class MsSqlReflector implements Dibi\Reflector
/**
* Returns count of rows in a table
* @param string
* @return int
* @return integer
*/
public function getTableCount($table, $fallback = TRUE)
public function getTableCount($table, $fallback=true)
{
if (empty($table)) {
return FALSE;
return false;
}
$result = $this->driver->query("
SELECT MAX(rowcnt)
FROM sys.sysindexes
WHERE id=OBJECT_ID({$this->driver->escapeIdentifier($table)})
WHERE id=OBJECT_ID({$this->driver->escape($table, dibi::IDENTIFIER)})
");
$row = $result->fetch(FALSE);
if (!is_array($row) || count($row) < 1) {
if ($fallback) {
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escapeIdentifier($table)}")->fetch(FALSE);
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escape($table, dibi::IDENTIFIER)}")->fetch(FALSE);
$count = intval($row[0]);
} else {
$count = FALSE;
$count = false;
}
} else {
$count = intval($row[0]);
@@ -91,22 +91,22 @@ class MsSqlReflector implements Dibi\Reflector
$res = $this->driver->query("
SELECT * FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = {$this->driver->escapeText($table)}
WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
ORDER BY TABLE_NAME, ORDINAL_POSITION
");
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$size = FALSE;
$size = false;
$type = strtoupper($row['DATA_TYPE']);
$size_cols = [
'DATETIME' => 'DATETIME_PRECISION',
'DECIMAL' => 'NUMERIC_PRECISION',
'CHAR' => 'CHARACTER_MAXIMUM_LENGTH',
'NCHAR' => 'CHARACTER_OCTET_LENGTH',
'NVARCHAR' => 'CHARACTER_OCTET_LENGTH',
'VARCHAR' => 'CHARACTER_OCTET_LENGTH',
];
$size_cols = array(
'DATETIME'=>'DATETIME_PRECISION',
'DECIMAL'=>'NUMERIC_PRECISION',
'CHAR'=>'CHARACTER_MAXIMUM_LENGTH',
'NCHAR'=>'CHARACTER_OCTET_LENGTH',
'NVARCHAR'=>'CHARACTER_OCTET_LENGTH',
'VARCHAR'=>'CHARACTER_OCTET_LENGTH'
);
if (isset($size_cols[$type])) {
if ($size_cols[$type]) {
@@ -114,7 +114,7 @@ class MsSqlReflector implements Dibi\Reflector
}
}
$columns[] = [
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $type,
@@ -122,9 +122,9 @@ class MsSqlReflector implements Dibi\Reflector
'unsigned' => NULL,
'nullable' => $row['IS_NULLABLE'] === 'YES',
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => FALSE,
'autoincrement' => false,
'vendor' => $row,
];
);
}
return $columns;
@@ -148,22 +148,22 @@ class MsSqlReflector implements Dibi\Reflector
(ic.object_id = col.object_id and ic.column_id = col.column_id)
INNER JOIN sys.tables t ON
(ind.object_id = t.object_id)
WHERE t.name = {$this->driver->escapeText($table)}
WHERE t.name = {$this->driver->escape($table, dibi::TEXT)}
AND t.is_ms_shipped = 0
ORDER BY
t.name, ind.name, ind.index_id, ic.index_column_id
");
$indexes = [];
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$index_name = $row['index_name'];
if (!isset($indexes[$index_name])) {
$indexes[$index_name] = [];
$indexes[$index_name] = array();
$indexes[$index_name]['name'] = $index_name;
$indexes[$index_name]['unique'] = (bool) $row['is_unique'];
$indexes[$index_name]['primary'] = (bool) $row['is_primary_key'];
$indexes[$index_name]['columns'] = [];
$indexes[$index_name]['unique'] = (bool)$row['is_unique'];
$indexes[$index_name]['primary'] = (bool)$row['is_primary_key'];
$indexes[$index_name]['columns'] = array();
}
$indexes[$index_name]['columns'][] = $row['column_name'];
}
@@ -191,20 +191,20 @@ class MsSqlReflector implements Dibi\Reflector
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escapeText($table)}
WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escape($table, dibi::TEXT)}
");
$keys = [];
$keys = array();
while ($row = $res->fetch(TRUE)) {
$key_name = $row['foreign_key'];
if (!isset($keys[$key_name])) {
$keys[$key_name]['name'] = $row['foreign_key']; // foreign key name
$keys[$key_name]['local'] = [$row['column_name']]; // local columns
$keys[$key_name]['local'] = array($row['column_name']); // local columns
$keys[$key_name]['table'] = $row['reference_table_name']; // referenced table
$keys[$key_name]['foreign'] = [$row['reference_column_name']]; // referenced columns
$keys[$key_name]['onDelete'] = FALSE;
$keys[$key_name]['onUpdate'] = FALSE;
$keys[$key_name]['foreign'] = array($row['reference_column_name']); // referenced columns
$keys[$key_name]['onDelete'] = false;
$keys[$key_name]['onUpdate'] = false;
} else {
$keys[$key_name]['local'][] = $row['column_name']; // local columns
$keys[$key_name]['foreign'][] = $row['reference_column_name']; // referenced columns

View File

@@ -2,12 +2,11 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
/**
@@ -26,20 +25,21 @@ use Dibi;
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
/** @var resource|NULL */
/** @var resource Connection resource */
private $connection;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
@@ -50,12 +50,12 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysql')) {
throw new Dibi\NotSupportedException("PHP extension 'mysql' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded.");
}
}
@@ -63,22 +63,22 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
Dibi\Helpers::alias($config, 'flags', 'options');
$config += [
DibiConnection::alias($config, 'flags', 'options');
$config += array(
'charset' => 'utf8',
'timezone' => date('P'),
'username' => ini_get('mysql.default_user'),
'password' => ini_get('mysql.default_password'),
];
);
if (!isset($config['host'])) {
$host = ini_get('mysql.default_host');
if ($host) {
@@ -106,18 +106,23 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(mysql_error(), mysql_errno());
throw new DibiDriverException(mysql_error(), mysql_errno());
}
if (isset($config['charset'])) {
if (!@mysql_set_charset($config['charset'], $this->connection)) { // intentionally @
$ok = FALSE;
if (function_exists('mysql_set_charset')) {
// affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.2.3)
$ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @
}
if (!$ok) {
$this->query("SET NAMES '$config[charset]'");
}
}
if (isset($config['database'])) {
if (!@mysql_select_db($config['database'], $this->connection)) { // intentionally @
throw new Dibi\DriverException(mysql_error($this->connection), mysql_errno($this->connection));
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
}
@@ -139,15 +144,15 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function disconnect()
{
@mysql_close($this->connection); // @ - connection can be already disconnected
mysql_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -157,8 +162,8 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
}
if ($code = mysql_errno($this->connection)) {
throw MySqliDriver::createException(mysql_error($this->connection), $code, $sql);
if (mysql_errno($this->connection)) {
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
@@ -172,10 +177,10 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getInfo()
{
$res = [];
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new Dibi\PcreException;
throw new DibiPcreException;
}
foreach ($matches as $m) {
@@ -209,7 +214,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -221,7 +226,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -233,7 +238,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -243,7 +248,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection resource.
* @return resource|NULL
* @return mixed
*/
public function getResource()
{
@@ -253,18 +258,18 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new MySqlReflector($this);
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -279,75 +284,42 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
switch ($type) {
case dibi::TEXT:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::BINARY:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::IDENTIFIER:
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
}
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
}
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
return '`' . str_replace('`', '``', $value) . '`';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? 1 : 0;
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
@@ -366,39 +338,30 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
if ($limit >= 0 || $offset > 0) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -423,7 +386,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getRowCount()
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
return mysql_num_rows($this->resultSet);
}
@@ -442,14 +405,14 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/
public function seek($row)
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
return mysql_data_seek($this->resultSet, $row);
@@ -474,17 +437,16 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
$count = mysql_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mysql_fetch_field($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => $row['name'],
'table' => $row['table'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => strtoupper($row['type']),
'type' => $row['type'] === 'time' ? Dibi\Type::TIME_INTERVAL : NULL,
'vendor' => $row,
];
);
}
return $columns;
}
@@ -492,7 +454,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the result set resource.
* @return resource|NULL
* @return mixed
*/
public function getResultResource()
{

View File

@@ -0,0 +1,120 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* The dibi reflector for MySQL databases.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class DibiMySqlReflector extends DibiObject implements IDibiReflector
{
/** @var IDibiDriver */
private $driver;
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
/*$this->query("
SELECT TABLE_NAME as name, TABLE_TYPE = 'VIEW' as view
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE()
");*/
$res = $this->driver->query("SHOW FULL TABLES");
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
/*$table = $this->escape($table, dibi::TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
");*/
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$columns = array();
while ($row = $res->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$columns[] = array(
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'unsigned' => (bool) strstr($row['Type'], 'unsigned'),
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
'vendor' => $row,
);
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
/*$table = $this->escape($table, dibi::TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
AND REFERENCED_COLUMN_NAME IS NULL
");*/
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['Key_name']]['name'] = $row['Key_name'];
$indexes[$row['Key_name']]['unique'] = !$row['Non_unique'];
$indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$indexes[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new DibiNotImplementedException;
}
}

View File

@@ -2,12 +2,11 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
/**
@@ -27,20 +26,21 @@ use Dibi;
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (mysqli) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
/** @var \mysqli|NULL */
/** @var mysqli Connection resource */
private $connection;
/** @var \mysqli_result|NULL */
/** @var mysqli_result Resultset resource */
private $resultSet;
/** @var bool */
@@ -51,12 +51,12 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysqli')) {
throw new Dibi\NotSupportedException("PHP extension 'mysqli' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded.");
}
}
@@ -64,9 +64,9 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
mysqli_report(MYSQLI_REPORT_OFF);
if (isset($config['resource'])) {
@@ -74,14 +74,14 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
} else {
// default values
$config += [
$config += array(
'charset' => 'utf8',
'timezone' => date('P'),
'username' => ini_get('mysqli.default_user'),
'password' => ini_get('mysqli.default_pw'),
'socket' => (string) ini_get('mysqli.default_socket'),
'socket' => ini_get('mysqli.default_socket'),
'port' => NULL,
];
);
if (!isset($config['host'])) {
$host = ini_get('mysqli.default_host');
if ($host) {
@@ -93,8 +93,8 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
}
}
$foo = &$config['flags'];
$foo = &$config['database'];
$foo = & $config['flags'];
$foo = & $config['database'];
$this->connection = mysqli_init();
if (isset($config['options'])) {
@@ -110,12 +110,17 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
@mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @
if ($errno = mysqli_connect_errno()) {
throw new Dibi\DriverException(mysqli_connect_error(), $errno);
throw new DibiDriverException(mysqli_connect_error(), $errno);
}
}
if (isset($config['charset'])) {
if (!@mysqli_set_charset($this->connection, $config['charset'])) {
$ok = FALSE;
if (version_compare(PHP_VERSION , '5.1.5', '>=')) {
// affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.0.5, fixed in PHP 5.1.5)
$ok = @mysqli_set_charset($this->connection, $config['charset']); // intentionally @
}
if (!$ok) {
$this->query("SET NAMES '$config[charset]'");
}
}
@@ -138,47 +143,26 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function disconnect()
{
@mysqli_close($this->connection); // @ - connection can be already disconnected
mysqli_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if ($code = mysqli_errno($this->connection)) {
throw self::createException(mysqli_error($this->connection), $code, $sql);
if (mysqli_errno($this->connection)) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
} elseif (is_object($res)) {
return $this->createResultDriver($res);
}
return NULL;
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if (in_array($code, [1216, 1217, 1451, 1452, 1701], TRUE)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1062, 1557, 1569, 1586], TRUE)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], TRUE)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
}
}
@@ -188,10 +172,10 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getInfo()
{
$res = [];
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new Dibi\PcreException;
throw new DibiPcreException;
}
foreach ($matches as $m) {
@@ -225,7 +209,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -237,7 +221,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -249,7 +233,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -259,7 +243,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection resource.
* @return \mysqli
* @return mysqli
*/
public function getResource()
{
@@ -269,19 +253,20 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new MySqlReflector($this);
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @return Dibi\ResultDriver
* @param mysqli_result
* @return IDibiResultDriver
*/
public function createResultDriver(\mysqli_result $resource)
public function createResultDriver(mysqli_result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -294,68 +279,35 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
}
switch ($type) {
case dibi::TEXT:
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
case dibi::BINARY:
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
}
case dibi::IDENTIFIER:
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '`' . str_replace('`', '``', $value) . '`';
}
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
@@ -374,39 +326,30 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
if ($limit >= 0 || $offset > 0) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -431,7 +374,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function getRowCount()
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
return mysqli_num_rows($this->resultSet);
}
@@ -450,14 +393,14 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/
public function seek($row)
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
return mysqli_data_seek($this->resultSet, $row);
}
@@ -481,10 +424,9 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
static $types;
if ($types === NULL) {
if (empty($types)) {
$consts = get_defined_constants(TRUE);
$types = [];
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) {
foreach ($consts['mysqli'] as $key => $value) {
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
$types[$value] = substr($key, 12);
}
@@ -493,17 +435,16 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
}
$count = mysqli_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => $row['name'],
'table' => $row['orgtable'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => isset($types[$row['type']]) ? $types[$row['type']] : $row['type'],
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : NULL,
'nativetype' => $types[$row['type']],
'vendor' => $row,
];
);
}
return $columns;
}
@@ -511,12 +452,12 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the result set resource.
* @return \mysqli_result|NULL
* @return mysqli_result
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return $this->resultSet;
return $this->resultSet === NULL || $this->resultSet->type === NULL ? NULL : $this->resultSet;
}
}

View File

@@ -2,13 +2,9 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver interacting with databases via ODBC connections.
@@ -19,16 +15,17 @@ use Dibi;
* - password (or pass)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
use Dibi\Strict;
/** @var resource|NULL */
/** @var resource Connection resource */
private $connection;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
@@ -42,12 +39,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('odbc')) {
throw new Dibi\NotSupportedException("PHP extension 'odbc' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'odbc' is not loaded.");
}
}
@@ -55,19 +52,19 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$config += [
$config += array(
'username' => ini_get('odbc.default_user'),
'password' => ini_get('odbc.default_pw'),
'dsn' => ini_get('odbc.default_db'),
];
);
if (empty($config['persistent'])) {
$this->connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @
@@ -77,7 +74,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg() . ' ' . odbc_error());
throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error());
}
}
@@ -88,15 +85,15 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function disconnect()
{
@odbc_close($this->connection); // @ - connection can be already disconnected
odbc_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -104,13 +101,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = @odbc_exec($this->connection, $sql); // intentionally @
if ($res === FALSE) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res);
return $this->createResultDriver($res);
}
return NULL;
}
@@ -130,7 +126,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getInsertId($sequence)
{
throw new Dibi\NotSupportedException('ODBC does not support autoincrementing.');
throw new DibiNotSupportedException('ODBC does not support autoincrementing.');
}
@@ -138,12 +134,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
if (!odbc_autocommit($this->connection, FALSE)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
}
@@ -152,12 +148,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
if (!odbc_commit($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
@@ -167,12 +163,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
if (!odbc_rollback($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
@@ -190,7 +186,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection resource.
* @return resource|NULL
* @return mixed
*/
public function getResource()
{
@@ -200,7 +196,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -211,7 +207,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -226,68 +222,33 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
return "'" . str_replace("'", "''", $value) . "'";
}
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("#m/d/Y#") : date("#m/d/Y#", $value);
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("#m/d/Y H:i:s#") : date("#m/d/Y H:i:s#", $value);
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return $value->format("#m/d/Y#");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("#m/d/Y H:i:s#");
}
@@ -299,47 +260,40 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
}
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
}
}
@@ -383,10 +337,8 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
return FALSE;
}
$count = odbc_num_fields($set);
$cols = [];
for ($i = 1; $i <= $count; $i++) {
$cols[] = odbc_result($set, $i);
}
$cols = array();
for ($i = 1; $i <= $count; $i++) $cols[] = odbc_result($set, $i);
return $cols;
}
}
@@ -394,8 +346,8 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
@@ -422,14 +374,14 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns()
{
$count = odbc_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 1; $i <= $count; $i++) {
$columns[] = [
'name' => odbc_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => odbc_field_name($this->resultSet, $i),
'nativetype' => odbc_field_type($this->resultSet, $i),
];
$columns[] = array(
'name' => odbc_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => odbc_field_name($this->resultSet, $i),
'nativetype'=> odbc_field_type($this->resultSet, $i),
);
}
return $columns;
}
@@ -437,7 +389,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the result set resource.
* @return resource|NULL
* @return mixed
*/
public function getResultResource()
{
@@ -446,7 +398,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
/********************* Dibi\Reflector ****************d*g**/
/********************* IDibiReflector ****************d*g**/
/**
@@ -456,13 +408,13 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getTables()
{
$res = odbc_tables($this->connection);
$tables = [];
$tables = array();
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$tables[] = [
$tables[] = array(
'name' => $row['TABLE_NAME'],
'view' => $row['TABLE_TYPE'] === 'VIEW',
];
);
}
}
odbc_free_result($res);
@@ -478,17 +430,17 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getColumns($table)
{
$res = odbc_columns($this->connection);
$columns = [];
$columns = array();
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_NAME'] === $table) {
$columns[] = [
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $row['TYPE_NAME'],
'size' => $row['COLUMN_SIZE'],
'nullable' => (bool) $row['NULLABLE'],
'default' => $row['COLUMN_DEF'],
];
);
}
}
odbc_free_result($res);
@@ -503,7 +455,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getIndexes($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -514,7 +466,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
}

View File

@@ -2,13 +2,9 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Oracle database.
@@ -18,21 +14,21 @@ use Dibi;
* - username (or user)
* - password (or pass)
* - charset => character encoding to set
* - schema => alters session schema
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - resource (resource) => existing connection resource
* - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
use Dibi\Strict;
/** @var resource|NULL */
/** @var resource Connection resource */
private $connection;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
@@ -44,17 +40,14 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var int|FALSE Number of affected rows */
private $affectedRows = FALSE;
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('oci8')) {
throw new Dibi\NotSupportedException("PHP extension 'oci8' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'oci8' is not loaded.");
}
}
@@ -62,11 +55,11 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
$foo = &$config['charset'];
$foo = & $config['charset'];
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
@@ -80,11 +73,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
if (!$this->connection) {
$err = oci_error();
throw new Dibi\DriverException($err['message'], $err['code']);
}
if (isset($config['schema'])) {
$this->query('ALTER SESSION SET CURRENT_SCHEMA=' . $config['schema']);
throw new DibiDriverException($err['message'], $err['code']);
}
}
@@ -95,54 +84,31 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function disconnect()
{
@oci_close($this->connection); // @ - connection can be already disconnected
oci_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$this->affectedRows = FALSE;
$res = oci_parse($this->connection, $sql);
if ($res) {
@oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($res);
if ($err) {
throw self::createException($err['message'], $err['code'], $sql);
throw new DibiDriverException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) {
$this->affectedRows = oci_num_rows($res);
return $this->createResultDriver($res);
}
} else {
$err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code'], $sql);
}
return NULL;
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if (in_array($code, [1, 2299, 38911], TRUE)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1400], TRUE)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [2266, 2291, 2292], TRUE)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
throw new DibiDriverException($err['message'], $err['code'], $sql);
}
}
@@ -153,7 +119,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getAffectedRows()
{
return $this->affectedRows;
throw new DibiNotImplementedException;
}
@@ -183,13 +149,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
if (!oci_commit($this->connection)) {
$err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']);
throw new DibiDriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
@@ -199,13 +165,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
if (!oci_rollback($this->connection)) {
$err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']);
throw new DibiDriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
@@ -213,7 +179,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection resource.
* @return resource|NULL
* @return mixed
*/
public function getResource()
{
@@ -223,7 +189,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -234,7 +200,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -249,69 +215,34 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
}
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
case dibi::IDENTIFIER:
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"';
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
}
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"';
}
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return $value->format($this->fmtDate);
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
@@ -331,42 +262,33 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($offset) {
if ($offset > 0) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit !== NULL) {
} elseif ($limit >= 0) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
}
@@ -391,7 +313,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -408,12 +330,12 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -435,15 +357,14 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns()
{
$count = oci_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 1; $i <= $count; $i++) {
$type = oci_field_type($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => oci_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => oci_field_name($this->resultSet, $i),
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
];
'nativetype'=> oci_field_type($this->resultSet, $i),
);
}
return $columns;
}
@@ -451,7 +372,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the result set resource.
* @return resource|NULL
* @return mixed
*/
public function getResultResource()
{
@@ -460,7 +381,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
/********************* Dibi\Reflector ****************d*g**/
/********************* IDibiReflector ****************d*g**/
/**
@@ -470,13 +391,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getTables()
{
$res = $this->query('SELECT * FROM cat');
$tables = [];
$tables = array();
while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'VIEW',
];
);
}
}
return $tables;
@@ -490,20 +411,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getColumns($table)
{
$res = $this->query('SELECT * FROM "ALL_TAB_COLUMNS" WHERE "TABLE_NAME" = ' . $this->escapeText($table));
$columns = [];
while ($row = $res->fetch(TRUE)) {
$columns[] = [
'table' => $row['TABLE_NAME'],
'name' => $row['COLUMN_NAME'],
'nativetype' => $row['DATA_TYPE'],
'size' => isset($row['DATA_LENGTH']) ? $row['DATA_LENGTH'] : NULL,
'nullable' => $row['NULLABLE'] === 'Y',
'default' => $row['DATA_DEFAULT'],
'vendor' => $row,
];
}
return $columns;
throw new DibiNotImplementedException;
}
@@ -514,7 +422,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getIndexes($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -525,7 +433,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
}

View File

@@ -0,0 +1,462 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
/**
* The dibi driver for PDO.
*
* Driver options:
* - dsn => driver specific DSN
* - username (or user)
* - password (or pass)
* - options (array) => driver specific options {@see PDO::__construct}
* - resource (PDO) => existing connection
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var PDO Connection resource */
private $connection;
/** @var PDOStatement Resultset resource */
private $resultSet;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $driverName;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pdo')) {
throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
*/
public function connect(array & $config)
{
$foo = & $config['dsn'];
$foo = & $config['options'];
DibiConnection::alias($config, 'resource', 'pdo');
if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource'];
} else try {
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
} catch (PDOException $e) {
throw new DibiDriverException($e->getMessage(), $e->getCode());
}
if (!$this->connection) {
throw new DibiDriverException('Connecting error.');
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection = NULL;
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
// must detect if SQL returns result set or num of affected rows
$cmd = strtoupper(substr(ltrim($sql), 0, 6));
static $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
$this->affectedRows = FALSE;
if (isset($list[$cmd])) {
$this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows === FALSE) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
}
} else {
$res = $this->connection->query($sql);
if ($res === FALSE) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
} else {
return $this->createResultDriver($res);
}
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertId();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
if (!$this->connection->commit()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Returns the connection resource.
* @return PDO
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
switch ($this->driverName) {
case 'mysql':
return new DibiMySqlReflector($this);
case 'sqlite':
case 'sqlite2':
return new DibiSqliteReflector($this);
default:
throw new DibiNotSupportedException;
}
}
/**
* Result set driver factory.
* @param PDOStatement
* @return IDibiResultDriver
*/
public function createResultDriver(PDOStatement $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
{
switch ($type) {
case dibi::TEXT:
return $this->connection->quote($value, PDO::PARAM_STR);
case dibi::BINARY:
return $this->connection->quote($value, PDO::PARAM_LOB);
case dibi::IDENTIFIER:
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'pgsql':
return '"' . str_replace('"', '""', $value) . '"';
case 'sqlite':
case 'sqlite2':
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'oci': // TODO: not tested
case 'mssql':
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case 'sqlsrv':
return '[' . str_replace(']', ']]', $value) . ']';
default:
return $value;
}
case dibi::BOOL:
return $this->connection->quote($value, PDO::PARAM_BOOL);
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
throw new DibiNotImplementedException;
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescape($value, $type)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 && $offset < 1) {
return;
}
switch ($this->driverName) {
case 'mysql':
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
break;
case 'pgsql':
if ($limit >= 0) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset > 0) {
$sql .= ' OFFSET ' . (int) $offset;
}
break;
case 'sqlite':
case 'sqlite2':
$sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
break;
case 'oci':
if ($offset > 0) {
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit >= 0) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
break;
case 'odbc':
case 'mssql':
case 'sqlsrv':
if ($offset < 1) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
break;
}
// intentionally break omitted
default:
throw new DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
}
}
/********************* result set ****************d*g**/
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return $this->resultSet->rowCount();
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
}
/**
* 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
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
* @throws DibiException
*/
public function getResultColumns()
{
$count = $this->resultSet->columnCount();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) {
throw new DibiNotSupportedException('Driver does not support meta data.');
}
// PHP < 5.2.3 compatibility
// @see: http://php.net/manual/en/pdostatement.getcolumnmeta.php#pdostatement.getcolumnmeta.changelog
$row = $row + array(
'table' => NULL,
'native_type' => 'VAR_STRING',
);
$columns[] = array(
'name' => $row['name'],
'table' => $row['table'],
'nativetype' => $row['native_type'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'vendor' => $row,
);
}
return $columns;
}
/**
* Returns the result set resource.
* @return PDOStatement
*/
public function getResultResource()
{
return $this->resultSet;
}
}

View File

@@ -2,13 +2,9 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for PostgreSQL database.
@@ -20,16 +16,17 @@ use Dibi;
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
use Dibi\Strict;
/** @var resource|NULL */
/** @var resource Connection resource */
private $connection;
/** @var resource|NULL */
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
@@ -38,14 +35,17 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var bool Escape method */
private $escMethod = FALSE;
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pgsql')) {
throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'pgsql' is not loaded.");
}
}
@@ -53,55 +53,58 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
$error = NULL;
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$config += [
$config += array(
'charset' => 'utf8',
];
);
if (isset($config['string'])) {
$string = $config['string'];
} else {
$string = '';
Dibi\Helpers::alias($config, 'user', 'username');
Dibi\Helpers::alias($config, 'dbname', 'database');
foreach (['host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'] as $key) {
DibiConnection::alias($config, 'user', 'username');
DibiConnection::alias($config, 'dbname', 'database');
foreach (array('host','hostaddr','port','dbname','user','password','connect_timeout','options','sslmode','service') as $key) {
if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' ';
}
}
}
set_error_handler(function($severity, $message) use (&$error) {
$error = $message;
});
DibiDriverException::tryError();
if (empty($config['persistent'])) {
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW);
}
restore_error_handler();
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException($error ?: 'Connecting error.');
throw new DibiDriverException('Connecting error.');
}
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE);
if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) {
throw self::createException(pg_last_error($this->connection));
if (isset($config['charset'])) {
DibiDriverException::tryError();
pg_set_client_encoding($this->connection, $config['charset']);
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
}
if (isset($config['schema'])) {
$this->query('SET search_path TO "' . implode('", "', (array) $config['schema']) . '"');
$this->query('SET search_path TO "' . $config['schema'] . '"');
}
$this->escMethod = version_compare(PHP_VERSION , '5.2.0', '>=');
}
@@ -111,25 +114,15 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function disconnect()
{
@pg_close($this->connection); // @ - connection can be already disconnected
}
/**
* Pings database.
* @return bool
*/
public function ping()
{
return pg_ping($this->connection);
pg_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -137,7 +130,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = @pg_query($this->connection, $sql); // intentionally @
if ($res === FALSE) {
throw self::createException(pg_last_error($this->connection), NULL, $sql);
throw new DibiDriverException(pg_last_error($this->connection), 0, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res);
@@ -145,35 +138,6 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
return $this->createResultDriver($res);
}
}
return NULL;
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code = NULL, $sql = NULL)
{
if ($code === NULL && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) {
$code = $m[1];
$message = substr($message, strlen($m[0]));
}
if ($code === '0A000' && strpos($message, 'truncate') !== FALSE) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23502') {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif ($code === '23503') {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23505') {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
}
}
@@ -195,7 +159,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
if ($sequence === NULL) {
// PostgreSQL 8.1 is needed
$res = $this->query('SELECT LASTVAL()');
$res = $this->query("SELECT LASTVAL()");
} else {
$res = $this->query("SELECT CURRVAL('$sequence')");
}
@@ -213,7 +177,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -225,7 +189,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -237,7 +201,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -251,13 +215,13 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function inTransaction()
{
return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], TRUE);
return !in_array(pg_transaction_status($this->connection), array(PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE), TRUE);
}
/**
* Returns the connection resource.
* @return resource|NULL
* @return mixed
*/
public function getResource()
{
@@ -267,7 +231,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -278,7 +242,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -293,75 +257,50 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
switch ($type) {
case dibi::TEXT:
if ($this->escMethod) {
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . pg_escape_string($this->connection, $value) . "'";
} else {
return "'" . pg_escape_string($value) . "'";
}
case dibi::BINARY:
if ($this->escMethod) {
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
} else {
return "'" . pg_escape_bytea($value) . "'";
}
case dibi::IDENTIFIER:
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
case dibi::BOOL:
return $value ? 'TRUE' : 'FALSE';
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return "'" . pg_escape_string($this->connection, $value) . "'";
}
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
}
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? 'TRUE' : 'FALSE';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
@@ -373,48 +312,44 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function escapeLike($value, $pos)
{
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
$value = pg_escape_string($this->connection, $value);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
if ($this->escMethod) {
$value = pg_escape_string($this->connection, $value);
} else {
$value = pg_escape_string($value);
}
$value = strtr($value, array( '%' => '\\\\%', '_' => '\\\\_'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return pg_unescape_bytea($value);
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return pg_unescape_bytea($value);
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
if ($limit !== NULL) {
if ($limit >= 0) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset) {
if ($offset > 0) {
$sql .= ' OFFSET ' . (int) $offset;
}
}
@@ -456,8 +391,8 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
@@ -482,14 +417,15 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getResultColumns()
{
$hasTable = version_compare(PHP_VERSION , '5.2.0', '>=');
$count = pg_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = [
'name' => pg_field_name($this->resultSet, $i),
'table' => pg_field_table($this->resultSet, $i),
'nativetype' => pg_field_type($this->resultSet, $i),
];
$row = array(
'name' => pg_field_name($this->resultSet, $i),
'table' => $hasTable ? pg_field_table($this->resultSet, $i) : NULL,
'nativetype'=> pg_field_type($this->resultSet, $i),
);
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
$columns[] = $row;
}
@@ -499,7 +435,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the result set resource.
* @return resource|NULL
* @return mixed
*/
public function getResultResource()
{
@@ -508,7 +444,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
/********************* Dibi\Reflector ****************d*g**/
/********************* IDibiReflector ****************d*g**/
/**
@@ -517,12 +453,12 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getTables()
{
$version = pg_parameter_status($this->getResource(), 'server_version');
$version = pg_parameter_status($this->resource, 'server_version');
if ($version < 7.4) {
throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.');
throw new DibiDriverException('Reflection requires PostgreSQL 7.4 and newer.');
}
$query = "
$res = $this->query("
SELECT
table_name AS name,
CASE table_type
@@ -532,22 +468,10 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM
information_schema.tables
WHERE
table_schema = ANY (current_schemas(false))";
if ($version >= 9.3) {
$query .= '
UNION ALL
SELECT
matviewname, 1
FROM
pg_matviews
WHERE
schemaname = ANY (current_schemas(false))';
}
$res = $this->query($query);
table_schema = current_schema()
");
$tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : [];
return $tables ? $tables : array();
}
@@ -558,61 +482,34 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getColumns($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$_table = $this->escape($table, dibi::TEXT);
$res = $this->query("
SELECT indkey
FROM pg_class
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary
WHERE pg_class.oid = $_table::regclass
WHERE pg_class.relname = $_table
");
$primary = (int) pg_fetch_object($res->resultSet)->indkey;
$res = $this->query("
SELECT *
FROM information_schema.columns c
JOIN pg_class ON pg_class.relname = c.table_name
JOIN pg_namespace nsp ON nsp.oid = pg_class.relnamespace AND nsp.nspname = c.table_schema
WHERE pg_class.oid = $_table::regclass
ORDER BY c.ordinal_position
FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema()
ORDER BY ordinal_position
");
if (!$res->getRowCount()) {
$res = $this->query("
SELECT
a.attname AS column_name,
pg_type.typname AS udt_name,
a.attlen AS numeric_precision,
a.atttypmod-4 AS character_maximum_length,
NOT a.attnotnull AS is_nullable,
a.attnum AS ordinal_position,
adef.adsrc AS column_default
FROM
pg_attribute a
JOIN pg_type ON a.atttypid = pg_type.oid
JOIN pg_class cls ON a.attrelid = cls.oid
LEFT JOIN pg_attrdef adef ON adef.adnum = a.attnum AND adef.adrelid = a.attrelid
WHERE
cls.relkind IN ('r', 'v', 'mv')
AND a.attrelid = $_table::regclass
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY ordinal_position
");
}
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$columns[] = [
$columns[] = array(
'name' => $row['column_name'],
'table' => $table,
'nativetype' => strtoupper($row['udt_name']),
'size' => $size > 0 ? $size : NULL,
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't',
'size' => $size ? $size : NULL,
'nullable' => $row['is_nullable'] === 'YES',
'default' => $row['column_default'],
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
'vendor' => $row,
];
);
}
return $columns;
}
@@ -625,22 +522,15 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getIndexes($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$_table = $this->escape($table, dibi::TEXT);
$res = $this->query("
SELECT
a.attnum AS ordinal_position,
a.attname AS column_name
FROM
pg_attribute a
JOIN pg_class cls ON a.attrelid = cls.oid
WHERE
a.attrelid = $_table::regclass
AND a.attnum > 0
AND NOT a.attisdropped
SELECT ordinal_position, column_name
FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema()
ORDER BY ordinal_position
");
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name'];
}
@@ -650,10 +540,10 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM pg_class
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid
INNER JOIN pg_class as pg_class2 on pg_class2.oid = pg_index.indexrelid
WHERE pg_class.oid = $_table::regclass
WHERE pg_class.relname = $_table
");
$indexes = [];
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
@@ -673,7 +563,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getForeignKeys($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$_table = $this->escape($table, dibi::TEXT);
$res = $this->query("
SELECT
@@ -714,17 +604,17 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
c.conrelid = $_table::regclass
");
$fKeys = $references = [];
$fKeys = $references = array();
while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = [
$fKeys[$row['name']] = array(
'name' => $row['name'],
'table' => $row['table'],
'local' => [],
'foreign' => [],
'local' => array(),
'foreign' => array(),
'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'],
];
);
$l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}'));

View File

@@ -2,13 +2,11 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use SQLite3;
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
/**
@@ -21,16 +19,17 @@ use SQLite3;
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
/** @var SQLite3|NULL */
/** @var SQLite3 Connection resource */
private $connection;
/** @var \SQLite3Result|NULL */
/** @var SQLite3Result Resultset resource */
private $resultSet;
/** @var bool */
@@ -44,12 +43,12 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite3')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlite3' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'sqlite3' is not loaded.");
}
}
@@ -57,22 +56,21 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array &$config)
public function connect(array & $config)
{
Dibi\Helpers::alias($config, 'database', 'file');
DibiConnection::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
if (isset($config['resource']) && $config['resource'] instanceof SQLite3) {
$this->connection = $config['resource'];
} else {
try {
$this->connection = new SQLite3($config['database']);
} catch (\Exception $e) {
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
} else try {
$this->connection = new SQLite3($config['database']);
} catch (Exception $e) {
throw new DibiDriverException($e->getMessage(), $e->getCode());
}
$this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
@@ -84,7 +82,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
$version = SQLite3::version();
if ($version['versionNumber'] >= '3006019') {
$this->query('PRAGMA foreign_keys = ON');
$this->query("PRAGMA foreign_keys = ON");
}
}
@@ -102,8 +100,8 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -112,43 +110,12 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
}
$res = @$this->connection->query($sql); // intentionally @
if ($code = $this->connection->lastErrorCode()) {
throw self::createException($this->connection->lastErrorMsg(), $code, $sql);
if ($this->connection->lastErrorCode()) {
throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
} elseif ($res instanceof \SQLite3Result) {
} elseif ($res instanceof SQLite3Result) {
return $this->createResultDriver($res);
}
return NULL;
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if ($code !== 19) {
return new Dibi\DriverException($message, $code, $sql);
} elseif (strpos($message, 'must be unique') !== FALSE
|| strpos($message, 'is not unique') !== FALSE
|| strpos($message, 'UNIQUE constraint failed') !== FALSE
) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'may not be NULL') !== FALSE
|| strpos($message, 'NOT NULL constraint failed') !== FALSE
) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'foreign key constraint failed') !== FALSE
|| strpos($message, 'FOREIGN KEY constraint failed') !== FALSE
) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\ConstraintViolationException($message, $code, $sql);
}
}
@@ -176,7 +143,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -188,7 +155,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -200,7 +167,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -210,7 +177,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection resource.
* @return SQLite3
* @return mixed
*/
public function getResource()
{
@@ -220,20 +187,20 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new SqliteReflector($this);
return new DibiSqliteReflector($this);
}
/**
* Result set driver factory.
* @param \SQLite3Result
* @return Dibi\ResultDriver
* @param SQLite3Result
* @return IDibiResultDriver
*/
public function createResultDriver(\SQLite3Result $resource)
public function createResultDriver(SQLite3Result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -246,68 +213,35 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escapeText($value)
public function escape($value, $type)
{
return "'" . $this->connection->escapeString($value) . "'";
}
switch ($type) {
case dibi::TEXT:
return "'" . $this->connection->escapeString($value) . "'";
case dibi::BINARY:
return "X'" . bin2hex((string) $value) . "'";
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "X'" . bin2hex((string) $value) . "'";
}
case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
return $value ? 1 : 0;
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '[' . strtr($value, '[]', ' ') . ']';
}
case dibi::DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return $value->format($this->fmtDate);
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
@@ -326,38 +260,28 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
if ($limit >= 0 || $offset > 0) {
$sql .= ' LIMIT ' . (int) $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -378,11 +302,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the number of rows in a result set.
* @return int
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -396,12 +320,12 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = [];
$tmp = array();
foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(['[', ']'], '', $k)] = $v;
$tmp[str_replace(array('[', ']'), '', $k)] = $v;
}
return $tmp;
}
@@ -411,13 +335,13 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\NotSupportedException
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiNotSupportedException
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -439,15 +363,15 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
$count = $this->resultSet->numColumns();
$columns = [];
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null'];
$columns = array();
static $types = array(SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null');
for ($i = 0; $i < $count; $i++) {
$columns[] = [
'name' => $this->resultSet->columnName($i),
$columns[] = array(
'name' => $this->resultSet->columnName($i),
'table' => NULL,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)],
];
);
}
return $columns;
}
@@ -455,7 +379,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the result set resource.
* @return \SQLite3Result|NULL
* @return mixed
*/
public function getResultResource()
{
@@ -474,7 +398,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @param int num of arguments
* @return void
*/
public function registerFunction($name, callable $callback, $numArgs = -1)
public function registerFunction($name, $callback, $numArgs = -1)
{
$this->connection->createFunction($name, $callback, $numArgs);
}
@@ -488,7 +412,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @param int num of arguments
* @return void
*/
public function registerAggregateFunction($name, callable $rowCallback, callable $agrCallback, $numArgs = -1)
public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}

View File

@@ -0,0 +1,418 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
/**
* The dibi driver for SQLite database.
*
* Driver options:
* - database (or file) => the filename of the SQLite database
* - persistent (bool) => try to find a persistent link?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool Is buffered (seekable and countable)? */
private $buffered;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var string character encoding */
private $dbcharset, $charset;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite')) {
throw new DibiNotSupportedException("PHP extension 'sqlite' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
*/
public function connect(array & $config)
{
DibiConnection::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
$errorMsg = '';
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} elseif (empty($config['persistent'])) {
$this->connection = @sqlite_open($config['database'], 0666, $errorMsg); // intentionally @
} else {
$this->connection = @sqlite_popen($config['database'], 0666, $errorMsg); // intentionally @
}
if (!$this->connection) {
throw new DibiDriverException($errorMsg);
}
$this->buffered = empty($config['unbuffered']);
$this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
$this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
if (strcasecmp($this->dbcharset, $this->charset) === 0) {
$this->dbcharset = $this->charset = NULL;
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
sqlite_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
if ($this->dbcharset !== NULL) {
$sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql);
}
DibiDriverException::tryError();
if ($this->buffered) {
$res = sqlite_query($this->connection, $sql);
} else {
$res = sqlite_unbuffered_query($this->connection, $sql);
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, sqlite_last_error($this->connection), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return sqlite_changes($this->connection);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return sqlite_last_insert_rowid($this->connection);
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
$this->query('BEGIN');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
$this->query('COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
$this->query('ROLLBACK');
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiSqliteReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . sqlite_escape_string($value) . "'";
case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
throw new DibiNotSupportedException;
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescape($value, $type)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit >= 0 || $offset > 0) {
$sql .= ' LIMIT ' . (int) $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
/********************* result set ****************d*g**/
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
return sqlite_num_rows($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
$row = sqlite_fetch_array($this->resultSet, $assoc ? SQLITE_ASSOC : SQLITE_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = array();
foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(array('[', ']'), '', $k)] = $v;
}
return $tmp;
}
return $row;
}
/**
* 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
* @throws DibiException
*/
public function seek($row)
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
return sqlite_seek($this->resultSet, $row);
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = sqlite_num_fields($this->resultSet);
$columns = array();
for ($i = 0; $i < $count; $i++) {
$name = str_replace(array('[', ']'), '', sqlite_field_name($this->resultSet, $i));
$pair = explode('.', $name);
$columns[] = array(
'name' => isset($pair[1]) ? $pair[1] : $pair[0],
'table' => isset($pair[1]) ? $pair[0] : NULL,
'fullname' => $name,
'nativetype' => NULL,
);
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* user defined functions ****************d*g**/
/**
* Registers an user defined function for use in SQL statements.
* @param string function name
* @param mixed callback
* @param int num of arguments
* @return void
*/
public function registerFunction($name, $callback, $numArgs = -1)
{
sqlite_create_function($this->connection, $name, $callback, $numArgs);
}
/**
* Registers an aggregating user defined function for use in SQL statements.
* @param string function name
* @param mixed callback called for each row of the result set
* @param mixed callback called to aggregate the "stepped" data from each row
* @param int num of arguments
* @return void
*/
public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
{
sqlite_create_aggregate($this->connection, $name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -2,27 +2,24 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for SQLite database.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class SqliteReflector implements Dibi\Reflector
class DibiSqliteReflector extends DibiObject implements IDibiReflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
/** @var IDibiDriver */
private $driver;
public function __construct(Dibi\Driver $driver)
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
@@ -40,7 +37,7 @@ class SqliteReflector implements Dibi\Reflector
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$tables = [];
$tables = array();
while ($row = $res->fetch(TRUE)) {
$tables[] = $row;
}
@@ -55,12 +52,19 @@ class SqliteReflector implements Dibi\Reflector
*/
public function getColumns($table)
{
$res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})");
$columns = [];
$meta = $this->driver->query("
SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
UNION ALL
SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
")->fetch(TRUE);
$res = $this->driver->query("PRAGMA table_info({$this->driver->escape($table, dibi::IDENTIFIER)})");
$columns = array();
while ($row = $res->fetch(TRUE)) {
$column = $row['name'];
$pattern = "/(\"$column\"|\[$column\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT/Ui";
$type = explode('(', $row['type']);
$columns[] = [
$columns[] = array(
'name' => $column,
'table' => $table,
'fullname' => "$table.$column",
@@ -68,9 +72,9 @@ class SqliteReflector implements Dibi\Reflector
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['notnull'] == '0',
'default' => $row['dflt_value'],
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
'autoincrement' => (bool) preg_match($pattern, $meta['sql']),
'vendor' => $row,
];
);
}
return $columns;
}
@@ -83,15 +87,15 @@ class SqliteReflector implements Dibi\Reflector
*/
public function getIndexes($table)
{
$res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})");
$indexes = [];
$res = $this->driver->query("PRAGMA index_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique'];
}
foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})");
$res = $this->driver->query("PRAGMA index_info({$this->driver->escape($index, dibi::IDENTIFIER)})");
while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
}
@@ -112,12 +116,12 @@ class SqliteReflector implements Dibi\Reflector
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) {
if ($column['vendor']['pk']) {
$indexes[] = [
$indexes[] = array(
'name' => 'ROWID',
'unique' => TRUE,
'primary' => TRUE,
'columns' => [$column['name']],
];
'columns' => array($column['name']),
);
break;
}
}
@@ -134,8 +138,11 @@ class SqliteReflector implements Dibi\Reflector
*/
public function getForeignKeys($table)
{
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})");
$keys = [];
if (!($this->driver instanceof DibiSqlite3Driver)) {
// throw new DibiNotSupportedException; // @see http://www.sqlite.org/foreignkeys.html
}
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$keys = array();
while ($row = $res->fetch(TRUE)) {
$keys[$row['id']]['name'] = $row['id']; // foreign key name
$keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns

View File

@@ -2,40 +2,41 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
use Traversable;
/**
* dibi connection.
*
* @author David Grudl
* @package dibi
*
* @property-read bool $connected
* @property-read mixed $config
* @property-read IDibiDriver $driver
* @property-read int $affectedRows
* @property-read int $insertId
* @property-read DibiDatabaseInfo $databaseInfo
*/
class Connection
class DibiConnection extends DibiObject
{
use Strict;
/** @var array of function (Event $event); Occurs after query is executed */
/** @var array of function(DibiEvent $event); Occurs after query is executed */
public $onEvent;
/** @var array Current connection configuration */
private $config;
/** @var Driver */
/** @var IDibiDriver */
private $driver;
/** @var Translator */
/** @var DibiTranslator */
private $translator;
/** @var bool Is connected? */
private $connected = FALSE;
/** @var HashMap Substitutes for identifiers */
/** @var DibiHashMap Substitutes for identifiers */
private $substitutes;
@@ -51,74 +52,75 @@ class Connection
* @param mixed connection parameters
* @param string connection name
* @throws Exception
* @throws DibiException
*/
public function __construct($config, $name = NULL)
{
class_exists('dibi'); // ensure class dibi is loaded
// DSN string
if (is_string($config)) {
parse_str($config, $config);
} elseif ($config instanceof Traversable) {
$tmp = [];
$tmp = array();
foreach ($config as $key => $val) {
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
}
$config = $tmp;
} elseif (!is_array($config)) {
throw new \InvalidArgumentException('Configuration must be array, string or object.');
throw new InvalidArgumentException('Configuration must be array, string or object.');
}
Helpers::alias($config, 'username', 'user');
Helpers::alias($config, 'password', 'pass');
Helpers::alias($config, 'host', 'hostname');
Helpers::alias($config, 'result|formatDate', 'resultDate');
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
self::alias($config, 'username', 'user');
self::alias($config, 'password', 'pass');
self::alias($config, 'host', 'hostname');
self::alias($config, 'result|formatDate', 'resultDate');
self::alias($config, 'result|formatDateTime', 'resultDateTime');
if (!isset($config['driver'])) {
$config['driver'] = \dibi::$defaultDriver;
$config['driver'] = dibi::$defaultDriver;
}
if ($config['driver'] instanceof Driver) {
$this->driver = $config['driver'];
$config['driver'] = get_class($this->driver);
} elseif (is_subclass_of($config['driver'], 'Dibi\Driver')) {
$this->driver = new $config['driver'];
} else {
$class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($config['driver'])));
$class = "Dibi\\Drivers\\{$class}Driver";
if (!class_exists($class)) {
throw new Exception("Unable to create instance of dibi driver '$class'.");
$class = preg_replace(array('#\W#', '#sql#'), array('_', 'Sql'), ucfirst(strtolower($config['driver'])));
$class = "Dibi{$class}Driver";
if (!class_exists($class, FALSE)) {
include_once dirname(__FILE__) . "/../drivers/$class.php";
if (!class_exists($class, FALSE)) {
throw new DibiException("Unable to create instance of dibi driver '$class'.");
}
$this->driver = new $class;
}
$config['name'] = $name;
$this->config = $config;
$this->driver = new $class;
$this->translator = new DibiTranslator($this);
// profiler
$profilerCfg = &$config['profiler'];
$profilerCfg = & $config['profiler'];
if (is_scalar($profilerCfg)) {
$profilerCfg = ['run' => (bool) $profilerCfg];
$profilerCfg = array('run' => (bool) $profilerCfg);
}
if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : Event::QUERY;
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
if (isset($profilerCfg['file'])) {
$this->onEvent[] = [new Loggers\FileLogger($profilerCfg['file'], $filter), 'logEvent'];
$this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
}
if (Loggers\FirePhpLogger::isAvailable()) {
$this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent'];
if (DibiFirePhpLogger::isAvailable()) {
$this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
}
if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('Dibi\Bridges\Nette\Panel')) {
$panel = new Bridges\Nette\Panel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
if (class_exists('DibiNettePanel', FALSE)) {
$panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this);
}
}
$this->substitutes = new HashMap(function ($expr) { return ":$expr:"; });
$this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) {
$this->substitutes->$key = $value;
@@ -148,13 +150,13 @@ class Connection
*/
final public function connect()
{
$event = $this->onEvent ? new Event($this, Event::CONNECT) : NULL;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
try {
$this->driver->connect($this->config);
$this->connected = TRUE;
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -203,17 +205,30 @@ class Connection
}
/** @deprecated */
public static function alias(&$config, $key, $alias)
/**
* Apply configuration alias or default values.
* @param array connect configuration
* @param string key
* @param string alias key
* @return void
*/
public static function alias(& $config, $key, $alias)
{
trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED);
Helpers::alias($config, $key, $alias);
$foo = & $config;
foreach (explode('|', $key) as $key) {
$foo = & $foo[$key];
}
if (!isset($foo) && isset($config[$alias])) {
$foo = $config[$alias];
unset($config[$alias]);
}
}
/**
* Returns the driver and connects to a database in lazy mode.
* @return Driver
* @return IDibiDriver
*/
final public function getDriver()
{
@@ -225,8 +240,8 @@ class Connection
/**
* Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments
* @return Result|int result set or number of affected rows
* @throws Exception
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
final public function query($args)
{
@@ -239,7 +254,7 @@ class Connection
* Generates SQL query.
* @param array|mixed one or more arguments
* @return string
* @throws Exception
* @throws DibiException
*/
final public function translate($args)
{
@@ -257,12 +272,12 @@ class Connection
{
$args = func_get_args();
try {
Helpers::dump($this->translateArgs($args));
dibi::dump($this->translateArgs($args));
return TRUE;
} catch (Exception $e) {
} catch (DibiException $e) {
if ($e->getSql()) {
Helpers::dump($e->getSql());
dibi::dump($e->getSql());
} else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
}
@@ -272,15 +287,15 @@ class Connection
/**
* Generates (translates) and returns SQL query as DataSource.
* Generates (translates) and returns SQL query as DibiDataSource.
* @param array|mixed one or more arguments
* @return DataSource
* @throws Exception
* @return DibiDataSource
* @throws DibiException
*/
final public function dataSource($args)
{
$args = func_get_args();
return new DataSource($this->translateArgs($args), $this);
return new DibiDataSource($this->translateArgs($args), $this);
}
@@ -289,33 +304,29 @@ class Connection
* @param array
* @return string
*/
protected function translateArgs($args)
private function translateArgs($args)
{
$this->connected || $this->connect();
if (!$this->translator) {
$this->translator = new Translator($this);
}
$translator = clone $this->translator;
return $translator->translate($args);
return $this->translator->translate($args);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Result|int result set or number of affected rows
* @throws Exception
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
final public function nativeQuery($sql)
{
$this->connected || $this->connect();
\dibi::$sql = $sql;
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : NULL;
dibi::$sql = $sql;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
try {
$res = $this->driver->query($sql);
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -334,14 +345,14 @@ class Connection
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int number of rows
* @throws Exception
* @throws DibiException
*/
public function getAffectedRows()
{
$this->connected || $this->connect();
$rows = $this->driver->getAffectedRows();
if (!is_int($rows) || $rows < 0) {
throw new Exception('Cannot retrieve number of affected rows.');
throw new DibiException('Cannot retrieve number of affected rows.');
}
return $rows;
}
@@ -350,7 +361,7 @@ class Connection
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws Exception
* @throws DibiException
*/
public function affectedRows()
{
@@ -362,14 +373,14 @@ class Connection
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string optional sequence name
* @return int
* @throws Exception
* @throws DibiException
*/
public function getInsertId($sequence = NULL)
{
$this->connected || $this->connect();
$id = $this->driver->getInsertId($sequence);
if ($id < 1) {
throw new Exception('Cannot retrieve last generated ID.');
throw new DibiException('Cannot retrieve last generated ID.');
}
return (int) $id;
}
@@ -379,7 +390,7 @@ class Connection
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws Exception
* @throws DibiException
*/
public function insertId($sequence = NULL)
{
@@ -395,12 +406,12 @@ class Connection
public function begin($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : NULL;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL;
try {
$this->driver->begin($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -415,12 +426,12 @@ class Connection
public function commit($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : NULL;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL;
try {
$this->driver->commit($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -435,12 +446,12 @@ class Connection
public function rollback($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : NULL;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL;
try {
$this->driver->rollback($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -449,14 +460,14 @@ class Connection
/**
* Result set factory.
* @param ResultDriver
* @return Result
* @param IDibiResultDriver
* @return DibiResult
*/
public function createResultSet(ResultDriver $resultDriver)
public function createResultSet(IDibiResultDriver $resultDriver)
{
$res = new Result($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']);
$res = new DibiResult($resultDriver);
return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
}
@@ -464,17 +475,17 @@ class Connection
/**
* @return Fluent
* @return DibiFluent
*/
public function command()
{
return new Fluent($this);
return new DibiFluent($this);
}
/**
* @param mixed column name
* @return Fluent
* @param string column name
* @return DibiFluent
*/
public function select($args)
{
@@ -486,12 +497,12 @@ class Connection
/**
* @param string table
* @param array
* @return Fluent
* @return DibiFluent
*/
public function update($table, $args)
{
if (!(is_array($args) || $args instanceof Traversable)) {
throw new \InvalidArgumentException('Arguments must be array or Traversable.');
throw new InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->update('%n', $table)->set($args);
}
@@ -500,14 +511,14 @@ class Connection
/**
* @param string table
* @param array
* @return Fluent
* @return DibiFluent
*/
public function insert($table, $args)
{
if ($args instanceof Traversable) {
$args = iterator_to_array($args);
} elseif (!is_array($args)) {
throw new \InvalidArgumentException('Arguments must be array or Traversable.');
throw new InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->insert()
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
@@ -516,7 +527,7 @@ class Connection
/**
* @param string table
* @return Fluent
* @return DibiFluent
*/
public function delete($table)
{
@@ -529,7 +540,7 @@ class Connection
/**
* Returns substitution hashmap.
* @return HashMap
* @return DibiHashMap
*/
public function getSubstitutes()
{
@@ -543,9 +554,16 @@ class Connection
*/
public function substitute($value)
{
return strpos($value, ':') === FALSE
? $value
: preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
return strpos($value, ':') === FALSE ? $value : preg_replace_callback('#:([^:\s]*):#', array($this, 'subCb'), $value);
}
/**
* Substitution callback.
*/
private function subCb($m)
{
return $this->substitutes->{$m[1]};
}
@@ -555,8 +573,8 @@ class Connection
/**
* Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments
* @return Row|FALSE
* @throws Exception
* @return DibiRow
* @throws DibiException
*/
public function fetch($args)
{
@@ -568,8 +586,8 @@ class Connection
/**
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
* @param array|mixed one or more arguments
* @return Row[]
* @throws Exception
* @return DibiRow[]
* @throws DibiException
*/
public function fetchAll($args)
{
@@ -581,8 +599,8 @@ class Connection
/**
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments
* @return mixed
* @throws Exception
* @return string
* @throws DibiException
*/
public function fetchSingle($args)
{
@@ -594,8 +612,8 @@ class Connection
/**
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments
* @return array
* @throws Exception
* @return string
* @throws DibiException
*/
public function fetchPairs($args)
{
@@ -604,37 +622,52 @@ class Connection
}
/**
* @return Literal
*/
public static function literal($value)
{
return new Literal($value);
}
/********************* misc ****************d*g**/
/**
* Import SQL dump from file.
* Import SQL dump from file - extreme fast!
* @param string filename
* @return int count of sql commands
*/
public function loadFile($file)
{
return Helpers::loadFromFile($this, $file);
$this->connected || $this->connect();
@set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @
if (!$handle) {
throw new RuntimeException("Cannot open file '$file'.");
}
$count = 0;
$sql = '';
while (!feof($handle)) {
$s = fgets($handle);
$sql .= $s;
if (substr(rtrim($s), -1) === ';') {
$this->driver->query($sql);
$sql = '';
$count++;
}
}
if (trim($sql) !== '') {
$this->driver->query($sql);
$count++;
}
fclose($handle);
return $count;
}
/**
* Gets a information about the current database.
* @return Reflection\Database
* @return DibiDatabaseInfo
*/
public function getDatabaseInfo()
{
$this->connected || $this->connect();
return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
}
@@ -643,7 +676,7 @@ class Connection
*/
public function __wakeup()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
}
@@ -652,15 +685,7 @@ class Connection
*/
public function __sleep()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
}
protected function onEvent($arg)
{
foreach ($this->onEvent ?: [] as $handler) {
call_user_func($handler, $arg);
}
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
}
}

View File

@@ -2,27 +2,30 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* Default implementation of IDataSource for dibi.
*
* @author David Grudl
* @package dibi
*
* @property-read DibiConnection $connection
* @property-read DibiResult $result
* @property-read DibiResultIterator $iterator
* @property-read int $totalCount
*/
class DataSource implements IDataSource
class DibiDataSource extends DibiObject implements IDataSource
{
use Strict;
/** @var Connection */
/** @var DibiConnection */
private $connection;
/** @var string */
private $sql;
/** @var Result */
/** @var DibiResult */
private $result;
/** @var int */
@@ -32,29 +35,29 @@ class DataSource implements IDataSource
private $totalCount;
/** @var array */
private $cols = [];
private $cols = array();
/** @var array */
private $sorting = [];
private $sorting = array();
/** @var array */
private $conds = [];
private $conds = array();
/** @var int|NULL */
/** @var int */
private $offset;
/** @var int|NULL */
/** @var int */
private $limit;
/**
* @param string SQL command or table or view name, as data source
* @param Connection connection
* @param DibiConnection connection
*/
public function __construct($sql, Connection $connection)
public function __construct($sql, DibiConnection $connection)
{
if (strpbrk($sql, " \t\r\n") === FALSE) {
$this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name
$this->sql = $connection->getDriver()->escape($sql, dibi::IDENTIFIER); // table name
} else {
$this->sql = '(' . $sql . ') t'; // SQL command
}
@@ -118,7 +121,7 @@ class DataSource implements IDataSource
/**
* Limits number of rows.
* @param int|NULL limit
* @param int limit
* @param int offset
* @return self
*/
@@ -133,7 +136,7 @@ class DataSource implements IDataSource
/**
* Returns the dibi connection.
* @return Connection
* @return DibiConnection
*/
final public function getConnection()
{
@@ -145,8 +148,8 @@ class DataSource implements IDataSource
/**
* Returns (and queries) Result.
* @return Result
* Returns (and queries) DibiResult.
* @return DibiResult
*/
public function getResult()
{
@@ -158,7 +161,7 @@ class DataSource implements IDataSource
/**
* @return ResultIterator
* @return DibiResultIterator
*/
public function getIterator()
{
@@ -168,7 +171,7 @@ class DataSource implements IDataSource
/**
* Generates, executes SQL query and fetches the single row.
* @return Row|FALSE
* @return DibiRow|FALSE array on success, FALSE if no next record
*/
public function fetch()
{
@@ -233,8 +236,8 @@ class DataSource implements IDataSource
/**
* Returns this data source wrapped in Fluent object.
* @return Fluent
* Returns this data source wrapped in DibiFluent object.
* @return DibiFluent
*/
public function toFluent()
{
@@ -243,8 +246,8 @@ class DataSource implements IDataSource
/**
* Returns this data source wrapped in DataSource object.
* @return DataSource
* Returns this data source wrapped in DibiDataSource object.
* @return DibiDataSource
*/
public function toDataSource()
{
@@ -262,11 +265,11 @@ class DataSource implements IDataSource
return $this->connection->translate('
SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
FROM %SQL', $this->sql, '
%ex', $this->conds ? ['WHERE %and', $this->conds] : NULL, '
%ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : NULL, '
%ex', $this->conds ? array('WHERE %and', $this->conds) : NULL, '
%ex', $this->sorting ? array('ORDER BY %by', $this->sorting) : NULL, '
%ofs %lmt', $this->offset, $this->limit
);
} catch (\Exception $e) {
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}

View File

@@ -0,0 +1,708 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* Reflection metadata class for a database.
*
* @author David Grudl
* @package dibi\reflection
*
* @property-read string $name
* @property-read array $tables
* @property-read array $tableNames
*/
class DibiDatabaseInfo extends DibiObject
{
/** @var IDibiReflector */
private $reflector;
/** @var string */
private $name;
/** @var array */
private $tables;
public function __construct(IDibiReflector $reflector, $name)
{
$this->reflector = $reflector;
$this->name = $name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return DibiTableInfo[]
*/
public function getTables()
{
$this->init();
return array_values($this->tables);
}
/**
* @return string[]
*/
public function getTableNames()
{
$this->init();
$res = array();
foreach ($this->tables as $table) {
$res[] = $table->getName();
}
return $res;
}
/**
* @param string
* @return DibiTableInfo
*/
public function getTable($name)
{
$this->init();
$l = strtolower($name);
if (isset($this->tables[$l])) {
return $this->tables[$l];
} else {
throw new DibiException("Database '$this->name' has no table '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasTable($name)
{
$this->init();
return isset($this->tables[strtolower($name)]);
}
/**
* @return void
*/
protected function init()
{
if ($this->tables === NULL) {
$this->tables = array();
foreach ($this->reflector->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new DibiTableInfo($this->reflector, $info);
}
}
}
}
/**
* Reflection metadata class for a database table.
*
* @author David Grudl
* @package dibi\reflection
*
* @property-read string $name
* @property-read bool $view
* @property-read array $columns
* @property-read array $columnNames
* @property-read array $foreignKeys
* @property-read array $indexes
* @property-read DibiIndexInfo $primaryKey
*/
class DibiTableInfo extends DibiObject
{
/** @var IDibiReflector */
private $reflector;
/** @var string */
private $name;
/** @var bool */
private $view;
/** @var array */
private $columns;
/** @var array */
private $foreignKeys;
/** @var array */
private $indexes;
/** @var DibiIndexInfo */
private $primaryKey;
public function __construct(IDibiReflector $reflector, array $info)
{
$this->reflector = $reflector;
$this->name = $info['name'];
$this->view = !empty($info['view']);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return bool
*/
public function isView()
{
return $this->view;
}
/**
* @return DibiColumnInfo[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @return string[]
*/
public function getColumnNames()
{
$this->initColumns();
$res = array();
foreach ($this->columns as $column) {
$res[] = $column->getName();
}
return $res;
}
/**
* @param string
* @return DibiColumnInfo
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->columns[$l])) {
return $this->columns[$l];
} else {
throw new DibiException("Table '$this->name' has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->columns[strtolower($name)]);
}
/**
* @return DibiForeignKeyInfo[]
*/
public function getForeignKeys()
{
$this->initForeignKeys();
return $this->foreignKeys;
}
/**
* @return DibiIndexInfo[]
*/
public function getIndexes()
{
$this->initIndexes();
return $this->indexes;
}
/**
* @return DibiIndexInfo
*/
public function getPrimaryKey()
{
$this->initIndexes();
return $this->primaryKey;
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = array();
foreach ($this->reflector->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new DibiColumnInfo($this->reflector, $info);
}
}
}
/**
* @return void
*/
protected function initIndexes()
{
if ($this->indexes === NULL) {
$this->initColumns();
$this->indexes = array();
foreach ($this->reflector->getIndexes($this->name) as $info) {
foreach ($info['columns'] as $key => $name) {
$info['columns'][$key] = $this->columns[strtolower($name)];
}
$this->indexes[strtolower($info['name'])] = new DibiIndexInfo($info);
if (!empty($info['primary'])) {
$this->primaryKey = $this->indexes[strtolower($info['name'])];
}
}
}
}
/**
* @return void
*/
protected function initForeignKeys()
{
throw new DibiNotImplementedException;
}
}
/**
* Reflection metadata class for a result set.
*
* @author David Grudl
* @package dibi\reflection
*
* @property-read array $columns
* @property-read array $columnNames
*/
class DibiResultInfo extends DibiObject
{
/** @var IDibiResultDriver */
private $driver;
/** @var array */
private $columns;
/** @var array */
private $names;
public function __construct(IDibiResultDriver $driver)
{
$this->driver = $driver;
}
/**
* @return DibiColumnInfo[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @param bool
* @return string[]
*/
public function getColumnNames($fullNames = FALSE)
{
$this->initColumns();
$res = array();
foreach ($this->columns as $column) {
$res[] = $fullNames ? $column->getFullName() : $column->getName();
}
return $res;
}
/**
* @param string
* @return DibiColumnInfo
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->names[$l])) {
return $this->names[$l];
} else {
throw new DibiException("Result set has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->names[strtolower($name)]);
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = array();
$reflector = $this->driver instanceof IDibiReflector ? $this->driver : NULL;
foreach ($this->driver->getResultColumns() as $info) {
$this->columns[] = $this->names[$info['name']] = new DibiColumnInfo($reflector, $info);
}
}
}
}
/**
* Reflection metadata class for a table or result set column.
*
* @author David Grudl
* @package dibi\reflection
*
* @property-read string $name
* @property-read string $fullName
* @property-read DibiTableInfo $table
* @property-read string $type
* @property-read mixed $nativeType
* @property-read int $size
* @property-read bool $unsigned
* @property-read bool $nullable
* @property-read bool $autoIncrement
* @property-read mixed $default
*/
class DibiColumnInfo extends DibiObject
{
/** @var array */
private static $types;
/** @var IDibiReflector|NULL when created by DibiResultInfo */
private $reflector;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info;
public function __construct(IDibiReflector $reflector = NULL, array $info)
{
$this->reflector = $reflector;
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return string
*/
public function getFullName()
{
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL;
}
/**
* @return bool
*/
public function hasTable()
{
return !empty($this->info['table']);
}
/**
* @return DibiTableInfo
*/
public function getTable()
{
if (empty($this->info['table']) || !$this->reflector) {
throw new DibiException("Table is unknown or not available.");
}
return new DibiTableInfo($this->reflector, array('name' => $this->info['table']));
}
/**
* @return string
*/
public function getTableName()
{
return isset($this->info['table']) ? $this->info['table'] : NULL;
}
/**
* @return string
*/
public function getType()
{
return self::getTypeCache()->{$this->info['nativetype']};
}
/**
* @return mixed
*/
public function getNativeType()
{
return $this->info['nativetype'];
}
/**
* @return int
*/
public function getSize()
{
return isset($this->info['size']) ? (int) $this->info['size'] : NULL;
}
/**
* @return bool
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/**
* @return bool
*/
public function isNullable()
{
return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : NULL;
}
/**
* @return bool
*/
public function isAutoIncrement()
{
return isset($this->info['autoincrement']) ? (bool) $this->info['autoincrement'] : NULL;
}
/**
* @return mixed
*/
public function getDefault()
{
return isset($this->info['default']) ? $this->info['default'] : NULL;
}
/**
* @param string
* @return mixed
*/
public function getVendorInfo($key)
{
return isset($this->info['vendor'][$key]) ? $this->info['vendor'][$key] : NULL;
}
/**
* Heuristic type detection.
* @param string
* @return string
* @internal
*/
public static function detectType($type)
{
static $patterns = array(
'^_' => dibi::TEXT, // PostgreSQL arrays
'BYTEA|BLOB|BIN' => dibi::BINARY,
'TEXT|CHAR|POINT|INTERVAL' => dibi::TEXT,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT' => dibi::INTEGER,
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => dibi::FLOAT,
'^TIME$' => dibi::TIME,
'TIME' => dibi::DATETIME, // DATETIME, TIMESTAMP
'DATE' => dibi::DATE,
'BOOL' => dibi::BOOL,
);
foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return $val;
}
}
return dibi::TEXT;
}
/**
* @internal
*/
public static function getTypeCache()
{
if (self::$types === NULL) {
self::$types = new DibiHashMap(array(__CLASS__, 'detectType'));
}
return self::$types;
}
}
/**
* Reflection metadata class for a foreign key.
*
* @author David Grudl
* @package dibi\reflection
* @todo
*
* @property-read string $name
* @property-read array $references
*/
class DibiForeignKeyInfo extends DibiObject
{
/** @var string */
private $name;
/** @var array of array(local, foreign, onDelete, onUpdate) */
private $references;
public function __construct($name, array $references)
{
$this->name = $name;
$this->references = $references;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getReferences()
{
return $this->references;
}
}
/**
* Reflection metadata class for a index or primary key.
*
* @author David Grudl
* @package dibi\reflection
*
* @property-read string $name
* @property-read array $columns
* @property-read bool $unique
* @property-read bool $primary
*/
class DibiIndexInfo extends DibiObject
{
/** @var array (name, columns, [unique], [primary]) */
private $info;
public function __construct(array $info)
{
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return array
*/
public function getColumns()
{
return $this->info['columns'];
}
/**
* @return bool
*/
public function isUnique()
{
return !empty($this->info['unique']);
}
/**
* @return bool
*/
public function isPrimary()
{
return !empty($this->info['primary']);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* DateTime with serialization and timestamp support for PHP 5.2.
*
* @author David Grudl
* @package dibi
*/
class DibiDateTime extends DateTime
{
public function __construct($time = 'now', DateTimeZone $timezone = NULL)
{
if (is_numeric($time)) {
$time = date('Y-m-d H:i:s', $time);
}
if ($timezone === NULL) {
parent::__construct($time);
} else {
parent::__construct($time, $timezone);
}
}
public function modifyClone($modify = '')
{
$dolly = clone($this);
return $modify ? $dolly->modify($modify) : $dolly;
}
public function modify($modify)
{
parent::modify($modify);
return $this;
}
public function __sleep()
{
$this->fix = array($this->format('Y-m-d H:i:s'), $this->getTimezone()->getName());
return array('fix');
}
public function __wakeup()
{
$this->__construct($this->fix[0], new DateTimeZone($this->fix[1]));
unset($this->fix);
}
public function getTimestamp()
{
return (int) $this->format('U');
}
public function setTimestamp($timestamp)
{
return $this->__construct(date('Y-m-d H:i:s', $timestamp), new DateTimeZone($this->getTimezone()->getName())); // getTimeZone() crashes in PHP 5.2.6
}
public function __toString()
{
return $this->format('Y-m-d H:i:s');
}
}

View File

@@ -2,19 +2,18 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* Profiler & logger event.
*
* @author David Grudl
* @package dibi
*/
class Event
class DibiEvent
{
use Strict;
/** event type */
const CONNECT = 1,
SELECT = 4,
@@ -28,7 +27,7 @@ class Event
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
ALL = 1023;
/** @var Connection */
/** @var DibiConnection */
public $connection;
/** @var int */
@@ -37,7 +36,7 @@ class Event
/** @var string */
public $sql;
/** @var Result|DriverException|NULL */
/** @var DibiResult|DibiDriverException|NULL */
public $result;
/** @var float */
@@ -50,7 +49,7 @@ class Event
public $source;
public function __construct(Connection $connection, $type, $sql = NULL)
public function __construct(DibiConnection $connection, $type, $sql = NULL)
{
$this->connection = $connection;
$this->type = $type;
@@ -58,25 +57,25 @@ class Event
$this->time = -microtime(TRUE);
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
static $types = [
static $types = array(
'SELECT' => self::SELECT, 'UPDATE' => self::UPDATE,
'INSERT' => self::INSERT, 'DELETE' => self::DELETE,
];
);
$this->type = $types[strtoupper($matches[1])];
}
$rc = new \ReflectionClass('dibi');
$rc = new ReflectionClass('dibi');
$dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR;
foreach (debug_backtrace(FALSE) as $row) {
if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) {
$this->source = [$row['file'], (int) $row['line']];
$this->source = array($row['file'], (int) $row['line']);
break;
}
}
\dibi::$elapsedTime = FALSE;
\dibi::$numOfQueries++;
\dibi::$sql = $sql;
dibi::$elapsedTime = FALSE;
dibi::$numOfQueries++;
dibi::$sql = $sql;
}
@@ -84,14 +83,14 @@ class Event
{
$this->result = $result;
try {
$this->count = $result instanceof Result ? count($result) : NULL;
} catch (Exception $e) {
$this->count = $result instanceof DibiResult ? count($result) : NULL;
} catch (DibiException $e) {
$this->count = NULL;
}
$this->time += microtime(TRUE);
\dibi::$elapsedTime = $this->time;
\dibi::$totalTime += $this->time;
dibi::$elapsedTime = $this->time;
dibi::$totalTime += $this->time;
return $this;
}

148
dibi/libs/DibiException.php Normal file
View File

@@ -0,0 +1,148 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* dibi common exception.
*
* @author David Grudl
* @package dibi
*/
class DibiException extends Exception
{
/** @var string */
private $sql;
/**
* Construct a dibi exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $sql = NULL)
{
parent::__construct($message, (int) $code);
$this->sql = $sql;
}
/**
* @return string The SQL passed to the constructor
*/
final public function getSql()
{
return $this->sql;
}
/**
* @return string string represenation of exception with SQL command
*/
public function __toString()
{
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
}
}
/**
* database server exception.
*
* @author David Grudl
* @package dibi
*/
class DibiDriverException extends DibiException
{
/********************* error catching ****************d*g**/
/** @var string */
private static $errorMsg;
/**
* Starts catching potential errors/warnings.
* @return void
*/
public static function tryError()
{
set_error_handler(array(__CLASS__, '_errorHandler'), E_ALL);
self::$errorMsg = NULL;
}
/**
* Returns catched error/warning message.
* @param string catched message
* @return bool
*/
public static function catchError(& $message)
{
restore_error_handler();
$message = self::$errorMsg;
self::$errorMsg = NULL;
return $message !== NULL;
}
/**
* Internal error handler. Do not call directly.
* @internal
*/
public static function _errorHandler($code, $message)
{
restore_error_handler();
if (ini_get('html_errors')) {
$message = strip_tags($message);
$message = html_entity_decode($message);
}
self::$errorMsg = $message;
}
}
/**
* PCRE exception.
*
* @author David Grudl
* @package dibi
*/
class DibiPcreException extends Exception {
public function __construct($message = '%msg.')
{
static $messages = array(
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
);
$code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
}
}
/**
* @package dibi
*/
class DibiNotImplementedException extends DibiException
{}
/**
* @package dibi
*/
class DibiNotSupportedException extends DibiException
{}

View File

@@ -2,21 +2,18 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Loggers;
use Dibi;
/**
* dibi file logger.
*
* @author David Grudl
* @package dibi
*/
class FileLogger
class DibiFileLogger extends DibiObject
{
use Dibi\Strict;
/** @var string Name of the file where SQL errors should be logged */
public $file;
@@ -27,7 +24,7 @@ class FileLogger
public function __construct($file, $filter = NULL)
{
$this->file = $file;
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
}
@@ -35,7 +32,7 @@ class FileLogger
* After event notification.
* @return void
*/
public function logEvent(Dibi\Event $event)
public function logEvent(DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -47,7 +44,7 @@ class FileLogger
}
flock($handle, LOCK_EX);
if ($event->result instanceof \Exception) {
if ($event->result instanceof Exception) {
$message = $event->result->getMessage();
if ($code = $event->result->getCode()) {
$message = "[$code] $message";
@@ -61,7 +58,7 @@ class FileLogger
);
} else {
fwrite($handle,
'OK: ' . $event->sql
"OK: " . $event->sql
. ($event->count ? ";\n-- rows: " . $event->count : '')
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
. "\n-- source: " . implode(':', $event->source)

View File

@@ -2,29 +2,23 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi\Loggers;
use Dibi;
/**
* dibi FirePHP logger.
*
* @author David Grudl
* @package dibi
*/
class FirePhpLogger
class DibiFirePhpLogger extends DibiObject
{
use Dibi\Strict;
/** maximum number of rows */
public static $maxQueries = 30;
static public $maxQueries = 30;
/** maximum SQL length */
public static $maxLength = 1000;
/** size of json stream chunk */
public static $streamChunkSize = 4990;
static public $maxLength = 1000;
/** @var int */
public $filter;
@@ -36,7 +30,7 @@ class FirePhpLogger
public $numOfQueries = 0;
/** @var array */
private static $fireTable = [['Time', 'SQL Statement', 'Rows', 'Connection']];
private static $fireTable = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
/**
@@ -50,7 +44,7 @@ class FirePhpLogger
public function __construct($filter = NULL)
{
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
}
@@ -58,34 +52,33 @@ class FirePhpLogger
* After event notification.
* @return void
*/
public function logEvent(Dibi\Event $event)
public function logEvent(DibiEvent $event)
{
if (headers_sent() || ($event->type & $this->filter) === 0 || count(self::$fireTable) > self::$maxQueries) {
return;
}
if (!$this->numOfQueries) {
header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
}
$this->totalTime += $event->time;
$this->numOfQueries++;
self::$fireTable[] = [
self::$fireTable[] = array(
sprintf('%0.3f', $event->time * 1000),
strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql,
$event->result instanceof \Exception ? 'ERROR' : (string) $event->count,
$event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'),
];
$event->result instanceof Exception ? 'ERROR' : (string) $event->count,
$event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
);
$payload = json_encode([
[
header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
$payload = json_encode(array(
array(
'Type' => 'TABLE',
'Label' => 'dibi profiler (' . $this->numOfQueries . ' SQL queries took ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms)',
],
),
self::$fireTable,
]);
foreach (str_split($payload, self::$streamChunkSize) as $num => $s) {
));
foreach (str_split($payload, 4990) as $num => $s) {
$num++;
header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index
}

View File

@@ -2,50 +2,44 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL builder via fluent interfaces. EXPERIMENTAL!
*
* @method Fluent select(...$field)
* @method Fluent distinct()
* @method Fluent from($table)
* @method Fluent where(...$cond)
* @method Fluent groupBy(...$field)
* @method Fluent having(...$cond)
* @method Fluent orderBy(...$field)
* @method Fluent limit(int $limit)
* @method Fluent offset(int $offset)
* @method Fluent join(...$table)
* @method Fluent leftJoin(...$table)
* @method Fluent innerJoin(...$table)
* @method Fluent rightJoin(...$table)
* @method Fluent outerJoin(...$table)
* @method Fluent as(...$field)
* @method Fluent on(...$cond)
* @method Fluent using(...$cond)
* @author David Grudl
* @package dibi
*
* @property-read string $command
* @property-read DibiConnection $connection
* @property-read DibiResultIterator $iterator
* @method DibiFluent select($field)
* @method DibiFluent distinct()
* @method DibiFluent from($table)
* @method DibiFluent where($cond)
* @method DibiFluent groupBy($field)
* @method DibiFluent having($cond)
* @method DibiFluent orderBy($field)
* @method DibiFluent limit(int $limit)
* @method DibiFluent offset(int $offset)
*/
class Fluent implements IDataSource
class DibiFluent extends DibiObject implements IDataSource
{
use Strict;
const REMOVE = FALSE;
/** @var array */
public static $masks = [
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'],
'UPDATE' => ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'],
'INSERT' => ['INSERT', 'INTO', 'VALUES', 'SELECT'],
'DELETE' => ['DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'],
];
public static $masks = array(
'SELECT' => array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'),
'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'),
'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT'),
'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'),
);
/** @var array default modifiers for arrays */
public static $modifiers = [
public static $modifiers = array(
'SELECT' => '%n',
'FROM' => '%n',
'IN' => '%in',
@@ -55,10 +49,10 @@ class Fluent implements IDataSource
'HAVING' => '%and',
'ORDER BY' => '%by',
'GROUP BY' => '%by',
];
);
/** @var array clauses separators */
public static $separators = [
public static $separators = array(
'SELECT' => ',',
'FROM' => ',',
'WHERE' => 'AND',
@@ -70,47 +64,47 @@ class Fluent implements IDataSource
'SET' => ',',
'VALUES' => ',',
'INTO' => FALSE,
];
);
/** @var array clauses */
public static $clauseSwitches = [
public static $clauseSwitches = array(
'JOIN' => 'FROM',
'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM',
];
);
/** @var Connection */
/** @var DibiConnection */
private $connection;
/** @var array */
private $setups = [];
private $setups = array();
/** @var string */
private $command;
/** @var array */
private $clauses = [];
private $clauses = array();
/** @var array */
private $flags = [];
private $flags = array();
/** @var array */
private $cursor;
/** @var HashMap normalized clauses */
/** @var DibiHashMap normalized clauses */
private static $normalizer;
/**
* @param Connection
* @param DibiConnection
*/
public function __construct(Connection $connection)
public function __construct(DibiConnection $connection)
{
$this->connection = $connection;
if (self::$normalizer === NULL) {
self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
}
}
@@ -130,22 +124,22 @@ class Fluent implements IDataSource
if (isset(self::$masks[$clause])) {
$this->clauses = array_fill_keys(self::$masks[$clause], NULL);
}
$this->cursor = &$this->clauses[$clause];
$this->cursor = [];
$this->cursor = & $this->clauses[$clause];
$this->cursor = array();
$this->command = $clause;
}
// auto-switch to a clause
if (isset(self::$clauseSwitches[$clause])) {
$this->cursor = &$this->clauses[self::$clauseSwitches[$clause]];
$this->cursor = & $this->clauses[self::$clauseSwitches[$clause]];
}
if (array_key_exists($clause, $this->clauses)) {
// append to clause
$this->cursor = &$this->clauses[$clause];
$this->cursor = & $this->clauses[$clause];
// TODO: really delete?
if ($args === [self::REMOVE]) {
if ($args === array(self::REMOVE)) {
$this->cursor = NULL;
return $this;
}
@@ -153,7 +147,7 @@ class Fluent implements IDataSource
if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause];
if ($sep === FALSE) { // means: replace
$this->cursor = [];
$this->cursor = array();
} elseif (!empty($this->cursor)) {
$this->cursor[] = $sep;
@@ -162,7 +156,7 @@ class Fluent implements IDataSource
} else {
// append to currect flow
if ($args === [self::REMOVE]) {
if ($args === array(self::REMOVE)) {
return $this;
}
@@ -170,7 +164,7 @@ class Fluent implements IDataSource
}
if ($this->cursor === NULL) {
$this->cursor = [];
$this->cursor = array();
}
// special types or argument
@@ -181,21 +175,21 @@ class Fluent implements IDataSource
return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
$args = [$clause === 'AS' ? '%N' : '%n', $arg];
$args = array('%n', $arg);
} elseif (is_array($arg) || ($arg instanceof \Traversable && !$arg instanceof self)) { // any array
} elseif (is_array($arg) || ($arg instanceof Traversable && !$arg instanceof self)) { // any array
if (isset(self::$modifiers[$clause])) {
$args = [self::$modifiers[$clause], $arg];
$args = array(self::$modifiers[$clause], $arg);
} elseif (is_string(key($arg))) { // associative array
$args = ['%a', $arg];
$args = array('%a', $arg);
}
} // case $arg === FALSE is handled above
}
foreach ($args as $arg) {
if ($arg instanceof self) {
$arg = new Literal("($arg)");
$arg = "($arg)";
}
$this->cursor[] = $arg;
}
@@ -209,11 +203,16 @@ class Fluent implements IDataSource
* @param string clause name
* @return self
*/
public function clause($clause)
public function clause($clause, $remove = FALSE)
{
$this->cursor = &$this->clauses[self::$normalizer->$clause];
if ($this->cursor === NULL) {
$this->cursor = [];
$this->cursor = & $this->clauses[self::$normalizer->$clause];
if ($remove) { // deprecated, use removeClause
trigger_error(__METHOD__ . '(..., TRUE) is deprecated; use removeClause() instead.', E_USER_NOTICE);
$this->cursor = NULL;
} elseif ($this->cursor === NULL) {
$this->cursor = array();
}
return $this;
@@ -273,7 +272,7 @@ class Fluent implements IDataSource
/**
* Returns the dibi connection.
* @return Connection
* @return DibiConnection
*/
final public function getConnection()
{
@@ -282,7 +281,7 @@ class Fluent implements IDataSource
/**
* Adds Result setup.
* Adds DibiResult setup.
* @param string method
* @param mixed args
* @return self
@@ -300,31 +299,24 @@ class Fluent implements IDataSource
/**
* Generates and executes SQL query.
* @param mixed what to return?
* @return Result|int result set or number of affected rows
* @throws Exception
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
public function execute($return = NULL)
{
$res = $this->query($this->_export());
switch ($return) {
case \dibi::IDENTIFIER:
return $this->connection->getInsertId();
case \dibi::AFFECTED_ROWS:
return $this->connection->getAffectedRows();
default:
return $res;
}
return $return === dibi::IDENTIFIER ? $this->connection->getInsertId() : $res;
}
/**
* Generates, executes SQL query and fetches the single row.
* @return Row|FALSE
* @return DibiRow|FALSE array on success, FALSE if no next record
*/
public function fetch()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetch();
if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch();
} else {
return $this->query($this->_export())->fetch();
}
@@ -337,8 +329,8 @@ class Fluent implements IDataSource
*/
public function fetchSingle()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetchSingle();
if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
} else {
return $this->query($this->_export())->fetchSingle();
}
@@ -353,7 +345,7 @@ class Fluent implements IDataSource
*/
public function fetchAll($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->fetchAll();
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll();
}
@@ -384,11 +376,11 @@ class Fluent implements IDataSource
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return ResultIterator
* @return DibiResultIterator
*/
public function getIterator($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->getIterator();
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
}
@@ -408,20 +400,20 @@ class Fluent implements IDataSource
*/
public function count()
{
return (int) $this->query([
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]',
])->fetchSingle();
return (int) $this->query(array(
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') AS [data]'
))->fetchSingle();
}
/**
* @return Result
* @return DibiResult
*/
private function query($args)
{
$res = $this->connection->query($args);
foreach ($this->setups as $setup) {
call_user_func_array([$res, array_shift($setup)], $setup);
call_user_func_array(array($res, array_shift($setup)), $setup);
}
return $res;
}
@@ -431,11 +423,11 @@ class Fluent implements IDataSource
/**
* @return DataSource
* @return DibiDataSource
*/
public function toDataSource()
{
return new DataSource($this->connection->translate($this->_export()), $this->connection);
return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
}
@@ -447,32 +439,28 @@ class Fluent implements IDataSource
{
try {
return $this->connection->translate($this->_export());
} catch (\Exception $e) {
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
/**
* Generates parameters for Translator.
* Generates parameters for DibiTranslator.
* @param string clause name
* @return array
*/
protected function _export($clause = NULL, $args = [])
protected function _export($clause = NULL, $args = array())
{
if ($clause === NULL) {
$data = $this->clauses;
if ($this->command === 'SELECT' && ($data['LIMIT'] || $data['OFFSET'])) {
$args = array_merge(['%lmt %ofs', $data['LIMIT'][0], $data['OFFSET'][0]], $args);
unset($data['LIMIT'], $data['OFFSET']);
}
} else {
$clause = self::$normalizer->$clause;
if (array_key_exists($clause, $this->clauses)) {
$data = [$clause => $this->clauses[$clause]];
$data = array($clause => $this->clauses[$clause]);
} else {
return [];
return array();
}
}
@@ -512,10 +500,10 @@ class Fluent implements IDataSource
{
// remove references
foreach ($this->clauses as $clause => $val) {
$this->clauses[$clause] = &$val;
$this->clauses[$clause] = & $val;
unset($val);
}
$this->cursor = &$foo;
$this->cursor = & $foo;
}
}

View File

@@ -2,29 +2,34 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* Lazy cached storage.
*
* @author David Grudl
* @package dibi
* @internal
*/
abstract class HashMapBase
abstract class DibiHashMapBase
{
private $callback;
public function __construct(callable $callback)
public function __construct($callback)
{
$this->callback = $callback;
$this->setCallback($callback);
}
public function setCallback(callable $callback)
public function setCallback($callback)
{
if (!is_callable($callback)) {
$able = is_callable($callback, TRUE, $textual);
throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
}
$this->callback = $callback;
}
@@ -40,9 +45,10 @@ abstract class HashMapBase
/**
* Lazy cached storage.
*
* @author David Grudl
* @internal
*/
final class HashMap extends HashMapBase
final class DibiHashMap extends DibiHashMapBase
{
public function __set($nm, $val)

View File

@@ -2,19 +2,18 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* SQL literal value.
*
* @author David Grudl
* @package dibi
*/
class Literal
class DibiLiteral extends DibiObject
{
use Strict;
/** @var string */
private $value;

312
dibi/libs/DibiObject.php Normal file
View File

@@ -0,0 +1,312 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* DibiObject is the ultimate ancestor of all instantiable classes.
*
* DibiObject is copy of Nette\Object from Nette Framework (http://nette.org).
*
* It defines some handful methods and enhances object core of PHP:
* - access to undeclared members throws exceptions
* - support for conventional properties with getters and setters
* - support for event raising functionality
* - ability to add new methods to class (extension methods)
*
* Properties is a syntactic sugar which allows access public getter and setter
* methods as normal object variables. A property is defined by a getter method
* and optional setter method (no setter method means read-only property).
* <code>
* $val = $obj->label; // equivalent to $val = $obj->getLabel();
* $obj->label = 'Nette'; // equivalent to $obj->setLabel('Nette');
* </code>
* Property names are case-sensitive, and they are written in the camelCaps
* or PascalCaps.
*
* Event functionality is provided by declaration of property named 'on{Something}'
* Multiple handlers are allowed.
* <code>
* public $onClick; // declaration in class
* $this->onClick[] = 'callback'; // attaching event handler
* if (!empty($this->onClick)) ... // are there any handlers?
* $this->onClick($sender, $arg); // raises the event with arguments
* </code>
*
* Adding method to class (i.e. to all instances) works similar to JavaScript
* prototype property. The syntax for adding a new method is:
* <code>
* MyClass::extensionMethod('newMethod', function(MyClass $obj, $arg, ...) { ... });
* $obj = new MyClass;
* $obj->newMethod($x);
* </code>
*
* @author David Grudl
* @package dibi
*/
abstract class DibiObject
{
/** @var array (method => array(type => callback)) */
private static $extMethods;
/**
* Returns the name of the class of this object.
* @return string
*/
final public /*static*/ function getClass()
{
return /*get_called_class()*/ /**/get_class($this)/**/;
}
/**
* Access to reflection.
* @return \ReflectionObject
*/
final public function getReflection()
{
return new ReflectionObject($this);
}
/**
* Call to undefined method.
* @param string method name
* @param array arguments
* @return mixed
* @throws \LogicException
*/
public function __call($name, $args)
{
$class = get_class($this);
if ($name === '') {
throw new LogicException("Call to class '$class' method without name.");
}
// event functionality
if (preg_match('#^on[A-Z]#', $name)) {
$rp = new ReflectionProperty($class, $name);
if ($rp->isPublic() && !$rp->isStatic()) {
$list = $this->$name;
if (is_array($list) || $list instanceof Traversable) {
foreach ($list as $handler) {
/**/if (is_object($handler)) {
call_user_func_array(array($handler, '__invoke'), $args);
} else /**/{
call_user_func_array($handler, $args);
}
}
}
return NULL;
}
}
// extension methods
if ($cb = self::extensionMethod("$class::$name")) {
array_unshift($args, $this);
return call_user_func_array($cb, $args);
}
throw new LogicException("Call to undefined method $class::$name().");
}
/**
* Call to undefined static method.
* @param string method name (in lower case!)
* @param array arguments
* @return mixed
* @throws \LogicException
*/
public static function __callStatic($name, $args)
{
$class = get_called_class();
throw new LogicException("Call to undefined static method $class::$name().");
}
/**
* Adding method to class.
* @param string method name
* @param mixed callback or closure
* @return mixed
*/
public static function extensionMethod($name, $callback = NULL)
{
if (self::$extMethods === NULL || $name === NULL) { // for backwards compatibility
$list = get_defined_functions();
foreach ($list['user'] as $fce) {
$pair = explode('_prototype_', $fce);
if (count($pair) === 2) {
self::$extMethods[$pair[1]][$pair[0]] = $fce;
self::$extMethods[$pair[1]][''] = NULL;
}
}
if ($name === NULL) {
return NULL;
}
}
$name = strtolower($name);
$a = strrpos($name, ':'); // search ::
if ($a === FALSE) {
$class = strtolower(get_called_class());
$l = & self::$extMethods[$name];
} else {
$class = substr($name, 0, $a - 1);
$l = & self::$extMethods[substr($name, $a + 1)];
}
if ($callback !== NULL) { // works as setter
$l[$class] = $callback;
$l[''] = NULL;
return NULL;
}
// works as getter
if (empty($l)) {
return FALSE;
} elseif (isset($l[''][$class])) { // cached value
return $l[''][$class];
}
$cl = $class;
do {
$cl = strtolower($cl);
if (isset($l[$cl])) {
return $l[''][$class] = $l[$cl];
}
} while (($cl = get_parent_class($cl)) !== FALSE);
foreach (class_implements($class) as $cl) {
$cl = strtolower($cl);
if (isset($l[$cl])) {
return $l[''][$class] = $l[$cl];
}
}
return $l[''][$class] = FALSE;
}
/**
* Returns property value. Do not call directly.
* @param string property name
* @return mixed property value
* @throws \LogicException if the property is not defined.
*/
public function & __get($name)
{
$class = get_class($this);
if ($name === '') {
throw new LogicException("Cannot read a class '$class' property without name.");
}
// property getter support
$name[0] = $name[0] & "\xDF"; // case-sensitive checking, capitalize first character
$m = 'get' . $name;
if (self::hasAccessor($class, $m)) {
// ampersands:
// - uses & __get() because declaration should be forward compatible (e.g. with Nette\Web\Html)
// - doesn't call & $this->$m because user could bypass property setter by: $x = & $obj->property; $x = 'new value';
$val = $this->$m();
return $val;
}
$m = 'is' . $name;
if (self::hasAccessor($class, $m)) {
$val = $this->$m();
return $val;
}
$name = func_get_arg(0);
throw new LogicException("Cannot read an undeclared property $class::\$$name.");
}
/**
* Sets value of a property. Do not call directly.
* @param string property name
* @param mixed property value
* @return void
* @throws \LogicException if the property is not defined or is read-only
*/
public function __set($name, $value)
{
$class = get_class($this);
if ($name === '') {
throw new LogicException("Cannot assign to a class '$class' property without name.");
}
// property setter support
$name[0] = $name[0] & "\xDF"; // case-sensitive checking, capitalize first character
if (self::hasAccessor($class, 'get' . $name) || self::hasAccessor($class, 'is' . $name)) {
$m = 'set' . $name;
if (self::hasAccessor($class, $m)) {
$this->$m($value);
return;
} else {
$name = func_get_arg(0);
throw new LogicException("Cannot assign to a read-only property $class::\$$name.");
}
}
$name = func_get_arg(0);
throw new LogicException("Cannot assign to an undeclared property $class::\$$name.");
}
/**
* Is property defined?
* @param string property name
* @return bool
*/
public function __isset($name)
{
$name[0] = $name[0] & "\xDF";
return $name !== '' && self::hasAccessor(get_class($this), 'get' . $name);
}
/**
* Access to undeclared property.
* @param string property name
* @return void
* @throws \LogicException
*/
public function __unset($name)
{
$class = get_class($this);
throw new LogicException("Cannot unset the property $class::\$$name.");
}
/**
* Has property an accessor?
* @param string class name
* @param string method name
* @return bool
*/
private static function hasAccessor($c, $m)
{
static $cache;
if (!isset($cache[$c])) {
// get_class_methods returns private, protected and public methods of Object (doesn't matter)
// and ONLY PUBLIC methods of descendants (perfect!)
// but returns static methods too (nothing doing...)
// and is much faster than reflection
// (works good since 5.0.4)
$cache[$c] = array_flip(get_class_methods($c));
}
return isset($cache[$c][$m]);
}
}

View File

@@ -2,11 +2,9 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi result set.
@@ -18,42 +16,48 @@ namespace Dibi;
* $value = $result->fetchSingle();
* $table = $result->fetchAll();
* $pairs = $result->fetchPairs();
* $assoc = $result->fetchAssoc('col1');
* $assoc = $result->fetchAssoc('col1[]col2->col3');
* $assoc = $result->fetchAssoc('id');
* $assoc = $result->fetchAssoc('active,#,id');
*
* unset($result);
* </code>
*
* @author David Grudl
* @package dibi
*
* @property-read mixed $resource
* @property-read IDibiResultDriver $driver
* @property-read int $rowCount
* @property-read DibiResultIterator $iterator
* @property string $rowClass
* @property-read DibiResultInfo $info
*/
class Result implements IDataSource
class DibiResult extends DibiObject implements IDataSource
{
use Strict;
/** @var array ResultDriver */
/** @var array IDibiResultDriver */
private $driver;
/** @var array Translate table */
private $types = [];
private $types = array();
/** @var Reflection\Result */
/** @var DibiResultInfo */
private $meta;
/** @var bool Already fetched? Used for allowance for first seek(0) */
private $fetched = FALSE;
/** @var string returned object class */
private $rowClass = 'Dibi\Row';
private $rowClass = 'DibiRow';
/** @var callable returned object factory*/
/** @var Callback returned object factory*/
private $rowFactory;
/** @var array format */
private $formats = [];
private $formats = array();
/**
* @param ResultDriver
* @param IDibiResultDriver
*/
public function __construct($driver)
{
@@ -67,7 +71,6 @@ class Result implements IDataSource
*/
final public function getResource()
{
trigger_error(__METHOD__ . '() is deprecated, use getResultDriver()->getResultResource().', E_USER_DEPRECATED);
return $this->getResultDriver()->getResultResource();
}
@@ -87,13 +90,13 @@ class Result implements IDataSource
/**
* Safe access to property $driver.
* @return ResultDriver
* @throws \RuntimeException
* @return IDibiResultDriver
* @throws RuntimeException
*/
final public function getResultDriver()
{
if ($this->driver === NULL) {
throw new \RuntimeException('Result-set was released from memory.');
throw new RuntimeException('Result-set was released from memory.');
}
return $this->driver;
@@ -106,8 +109,8 @@ class Result implements IDataSource
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Exception
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/
final public function seek($row)
{
@@ -135,13 +138,27 @@ class Result implements IDataSource
}
/**
* Returns the number of rows in a result set. Alias for getRowCount().
* @deprecated
*/
final public function rowCount()
{
trigger_error(__METHOD__ . '() is deprecated; use count($res) or $res->getRowCount() instead.', E_USER_WARNING);
return $this->getResultDriver()->getRowCount();
}
/**
* Required by the IteratorAggregate interface.
* @return ResultIterator
* @return DibiResultIterator
*/
final public function getIterator()
{
return new ResultIterator($this);
if (func_num_args()) {
trigger_error(__METHOD__ . ' arguments $offset & $limit have been dropped; use SQL clauses instead.', E_USER_WARNING);
}
return new DibiResultIterator($this);
}
@@ -149,7 +166,7 @@ class Result implements IDataSource
/**
* Set fetched object class. This class should extend the Row class.
* Set fetched object class. This class should extend the DibiRow class.
* @param string
* @return self
*/
@@ -171,10 +188,11 @@ class Result implements IDataSource
/**
* Set a factory to create fetched object instances. These should extend the Row class.
* Set a factory to create fetched object instances. These should extend the DibiRow class.
* @param callback
* @return self
*/
public function setRowFactory(callable $callback)
public function setRowFactory($callback)
{
$this->rowFactory = $callback;
return $this;
@@ -184,7 +202,7 @@ class Result implements IDataSource
/**
* Fetches the row at current position, process optional type conversion.
* and moves the internal cursor to the next position
* @return Row|FALSE
* @return DibiRow|FALSE array on success, FALSE if no next record
*/
final public function fetch()
{
@@ -205,7 +223,7 @@ class Result implements IDataSource
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record
* @return mixed value on success, FALSE if no next record
*/
final public function fetchSingle()
{
@@ -223,7 +241,7 @@ class Result implements IDataSource
* Fetches all records from table.
* @param int offset
* @param int limit
* @return Row[]
* @return DibiRow[]
*/
final public function fetchAll($offset = NULL, $limit = NULL)
{
@@ -231,10 +249,10 @@ class Result implements IDataSource
$this->seek((int) $offset);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = [];
$data = array();
do {
if ($limit === 0) {
break;
@@ -255,8 +273,8 @@ class Result implements IDataSource
* - associative descriptor: col1|col2->col3=col4
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor
* @return array
* @throws \InvalidArgumentException
* @return DibiRow
* @throws InvalidArgumentException
*/
final public function fetchAssoc($assoc)
{
@@ -267,17 +285,17 @@ class Result implements IDataSource
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = NULL;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
// check columns
foreach ($assoc as $as) {
// offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts
if ($as !== '[]' && $as !== '=' && $as !== '->' && $as !== '|' && !property_exists($row, $as)) {
throw new \InvalidArgumentException("Unknown column '$as' in associative descriptor.");
throw new InvalidArgumentException("Unknown column '$as' in associative descriptor.");
}
}
@@ -291,28 +309,28 @@ class Result implements IDataSource
// make associative tree
do {
$x = &$data;
$x = & $data;
// iterative deepening
foreach ($assoc as $i => $as) {
if ($as === '[]') { // indexed-array node
$x = &$x[];
$x = & $x[];
} elseif ($as === '=') { // "value" node
$x = $row->{$assoc[$i + 1]};
$x = $row->{$assoc[$i+1]};
continue 2;
} elseif ($as === '->') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = &$x->{$assoc[$i + 1]};
$x = & $x->{$assoc[$i+1]};
$x = NULL; // prepare child node
} else {
$x = &$x->{$assoc[$i + 1]};
$x = & $x->{$assoc[$i+1]};
}
} elseif ($as !== '|') { // associative-array node
$x = &$x[$row->$as];
$x = & $x[$row->$as];
}
}
@@ -335,7 +353,7 @@ class Result implements IDataSource
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = NULL;
@@ -356,32 +374,33 @@ class Result implements IDataSource
}
do {
$x = &$data;
$x = & $data;
foreach ($assoc as $i => $as) {
if ($as === '#') { // indexed-array node
$x = &$x[];
$x = & $x[];
} elseif ($as === '=') { // "record" node
if ($x === NULL) {
$x = $row->toArray();
$x = &$x[ $assoc[$i + 1] ];
$x = & $x[ $assoc[$i+1] ];
$x = NULL; // prepare child node
} else {
$x = &$x[ $assoc[$i + 1] ];
$x = & $x[ $assoc[$i+1] ];
}
} elseif ($as === '@') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = &$x->{$assoc[$i + 1]};
$x = & $x->{$assoc[$i+1]};
$x = NULL; // prepare child node
} else {
$x = &$x->{$assoc[$i + 1]};
$x = & $x->{$assoc[$i+1]};
}
} else { // associative-array node
$x = &$x[$row->$as];
$x = & $x[$row->$as];
}
}
@@ -405,21 +424,21 @@ class Result implements IDataSource
* @param string associative key
* @param string value
* @return array
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
final public function fetchPairs($key = NULL, $value = NULL)
{
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = [];
$data = array();
if ($value === NULL) {
if ($key !== NULL) {
throw new \InvalidArgumentException('Either none or both columns must be specified.');
throw new InvalidArgumentException("Either none or both columns must be specified.");
}
// autodetect
@@ -436,7 +455,7 @@ class Result implements IDataSource
} else {
if (!property_exists($row, $value)) {
throw new \InvalidArgumentException("Unknown value column '$value'.");
throw new InvalidArgumentException("Unknown value column '$value'.");
}
if ($key === NULL) { // indexed-array
@@ -447,7 +466,7 @@ class Result implements IDataSource
}
if (!property_exists($row, $key)) {
throw new \InvalidArgumentException("Unknown key column '$key'.");
throw new InvalidArgumentException("Unknown key column '$key'.");
}
}
@@ -468,13 +487,12 @@ class Result implements IDataSource
*/
private function detectTypes()
{
$cache = Helpers::getTypeCache();
$cache = DibiColumnInfo::getTypeCache();
try {
foreach ($this->getResultDriver()->getResultColumns() as $col) {
$this->types[$col['name']] = isset($col['type']) ? $col['type'] : $cache->{$col['nativetype']};
$this->types[$col['name']] = $cache->{$col['nativetype']};
}
} catch (NotSupportedException $e) {
}
} catch (DibiNotSupportedException $e) {}
}
@@ -483,52 +501,43 @@ class Result implements IDataSource
* @param array
* @return void
*/
private function normalize(array &$row)
private function normalize(array & $row)
{
foreach ($this->types as $key => $type) {
if (!isset($row[$key])) { // NULL
continue;
}
$value = $row[$key];
if ($type === Type::TEXT) {
$row[$key] = (string) $value;
if ($value === FALSE || $type === dibi::TEXT) {
} elseif ($type === Type::INTEGER) {
$row[$key] = is_float($tmp = $value * 1)
? (is_string($value) ? $value : (int) $value)
: $tmp;
} elseif ($type === dibi::INTEGER) {
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
} elseif ($type === Type::FLOAT) {
$value = ltrim((string) $value, '0');
$p = strpos($value, '.');
if ($p !== FALSE) {
$value = rtrim(rtrim($value, '0'), '.');
}
if ($value === '' || $value[0] === '.') {
$value = '0' . $value;
}
$row[$key] = $value === str_replace(',', '.', (string) ($float = (float) $value))
? $float
: $value;
} elseif ($type === dibi::FLOAT) {
$row[$key] = ltrim((string) ($tmp = (float) $value), '0') === ltrim(rtrim(rtrim($value, '0'), '.'), '0') ? $tmp : $value;
} elseif ($type === Type::BOOL) {
} elseif ($type === dibi::BOOL) {
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
if ($value && substr((string) $value, 0, 3) !== '000') { // '', NULL, FALSE, '0000-00-00', ...
$value = new DateTime($value);
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
} elseif ($type === dibi::DATE || $type === dibi::DATETIME) {
if ((int) $value === 0 && substr((string) $value, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ...
} elseif (empty($this->formats[$type])) { // return DateTime object (default)
$row[$key] = new DibiDateTime(is_numeric($value) ? date('Y-m-d H:i:s', $value) : $value);
} elseif ($this->formats[$type] === 'U') { // return timestamp
$row[$key] = is_numeric($value) ? (int) $value : strtotime($value);
} elseif (is_numeric($value)) { // formatted date
$row[$key] = date($this->formats[$type], $value);
} else {
$row[$key] = NULL;
$value = new DibiDateTime($value);
$row[$key] = $value->format($this->formats[$type]);
}
} elseif ($type === Type::TIME_INTERVAL) {
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
$row[$key]->invert = (int) (bool) $m[1];
} elseif ($type === Type::BINARY) {
$row[$key] = $this->getResultDriver()->unescapeBinary($value);
} elseif ($type === dibi::BINARY) {
$row[$key] = $this->getResultDriver()->unescape($value, $type);
}
}
}
@@ -537,7 +546,7 @@ class Result implements IDataSource
/**
* Define column type.
* @param string column
* @param string type (use constant Type::*)
* @param string type (use constant Dibi::*)
* @return self
*/
final public function setType($col, $type)
@@ -558,9 +567,9 @@ class Result implements IDataSource
/**
* Sets date format.
* @param string
* @param string|NULL format
* Sets data format.
* @param string type (use constant Dibi::*)
* @param string format
* @return self
*/
final public function setFormat($type, $format)
@@ -572,7 +581,7 @@ class Result implements IDataSource
/**
* Returns data format.
* @return string|NULL
* @return string
*/
final public function getFormat($type)
{
@@ -585,19 +594,19 @@ class Result implements IDataSource
/**
* Returns a meta information about the current result set.
* @return Reflection\Result
* @return DibiResultInfo
*/
public function getInfo()
{
if ($this->meta === NULL) {
$this->meta = new Reflection\Result($this->getResultDriver());
$this->meta = new DibiResultInfo($this->getResultDriver());
}
return $this->meta;
}
/**
* @return Reflection\Column[]
* @deprecated
*/
final public function getColumns()
{
@@ -605,6 +614,14 @@ class Result implements IDataSource
}
/** @deprecated */
public function getColumnNames($fullNames = FALSE)
{
trigger_error(__METHOD__ . '() is deprecated; use $res->getInfo()->getColumnNames() instead.', E_USER_WARNING);
return $this->getInfo()->getColumnNames($fullNames);
}
/********************* misc tools ****************d*g**/
@@ -614,7 +631,66 @@ class Result implements IDataSource
*/
final public function dump()
{
echo Helpers::dump($this);
$i = 0;
$this->seek(0);
if (PHP_SAPI === 'cli') {
$hasColors = (substr(getenv('TERM'), 0, 5) === 'xterm');
$maxLen = 0;
while ($row = $this->fetch()) {
if ($i === 0) {
foreach ($row as $col => $foo) {
$len = mb_strlen($col);
$maxLen = max($len, $maxLen);
}
}
if ($hasColors) {
echo "\033[1;37m#row: $i\033[0m\n";
} else {
echo "#row: $i\n";
}
foreach ($row as $col => $val) {
$spaces = $maxLen - mb_strlen($col) + 2;
echo "$col" . str_repeat(" ", $spaces) . "$val\n";
}
echo "\n";
$i++;
}
if ($i === 0) {
echo "empty result set\n";
}
echo "\n";
} else {
while ($row = $this->fetch()) {
if ($i === 0) {
echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
foreach ($row as $col => $foo) {
echo "\t\t<th>" . htmlSpecialChars($col) . "</th>\n";
}
echo "\t</tr>\n</thead>\n<tbody>\n";
}
echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
foreach ($row as $col) {
//if (is_object($col)) $col = $col->__toString();
echo "\t\t<td>", htmlSpecialChars($col), "</td>\n";
}
echo "\t</tr>\n";
$i++;
}
if ($i === 0) {
echo '<p><em>empty result set</em></p>';
} else {
echo "</tbody>\n</table>\n";
}
}
}
}

View File

@@ -2,16 +2,14 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* External result set iterator.
*
* This can be returned by Result::getIterator() method or using foreach
* This can be returned by DibiResult::getIterator() method or using foreach
* <code>
* $result = dibi::query('SELECT * FROM table');
* foreach ($result as $row) {
@@ -19,15 +17,16 @@ namespace Dibi;
* }
* unset($result);
* </code>
*
* @author David Grudl
* @package dibi
*/
class ResultIterator implements \Iterator, \Countable
class DibiResultIterator implements Iterator, Countable
{
use Strict;
/** @var Result */
/** @var DibiResult */
private $result;
/** @var mixed */
/** @var int */
private $row;
/** @var int */
@@ -35,9 +34,9 @@ class ResultIterator implements \Iterator, \Countable
/**
* @param Result
* @param DibiResult
*/
public function __construct(Result $result)
public function __construct(DibiResult $result)
{
$this->result = $result;
}

125
dibi/libs/DibiRow.php Normal file
View File

@@ -0,0 +1,125 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* Result set single row.
*
* @author David Grudl
* @package dibi
*/
class DibiRow implements ArrayAccess, IteratorAggregate, Countable
{
public function __construct($arr)
{
foreach ($arr as $k => $v) {
$this->$k = $v;
}
}
public function toArray()
{
return (array) $this;
}
/**
* Converts value to DateTime object.
* @param string key
* @param string format
* @return DateTime
*/
public function asDateTime($key, $format = NULL)
{
$time = $this[$key];
if (!$time instanceof DibiDateTime) {
if ((int) $time === 0 && substr((string) $time, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ...
return NULL;
}
$time = new DibiDateTime(is_numeric($time) ? date('Y-m-d H:i:s', $time) : $time);
}
return $format === NULL ? $time : $time->format($format);
}
/**
* Converts value to UNIX timestamp.
* @param string key
* @return int
*/
public function asTimestamp($key)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_WARNING);
return $this->asDateTime($key, 'U');
}
/**
* Converts value to boolean.
* @param string key
* @return mixed
*/
public function asBool($key)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_WARNING);
return $this[$key];
}
/** @deprecated */
public function asDate($key, $format = NULL)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_WARNING);
if ($format === NULL) {
return $this->asTimestamp($key);
} else {
return $this->asDateTime($key, $format === TRUE ? NULL : $format);
}
}
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
final public function count()
{
return count((array) $this);
}
final public function getIterator()
{
return new ArrayIterator($this);
}
final public function offsetSet($nm, $val)
{
$this->$nm = $val;
}
final public function offsetGet($nm)
{
return $this->$nm;
}
final public function offsetExists($nm)
{
return isset($this->$nm);
}
final public function offsetUnset($nm)
{
unset($this->$nm);
}
}

View File

@@ -2,42 +2,41 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL translator.
*
* @author David Grudl
* @package dibi
*/
final class Translator
final class DibiTranslator extends DibiObject
{
use Strict;
/** @var Connection */
/** @var DibiConnection */
private $connection;
/** @var Driver */
/** @var IDibiDriver */
private $driver;
/** @var int */
private $cursor = 0;
private $cursor;
/** @var array */
private $args;
/** @var string[] */
private $errors;
/** @var bool */
private $hasError;
/** @var bool */
private $comment = FALSE;
private $comment;
/** @var int */
private $ifLevel = 0;
private $ifLevel;
/** @var int */
private $ifLevelStart = 0;
private $ifLevelStart;
/** @var int */
private $limit;
@@ -45,39 +44,49 @@ final class Translator
/** @var int */
private $offset;
/** @var HashMap */
/** @var DibiHashMap */
private $identifiers;
public function __construct(Connection $connection)
public function __construct(DibiConnection $connection)
{
$this->connection = $connection;
$this->driver = $connection->getDriver();
$this->identifiers = new HashMap([$this, 'delimite']);
}
/**
* Generates SQL. Can be called only once.
* Generates SQL.
* @param array
* @return string
* @throws Exception
* @throws DibiException
*/
public function translate(array $args)
{
$this->identifiers = new DibiHashMap(array($this, 'delimite'));
$this->driver = $this->connection->getDriver();
$args = array_values($args);
while (count($args) === 1 && is_array($args[0])) { // implicit array expansion
$args = array_values($args[0]);
}
$this->args = $args;
$this->limit = -1;
$this->offset = 0;
$this->hasError = FALSE;
$commandIns = NULL;
$lastArr = NULL;
$cursor = &$this->cursor;
$comment = &$this->comment;
// shortcuts
$cursor = & $this->cursor;
$cursor = 0;
// conditional sql
$this->ifLevel = $this->ifLevelStart = 0;
$comment = & $this->comment;
$comment = FALSE;
// iterate
$sql = [];
$sql = array();
while ($cursor < count($this->args)) {
$arg = $this->args[$cursor];
$cursor++;
@@ -106,11 +115,11 @@ final class Translator
)/xs',
*/ // note: this can change $this->args & $this->cursor & ...
. preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s',
[$this, 'cb'],
array($this, 'cb'),
substr($arg, $toSkip)
);
if (preg_last_error()) {
throw new PcreException;
throw new DibiPcreException;
}
}
continue;
@@ -121,24 +130,26 @@ final class Translator
continue;
}
if ($arg instanceof \Traversable) {
if ($arg instanceof Traversable) {
$arg = iterator_to_array($arg);
}
if (is_array($arg) && is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else {
if ($lastArr === $cursor - 1) {
$sql[] = ',';
if (is_array($arg)) {
if (is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else {
if ($lastArr === $cursor - 1) {
$sql[] = ',';
}
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
}
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
$lastArr = $cursor;
continue;
}
$lastArr = $cursor;
continue;
}
// default processing
@@ -147,17 +158,17 @@ final class Translator
if ($comment) {
$sql[] = '*/';
$sql[] = "*/";
}
$sql = implode(' ', $sql);
if ($this->errors) {
throw new Exception('SQL translate error: ' . trim(reset($this->errors), '*'), 0, $sql);
if ($this->hasError) {
throw new DibiException('SQL translate error', 0, $sql);
}
// apply limit
if ($this->limit !== NULL || $this->offset !== NULL) {
if ($this->limit > -1 || $this->offset > 0) {
$this->driver->applyLimit($sql, $this->limit, $this->offset);
}
@@ -174,16 +185,16 @@ final class Translator
public function formatValue($value, $modifier)
{
if ($this->comment) {
return '...';
return "...";
}
// array processing (with or without modifier)
if ($value instanceof \Traversable) {
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
}
if (is_array($value)) {
$vx = $kx = [];
$vx = $kx = array();
switch ($modifier) {
case 'and':
case 'or': // key=val AND key IS NULL AND ...
@@ -199,7 +210,7 @@ final class Translator
$v = $this->formatValue($v, FALSE);
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
} elseif ($pair[1] === 'ex') {
} elseif ($pair[1] === 'ex') { // TODO: this will be removed
$vx[] = $k . $this->formatValue($v, 'ex');
} else {
@@ -225,7 +236,7 @@ final class Translator
case 'n': // key, key, ... identifier names
foreach ($value as $k => $v) {
if (is_string($k)) {
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->driver->escapeIdentifier($v));
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->identifiers->$v);
} else {
$pair = explode('%', $v, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]};
@@ -246,7 +257,7 @@ final class Translator
case 'in':// replaces scalar %in modifier!
case 'l': // (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', (string) $k, 2); // split into identifier & modifier
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')';
@@ -265,13 +276,15 @@ final class Translator
if (is_array($v)) {
if (isset($proto)) {
if ($proto !== array_keys($v)) {
return $this->errors[] = '**Multi-insert array "' . $k . '" is different**';
$this->hasError = TRUE;
return '**Multi-insert array "' . $k . '" is different.**';
}
} else {
$proto = array_keys($v);
}
} else {
return $this->errors[] = '**Unexpected type ' . (is_object($v) ? get_class($v) : gettype($v)) . '**';
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($v) . '**';
}
$pair = explode('%', $k, 2); // split into identifier & modifier
@@ -300,7 +313,8 @@ final class Translator
case 'ex':
case 'sql':
return call_user_func_array([$this->connection, 'translate'], $value);
$translator = new self($this->connection);
return $translator->translate($value);
default: // value, value, value - all with the same modifier
foreach ($value as $v) {
@@ -313,36 +327,23 @@ final class Translator
// with modifier procession
if ($modifier) {
if ($value !== NULL && !is_scalar($value)) { // array is already processed
if ($value instanceof Literal && ($modifier === 'sql' || $modifier === 'SQL')) {
$modifier = 'SQL';
} elseif (($value instanceof \DateTime || $value instanceof \DateTimeInterface) && ($modifier === 'd' || $modifier === 't')) {
// continue
} else {
$type = is_object($value) ? get_class($value) : gettype($value);
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
}
if ($value !== NULL && !is_scalar($value) && !($value instanceof DateTime)) { // array is already processed
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
}
switch ($modifier) {
case 's': // string
return $value === NULL ? 'NULL' : $this->driver->escapeText((string) $value);
case 'bin':// binary
return $value === NULL ? 'NULL' : $this->driver->escapeBinary($value);
case 'b': // boolean
return $value === NULL ? 'NULL' : $this->driver->escapeBool($value);
return $value === NULL ? 'NULL' : $this->driver->escape($value, $modifier);
case 'sN': // string or NULL
case 'sn':
return $value == '' ? 'NULL' : $this->driver->escapeText((string) $value); // notice two equal signs
case 'in': // deprecated
trigger_error('Modifier %in is deprecated, use %iN.', E_USER_DEPRECATED);
// intentionally break omitted
return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::TEXT); // notice two equal signs
case 'iN': // signed int or NULL
case 'in': // deprecated
if ($value == '') {
$value = NULL;
}
@@ -350,42 +351,39 @@ final class Translator
case 'i': // signed int
case 'u': // unsigned int, ignored
if ($value === NULL) {
return 'NULL';
} elseif (is_string($value) && preg_match('#[+-]?\d++(?:e\d+)?\z#A', $value)) {
return $value; // support for long numbers - keep them unchanged
} elseif (is_string($value) && substr($value, 1, 1) === 'x' && is_numeric($value)) {
trigger_error('Support for hex strings has been deprecated.', E_USER_DEPRECATED);
return (string) hexdec($value);
// support for long numbers - keep them unchanged
if (is_string($value) && preg_match('#[+-]?\d++(e\d+)?\z#A', $value)) {
return $value;
} else {
return (string) (int) $value;
return $value === NULL ? 'NULL' : (string) (int) ($value + 0);
}
case 'f': // float
if ($value === NULL) {
return 'NULL';
} elseif (is_string($value) && is_numeric($value) && substr($value, 1, 1) !== 'x') {
return $value; // support for extreme numbers - keep them unchanged
// support for extreme numbers - keep them unchanged
if (is_string($value) && is_numeric($value) && strpos($value, 'x') === FALSE) {
return $value; // something like -9E-005 is accepted by SQL, HEX values are not
} else {
return rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
return $value === NULL ? 'NULL' : rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
}
case 'd': // date
case 't': // datetime
case 'dt': // datetime
if ($value === NULL) {
return 'NULL';
} else {
return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value);
if (is_numeric($value)) {
$value = (int) $value; // timestamp
} elseif (is_string($value)) {
$value = new DateTime($value);
}
return $this->driver->escape($value, $modifier);
}
case 'by':
case 'n': // composed identifier name
case 'n': // identifier name
return $this->identifiers->$value;
case 'N': // identifier name
return $this->driver->escapeIdentifier($value);
case 'ex':
case 'sql': // preserve as dibi-SQL (TODO: leave only %ex)
$value = (string) $value;
@@ -395,11 +393,11 @@ final class Translator
$value = substr($value, 0, $toSkip)
. preg_replace_callback(
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
[$this, 'cb'],
array($this, 'cb'),
substr($value, $toSkip)
);
if (preg_last_error()) {
throw new PcreException;
throw new DibiPcreException;
}
}
return $value;
@@ -421,18 +419,19 @@ final class Translator
case 'a':
case 'l':
case 'v':
$type = gettype($value);
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
default:
return $this->errors[] = "**Unknown or unexpected modifier %$modifier**";
$this->hasError = TRUE;
return "**Unknown or invalid modifier %$modifier**";
}
}
// without modifier procession
if (is_string($value)) {
return $this->driver->escapeText($value);
return $this->driver->escape($value, dibi::TEXT);
} elseif (is_int($value)) {
return (string) $value;
@@ -441,20 +440,20 @@ final class Translator
return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
} elseif (is_bool($value)) {
return $this->driver->escapeBool($value);
return $this->driver->escape($value, dibi::BOOL);
} elseif ($value === NULL) {
return 'NULL';
} elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) {
return $this->driver->escapeDateTime($value);
} elseif ($value instanceof DateTime) {
return $this->driver->escape($value, dibi::DATETIME);
} elseif ($value instanceof Literal) {
} elseif ($value instanceof DibiLiteral) {
return (string) $value;
} else {
$type = is_object($value) ? get_class($value) : gettype($value);
return $this->errors[] = "**Unexpected $type**";
$this->hasError = TRUE;
return '**Unexpected ' . gettype($value) . '**';
}
}
@@ -480,10 +479,11 @@ final class Translator
if (!empty($matches[11])) { // placeholder
$cursor = &$this->cursor;
$cursor = & $this->cursor;
if ($cursor >= count($this->args)) {
return $this->errors[] = '**Extra placeholder**';
$this->hasError = TRUE;
return "**Extra placeholder**";
}
$cursor++;
@@ -492,10 +492,11 @@ final class Translator
if (!empty($matches[10])) { // modifier
$mod = $matches[10];
$cursor = &$this->cursor;
$cursor = & $this->cursor;
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
return $this->errors[] = "**Extra modifier %$mod**";
$this->hasError = TRUE;
return "**Extra modifier %$mod**";
}
if ($mod === 'if') {
@@ -505,7 +506,7 @@ final class Translator
// open comment
$this->ifLevelStart = $this->ifLevel;
$this->comment = TRUE;
return '/*';
return "/*";
}
return '';
@@ -513,11 +514,11 @@ final class Translator
if ($this->ifLevelStart === $this->ifLevel) {
$this->ifLevelStart = 0;
$this->comment = FALSE;
return '*/';
return "*/";
} elseif (!$this->comment) {
$this->ifLevelStart = $this->ifLevel;
$this->comment = TRUE;
return '/*';
return "/*";
}
} elseif ($mod === 'end') {
@@ -526,7 +527,7 @@ final class Translator
// close comment
$this->ifLevelStart = 0;
$this->comment = FALSE;
return '*/';
return "*/";
}
return '';
@@ -535,23 +536,17 @@ final class Translator
return '';
} elseif ($mod === 'lmt') { // apply limit
$arg = $this->args[$cursor++];
if ($arg === NULL) {
} elseif ($this->comment) {
return "(limit $arg)";
} else {
$this->limit = (int) $arg;
if ($this->args[$cursor] !== NULL) {
$this->limit = (int) $this->args[$cursor];
}
$cursor++;
return '';
} elseif ($mod === 'ofs') { // apply offset
$arg = $this->args[$cursor++];
if ($arg === NULL) {
} elseif ($this->comment) {
return "(offset $arg)";
} else {
$this->offset = (int) $arg;
if ($this->args[$cursor] !== NULL) {
$this->offset = (int) $this->args[$cursor];
}
$cursor++;
return '';
} else { // default processing
@@ -571,13 +566,14 @@ final class Translator
return $this->identifiers->{$matches[2]};
} elseif ($matches[3]) { // SQL strings: '...'
return $this->driver->escapeText(str_replace("''", "'", $matches[4]));
return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::TEXT);
} elseif ($matches[5]) { // SQL strings: "..."
return $this->driver->escapeText(str_replace('""', '"', $matches[6]));
return $this->driver->escape( str_replace('""', '"', $matches[6]), dibi::TEXT);
} elseif ($matches[7]) { // string quote
return $this->errors[] = '**Alone quote**';
$this->hasError = TRUE;
return '**Alone quote**';
}
if ($matches[8]) { // SQL identifier substitution
@@ -586,7 +582,7 @@ final class Translator
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
}
throw new \Exception('this should be never executed');
die('this should be never executed');
}
@@ -600,9 +596,9 @@ final class Translator
{
$value = $this->connection->substitute($value);
$parts = explode('.', $value);
foreach ($parts as &$v) {
foreach ($parts as & $v) {
if ($v !== '*') {
$v = $this->driver->escapeIdentifier($v);
$v = $this->driver->escape($v, dibi::IDENTIFIER);
}
}
return implode('.', $parts);

View File

@@ -2,48 +2,48 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
namespace Dibi;
/**
* Provides an interface between a dataset and data-aware components.
* @package dibi
*/
interface IDataSource extends \Countable, \IteratorAggregate
interface IDataSource extends Countable, IteratorAggregate
{
//function \IteratorAggregate::getIterator();
//function \Countable::count();
//function IteratorAggregate::getIterator();
//function Countable::count();
}
/**
* dibi driver interface.
* @package dibi
*/
interface Driver
interface IDibiDriver
{
/**
* Connects to a database.
* @param array
* @return void
* @throws Exception
* @throws DibiException
*/
function connect(array &$config);
function connect(array & $config);
/**
* Disconnects from a database.
* @return void
* @throws Exception
* @throws DibiException
*/
function disconnect();
/**
* Internal: Executes the SQL query.
* @param string SQL statement.
* @return ResultDriver|NULL
* @throws DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
function query($sql);
@@ -63,7 +63,7 @@ interface Driver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DriverException
* @throws DibiDriverException
*/
function begin($savepoint = NULL);
@@ -71,7 +71,7 @@ interface Driver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DriverException
* @throws DibiDriverException
*/
function commit($savepoint = NULL);
@@ -79,7 +79,7 @@ interface Driver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DriverException
* @throws DibiDriverException
*/
function rollback($savepoint = NULL);
@@ -91,46 +91,18 @@ interface Driver
/**
* Returns the connection reflector.
* @return Reflector
* @return IDibiReflector
*/
function getReflector();
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
function escapeText($value);
/**
* @param string
* @return string
*/
function escapeBinary($value);
/**
* @param string
* @return string
*/
function escapeIdentifier($value);
/**
* @param bool
* @return string
*/
function escapeBool($value);
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
function escapeDate($value);
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
function escapeDateTime($value);
function escape($value, $type);
/**
* Encodes string for use in a LIKE statement.
@@ -142,20 +114,18 @@ interface Driver
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
function applyLimit(&$sql, $limit, $offset);
function applyLimit(& $sql, $limit, $offset);
}
/**
* dibi result set driver interface.
* @package dibi
*/
interface ResultDriver
interface IDibiResultDriver
{
/**
@@ -168,7 +138,7 @@ interface ResultDriver
* 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
* @throws Exception
* @throws DibiException
*/
function seek($row);
@@ -201,18 +171,23 @@ interface ResultDriver
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
function unescapeBinary($value);
function unescape($value, $type);
}
/**
* dibi driver reflection.
*
* @author David Grudl
* @package dibi
*/
interface Reflector
interface IDibiReflector
{
/**

1
examples/.gitignore vendored
View File

@@ -1,4 +1,3 @@
_test.bat
ref
output
log

834
examples/Nette/Debugger.php Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,60 @@
Licenses
========
Good news! You may use Nette Framework under the terms of either
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
The BSD License is recommended for most projects. It is easy to understand and it
places almost no restrictions on what you can do with the framework. If the GPL
fits better to your project, you can use the framework under this license.
You don't have to notify anyone which license you are using. You can freely
use Nette Framework in commercial projects as long as the copyright header
remains intact.
Please be advised that the name "Nette Framework" is a protected trademark and its
usage has some limitations. So please do not use word "Nette" in the name of your
project or top-level domain, and choose a name that stands on its own merits.
If your stuff is good, it will not take long to establish a reputation for yourselves.
New BSD License
---------------
Copyright (c) 2004, 2012 David Grudl (http://davidgrudl.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of "Nette Framework" nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
GNU General Public License
--------------------------
GPL licenses are very very long, so instead of including them here we offer
you URLs with full text:
- GPL version 2: http://www.gnu.org/licenses/gpl-2.0.html
- GPL version 3: http://www.gnu.org/licenses/gpl-3.0.html

View File

@@ -0,0 +1,3 @@
This file is part of Nette Framework
For more information please see http://nette.org

View File

@@ -4,32 +4,35 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
// connects to SQlite using dibi class
echo '<p>Connecting to Sqlite: ';
try {
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to SQlite using Dibi\Connection object
// connects to SQlite using DibiConnection object
echo '<p>Connecting to Sqlite: ';
try {
$connection = new Dibi\Connection([
'driver' => 'sqlite3',
$connection = new DibiConnection(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
@@ -40,7 +43,8 @@ echo '<p>Connecting to MySQL: ';
try {
dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=cp1250');
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
@@ -49,19 +53,20 @@ echo "</p>\n";
// connects to MySQLi using array
echo '<p>Connecting to MySQLi: ';
try {
dibi::connect([
'driver' => 'mysqli',
'host' => 'localhost',
dibi::connect(array(
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx',
'database' => 'dibi',
'options' => [
MYSQLI_OPT_CONNECT_TIMEOUT => 30,
],
'flags' => MYSQLI_CLIENT_COMPRESS,
]);
'options' => array(
MYSQLI_OPT_CONNECT_TIMEOUT => 30
),
'flags' => MYSQLI_CLIENT_COMPRESS,
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
@@ -70,14 +75,15 @@ echo "</p>\n";
// connects to ODBC
echo '<p>Connecting to ODBC: ';
try {
dibi::connect([
'driver' => 'odbc',
dibi::connect(array(
'driver' => 'odbc',
'username' => 'root',
'password' => '***',
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.__DIR__.'/data/sample.mdb',
]);
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.dirname(__FILE__).'/data/sample.mdb',
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
@@ -86,13 +92,14 @@ echo "</p>\n";
// connects to PostgreSql
echo '<p>Connecting to PostgreSql: ';
try {
dibi::connect([
'driver' => 'postgre',
'string' => 'host=localhost port=5432 dbname=mary',
dibi::connect(array(
'driver' => 'postgre',
'string' => 'host=localhost port=5432 dbname=mary',
'persistent' => TRUE,
]);
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
@@ -101,12 +108,13 @@ echo "</p>\n";
// connects to PDO
echo '<p>Connecting to Sqlite via PDO: ';
try {
dibi::connect([
'driver' => 'pdo',
'dsn' => 'sqlite::memory:',
]);
dibi::connect(array(
'driver' => 'pdo',
'dsn' => 'sqlite2::memory:',
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
@@ -115,31 +123,33 @@ echo "</p>\n";
// connects to MS SQL
echo '<p>Connecting to MS SQL: ';
try {
dibi::connect([
'driver' => 'mssql',
'host' => 'localhost',
dibi::connect(array(
'driver' => 'mssql',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx',
]);
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to SQLSRV
echo '<p>Connecting to Microsoft SQL Server: ';
// connects to MS SQL 2005
echo '<p>Connecting to MS SQL 2005: ';
try {
dibi::connect([
'driver' => 'sqlsrv',
'host' => '(local)',
dibi::connect(array(
'driver' => 'mssql2005',
'host' => '(local)',
'username' => 'Administrator',
'password' => 'xxx',
'database' => 'main',
]);
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
@@ -148,14 +158,15 @@ echo "</p>\n";
// connects to Oracle
echo '<p>Connecting to Oracle: ';
try {
dibi::connect([
'driver' => 'oracle',
dibi::connect(array(
'driver' => 'oracle',
'username' => 'root',
'password' => 'xxx',
'database' => 'db',
]);
));
echo 'OK';
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";

BIN
examples/data/sample.sdb Normal file

Binary file not shown.

View File

@@ -2,7 +2,7 @@ body {
font: 15px/1.5 Tahoma, Verdana, Myriad Web, Syntax, sans-serif;
color: #333;
background: #fff url('dibi-powered.gif') no-repeat 99% 1em;
margin: 1.6em;
margin: 1.6em;
padding: 0;
}
@@ -41,24 +41,24 @@ table.dump th {
}
/* dump() */
pre.tracy-dump, pre.dump {
pre.nette-dump, pre.dump {
color: #444; background: white;
border: 1px solid silver;
padding: 1em;
margin: 1em 0;
}
pre.tracy-dump .php-array, pre.tracy-dump .php-object {
pre.nette-dump .php-array, pre.nette-dump .php-object {
color: #C22;
}
pre.tracy-dump .php-string {
pre.nette-dump .php-string {
color: #080;
}
pre.tracy-dump .php-int, pre.tracy-dump .php-float {
pre.nette-dump .php-int, pre.nette-dump .php-float {
color: #37D;
}
pre.tracy-dump .php-null, pre.tracy-dump .php-bool {
pre.nette-dump .php-null, pre.nette-dump .php-bool {
color: black;
}
pre.tracy-dump .php-visibility {
pre.nette-dump .php-visibility {
font-size: 85%; color: #999;
}

View File

@@ -4,22 +4,23 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// retrieve database reflection
$database = dibi::getDatabaseInfo();
echo "<h2>Database '{$database->getName()}'</h2>\n";
echo "<h2>Database '{$database->name}'</h2>\n";
echo "<ul>\n";
foreach ($database->getTables() as $table) {
echo '<li>', ($table->isView() ? 'view' : 'table') . " {$table->getName()}</li>\n";
echo '<li>', ($table->view ? 'view' : 'table') . " $table->name</li>\n";
}
echo "</ul>\n";
@@ -27,22 +28,22 @@ echo "</ul>\n";
// table reflection
$table = $database->getTable('products');
echo "<h2>Table '{$table->getName()}'</h2>\n";
echo "<h2>Table '{$table->name}'</h2>\n";
echo "Columns\n";
echo "<ul>\n";
foreach ($table->getColumns() as $column) {
echo "<li>{$column->getName()} <i>{$column->getNativeType()}</i> <code>{$column->getDefault()}</code></li>\n";
echo "<li>{$column->name} <i>{$column->nativeType}</i> <code>{$column->default}</code></li>\n";
}
echo "</ul>\n";
echo 'Indexes';
echo "Indexes";
echo "<ul>\n";
foreach ($table->getIndexes() as $index) {
echo "<li>{$index->getName()} " . ($index->isPrimary() ? 'primary ' : '') . ($index->isUnique() ? 'unique' : '') . ' (';
echo "<li>{$index->name} " . ($index->primary ? 'primary ' : '') . ($index->unique ? 'unique' : '') . ' (';
foreach ($index->getColumns() as $column) {
echo $column->getName(), ', ';
echo "$column->name, ";
}
echo ")</li>\n";
}

View File

@@ -4,13 +4,14 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
$res = dibi::query('
@@ -27,6 +28,6 @@ dibi::dump();
// dump result table
echo '<h2>Dibi\Result::dump()</h2>';
echo '<h2>DibiResult::dump()</h2>';
$res->dump();

View File

@@ -1,22 +1,18 @@
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
Tracy\Debugger::enable();
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Fetching Examples | dibi</h1>
<?php
dibi::connect([
'driver' => 'sqlite3',
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
ndebug();
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
/*
@@ -34,40 +30,38 @@ product_id | title
// fetch a single row
echo "<h2>fetch()</h2>\n";
$row = dibi::fetch('SELECT title FROM products');
Tracy\Dumper::dump($row); // Chair
dump($row); // Chair
// fetch a single value
echo "<h2>fetchSingle()</h2>\n";
$value = dibi::fetchSingle('SELECT title FROM products');
Tracy\Dumper::dump($value); // Chair
dump($value); // Chair
// fetch complete result set
echo "<h2>fetchAll()</h2>\n";
$all = dibi::fetchAll('SELECT * FROM products');
Tracy\Dumper::dump($all);
dump($all);
// fetch complete result set like association array
echo "<h2>fetchAssoc('title')</h2>\n";
$res = dibi::query('SELECT * FROM products');
$assoc = $res->fetchAssoc('title'); // key
Tracy\Dumper::dump($assoc);
dump($assoc);
// fetch complete result set like pairs key => value
echo "<h2>fetchPairs('product_id', 'title')</h2>\n";
$res = dibi::query('SELECT * FROM products');
$pairs = $res->fetchPairs('product_id', 'title');
Tracy\Dumper::dump($pairs);
dump($pairs);
// fetch row by row
echo "<h2>using foreach</h2>\n";
$res = dibi::query('SELECT * FROM products');
foreach ($res as $n => $row) {
Tracy\Dumper::dump($row);
dump($row);
}
@@ -79,16 +73,14 @@ $res = dibi::query('
INNER JOIN customers USING (customer_id)
');
echo "<h2>fetchAssoc('name|title')</h2>\n";
$assoc = $res->fetchAssoc('name|title'); // key
Tracy\Dumper::dump($assoc);
echo "<h2>fetchAssoc('customers.name|products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name|products.title'); // key
dump($assoc);
echo "<h2>fetchAssoc('name[]title')</h2>\n";
$res = dibi::query('SELECT * FROM products INNER JOIN orders USING (product_id) INNER JOIN customers USING (customer_id)');
$assoc = $res->fetchAssoc('name[]title'); // key
Tracy\Dumper::dump($assoc);
echo "<h2>fetchAssoc('customers.name[]products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name[]products.title'); // key
dump($assoc);
echo "<h2>fetchAssoc('name->title')</h2>\n";
$res = dibi::query('SELECT * FROM products INNER JOIN orders USING (product_id) INNER JOIN customers USING (customer_id)');
$assoc = $res->fetchAssoc('name->title'); // key
Tracy\Dumper::dump($assoc);
echo "<h2>fetchAssoc('customers.name->products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name->products.title'); // key
dump($assoc);

View File

@@ -4,13 +4,14 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
$count = dibi::loadFile('compress.zlib://data/sample.dump.sql.gz');

View File

@@ -0,0 +1,44 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Nette Debugger & SQL Exceptions | dibi</h1>
<p>Dibi can display and log exceptions via Nette Debugger, part of Nette Framework.</p>
<ul>
<li>Nette Framework: http://nette.org
</ul>
<?php
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
// enable Nette Debugger
ndebug();
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => array(
'run' => TRUE,
)
));
// throws error because SQL is bad
dibi::query('SELECT * FROM customers WHERE customer_id < ?', 38);
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => array(
'run' => TRUE,
)
));
// throws error because SQL is bad
dibi::query('SELECT FROM customers WHERE customer_id < ?', 38);

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<style> html { background: url(data/arrow.png) no-repeat bottom right; height: 100%; } </style>
<h1>Nette Debugger & Variables | dibi</h1>
<p>Dibi can dump variables via Nette Debugger, part of Nette Framework.</p>
<ul>
<li>Nette Framework: http://nette.org
</ul>
<?php
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
// enable Nette Debugger
NDebugger::enable();
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => array(
'run' => TRUE,
)
));
NDebugger::barDump( dibi::fetchAll('SELECT * FROM customers WHERE customer_id < ?', 38), '[customers]' );

View File

@@ -4,13 +4,14 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// some variables
@@ -32,13 +33,13 @@ dibi::test('
// if & else & (optional) end
dibi::test('
dibi::test("
SELECT *
FROM people
WHERE id > 0
%if', ($foo > 0), 'AND foo=?', $foo, '
%else %if', ($bar > 0), 'AND bar=?', $bar, '
');
%if", ($foo > 0), "AND foo=?", $foo, "
%else %if", ($bar > 0), "AND bar=?", $bar, "
");
// -> SELECT * FROM people WHERE id > 0 AND bar=2
@@ -55,7 +56,7 @@ dibi::test('
// IF()
dibi::test('UPDATE products SET', [
'price' => ['IF(price_fixed, price, ?)', 123],
]);
dibi::test('UPDATE products SET', array(
'price' => array('IF(price_fixed, price, ?)', 123),
));
// -> SELECT * FROM customers WHERE LIMIT 10

View File

@@ -4,15 +4,16 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// SELECT
@@ -23,62 +24,62 @@ dibi::test('
SELECT COUNT(*) as [count]
FROM [comments]
WHERE [ip] LIKE ?', $ipMask, '
AND [date] > ', new Dibi\DateTime($timestamp)
AND [date] > ', new DibiDateTime($timestamp)
);
// -> SELECT COUNT(*) as [count] FROM [comments] WHERE [ip] LIKE '192.168.%' AND [date] > 876693600
// dibi detects INSERT or REPLACE command
dibi::test('
REPLACE INTO products', [
REPLACE INTO products', array(
'title' => 'Super product',
'price' => 318,
'active' => TRUE,
]);
));
// -> REPLACE INTO products ([title], [price], [active]) VALUES ('Super product', 318, 1)
// multiple INSERT command
$array = [
$array = array(
'title' => 'Super Product',
'price' => 12,
'brand' => NULL,
'created' => new DateTime,
];
dibi::test('INSERT INTO products', $array, $array, $array);
);
dibi::test("INSERT INTO products", $array, $array, $array);
// -> INSERT INTO products ([title], [price], [brand], [created]) VALUES ('Super Product', ...) , (...) , (...)
// dibi detects UPDATE command
dibi::test('
UPDATE colors SET', [
dibi::test("
UPDATE colors SET", array(
'color' => 'blue',
'order' => 12,
], '
WHERE id=?', 123);
), "
WHERE id=?", 123);
// -> UPDATE colors SET [color]='blue', [order]=12 WHERE id=123
// modifier applied to array
$array = [1, 2, 3];
dibi::test('
$array = array(1, 2, 3);
dibi::test("
SELECT *
FROM people
WHERE id IN (?)', $array
WHERE id IN (?)", $array
);
// -> SELECT * FROM people WHERE id IN ( 1, 2, 3 )
// modifier %by for ORDER BY
$order = [
$order = array(
'field1' => 'asc',
'field2' => 'desc',
];
dibi::test('
);
dibi::test("
SELECT *
FROM people
ORDER BY %by', $order, '
');
ORDER BY %by", $order, "
");
// -> SELECT * FROM people ORDER BY [field1] ASC, [field2] DESC

View File

@@ -1,39 +1,33 @@
<?php
use Dibi\Type;
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
Tracy\Debugger::enable();
date_default_timezone_set('Europe/Prague');
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Result Set Data Types | dibi</h1>
<h1>Result Set Data Types | dibi</h1>
<?php
dibi::connect([
'driver' => 'sqlite3',
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
ndebug();
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// using manual hints
$res = dibi::query('SELECT * FROM [customers]');
$res->setType('customer_id', Type::INTEGER)
->setType('added', Type::DATETIME)
->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
$res->setType('customer_id', Dibi::INTEGER)
->setType('added', Dibi::DATETIME)
->setFormat(dibi::DATETIME, 'Y-m-d H:i:s');
Tracy\Dumper::dump($res->fetch());
dump( $res->fetch() );
// outputs:
// Dibi\Row(3) {
// DibiRow(3) {
// customer_id => 1
// name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19)
@@ -42,9 +36,9 @@ Tracy\Dumper::dump($res->fetch());
// using auto-detection (works well with MySQL or other strictly typed databases)
$res = dibi::query('SELECT * FROM [customers]');
Tracy\Dumper::dump($res->fetch());
dump( $res->fetch() );
// outputs:
// Dibi\Row(3) {
// DibiRow(3) {
// customer_id => 1
// name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19)

View File

@@ -1,33 +0,0 @@
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
// enable Tracy
Tracy\Debugger::enable();
$connection = dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => [
'run' => TRUE,
],
]);
// add panel to debug bar
$panel = new Dibi\Bridges\Tracy\Panel;
$panel->register($connection);
// throws error because SQL is bad
dibi::query('SELECT FROM customers WHERE customer_id < ?', 38);
?><!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Tracy & SQL Exceptions | dibi</h1>
<p>Dibi can display and log exceptions via <a href="https://tracy.nette.org">Tracy</a>.</p>

View File

@@ -1,40 +0,0 @@
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
// enable Tracy
Tracy\Debugger::enable();
$connection = dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => [
'run' => TRUE,
],
]);
// add panel to debug bar
$panel = new Dibi\Bridges\Tracy\Panel;
$panel->register($connection);
// query will be logged
dibi::query('SELECT 123');
// result set will be dumped
Tracy\Debugger::barDump(dibi::fetchAll('SELECT * FROM customers WHERE customer_id < ?', 38), '[customers]');
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<style> html { background: url(data/arrow.png) no-repeat bottom right; height: 100%; } </style>
<h1>Tracy | dibi</h1>
<p>Dibi can log queries and dump variables to the <a href="https://tracy.nette.org">Tracy</a>.</p>

View File

@@ -4,26 +4,27 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS!
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'formatDate' => "'Y-m-d'",
'formatDateTime' => "'Y-m-d H-i-s'",
]);
));
// generate and dump SQL
dibi::test('
INSERT INTO [mytable]', [
'id' => 123,
'date' => new DateTime('12.3.2007'),
dibi::test("
INSERT INTO [mytable]", array(
'id' => 123,
'date' => new DateTime('12.3.2007'),
'stamp' => new DateTime('23.1.2007 10:23'),
]
)
);
// -> INSERT INTO [mytable] ([id], [date], [stamp]) VALUES (123, '2007-03-12', '2007-01-23 10-23-00')

View File

@@ -1,33 +1,30 @@
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
Tracy\Debugger::enable();
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Extension Methods | dibi</h1>
<?php
dibi::connect([
'driver' => 'sqlite3',
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
ndebug();
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// using the "prototype" to add custom method to class Dibi\Result
Dibi\Result::extensionMethod('fetchShuffle', function (Dibi\Result $obj) {
// using the "prototype" to add custom method to class DibiResult
function DibiResult_prototype_fetchShuffle(DibiResult $obj)
{
$all = $obj->fetchAll();
shuffle($all);
return $all;
});
}
// fetch complete result set shuffled
$res = dibi::query('SELECT * FROM [customers]');
$all = $res->fetchShuffle();
Tracy\Dumper::dump($all);
dump($all);

View File

@@ -4,23 +4,24 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
$id = 10;
$record = [
'title' => 'Super product',
'price' => 318,
$record = array(
'title' => 'Super product',
'price' => 318,
'active' => TRUE,
];
);
// SELECT ...
dibi::select('product_id')->as('id')

View File

@@ -4,13 +4,14 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// no limit

View File

@@ -4,20 +4,21 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
// enable query logging to this file
'profiler' => [
'profiler' => array(
'run' => TRUE,
'file' => 'data/log.sql',
],
]);
),
));
try {
@@ -26,12 +27,13 @@ try {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', 5);
$res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < ?', 38);
} catch (Dibi\Exception $e) {
} catch (DibiException $e) {
echo '<p>', get_class($e), ': ', $e->getMessage(), '</p>';
}
// outputs a log file
echo '<h2>File data/log.sql:</h2>';
echo "<h2>File data/log.sql:</h2>";
echo '<pre>', file_get_contents('data/log.sql'), '</pre>';

View File

@@ -6,20 +6,21 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => [
'profiler' => array(
'run' => TRUE,
],
]);
)
));
// execute some queries...
for ($i = 0; $i < 20; $i++) {
for ($i=0; $i<20; $i++) {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', $i);
}

View File

@@ -4,19 +4,20 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// create new substitution :blog: ==> wp_
dibi::getSubstitutes()->blog = 'wp_';
dibi::test('SELECT * FROM [:blog:items]');
dibi::test("SELECT * FROM [:blog:items]");
// -> SELECT * FROM [wp_items]

View File

@@ -4,13 +4,14 @@
<?php
require __DIR__ . '/../src/loader.php';
require dirname(__FILE__) . '/Nette/Debugger.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
dibi::connect([
'driver' => 'sqlite3',
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
echo "<h2>Before</h2>\n";
@@ -19,9 +20,9 @@ dibi::query('SELECT * FROM [products]')->dump();
dibi::begin();
dibi::query('INSERT INTO [products]', [
dibi::query('INSERT INTO [products]', array(
'title' => 'Test product',
]);
));
echo "<h2>After INSERT</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();

View File

@@ -16,7 +16,7 @@ remains intact.
New BSD License
---------------
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
Copyright (c) 2004, 2014 David Grudl (http://davidgrudl.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,

View File

@@ -1,42 +1,36 @@
[Dibi](https://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN)
[Dibi](http://dibiphp.com) - smart database layer for PHP
=========================================================
[![Downloads this Month](https://img.shields.io/packagist/dm/dibi/dibi.svg)](https://packagist.org/packages/dibi/dibi)
[![Build Status](https://travis-ci.org/dg/dibi.svg?branch=master)](https://travis-ci.org/dg/dibi)
[![Build Status Windows](https://ci.appveyor.com/api/projects/status/github/dg/dibi?branch=master&svg=true)](https://ci.appveyor.com/project/dg/dibi/branch/master)
[![Latest Stable Version](https://poser.pugx.org/dibi/dibi/v/stable)](https://github.com/dg/dibi/releases)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/dg/dibi/blob/master/license.md)
Database access functions in PHP are not standardised. This library
hides the differences between them, and above all, it gives you a very handy interface.
The best way to install Dibi is to use a [Composer](https://getcomposer.org/download):
The best way to install Dibi is to use a [Composer](http://getcomposer.org/download):
php composer.phar require dibi/dibi
Or you can download the latest package from https://dibiphp.com. In this
Or you can download the latest package from http://dibiphp.com. In this
package is also `Dibi.minified`, shrinked single-file version of whole Dibi,
useful when you don't want to modify the library, but just use it.
Dibi requires PHP 5.4.4 or later. It has been tested with PHP 7 too.
Dibi requires PHP 5.2.0 or later. It has been tested with PHP 5.5 too.
Examples
--------
Refer to the `examples` directory for examples. Dibi documentation is
available on the [homepage](https://dibiphp.com).
available on the [homepage](http://dibiphp.com).
Connect to database:
```php
// connect to database (static way)
dibi::connect([
dibi::connect(array(
'driver' => 'mysql',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
]);
));
// or object way; in all other examples use $connection-> instead of dibi::
$connection = new DibiConnection($options);
@@ -47,19 +41,19 @@ SELECT, INSERT, UPDATE
```php
dibi::query('SELECT * FROM users WHERE id = ?', $id);
$arr = [
$arr = array(
'name' => 'John',
'is_admin' => TRUE,
];
);
dibi::query('INSERT INTO users', $arr);
// INSERT INTO users (`name`, `is_admin`) VALUES ('John', 1)
dibi::query('UPDATE users SET', $arr, 'WHERE `id`=?', $x);
// UPDATE users SET `name`='John', `is_admin`=1 WHERE `id` = 123
dibi::query('UPDATE users SET', [
dibi::query('UPDATE users SET', array(
'title' => array('SHA1(?)', 'tajneheslo'),
]);
));
// UPDATE users SET 'title' = SHA1('tajneheslo')
```
@@ -82,10 +76,10 @@ foreach ($result as $n => $row) {
Modifiers for arrays:
```php
dibi::query('SELECT * FROM users WHERE %and', [
dibi::query('SELECT * FROM users WHERE %and', array(
array('number > ?', 10),
array('number < ?', 100),
]);
));
// SELECT * FROM users WHERE (number > 10) AND (number < 100)
```
@@ -117,9 +111,9 @@ dibi::query("SELECT * FROM table WHERE name LIKE %like~", $query);
DateTime:
```php
dibi::query('UPDATE users SET', [
dibi::query('UPDATE users SET', array(
'time' => new DateTime,
]);
));
// UPDATE users SET ('2008-01-01 01:08:10')
```
@@ -130,4 +124,3 @@ echo dibi::$sql; // last SQL query
echo dibi::$elapsedTime;
echo dibi::$numOfQueries;
echo dibi::$totalTime;
```

View File

@@ -1,59 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Bridges\Nette;
use Dibi;
use Nette;
/**
* Dibi extension for Nette Framework 2.2. Creates 'connection' & 'panel' services.
*/
class DibiExtension22 extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$container = $this->getContainerBuilder();
$config = $this->getConfig();
$useProfiler = isset($config['profiler'])
? $config['profiler']
: class_exists('Tracy\Debugger') && $container->parameters['debugMode'];
unset($config['profiler']);
if (isset($config['flags'])) {
$flags = 0;
foreach ((array) $config['flags'] as $flag) {
$flags |= constant($flag);
}
$config['flags'] = $flags;
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
if (class_exists('Tracy\Debugger')) {
$connection->addSetup(
[new Nette\DI\Statement('Tracy\Debugger::getBlueScreen'), 'addPanel'],
[['Dibi\Bridges\Tracy\Panel', 'renderException']]
);
}
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Tracy\Panel', [
isset($config['explain']) ? $config['explain'] : TRUE,
isset($config['filter']) && $config['filter'] === FALSE ? Dibi\Event::ALL : Dibi\Event::QUERY,
]);
$connection->addSetup([$panel, 'register'], [$connection]);
}
}
}

View File

@@ -1,155 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Bridges\Tracy;
use Dibi;
use Dibi\Event;
use Dibi\Helpers;
use Tracy;
/**
* Dibi panel for Tracy.
*/
class Panel implements Tracy\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
public static $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
/** @var int */
public $filter;
/** @var array */
private $events = [];
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain;
}
public function register(Dibi\Connection $connection)
{
Tracy\Debugger::getBar()->addPanel($this);
Tracy\Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
/**
* After event notification.
* @return void
*/
public function logEvent(Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$this->events[] = $event;
}
/**
* Returns blue-screen custom tab.
* @return array|NULL
*/
public static function renderException($e)
{
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE),
];
}
}
/**
* Returns HTML code for custom tab. (Tracy\IBarPanel)
* @return string
*/
public function getTab()
{
$totalTime = 0;
$count = count($this->events);
foreach ($this->events as $event) {
$totalTime += $event->time;
}
return '<span title="dibi"><svg viewBox="0 0 2048 2048" style="vertical-align: bottom; width:1.23em; height:1.55em"><path fill="' . ($count ? '#b079d6' : '#aaa') . '" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/></svg><span class="tracy-label">'
. $count . ' queries'
. ($totalTime ? ' / ' . number_format($totalTime * 1000, 1, '.', '') . 'ms' : '')
. '</span></span>';
}
/**
* Returns HTML code for custom panel. (Tracy\IBarPanel)
* @return string|NULL
*/
public function getPanel()
{
if (!$this->events) {
return NULL;
}
$totalTime = $s = NULL;
foreach ($this->events as $event) {
$totalTime += $event->time;
$connection = $event->connection;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === Event::SELECT) {
try {
$backup = [$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) {
}
list($connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
}
$s .= '<tr><td>' . number_format($event->time * 1000, 3, '.', '');
if ($explain) {
static $counter;
$counter++;
$s .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>";
}
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) {
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
}
if ($event->source) {
$s .= Tracy\Helpers::editorLink($event->source[0], $event->source[1]);//->class('tracy-DibiProfiler-source');
}
$s .= "</td><td>{$event->count}</td></tr>";
}
return '<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
<h1>Queries: ' . count($this->events)
. ($totalTime === NULL ? '' : ', time: ' . number_format($totalTime * 1000, 1, '.', '') . 'ms') . ', '
. htmlSpecialChars($connection->getConfig('driver') . ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
. ($connection->getConfig('host') ? '@' . $connection->getConfig('host') : '')) . '</h1>
<div class="tracy-inner tracy-DibiProfiler">
<table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th></tr>' . $s . '
</table>
</div>';
}
}

View File

@@ -1,75 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* DateTime.
*/
class DateTime extends \DateTime
{
use Strict;
/**
* @param string|int
*/
public function __construct($time = 'now', \DateTimeZone $timezone = NULL)
{
if (is_numeric($time)) {
parent::__construct('@' . $time);
$this->setTimeZone($timezone ? $timezone : new \DateTimeZone(date_default_timezone_get()));
} elseif ($timezone === NULL) {
parent::__construct($time);
} else {
parent::__construct($time, $timezone);
}
}
public function modifyClone($modify = '')
{
$dolly = clone($this);
return $modify ? $dolly->modify($modify) : $dolly;
}
public function setTimestamp($timestamp)
{
$zone = $this->getTimezone();
$this->__construct('@' . $timestamp);
return $this->setTimeZone($zone);
}
public function getTimestamp()
{
$ts = $this->format('U');
return is_float($tmp = $ts * 1) ? $ts : $tmp;
}
public function __toString()
{
return $this->format('Y-m-d H:i:s');
}
public function __wakeup()
{
if (isset($this->fix, $this->fix[1])) {
$this->__construct($this->fix[0], new \DateTimeZone($this->fix[1]));
unset($this->fix);
} elseif (isset($this->fix)) {
$this->__construct($this->fix[0]);
unset($this->fix);
} else {
parent::__wakeup();
}
}
}

View File

@@ -1,135 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for MySQL databases.
* @internal
*/
class MySqlReflector implements Dibi\Reflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->driver->query('SHOW FULL TABLES');
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = [
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}");
$columns = [];
while ($row = $res->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$columns[] = [
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'unsigned' => (bool) strstr($row['Type'], 'unsigned'),
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}");
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$indexes[$row['Key_name']]['name'] = $row['Key_name'];
$indexes[$row['Key_name']]['unique'] = !$row['Non_unique'];
$indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$indexes[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
* @throws Dibi\NotSupportedException
*/
public function getForeignKeys($table)
{
$data = $this->driver->query("SELECT `ENGINE` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = {$this->driver->escapeText($table)}")->fetch(TRUE);
if ($data['ENGINE'] !== 'InnoDB') {
throw new Dibi\NotSupportedException("Foreign keys are not supported in {$data['ENGINE']} tables.");
}
$res = $this->driver->query("
SELECT rc.CONSTRAINT_NAME, rc.UPDATE_RULE, rc.DELETE_RULE, kcu.REFERENCED_TABLE_NAME,
GROUP_CONCAT(kcu.REFERENCED_COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) AS REFERENCED_COLUMNS,
GROUP_CONCAT(kcu.COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) AS COLUMNS
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
INNER JOIN information_schema.KEY_COLUMN_USAGE kcu ON
kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
WHERE rc.CONSTRAINT_SCHEMA = DATABASE()
AND rc.TABLE_NAME = {$this->driver->escapeText($table)}
GROUP BY rc.CONSTRAINT_NAME
");
$foreignKeys = [];
while ($row = $res->fetch(TRUE)) {
$keyName = $row['CONSTRAINT_NAME'];
$foreignKeys[$keyName]['name'] = $keyName;
$foreignKeys[$keyName]['local'] = explode(',', $row['COLUMNS']);
$foreignKeys[$keyName]['table'] = $row['REFERENCED_TABLE_NAME'];
$foreignKeys[$keyName]['foreign'] = explode(',', $row['REFERENCED_COLUMNS']);
$foreignKeys[$keyName]['onDelete'] = $row['DELETE_RULE'];
$foreignKeys[$keyName]['onUpdate'] = $row['UPDATE_RULE'];
}
return array_values($foreignKeys);
}
}

View File

@@ -1,581 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use PDO;
/**
* The dibi driver for PDO.
*
* Driver options:
* - dsn => driver specific DSN
* - username (or user)
* - password (or pass)
* - options (array) => driver specific options {@see PDO::__construct}
* - resource (PDO) => existing connection
* - version
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var PDO Connection resource */
private $connection;
/** @var \PDOStatement|NULL Resultset resource */
private $resultSet;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $driverName;
/** @var string */
private $serverVersion = '';
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pdo')) {
throw new Dibi\NotSupportedException("PHP extension 'pdo' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array &$config)
{
$foo = &$config['dsn'];
$foo = &$config['options'];
Dibi\Helpers::alias($config, 'resource', 'pdo');
if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource'];
unset($config['resource'], $config['pdo']);
} else {
try {
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
} catch (\PDOException $e) {
if ($e->getMessage() === 'could not find driver') {
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
}
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
$this->serverVersion = isset($config['version'])
? $config['version']
: @$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); // @ - may be not supported
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection = NULL;
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
// must detect if SQL returns result set or num of affected rows
$cmd = strtoupper(substr(ltrim($sql), 0, 6));
static $list = ['UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1];
$this->affectedRows = FALSE;
if (isset($list[$cmd])) {
$this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows !== FALSE) {
return NULL;
}
} else {
$res = $this->connection->query($sql);
if ($res) {
return $this->createResultDriver($res);
}
}
list($sqlState, $code, $message) = $this->connection->errorInfo();
$message = "SQLSTATE[$sqlState]: $message";
switch ($this->driverName) {
case 'mysql':
throw MySqliDriver::createException($message, $code, $sql);
case 'oci':
throw OracleDriver::createException($message, $code, $sql);
case 'pgsql':
throw PostgreDriver::createException($message, $sqlState, $sql);
case 'sqlite':
throw Sqlite3Driver::createException($message, $code, $sql);
default:
throw new Dibi\DriverException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertId();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!$this->connection->commit()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Returns the connection resource.
* @return PDO
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
switch ($this->driverName) {
case 'mysql':
return new MySqlReflector($this);
case 'sqlite':
return new SqliteReflector($this);
default:
throw new Dibi\NotSupportedException;
}
}
/**
* Result set driver factory.
* @param \PDOStatement
* @return Dibi\ResultDriver
*/
public function createResultDriver(\PDOStatement $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param string value
* @return string encoded value
*/
public function escapeText($value)
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_STR);
}
}
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_LOB);
}
}
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'oci':
case 'pgsql':
return '"' . str_replace('"', '""', $value) . '"';
case 'sqlite':
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'mssql':
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
case 'dblib':
case 'sqlsrv':
return '[' . str_replace(']', ']]', $value) . ']';
default:
return $value;
}
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
if ($this->driverName === 'pgsql') {
return $value ? 'TRUE' : 'FALSE';
} else {
return $value ? '1' : '0';
}
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->driverName === 'odbc' ? '#m/d/Y#' : "'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->driverName === 'odbc' ? "#m/d/Y H:i:s#" : "'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
switch ($this->driverName) {
case 'mysql':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'oci':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'pgsql':
$bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off
$value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'sqlite':
$value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
case 'odbc':
case 'mssql':
case 'dblib':
case 'sqlsrv':
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
default:
throw new Dibi\NotImplementedException;
}
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
switch ($this->driverName) {
case 'mysql':
if ($limit !== NULL || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break;
case 'pgsql':
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset) {
$sql .= ' OFFSET ' . (int) $offset;
}
break;
case 'sqlite':
if ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break;
case 'oci':
if ($offset) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit !== NULL) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
break;
case 'mssql':
case 'sqlsrv':
case 'dblib':
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
if ($limit !== NULL) {
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
} elseif ($offset) {
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
}
break;
}
// intentionally break omitted
case 'odbc':
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
break;
}
// intentionally break omitted
default:
throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.');
}
}
/********************* result set ****************d*g**/
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return $this->resultSet->rowCount();
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
* @throws Dibi\Exception
*/
public function getResultColumns()
{
$count = $this->resultSet->columnCount();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) {
throw new Dibi\NotSupportedException('Driver does not support meta data.');
}
$row = $row + [
'table' => NULL,
'native_type' => 'VAR_STRING',
];
$columns[] = [
'name' => $row['name'],
'table' => $row['table'],
'nativetype' => $row['native_type'],
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TIME_INTERVAL : NULL,
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return \PDOStatement|NULL
*/
public function getResultResource()
{
return $this->resultSet;
}
}

View File

@@ -1,277 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
class Helpers
{
use Strict;
/** @var array */
private static $types;
/**
* Prints out a syntax highlighted version of the SQL command or Result.
* @param string|Result
* @param bool return output instead of printing it?
* @return string
*/
public static function dump($sql = NULL, $return = FALSE)
{
ob_start();
if ($sql instanceof Result && PHP_SAPI === 'cli') {
$hasColors = (substr(getenv('TERM'), 0, 5) === 'xterm');
$maxLen = 0;
foreach ($sql as $i => $row) {
if ($i === 0) {
foreach ($row as $col => $foo) {
$len = mb_strlen($col);
$maxLen = max($len, $maxLen);
}
}
echo $hasColors ? "\033[1;37m#row: $i\033[0m\n" : "#row: $i\n";
foreach ($row as $col => $val) {
$spaces = $maxLen - mb_strlen($col) + 2;
echo "$col" . str_repeat(' ', $spaces) . "$val\n";
}
echo "\n";
}
echo empty($row) ? "empty result set\n\n" : "\n";
} elseif ($sql instanceof Result) {
foreach ($sql as $i => $row) {
if ($i === 0) {
echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
foreach ($row as $col => $foo) {
echo "\t\t<th>" . htmlSpecialChars((string) $col) . "</th>\n";
}
echo "\t</tr>\n</thead>\n<tbody>\n";
}
echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
foreach ($row as $col) {
echo "\t\t<td>", htmlSpecialChars((string) $col), "</td>\n";
}
echo "\t</tr>\n";
}
echo empty($row)
? '<p><em>empty result set</em></p>'
: "</tbody>\n</table>\n";
} else {
if ($sql === NULL) {
$sql = \dibi::$sql;
}
static $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|FETCH\s+NEXT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE|START\s+TRANSACTION|BEGIN|COMMIT|ROLLBACK(?:\s+TO\s+SAVEPOINT)?|(?:RELEASE\s+)?SAVEPOINT';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE';
// insert new lines
$sql = " $sql ";
$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
// reduce spaces
$sql = preg_replace('#[ \t]{2,}#', ' ', $sql);
$sql = wordwrap($sql, 100);
$sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql);
// syntax highlight
$highlighter = "#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is";
if (PHP_SAPI === 'cli') {
if (substr(getenv('TERM'), 0, 5) === 'xterm') {
$sql = preg_replace_callback($highlighter, function ($m) {
if (!empty($m[1])) { // comment
return "\033[1;30m" . $m[1] . "\033[0m";
} elseif (!empty($m[2])) { // error
return "\033[1;31m" . $m[2] . "\033[0m";
} elseif (!empty($m[3])) { // most important keywords
return "\033[1;34m" . $m[3] . "\033[0m";
} elseif (!empty($m[4])) { // other keywords
return "\033[1;32m" . $m[4] . "\033[0m";
}
}, $sql);
}
echo trim($sql) . "\n\n";
} else {
$sql = htmlSpecialChars($sql);
$sql = preg_replace_callback($highlighter, function ($m) {
if (!empty($m[1])) { // comment
return '<em style="color:gray">' . $m[1] . '</em>';
} elseif (!empty($m[2])) { // error
return '<strong style="color:red">' . $m[2] . '</strong>';
} elseif (!empty($m[3])) { // most important keywords
return '<strong style="color:blue">' . $m[3] . '</strong>';
} elseif (!empty($m[4])) { // other keywords
return '<strong style="color:green">' . $m[4] . '</strong>';
}
}, $sql);
echo '<pre class="dump">', trim($sql), "</pre>\n\n";
}
}
if ($return) {
return ob_get_clean();
} else {
ob_end_flush();
}
}
/**
* Finds the best suggestion.
* @return string|NULL
* @internal
*/
public static function getSuggestion(array $items, $value)
{
$best = NULL;
$min = (strlen($value) / 4 + 1) * 10 + .1;
foreach (array_unique($items, SORT_REGULAR) as $item) {
$item = is_object($item) ? $item->getName() : $item;
if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) {
$min = $len;
$best = $item;
}
}
return $best;
}
/** @internal */
public static function escape(Driver $driver, $value, $type)
{
static $types = [
Type::TEXT => 'text',
Type::BINARY => 'binary',
Type::BOOL => 'bool',
Type::DATE => 'date',
Type::DATETIME => 'datetime',
\dibi::IDENTIFIER => 'identifier',
];
if (isset($types[$type])) {
return $driver->{'escape' . $types[$type]}($value);
} else {
throw new \InvalidArgumentException('Unsupported type.');
}
}
/**
* Heuristic type detection.
* @param string
* @return string|NULL
* @internal
*/
public static function detectType($type)
{
static $patterns = [
'^_' => Type::TEXT, // PostgreSQL arrays
'BYTEA|BLOB|BIN' => Type::BINARY,
'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::TEXT,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER,
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT,
'^TIME$' => Type::TIME,
'TIME' => Type::DATETIME, // DATETIME, TIMESTAMP
'DATE' => Type::DATE,
'BOOL' => Type::BOOL,
];
foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return $val;
}
}
return NULL;
}
/**
* @internal
*/
public static function getTypeCache()
{
if (self::$types === NULL) {
self::$types = new HashMap([__CLASS__, 'detectType']);
}
return self::$types;
}
/**
* Apply configuration alias or default values.
* @param array connect configuration
* @param string key
* @param string alias key
* @return void
*/
public static function alias(&$config, $key, $alias)
{
$foo = &$config;
foreach (explode('|', $key) as $key) {
$foo = &$foo[$key];
}
if (!isset($foo) && isset($config[$alias])) {
$foo = $config[$alias];
unset($config[$alias]);
}
}
/**
* Import SQL dump from file.
* @return int count of sql commands
*/
public static function loadFromFile(Connection $connection, $file)
{
@set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @
if (!$handle) {
throw new \RuntimeException("Cannot open file '$file'.");
}
$count = 0;
$delimiter = ';';
$sql = '';
$driver = $connection->getDriver();
while (!feof($handle)) {
$s = rtrim(fgets($handle));
if (substr($s, 0, 10) === 'DELIMITER ') {
$delimiter = substr($s, 10);
} elseif (substr($s, -strlen($delimiter)) === $delimiter) {
$sql .= substr($s, 0, -strlen($delimiter));
$driver->query($sql);
$sql = '';
$count++;
} else {
$sql .= $s . "\n";
}
}
if (trim($sql) !== '') {
$driver->query($sql);
$count++;
}
fclose($handle);
return $count;
}
}

View File

@@ -1,166 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
use Dibi\Type;
/**
* Reflection metadata class for a table or result set column.
*
* @property-read string $name
* @property-read string $fullName
* @property-read Table $table
* @property-read string $type
* @property-read mixed $nativeType
* @property-read int|NULL $size
* @property-read bool|NULL $unsigned
* @property-read bool|NULL $nullable
* @property-read bool|NULL $autoIncrement
* @property-read mixed $default
*/
class Column
{
use Dibi\Strict;
/** @var Dibi\Reflector|NULL when created by Result */
private $reflector;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info;
public function __construct(Dibi\Reflector $reflector = NULL, array $info)
{
$this->reflector = $reflector;
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return string
*/
public function getFullName()
{
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL;
}
/**
* @return bool
*/
public function hasTable()
{
return !empty($this->info['table']);
}
/**
* @return Table
*/
public function getTable()
{
if (empty($this->info['table']) || !$this->reflector) {
throw new Dibi\Exception("Table is unknown or not available.");
}
return new Table($this->reflector, ['name' => $this->info['table']]);
}
/**
* @return string|NULL
*/
public function getTableName()
{
return isset($this->info['table']) && $this->info['table'] != NULL ? $this->info['table'] : NULL; // intentionally ==
}
/**
* @return string
*/
public function getType()
{
return Dibi\Helpers::getTypeCache()->{$this->info['nativetype']};
}
/**
* @return string
*/
public function getNativeType()
{
return $this->info['nativetype'];
}
/**
* @return int|NULL
*/
public function getSize()
{
return isset($this->info['size']) ? (int) $this->info['size'] : NULL;
}
/**
* @return bool|NULL
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/**
* @return bool|NULL
*/
public function isNullable()
{
return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : NULL;
}
/**
* @return bool|NULL
*/
public function isAutoIncrement()
{
return isset($this->info['autoincrement']) ? (bool) $this->info['autoincrement'] : NULL;
}
/**
* @return mixed
*/
public function getDefault()
{
return isset($this->info['default']) ? $this->info['default'] : NULL;
}
/**
* @param string
* @return mixed
*/
public function getVendorInfo($key)
{
return isset($this->info['vendor'][$key]) ? $this->info['vendor'][$key] : NULL;
}
}

View File

@@ -1,115 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a database.
*
* @property-read string $name
* @property-read array $tables
* @property-read array $tableNames
*/
class Database
{
use Dibi\Strict;
/** @var Dibi\Reflector */
private $reflector;
/** @var string */
private $name;
/** @var array */
private $tables;
public function __construct(Dibi\Reflector $reflector, $name)
{
$this->reflector = $reflector;
$this->name = $name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return Table[]
*/
public function getTables()
{
$this->init();
return array_values($this->tables);
}
/**
* @return string[]
*/
public function getTableNames()
{
$this->init();
$res = [];
foreach ($this->tables as $table) {
$res[] = $table->getName();
}
return $res;
}
/**
* @param string
* @return Table
*/
public function getTable($name)
{
$this->init();
$l = strtolower($name);
if (isset($this->tables[$l])) {
return $this->tables[$l];
} else {
throw new Dibi\Exception("Database '$this->name' has no table '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasTable($name)
{
$this->init();
return isset($this->tables[strtolower($name)]);
}
/**
* @return void
*/
protected function init()
{
if ($this->tables === NULL) {
$this->tables = [];
foreach ($this->reflector->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new Table($this->reflector, $info);
}
}
}
}

View File

@@ -1,54 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a foreign key.
*
* @property-read string $name
* @property-read array $references
*/
class ForeignKey
{
use Dibi\Strict;
/** @var string */
private $name;
/** @var array of [local, foreign, onDelete, onUpdate] */
private $references;
public function __construct($name, array $references)
{
$this->name = $name;
$this->references = $references;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getReferences()
{
return $this->references;
}
}

View File

@@ -1,70 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a index or primary key.
*
* @property-read string $name
* @property-read array $columns
* @property-read bool $unique
* @property-read bool $primary
*/
class Index
{
use Dibi\Strict;
/** @var array (name, columns, [unique], [primary]) */
private $info;
public function __construct(array $info)
{
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return array
*/
public function getColumns()
{
return $this->info['columns'];
}
/**
* @return bool
*/
public function isUnique()
{
return !empty($this->info['unique']);
}
/**
* @return bool
*/
public function isPrimary()
{
return !empty($this->info['primary']);
}
}

View File

@@ -1,106 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a result set.
*
* @property-read array $columns
* @property-read array $columnNames
*/
class Result
{
use Dibi\Strict;
/** @var Dibi\ResultDriver */
private $driver;
/** @var array */
private $columns;
/** @var array */
private $names;
public function __construct(Dibi\ResultDriver $driver)
{
$this->driver = $driver;
}
/**
* @return Column[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @param bool
* @return string[]
*/
public function getColumnNames($fullNames = FALSE)
{
$this->initColumns();
$res = [];
foreach ($this->columns as $column) {
$res[] = $fullNames ? $column->getFullName() : $column->getName();
}
return $res;
}
/**
* @param string
* @return Column
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->names[$l])) {
return $this->names[$l];
} else {
throw new Dibi\Exception("Result set has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->names[strtolower($name)]);
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = [];
$reflector = $this->driver instanceof Dibi\Reflector ? $this->driver : NULL;
foreach ($this->driver->getResultColumns() as $info) {
$this->columns[] = $this->names[strtolower($info['name'])] = new Column($reflector, $info);
}
}
}
}

View File

@@ -1,201 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a database table.
*
* @property-read string $name
* @property-read bool $view
* @property-read array $columns
* @property-read array $columnNames
* @property-read array $foreignKeys
* @property-read array $indexes
* @property-read Index $primaryKey
*/
class Table
{
use Dibi\Strict;
/** @var Dibi\Reflector */
private $reflector;
/** @var string */
private $name;
/** @var bool */
private $view;
/** @var array */
private $columns;
/** @var array */
private $foreignKeys;
/** @var array */
private $indexes;
/** @var Index */
private $primaryKey;
public function __construct(Dibi\Reflector $reflector, array $info)
{
$this->reflector = $reflector;
$this->name = $info['name'];
$this->view = !empty($info['view']);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return bool
*/
public function isView()
{
return $this->view;
}
/**
* @return Column[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @return string[]
*/
public function getColumnNames()
{
$this->initColumns();
$res = [];
foreach ($this->columns as $column) {
$res[] = $column->getName();
}
return $res;
}
/**
* @param string
* @return Column
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->columns[$l])) {
return $this->columns[$l];
} else {
throw new Dibi\Exception("Table '$this->name' has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->columns[strtolower($name)]);
}
/**
* @return ForeignKey[]
*/
public function getForeignKeys()
{
$this->initForeignKeys();
return $this->foreignKeys;
}
/**
* @return Index[]
*/
public function getIndexes()
{
$this->initIndexes();
return $this->indexes;
}
/**
* @return Index
*/
public function getPrimaryKey()
{
$this->initIndexes();
return $this->primaryKey;
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = [];
foreach ($this->reflector->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new Column($this->reflector, $info);
}
}
}
/**
* @return void
*/
protected function initIndexes()
{
if ($this->indexes === NULL) {
$this->initColumns();
$this->indexes = [];
foreach ($this->reflector->getIndexes($this->name) as $info) {
foreach ($info['columns'] as $key => $name) {
$info['columns'][$key] = $this->columns[strtolower($name)];
}
$this->indexes[strtolower($info['name'])] = new Index($info);
if (!empty($info['primary'])) {
$this->primaryKey = $this->indexes[strtolower($info['name'])];
}
}
}
}
/**
* @return void
*/
protected function initForeignKeys()
{
throw new Dibi\NotImplementedException;
}
}

View File

@@ -1,95 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Result set single row.
*/
class Row implements \ArrayAccess, \IteratorAggregate, \Countable
{
public function __construct($arr)
{
foreach ($arr as $k => $v) {
$this->$k = $v;
}
}
public function toArray()
{
return (array) $this;
}
/**
* Converts value to DateTime object.
* @param string key
* @param string format
* @return \DateTime
*/
public function asDateTime($key, $format = NULL)
{
$time = $this[$key];
if (!$time instanceof DateTime) {
if (!$time || substr((string) $time, 0, 3) === '000') { // '', NULL, FALSE, '0000-00-00', ...
return NULL;
}
$time = new DateTime($time);
}
return $format === NULL ? $time : $time->format($format);
}
public function __get($key)
{
$hint = Helpers::getSuggestion(array_keys((array) $this), $key);
trigger_error("Attempt to read missing column '$key'" . ($hint ? ", did you mean '$hint'?" : '.'), E_USER_NOTICE);
}
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
final public function count()
{
return count((array) $this);
}
final public function getIterator()
{
return new \ArrayIterator($this);
}
final public function offsetSet($nm, $val)
{
$this->$nm = $val;
}
final public function offsetGet($nm)
{
return $this->$nm;
}
final public function offsetExists($nm)
{
return isset($this->$nm);
}
final public function offsetUnset($nm)
{
unset($this->$nm);
}
}

View File

@@ -1,152 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
/**
* Better OOP experience.
*/
trait Strict
{
/** @var array [method => [type => callback]] */
private static $extMethods;
/**
* Call to undefined method.
* @throws \LogicException
*/
public function __call($name, $args)
{
if ($cb = self::extensionMethod(get_class($this) . '::' . $name)) { // back compatiblity
array_unshift($args, $this);
return call_user_func_array($cb, $args);
}
$class = method_exists($this, $name) ? 'parent' : get_class($this);
$items = (new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC);
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
throw new \LogicException("Call to undefined method $class::$name()$hint");
}
/**
* Call to undefined static method.
* @throws \LogicException
*/
public static function __callStatic($name, $args)
{
$rc = new ReflectionClass(get_called_class());
$items = array_intersect($rc->getMethods(ReflectionMethod::IS_PUBLIC), $rc->getMethods(ReflectionMethod::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
throw new \LogicException("Call to undefined static method {$rc->getName()}::$name()$hint");
}
/**
* Access to undeclared property.
* @throws \LogicException
*/
public function &__get($name)
{
if ((method_exists($this, $m = 'get' . $name) || method_exists($this, $m = 'is' . $name))
&& (new ReflectionMethod($this, $m))->isPublic()
) { // back compatiblity
$ret = $this->$m();
return $ret;
}
$rc = new ReflectionClass($this);
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
throw new \LogicException("Attempt to read undeclared property {$rc->getName()}::$$name$hint");
}
/**
* Access to undeclared property.
* @throws \LogicException
*/
public function __set($name, $value)
{
$rc = new ReflectionClass($this);
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
throw new \LogicException("Attempt to write to undeclared property {$rc->getName()}::$$name$hint");
}
/**
* @return bool
*/
public function __isset($name)
{
return FALSE;
}
/**
* Access to undeclared property.
* @throws \LogicException
*/
public function __unset($name)
{
$class = get_class($this);
throw new \LogicException("Attempt to unset undeclared property $class::$$name.");
}
/**
* @param string method name
* @param callable
* @return mixed
*/
public static function extensionMethod($name, $callback = NULL)
{
if (strpos($name, '::') === FALSE) {
$class = get_called_class();
} else {
list($class, $name) = explode('::', $name);
$class = (new ReflectionClass($class))->getName();
}
if (self::$extMethods === NULL) { // for backwards compatibility
$list = get_defined_functions();
foreach ($list['user'] as $fce) {
$pair = explode('_prototype_', $fce);
if (count($pair) === 2) {
trigger_error("Extension method defined as $fce() is deprecated, use $class::extensionMethod('$name', ...).", E_USER_DEPRECATED);
self::$extMethods[$pair[1]][(new ReflectionClass($pair[0]))->getName()] = $fce;
self::$extMethods[$pair[1]][''] = NULL;
}
}
}
$list = & self::$extMethods[strtolower($name)];
if ($callback === NULL) { // getter
$cache = &$list[''][$class];
if (isset($cache)) {
return $cache;
}
foreach ([$class] + class_parents($class) + class_implements($class) as $cl) {
if (isset($list[$cl])) {
return $cache = $list[$cl];
}
}
return $cache = FALSE;
} else { // setter
$list[$class] = $callback;
$list[''] = NULL;
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Data types.
*/
class Type
{
const
TEXT = 's', // as 'string'
BINARY = 'bin',
BOOL = 'b',
INTEGER = 'i',
FLOAT = 'f',
DATE = 'd',
DATETIME = 'dt',
TIME = 't',
TIME_INTERVAL = 'ti';
final public function __construct()
{
throw new \LogicException('Cannot instantiate static class ' . __CLASS__);
}
}

View File

@@ -1,454 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
use Dibi\Type;
/**
* This class is static container class for creating DB objects and
* store connections info.
*/
class dibi
{
use Dibi\Strict;
const
AFFECTED_ROWS = 'a',
IDENTIFIER = 'n';
/** version */
const
VERSION = '3.0.8',
REVISION = 'released on 2017-06-09';
/** sorting order */
const
ASC = 'ASC',
DESC = 'DESC';
/** @deprecated */
const
TEXT = Type::TEXT,
BINARY = Type::BINARY,
BOOL = Type::BOOL,
INTEGER = Type::INTEGER,
FLOAT = Type::FLOAT,
DATE = Type::DATE,
DATETIME = Type::DATETIME,
TIME = Type::TIME,
FIELD_TEXT = Type::TEXT,
FIELD_BINARY = Type::BINARY,
FIELD_BOOL = Type::BOOL,
FIELD_INTEGER = Type::INTEGER,
FIELD_FLOAT = Type::FLOAT,
FIELD_DATE = Type::DATE,
FIELD_DATETIME = Type::DATETIME,
FIELD_TIME = Type::TIME;
/** @var Dibi\Connection[] Connection registry storage for DibiConnection objects */
private static $registry = [];
/** @var Dibi\Connection Current connection */
private static $connection;
/** @var string Last SQL command @see dibi::query() */
public static $sql;
/** @var int Elapsed time for last query */
public static $elapsedTime;
/** @var int Elapsed time for all queries */
public static $totalTime;
/** @var int Number or queries */
public static $numOfQueries = 0;
/** @var string Default dibi driver */
public static $defaultDriver = 'mysqli';
/**
* Static class - cannot be instantiated.
*/
final public function __construct()
{
throw new LogicException('Cannot instantiate static class ' . get_class($this));
}
/********************* connections handling ****************d*g**/
/**
* Creates a new Connection object and connects it to specified database.
* @param mixed connection parameters
* @param string connection name
* @return Dibi\Connection
* @throws Dibi\Exception
*/
public static function connect($config = [], $name = '0')
{
return self::$connection = self::$registry[$name] = new Dibi\Connection($config, $name);
}
/**
* Disconnects from database (doesn't destroy Connection object).
* @return void
*/
public static function disconnect()
{
self::getConnection()->disconnect();
}
/**
* Returns TRUE when connection was established.
* @return bool
*/
public static function isConnected()
{
return (self::$connection !== NULL) && self::$connection->isConnected();
}
/**
* Retrieve active connection.
* @param string connection registy name
* @return Dibi\Connection
* @throws Dibi\Exception
*/
public static function getConnection($name = NULL)
{
if ($name === NULL) {
if (self::$connection === NULL) {
throw new Dibi\Exception('Dibi is not connected to database.');
}
return self::$connection;
}
if (!isset(self::$registry[$name])) {
throw new Dibi\Exception("There is no connection named '$name'.");
}
return self::$registry[$name];
}
/**
* Sets connection.
* @param Dibi\Connection
* @return Dibi\Connection
*/
public static function setConnection(Dibi\Connection $connection)
{
return self::$connection = $connection;
}
/**
* @deprecated
*/
public static function activate($name)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
self::$connection = self::getConnection($name);
}
/********************* monostate for active connection ****************d*g**/
/**
* Generates and executes SQL query - Monostate for Dibi\Connection::query().
* @param array|mixed one or more arguments
* @return Dibi\Result|int result set or number of affected rows
* @throws Dibi\Exception
*/
public static function query($args)
{
$args = func_get_args();
return self::getConnection()->query($args);
}
/**
* Executes the SQL query - Monostate for Dibi\Connection::nativeQuery().
* @param string SQL statement.
* @return Dibi\Result|int result set or number of affected rows
*/
public static function nativeQuery($sql)
{
return self::getConnection()->nativeQuery($sql);
}
/**
* Generates and prints SQL query - Monostate for Dibi\Connection::test().
* @param array|mixed one or more arguments
* @return bool
*/
public static function test($args)
{
$args = func_get_args();
return self::getConnection()->test($args);
}
/**
* Generates and returns SQL query as DataSource - Monostate for Dibi\Connection::test().
* @param array|mixed one or more arguments
* @return Dibi\DataSource
*/
public static function dataSource($args)
{
$args = func_get_args();
return self::getConnection()->dataSource($args);
}
/**
* Executes SQL query and fetch result - Monostate for Dibi\Connection::query() & fetch().
* @param array|mixed one or more arguments
* @return Dibi\Row
* @throws Dibi\Exception
*/
public static function fetch($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetch();
}
/**
* Executes SQL query and fetch results - Monostate for Dibi\Connection::query() & fetchAll().
* @param array|mixed one or more arguments
* @return Dibi\Row[]
* @throws Dibi\Exception
*/
public static function fetchAll($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchAll();
}
/**
* Executes SQL query and fetch first column - Monostate for Dibi\Connection::query() & fetchSingle().
* @param array|mixed one or more arguments
* @return mixed
* @throws Dibi\Exception
*/
public static function fetchSingle($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchSingle();
}
/**
* Executes SQL query and fetch pairs - Monostate for Dibi\Connection::query() & fetchPairs().
* @param array|mixed one or more arguments
* @return array
* @throws Dibi\Exception
*/
public static function fetchPairs($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchPairs();
}
/**
* Gets the number of affected rows.
* Monostate for Dibi\Connection::getAffectedRows()
* @return int number of rows
* @throws Dibi\Exception
*/
public static function getAffectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws Dibi\Exception
*/
public static function affectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* Monostate for Dibi\Connection::getInsertId()
* @param string optional sequence name
* @return int
* @throws Dibi\Exception
*/
public static function getInsertId($sequence = NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws Dibi\Exception
*/
public static function insertId($sequence = NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Begins a transaction - Monostate for Dibi\Connection::begin().
* @param string optional savepoint name
* @return void
* @throws Dibi\Exception
*/
public static function begin($savepoint = NULL)
{
self::getConnection()->begin($savepoint);
}
/**
* Commits statements in a transaction - Monostate for Dibi\Connection::commit($savepoint = NULL).
* @param string optional savepoint name
* @return void
* @throws Dibi\Exception
*/
public static function commit($savepoint = NULL)
{
self::getConnection()->commit($savepoint);
}
/**
* Rollback changes in a transaction - Monostate for Dibi\Connection::rollback().
* @param string optional savepoint name
* @return void
* @throws Dibi\Exception
*/
public static function rollback($savepoint = NULL)
{
self::getConnection()->rollback($savepoint);
}
/**
* Gets a information about the current database - Monostate for Dibi\Connection::getDatabaseInfo().
* @return Dibi\Reflection\Database
*/
public static function getDatabaseInfo()
{
return self::getConnection()->getDatabaseInfo();
}
/**
* Import SQL dump from file - extreme fast!
* @param string filename
* @return int count of sql commands
*/
public static function loadFile($file)
{
return Dibi\Helpers::loadFromFile(self::getConnection(), $file);
}
/********************* fluent SQL builders ****************d*g**/
/**
* @return Dibi\Fluent
*/
public static function command()
{
return self::getConnection()->command();
}
/**
* @param mixed column name
* @return Dibi\Fluent
*/
public static function select($args)
{
$args = func_get_args();
return call_user_func_array([self::getConnection(), 'select'], $args);
}
/**
* @param string table
* @param array
* @return Dibi\Fluent
*/
public static function update($table, $args)
{
return self::getConnection()->update($table, $args);
}
/**
* @param string table
* @param array
* @return Dibi\Fluent
*/
public static function insert($table, $args)
{
return self::getConnection()->insert($table, $args);
}
/**
* @param string table
* @return Dibi\Fluent
*/
public static function delete($table)
{
return self::getConnection()->delete($table);
}
/********************* substitutions ****************d*g**/
/**
* Returns substitution hashmap - Monostate for Dibi\Connection::getSubstitutes().
* @return Dibi\HashMap
*/
public static function getSubstitutes()
{
return self::getConnection()->getSubstitutes();
}
/********************* misc tools ****************d*g**/
/**
* Prints out a syntax highlighted version of the SQL command or Result.
* @param string|Result
* @param bool return output instead of printing it?
* @return string
*/
public static function dump($sql = NULL, $return = FALSE)
{
return Dibi\Helpers::dump($sql, $return);
}
}

View File

@@ -1,156 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi common exception.
*/
class Exception extends \Exception
{
/** @var string|NULL */
private $sql;
/**
* Construct a dibi exception.
* @param string Message describing the exception
* @param mixed
* @param string SQL command
*/
public function __construct($message = '', $code = 0, $sql = NULL)
{
parent::__construct($message);
$this->code = $code;
$this->sql = $sql;
}
/**
* @return string The SQL passed to the constructor
*/
final public function getSql()
{
return $this->sql;
}
/**
* @return string string represenation of exception with SQL command
*/
public function __toString()
{
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
}
}
/**
* database server exception.
*/
class DriverException extends Exception
{
}
/**
* PCRE exception.
*/
class PcreException extends Exception
{
public function __construct($message = '%msg.')
{
static $messages = [
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
];
$code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
}
}
class NotImplementedException extends Exception
{
}
class NotSupportedException extends Exception
{
}
/**
* Database procedure exception.
*/
class ProcedureException extends Exception
{
/** @var string */
protected $severity;
/**
* Construct the exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL)
{
parent::__construct($message, (int) $code, $sql);
$this->severity = $severity;
}
/**
* Gets the exception severity.
* @return string
*/
public function getSeverity()
{
$this->severity;
}
}
/**
* Base class for all constraint violation related exceptions.
*/
class ConstraintViolationException extends DriverException
{
}
/**
* Exception for a foreign key constraint violation.
*/
class ForeignKeyConstraintViolationException extends ConstraintViolationException
{
}
/**
* Exception for a NOT NULL constraint violation.
*/
class NotNullConstraintViolationException extends ConstraintViolationException
{
}
/**
* Exception for a unique constraint violation.
*/
class UniqueConstraintViolationException extends ConstraintViolationException
{
}

View File

@@ -1,144 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
if (PHP_VERSION_ID < 50404) {
throw new Exception('Dibi requires PHP 5.4.4 or newer.');
}
spl_autoload_register(function ($class) {
static $map = [
'dibi' => 'dibi.php',
'Dibi\Bridges\Nette\DibiExtension22' => 'Bridges/Nette/DibiExtension22.php',
'Dibi\Bridges\Nette\DibiExtension21' => 'Bridges/Nette/DibiExtension21.php',
'Dibi\Bridges\Nette\Panel' => 'Bridges/Nette/Panel.php',
'Dibi\Bridges\Tracy\Panel' => 'Bridges/Tracy/Panel.php',
'Dibi\Connection' => 'Connection.php',
'Dibi\DataSource' => 'DataSource.php',
'Dibi\DateTime' => 'DateTime.php',
'Dibi\Driver' => 'interfaces.php',
'Dibi\DriverException' => 'exceptions.php',
'Dibi\Drivers\FirebirdDriver' => 'Drivers/FirebirdDriver.php',
'Dibi\Drivers\SqlsrvDriver' => 'Drivers/SqlsrvDriver.php',
'Dibi\Drivers\SqlsrvReflector' => 'Drivers/SqlsrvReflector.php',
'Dibi\Drivers\MsSqlDriver' => 'Drivers/MsSqlDriver.php',
'Dibi\Drivers\MsSqlReflector' => 'Drivers/MsSqlReflector.php',
'Dibi\Drivers\MySqlDriver' => 'Drivers/MySqlDriver.php',
'Dibi\Drivers\MySqliDriver' => 'Drivers/MySqliDriver.php',
'Dibi\Drivers\MySqlReflector' => 'Drivers/MySqlReflector.php',
'Dibi\Drivers\OdbcDriver' => 'Drivers/OdbcDriver.php',
'Dibi\Drivers\OracleDriver' => 'Drivers/OracleDriver.php',
'Dibi\Drivers\PdoDriver' => 'Drivers/PdoDriver.php',
'Dibi\Drivers\PostgreDriver' => 'Drivers/PostgreDriver.php',
'Dibi\Drivers\Sqlite3Driver' => 'Drivers/Sqlite3Driver.php',
'Dibi\Drivers\SqliteReflector' => 'Drivers/SqliteReflector.php',
'Dibi\Event' => 'Event.php',
'Dibi\Exception' => 'exceptions.php',
'Dibi\Fluent' => 'Fluent.php',
'Dibi\HashMap' => 'HashMap.php',
'Dibi\HashMapBase' => 'HashMap.php',
'Dibi\Helpers' => 'Helpers.php',
'Dibi\IDataSource' => 'interfaces.php',
'Dibi\Literal' => 'Literal.php',
'Dibi\Loggers\FileLogger' => 'Loggers/FileLogger.php',
'Dibi\Loggers\FirePhpLogger' => 'Loggers/FirePhpLogger.php',
'Dibi\NotImplementedException' => 'exceptions.php',
'Dibi\NotSupportedException' => 'exceptions.php',
'Dibi\PcreException' => 'exceptions.php',
'Dibi\ProcedureException' => 'exceptions.php',
'Dibi\Reflection\Column' => 'Reflection/Column.php',
'Dibi\Reflection\Database' => 'Reflection/Database.php',
'Dibi\Reflection\ForeignKey' => 'Reflection/ForeignKey.php',
'Dibi\Reflection\Index' => 'Reflection/Index.php',
'Dibi\Reflection\Result' => 'Reflection/Result.php',
'Dibi\Reflection\Table' => 'Reflection/Table.php',
'Dibi\Reflector' => 'interfaces.php',
'Dibi\Result' => 'Result.php',
'Dibi\ResultDriver' => 'interfaces.php',
'Dibi\ResultIterator' => 'ResultIterator.php',
'Dibi\Row' => 'Row.php',
'Dibi\Strict' => 'Strict.php',
'Dibi\Translator' => 'Translator.php',
'Dibi\Type' => 'Type.php',
], $old2new = [
'Dibi' => 'dibi.php',
'DibiColumnInfo' => 'Dibi\Reflection\Column',
'DibiConnection' => 'Dibi\Connection',
'DibiDatabaseInfo' => 'Dibi\Reflection\Database',
'DibiDataSource' => 'Dibi\DataSource',
'DibiDateTime' => 'Dibi\DateTime',
'DibiDriverException' => 'Dibi\DriverException',
'DibiEvent' => 'Dibi\Event',
'DibiException' => 'Dibi\Exception',
'DibiFileLogger' => 'Dibi\Loggers\FileLogger',
'DibiFirebirdDriver' => 'Dibi\Drivers\FirebirdDriver',
'DibiFirePhpLogger' => 'Dibi\Loggers\FirePhpLogger',
'DibiFluent' => 'Dibi\Fluent',
'DibiForeignKeyInfo' => 'Dibi\Reflection\ForeignKey',
'DibiHashMap' => 'Dibi\HashMap',
'DibiHashMapBase' => 'Dibi\HashMapBase',
'DibiIndexInfo' => 'Dibi\Reflection\Index',
'DibiLiteral' => 'Dibi\Literal',
'DibiMsSql2005Driver' => 'Dibi\Drivers\SqlsrvDriver',
'DibiMsSql2005Reflector' => 'Dibi\Drivers\SqlsrvReflector',
'DibiMsSqlDriver' => 'Dibi\Drivers\MsSqlDriver',
'DibiMsSqlReflector' => 'Dibi\Drivers\MsSqlReflector',
'DibiMySqlDriver' => 'Dibi\Drivers\MySqlDriver',
'DibiMySqliDriver' => 'Dibi\Drivers\MySqliDriver',
'DibiMySqlReflector' => 'Dibi\Drivers\MySqlReflector',
'DibiNette21Extension' => 'Dibi\Bridges\Nette\DibiExtension21',
'DibiNettePanel' => 'Dibi\Bridges\Nette\Panel',
'DibiNotImplementedException' => 'Dibi\NotImplementedException',
'DibiNotSupportedException' => 'Dibi\NotSupportedException',
'DibiOdbcDriver' => 'Dibi\Drivers\OdbcDriver',
'DibiOracleDriver' => 'Dibi\Drivers\OracleDriver',
'DibiPcreException' => 'Dibi\PcreException',
'DibiPdoDriver' => 'Dibi\Drivers\PdoDriver',
'DibiPostgreDriver' => 'Dibi\Drivers\PostgreDriver',
'DibiProcedureException' => 'Dibi\ProcedureException',
'DibiResult' => 'Dibi\Result',
'DibiResultInfo' => 'Dibi\Reflection\Result',
'DibiResultIterator' => 'Dibi\ResultIterator',
'DibiRow' => 'Dibi\Row',
'DibiSqlite3Driver' => 'Dibi\Drivers\Sqlite3Driver',
'DibiSqliteReflector' => 'Dibi\Drivers\SqliteReflector',
'DibiTableInfo' => 'Dibi\Reflection\Table',
'DibiTranslator' => 'Dibi\Translator',
'IDataSource' => 'Dibi\IDataSource',
'IDibiDriver' => 'Dibi\Driver',
'IDibiReflector' => 'Dibi\Reflector',
'IDibiResultDriver' => 'Dibi\ResultDriver',
'Dibi\Drivers\MsSql2005Driver' => 'Dibi\Drivers\SqlsrvDriver',
'Dibi\Drivers\MsSql2005Reflector' => 'Dibi\Drivers\SqlsrvReflector',
];
if (isset($map[$class])) {
require __DIR__ . '/Dibi/' . $map[$class];
} elseif (isset($old2new[$class])) {
class_alias($old2new[$class], $class);
}
});
// preload for compatiblity
array_map('class_exists', [
'DibiConnection',
'DibiDateTime',
'DibiDriverException',
'DibiEvent',
'DibiException',
'DibiFluent',
'DibiLiteral',
'DibiNotImplementedException',
'DibiNotSupportedException',
'DibiPcreException',
'DibiProcedureException',
'DibiResult',
'DibiRow',
'IDataSource',
'IDibiDriver',
]);

View File

@@ -1,4 +0,0 @@
# for php-coveralls
service_name: travis-ci
coverage_clover: coverage.xml
json_path: coverage.json

4
tests/.gitignore vendored
View File

@@ -1,4 +0,0 @@
output
/tmp
/test.log
/databases.ini

View File

@@ -0,0 +1,40 @@
<?php
/**
* Test: Cloning of DibiFluent
*
* @author David Grudl
*/
require dirname(__FILE__) . '/bootstrap.php';
dibi::connect($config['sqlite3']);
$fluent = new DibiFluent(dibi::getConnection());
$fluent->select('*')->from('table')->where('x=1');
$dolly = clone $fluent;
$dolly->where('y=1');
$dolly->clause('FOO');
Assert::same( 'SELECT * FROM [table] WHERE x=1', (string) $fluent );
Assert::same( 'SELECT * FROM [table] WHERE x=1 AND y=1 FOO', (string) $dolly );
$fluent = dibi::select('id')->from('table')->where('id = %i',1);
$dolly = clone $fluent;
$dolly->where('cd = %i',5);
Assert::same( 'SELECT [id] FROM [table] WHERE id = 1', (string) $fluent );
Assert::same( 'SELECT [id] FROM [table] WHERE id = 1 AND cd = 5', (string) $dolly );
$fluent = dibi::select("*")->from("table");
$dolly = clone $fluent;
$dolly->removeClause("select")->select("count(*)");
Assert::same( 'SELECT * FROM [table]', (string) $fluent );
Assert::same( 'SELECT count(*) FROM [table]', (string) $dolly );

20
tests/bootstrap.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
// The Nette Tester command-line runner can be
// invoked through the command: ../../vendor/bin/tester .
if (@!include __DIR__ . '/../vendor/autoload.php') {
echo 'Install Nette Tester using `composer update --dev`';
exit(1);
}
// configure environment
Tester\Environment::setup();
date_default_timezone_set('Europe/Prague');
class_alias('Tester\Assert', 'Assert');
// load connections
define('DIR', dirname(__FILE__));
$config = parse_ini_file(dirname(__FILE__) . '/config.ini', TRUE);

67
tests/config.ini Normal file
View File

@@ -0,0 +1,67 @@
[mysql]
driver = mysql
host = localhost
username = dibi
password = dibi
database = dibi_test
charset = utf8
[mysqli]
driver = mysqli
host = localhost
username = dibi
password = dibi
database = dibi_test
charset = utf8
[sqlite]
driver = sqlite
database = DIR "/data/sample.sdb"
[sqlite3]
driver = sqlite3
database = DIR "/data/sample.sdb3"
[odbc]
driver = odbc
username = dibi
password = dibi
dsn = "Driver={Microsoft Access Driver (*.mdb)};Dbq=" DIR "/data/sample.mdb"
[postgresql]
driver = postgre
host = localhost
port = 5432
username = dibi
password = dibi
database = dibi_test
persistent = TRUE
[sqlite-pdo]
driver = pdo
dsn = "sqlite2::" DIR "/data/sample.sdb"
[mysql-pdo]
driver = pdo
dsn = "mysql:dbname=dibi_test;host=localhost"
username = dibi
password = dibi
[mssql]
driver = mssql
host = localhost
username = dibi
password = dibi
[mssql2005]
driver = mssql2005
host = "(local)"
username = dibi
password = dibi
database = dibi_test
[oracle]
driver = oracle
username = dibi
password = dibi
database = dibi_test

BIN
tests/data/sample.mdb Normal file

Binary file not shown.

149
tests/data/sample.mysql Normal file
View File

@@ -0,0 +1,149 @@
-- phpMyAdmin SQL Dump
-- version 2.11.1.2
-- http://www.phpmyadmin.net
--
-- Po<50><6F>ta<74>: localhost
-- Vygenerov<6F>no: Ned<65>le 02. prosince 2007, 19:49
-- Verze MySQL: 5.0.45
-- Verze PHP: 5.2.1
SET FOREIGN_KEY_CHECKS=0;
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
--
-- Datab<61>ze: `dibi`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `customers`
--
DROP TABLE IF EXISTS `customers`;
CREATE TABLE IF NOT EXISTS `customers` (
`customer_id` int(11) NOT NULL auto_increment,
`name` varchar(100) default NULL,
PRIMARY KEY (`customer_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
--
-- Vypisuji data pro tabulku `customers`
--
INSERT INTO `customers` (`customer_id`, `name`) VALUES
(1, 'Dave Lister'),
(2, 'Arnold Rimmer'),
(3, 'The Cat'),
(4, 'Holly'),
(5, 'Kryten'),
(6, 'Kristine Kochanski');
-- --------------------------------------------------------
--
-- Struktura tabulky `enumtest`
--
DROP TABLE IF EXISTS `enumtest`;
CREATE TABLE IF NOT EXISTS `enumtest` (
`id` int(11) NOT NULL auto_increment,
`test` enum('a','b','c') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `enumtest`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `orders`
--
DROP TABLE IF EXISTS `orders`;
CREATE TABLE IF NOT EXISTS `orders` (
`order_id` int(11) NOT NULL,
`customer_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`amount` float NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Vypisuji data pro tabulku `orders`
--
INSERT INTO `orders` (`order_id`, `customer_id`, `product_id`, `amount`) VALUES
(1, 2, 1, 7),
(2, 2, 3, 2),
(3, 1, 2, 3),
(4, 6, 3, 5);
-- --------------------------------------------------------
--
-- Struktura tabulky `products`
--
DROP TABLE IF EXISTS `products`;
CREATE TABLE IF NOT EXISTS `products` (
`product_id` int(11) NOT NULL auto_increment,
`title` varchar(100) default NULL,
PRIMARY KEY (`product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `products`
--
INSERT INTO `products` (`product_id`, `title`) VALUES
(1, 'Chair'),
(2, 'Table'),
(3, 'Computer');
-- --------------------------------------------------------
--
-- Struktura tabulky `settest`
--
DROP TABLE IF EXISTS `settest`;
CREATE TABLE IF NOT EXISTS `settest` (
`id` int(11) NOT NULL auto_increment,
`test` set('a','b','c') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `settest`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `where`
--
DROP TABLE IF EXISTS `where`;
CREATE TABLE IF NOT EXISTS `where` (
`select` int(11) NOT NULL,
`dot.dot` int(11) NOT NULL,
`is` int(11) NOT NULL,
`quot'n' space` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Vypisuji data pro tabulku `where`
--
INSERT INTO `where` (`select`, `dot.dot`, `is`, `quot'n' space`) VALUES
(1, 2, 3, 4);
SET FOREIGN_KEY_CHECKS=1;
SET SQL_MODE="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";

BIN
tests/data/sample.sdb Normal file

Binary file not shown.

BIN
tests/data/sample.sdb3 Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More