mirror of
https://github.com/dg/dibi.git
synced 2025-08-18 03:41:30 +02:00
new directory structure, moved to /src
This commit is contained in:
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Dibi extension for Nette Framework 2.1. Creates 'connection' service.
|
||||
*
|
||||
* @package dibi\nette
|
||||
* @phpversion 5.3
|
||||
*/
|
||||
class DibiNette21Extension extends Nette\DI\CompilerExtension
|
||||
{
|
||||
|
||||
public function loadConfiguration()
|
||||
{
|
||||
$container = $this->getContainerBuilder();
|
||||
$config = $this->getConfig();
|
||||
|
||||
$useProfiler = isset($config['profiler'])
|
||||
? $config['profiler']
|
||||
: $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('DibiConnection', array($config))
|
||||
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
|
||||
|
||||
if ($useProfiler) {
|
||||
$panel = $container->addDefinition($this->prefix('panel'))
|
||||
->setClass('DibiNettePanel')
|
||||
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', array('@self'))
|
||||
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', array('DibiNettePanel::renderException'));
|
||||
|
||||
$connection->addSetup('$service->onEvent[] = ?', array(array($panel, 'logEvent')));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
use Nette\Diagnostics\Debugger;
|
||||
|
||||
|
||||
/**
|
||||
* Dibi panel for Nette\Diagnostics.
|
||||
*
|
||||
* @package dibi\nette
|
||||
*/
|
||||
class DibiNettePanel extends DibiObject implements Nette\Diagnostics\IBarPanel
|
||||
{
|
||||
/** @var int maximum SQL length */
|
||||
static public $maxLength = 1000;
|
||||
|
||||
/** @var bool explain queries? */
|
||||
public $explain;
|
||||
|
||||
/** @var int */
|
||||
public $filter;
|
||||
|
||||
/** @var array */
|
||||
private $events = array();
|
||||
|
||||
|
||||
public function __construct($explain = TRUE, $filter = NULL)
|
||||
{
|
||||
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
|
||||
$this->explain = $explain;
|
||||
}
|
||||
|
||||
|
||||
public function register(DibiConnection $connection)
|
||||
{
|
||||
Debugger::getBar()->addPanel($this);
|
||||
Debugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
|
||||
$connection->onEvent[] = array($this, 'logEvent');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After event notification.
|
||||
* @return void
|
||||
*/
|
||||
public function logEvent(DibiEvent $event)
|
||||
{
|
||||
if (($event->type & $this->filter) === 0) {
|
||||
return;
|
||||
}
|
||||
$this->events[] = $event;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns blue-screen custom tab.
|
||||
* @return mixed
|
||||
*/
|
||||
public static function renderException($e)
|
||||
{
|
||||
if ($e instanceof DibiException && $e->getSql()) {
|
||||
return array(
|
||||
'tab' => 'SQL',
|
||||
'panel' => dibi::dump($e->getSql(), TRUE),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns HTML code for custom tab. (Nette\Diagnostics\IBarPanel)
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTab()
|
||||
{
|
||||
$totalTime = 0;
|
||||
foreach ($this->events as $event) {
|
||||
$totalTime += $event->time;
|
||||
}
|
||||
return '<span title="dibi"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEYSURBVBgZBcHPio5hGAfg6/2+R980k6wmJgsJ5U/ZOAqbSc2GnXOwUg7BESgLUeIQ1GSjLFnMwsKGGg1qxJRmPM97/1zXFAAAAEADdlfZzr26miup2svnelq7d2aYgt3rebl585wN6+K3I1/9fJe7O/uIePP2SypJkiRJ0vMhr55FLCA3zgIAOK9uQ4MS361ZOSX+OrTvkgINSjS/HIvhjxNNFGgQsbSmabohKDNoUGLohsls6BaiQIMSs2FYmnXdUsygQYmumy3Nhi6igwalDEOJEjPKP7CA2aFNK8Bkyy3fdNCg7r9/fW3jgpVJbDmy5+PB2IYp4MXFelQ7izPrhkPHB+P5/PjhD5gCgCenx+VR/dODEwD+A3T7nqbxwf1HAAAAAElFTkSuQmCC" />'
|
||||
. count($this->events) . ' queries'
|
||||
. ($totalTime ? sprintf(' / %0.1f ms', $totalTime * 1000) : '')
|
||||
. '</span>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns HTML code for custom panel. (Nette\Diagnostics\IBarPanel)
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPanel()
|
||||
{
|
||||
$totalTime = $s = NULL;
|
||||
$h = 'htmlSpecialChars';
|
||||
foreach ($this->events as $event) {
|
||||
$totalTime += $event->time;
|
||||
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
|
||||
if ($this->explain && $event->type === DibiEvent::SELECT) {
|
||||
try {
|
||||
$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 = 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);
|
||||
if ($explain) {
|
||||
static $counter;
|
||||
$counter++;
|
||||
$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">' . 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>";
|
||||
}
|
||||
if ($event->source) {
|
||||
$helpers = 'Nette\Diagnostics\Helpers';
|
||||
if (!class_exists($helpers)) {
|
||||
$helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers';
|
||||
}
|
||||
$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>";
|
||||
}
|
||||
|
||||
return empty($this->events) ? '' :
|
||||
'<style> #nette-debug td.nette-DibiProfiler-sql { background: white !important }
|
||||
#nette-debug .nette-DibiProfiler-source { color: #999 !important }
|
||||
#nette-debug nette-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
|
||||
<h1>Queries: ' . count($this->events) . ($totalTime === NULL ? '' : sprintf(', time: %0.3f ms', $totalTime * 1000)) . '</h1>
|
||||
<div class="nette-inner nette-DibiProfiler">
|
||||
<table>
|
||||
<tr><th>Time ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . '
|
||||
</table>
|
||||
</div>';
|
||||
}
|
||||
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
# This will create service named 'dibi.connection'.
|
||||
# Requires Nette Framework 2.1
|
||||
|
||||
extensions:
|
||||
dibi: DibiNette21Extension
|
||||
|
||||
dibi:
|
||||
host: localhost
|
||||
username: root
|
||||
password: ***
|
||||
database: foo
|
||||
lazy: TRUE
|
@@ -1,52 +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.
|
||||
*
|
||||
* @package dibi\nette
|
||||
*/
|
||||
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('DibiConnection', array($config))
|
||||
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
|
||||
|
||||
if ($useProfiler) {
|
||||
$panel = $container->addDefinition($this->prefix('panel'))
|
||||
->setClass('Dibi\Bridges\Tracy\Panel');
|
||||
$connection->addSetup(array($panel, 'register'), array($connection));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
# This will create service named 'dibi.connection'.
|
||||
# Requires Nette Framework 2.2
|
||||
|
||||
extensions:
|
||||
dibi: Dibi\Bridges\Nette\DibiExtension22
|
||||
|
||||
dibi:
|
||||
host: localhost
|
||||
username: root
|
||||
password: ***
|
||||
database: foo
|
||||
lazy: TRUE
|
@@ -1,146 +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 Tracy;
|
||||
|
||||
|
||||
/**
|
||||
* Dibi panel for Tracy.
|
||||
* @package dibi\nette
|
||||
*/
|
||||
class Panel extends \DibiObject implements Tracy\IBarPanel
|
||||
{
|
||||
/** @var int maximum SQL length */
|
||||
static public $maxLength = 1000;
|
||||
|
||||
/** @var bool explain queries? */
|
||||
public $explain;
|
||||
|
||||
/** @var int */
|
||||
public $filter;
|
||||
|
||||
/** @var array */
|
||||
private $events = array();
|
||||
|
||||
|
||||
public function __construct($explain = TRUE, $filter = NULL)
|
||||
{
|
||||
$this->filter = $filter ? (int) $filter : \DibiEvent::QUERY;
|
||||
$this->explain = $explain;
|
||||
}
|
||||
|
||||
|
||||
public function register(\DibiConnection $connection)
|
||||
{
|
||||
Tracy\Debugger::getBar()->addPanel($this);
|
||||
Tracy\Debugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
|
||||
$connection->onEvent[] = array($this, 'logEvent');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After event notification.
|
||||
* @return void
|
||||
*/
|
||||
public function logEvent(\DibiEvent $event)
|
||||
{
|
||||
if (($event->type & $this->filter) === 0) {
|
||||
return;
|
||||
}
|
||||
$this->events[] = $event;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns blue-screen custom tab.
|
||||
* @return mixed
|
||||
*/
|
||||
public static function renderException($e)
|
||||
{
|
||||
if ($e instanceof \DibiException && $e->getSql()) {
|
||||
return array(
|
||||
'tab' => 'SQL',
|
||||
'panel' => dibi::dump($e->getSql(), TRUE),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns HTML code for custom tab. (Tracy\IBarPanel)
|
||||
* @return mixed
|
||||
*/
|
||||
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 ? sprintf(' / %0.1f ms', $totalTime * 1000) : '')
|
||||
. '</span></span>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns HTML code for custom panel. (Tracy\IBarPanel)
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPanel()
|
||||
{
|
||||
$totalTime = $s = NULL;
|
||||
$h = 'htmlSpecialChars';
|
||||
foreach ($this->events as $event) {
|
||||
$totalTime += $event->time;
|
||||
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
|
||||
if ($this->explain && $event->type === \DibiEvent::SELECT) {
|
||||
try {
|
||||
$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 = 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);
|
||||
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">' . dibi::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><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";
|
||||
}
|
||||
|
||||
return empty($this->events) ? '' :
|
||||
'<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 ? '' : sprintf(', time: %0.3f ms', $totalTime * 1000)) . '</h1>
|
||||
<div class="tracy-inner tracy-DibiProfiler">
|
||||
<table>
|
||||
<tr><th>Time ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . '
|
||||
</table>
|
||||
</div>';
|
||||
}
|
||||
|
||||
}
|
@@ -1,35 +1,3 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dibi - smart database abstraction layer (http://dibiphp.com)
|
||||
*
|
||||
* Copyright (c) 2005, 2012 David Grudl (https://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/Dibi.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';
|
||||
trigger_error('Dibi was moved to /src/loader.php', E_USER_WARNING);
|
||||
require dirname(__FILE__) . '/../src/loader.php';
|
||||
|
@@ -1,839 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver for Firebird/InterBase database.
|
||||
*
|
||||
* Driver options:
|
||||
* - database => the path to database file (server:/path/database.fdb)
|
||||
* - username (or user)
|
||||
* - password (or pass)
|
||||
* - 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 DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
|
||||
{
|
||||
const ERROR_EXCEPTION_THROWN = -836;
|
||||
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $transaction;
|
||||
|
||||
/** @var bool */
|
||||
private $inTransaction = FALSE;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('interbase')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'interbase' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
DibiConnection::alias($config, 'database', 'db');
|
||||
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
} else {
|
||||
// default values
|
||||
$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 @
|
||||
} else {
|
||||
$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 DibiDriverException(ibase_errmsg(), ibase_errcode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
ibase_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @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 (DibiDriverException::catchError($msg)) {
|
||||
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
|
||||
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
|
||||
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
|
||||
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 ibase_affected_rows($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
||||
* @param string generator name
|
||||
* @return int|FALSE int on success or FALSE on failure
|
||||
*/
|
||||
public function getInsertId($sequence)
|
||||
{
|
||||
return ibase_gen_id($sequence, 0, $this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
if ($savepoint !== NULL) {
|
||||
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
|
||||
}
|
||||
$this->transaction = ibase_trans($this->resource);
|
||||
$this->inTransaction = TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
if ($savepoint !== NULL) {
|
||||
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
|
||||
}
|
||||
|
||||
if (!ibase_commit($this->transaction)) {
|
||||
throw new DibiDriverException('Unable to handle operation - failure when commiting transaction.');
|
||||
}
|
||||
|
||||
$this->inTransaction = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
if ($savepoint !== NULL) {
|
||||
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
|
||||
}
|
||||
|
||||
if (!ibase_rollback($this->transaction)) {
|
||||
throw new DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
|
||||
}
|
||||
|
||||
$this->inTransaction = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is in transaction?
|
||||
* @return bool
|
||||
*/
|
||||
public function inTransaction()
|
||||
{
|
||||
return $this->inTransaction;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection resource.
|
||||
* @return resource
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
return is_resource($this->connection) ? $this->connection : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection reflector.
|
||||
* @return IDibiReflector
|
||||
*/
|
||||
public function getReflector()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result set driver factory.
|
||||
* @param resource
|
||||
* @return IDibiResultDriver
|
||||
*/
|
||||
public function createResultDriver($resource)
|
||||
{
|
||||
$res = clone $this;
|
||||
$res->resultSet = $resource;
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/********************* SQL ********************/
|
||||
|
||||
|
||||
/**
|
||||
* 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 "'" . str_replace("'", "''", $value) . "'";
|
||||
|
||||
case dibi::IDENTIFIER:
|
||||
return $value;
|
||||
|
||||
case dibi::BOOL:
|
||||
return $value ? 1 : 0;
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
|
||||
|
||||
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 > 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 . ')';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ********************/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && $this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
throw new DibiNotSupportedException('Firebird/Interbase do not support returning number of rows in result set.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
DibiDriverException::tryError();
|
||||
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
|
||||
|
||||
if (DibiDriverException::catchError($msg)) {
|
||||
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
|
||||
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
|
||||
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
|
||||
|
||||
} else {
|
||||
throw new DibiDriverException($msg, ibase_errcode(), dibi::$sql);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 DibiException
|
||||
*/
|
||||
public function seek($row)
|
||||
{
|
||||
throw new DibiNotSupportedException('Firebird/Interbase do not support seek in result set.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
ibase_free_result($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return resource
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$count = ibase_num_fields($this->resultSet);
|
||||
$columns = array();
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$row = (array) ibase_field_info($this->resultSet, $i);
|
||||
$columns[] = array(
|
||||
'name' => $row['name'],
|
||||
'fullname' => $row['name'],
|
||||
'table' => $row['relation'],
|
||||
'nativetype' => $row['type'],
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/********************* IDibiReflector ********************/
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of tables.
|
||||
* @return array
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT TRIM(RDB\$RELATION_NAME),
|
||||
CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END
|
||||
FROM RDB\$RELATIONS
|
||||
WHERE RDB\$SYSTEM_FLAG = 0;"
|
||||
);
|
||||
$tables = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$tables[] = array(
|
||||
'name' => $row[0],
|
||||
'view' => $row[1] === 'TRUE',
|
||||
);
|
||||
}
|
||||
return $tables;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
$table = strtoupper($table);
|
||||
$res = $this->query("
|
||||
SELECT TRIM(r.RDB\$FIELD_NAME) AS FIELD_NAME,
|
||||
CASE f.RDB\$FIELD_TYPE
|
||||
WHEN 261 THEN 'BLOB'
|
||||
WHEN 14 THEN 'CHAR'
|
||||
WHEN 40 THEN 'CSTRING'
|
||||
WHEN 11 THEN 'D_FLOAT'
|
||||
WHEN 27 THEN 'DOUBLE'
|
||||
WHEN 10 THEN 'FLOAT'
|
||||
WHEN 16 THEN 'INT64'
|
||||
WHEN 8 THEN 'INTEGER'
|
||||
WHEN 9 THEN 'QUAD'
|
||||
WHEN 7 THEN 'SMALLINT'
|
||||
WHEN 12 THEN 'DATE'
|
||||
WHEN 13 THEN 'TIME'
|
||||
WHEN 35 THEN 'TIMESTAMP'
|
||||
WHEN 37 THEN 'VARCHAR'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS FIELD_TYPE,
|
||||
f.RDB\$FIELD_LENGTH AS FIELD_LENGTH,
|
||||
r.RDB\$DEFAULT_VALUE AS DEFAULT_VALUE,
|
||||
CASE r.RDB\$NULL_FLAG
|
||||
WHEN 1 THEN 'FALSE' ELSE 'TRUE'
|
||||
END AS NULLABLE
|
||||
FROM RDB\$RELATION_FIELDS r
|
||||
LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME
|
||||
WHERE r.RDB\$RELATION_NAME = '$table'
|
||||
ORDER BY r.RDB\$FIELD_POSITION;"
|
||||
|
||||
);
|
||||
$columns = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$key = $row['FIELD_NAME'];
|
||||
$columns[$key] = array(
|
||||
'name' => $key,
|
||||
'table' => $table,
|
||||
'nativetype' => trim($row['FIELD_TYPE']),
|
||||
'size' => $row['FIELD_LENGTH'],
|
||||
'nullable' => $row['NULLABLE'] === 'TRUE',
|
||||
'default' => $row['DEFAULT_VALUE'],
|
||||
'autoincrement' => FALSE,
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table (the constraints are included).
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
$table = strtoupper($table);
|
||||
$res = $this->query("
|
||||
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
|
||||
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
|
||||
i.RDB\$UNIQUE_FLAG AS UNIQUE_FLAG,
|
||||
i.RDB\$FOREIGN_KEY AS FOREIGN_KEY,
|
||||
TRIM(r.RDB\$CONSTRAINT_TYPE) AS CONSTRAINT_TYPE,
|
||||
s.RDB\$FIELD_POSITION AS FIELD_POSITION
|
||||
FROM RDB\$INDEX_SEGMENTS s
|
||||
LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
||||
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
||||
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
|
||||
ORDER BY s.RDB\$FIELD_POSITION"
|
||||
);
|
||||
$indexes = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$key = $row['INDEX_NAME'];
|
||||
$indexes[$key]['name'] = $key;
|
||||
$indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1;
|
||||
$indexes[$key]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
|
||||
$indexes[$key]['table'] = $table;
|
||||
$indexes[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME'];
|
||||
}
|
||||
return $indexes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
$table = strtoupper($table);
|
||||
$res = $this->query("
|
||||
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
|
||||
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
|
||||
FROM RDB\$INDEX_SEGMENTS s
|
||||
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
||||
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
|
||||
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
|
||||
ORDER BY s.RDB\$FIELD_POSITION"
|
||||
);
|
||||
$keys = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$key = $row['INDEX_NAME'];
|
||||
$keys[$key] = array(
|
||||
'name' => $key,
|
||||
'column' => $row['FIELD_NAME'],
|
||||
'table' => $table,
|
||||
);
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of indices in given table (the constraints are not listed).
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndices($table)
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT TRIM(RDB\$INDEX_NAME)
|
||||
FROM RDB\$INDICES
|
||||
WHERE RDB\$RELATION_NAME = UPPER('$table')
|
||||
AND RDB\$UNIQUE_FLAG IS NULL
|
||||
AND RDB\$FOREIGN_KEY IS NULL;"
|
||||
);
|
||||
$indices = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$indices[] = $row[0];
|
||||
}
|
||||
return $indices;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of constraints in given table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getConstraints($table)
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT TRIM(RDB\$INDEX_NAME)
|
||||
FROM RDB\$INDICES
|
||||
WHERE RDB\$RELATION_NAME = UPPER('$table')
|
||||
AND (
|
||||
RDB\$UNIQUE_FLAG IS NOT NULL
|
||||
OR RDB\$FOREIGN_KEY IS NOT NULL
|
||||
);"
|
||||
);
|
||||
$constraints = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$constraints[] = $row[0];
|
||||
}
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all triggers in a table or database.
|
||||
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
|
||||
* @param string
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getTriggersMeta($table = NULL)
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
|
||||
TRIM(RDB\$RELATION_NAME) AS TABLE_NAME,
|
||||
CASE RDB\$TRIGGER_TYPE
|
||||
WHEN 1 THEN 'BEFORE'
|
||||
WHEN 2 THEN 'AFTER'
|
||||
WHEN 3 THEN 'BEFORE'
|
||||
WHEN 4 THEN 'AFTER'
|
||||
WHEN 5 THEN 'BEFORE'
|
||||
WHEN 6 THEN 'AFTER'
|
||||
END AS TRIGGER_TYPE,
|
||||
CASE RDB\$TRIGGER_TYPE
|
||||
WHEN 1 THEN 'INSERT'
|
||||
WHEN 2 THEN 'INSERT'
|
||||
WHEN 3 THEN 'UPDATE'
|
||||
WHEN 4 THEN 'UPDATE'
|
||||
WHEN 5 THEN 'DELETE'
|
||||
WHEN 6 THEN 'DELETE'
|
||||
END AS TRIGGER_EVENT,
|
||||
CASE RDB\$TRIGGER_INACTIVE
|
||||
WHEN 1 THEN 'FALSE' ELSE 'TRUE'
|
||||
END AS TRIGGER_ENABLED
|
||||
FROM RDB\$TRIGGERS
|
||||
WHERE RDB\$SYSTEM_FLAG = 0"
|
||||
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
|
||||
);
|
||||
$triggers = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of triggers for given table.
|
||||
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getTriggers($table = NULL)
|
||||
{
|
||||
$q = "SELECT TRIM(RDB\$TRIGGER_NAME)
|
||||
FROM RDB\$TRIGGERS
|
||||
WHERE RDB\$SYSTEM_FLAG = 0";
|
||||
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
|
||||
|
||||
$res = $this->query($q);
|
||||
$triggers = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$triggers[] = $row[0];
|
||||
}
|
||||
return $triggers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata from stored procedures and their input and output parameters.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getProceduresMeta()
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT
|
||||
TRIM(p.RDB\$PARAMETER_NAME) AS PARAMETER_NAME,
|
||||
TRIM(p.RDB\$PROCEDURE_NAME) AS PROCEDURE_NAME,
|
||||
CASE p.RDB\$PARAMETER_TYPE
|
||||
WHEN 0 THEN 'INPUT'
|
||||
WHEN 1 THEN 'OUTPUT'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS PARAMETER_TYPE,
|
||||
CASE f.RDB\$FIELD_TYPE
|
||||
WHEN 261 THEN 'BLOB'
|
||||
WHEN 14 THEN 'CHAR'
|
||||
WHEN 40 THEN 'CSTRING'
|
||||
WHEN 11 THEN 'D_FLOAT'
|
||||
WHEN 27 THEN 'DOUBLE'
|
||||
WHEN 10 THEN 'FLOAT'
|
||||
WHEN 16 THEN 'INT64'
|
||||
WHEN 8 THEN 'INTEGER'
|
||||
WHEN 9 THEN 'QUAD'
|
||||
WHEN 7 THEN 'SMALLINT'
|
||||
WHEN 12 THEN 'DATE'
|
||||
WHEN 13 THEN 'TIME'
|
||||
WHEN 35 THEN 'TIMESTAMP'
|
||||
WHEN 37 THEN 'VARCHAR'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS FIELD_TYPE,
|
||||
f.RDB\$FIELD_LENGTH AS FIELD_LENGTH,
|
||||
p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER
|
||||
FROM RDB\$PROCEDURE_PARAMETERS p
|
||||
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 = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$key = $row['PROCEDURE_NAME'];
|
||||
$io = trim($row['PARAMETER_TYPE']);
|
||||
$num = $row['PARAMETER_NUMBER'];
|
||||
$procedures[$key]['name'] = $row['PROCEDURE_NAME'];
|
||||
$procedures[$key]['params'][$io][$num]['name'] = $row['PARAMETER_NAME'];
|
||||
$procedures[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']);
|
||||
$procedures[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH'];
|
||||
}
|
||||
return $procedures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of stored procedures.
|
||||
* @return array
|
||||
*/
|
||||
public function getProcedures()
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT TRIM(RDB\$PROCEDURE_NAME)
|
||||
FROM RDB\$PROCEDURES;"
|
||||
);
|
||||
$procedures = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$procedures[] = $row[0];
|
||||
}
|
||||
return $procedures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of generators.
|
||||
* @return array
|
||||
*/
|
||||
public function getGenerators()
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT TRIM(RDB\$GENERATOR_NAME)
|
||||
FROM RDB\$GENERATORS
|
||||
WHERE RDB\$SYSTEM_FLAG = 0;"
|
||||
);
|
||||
$generators = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$generators[] = $row[0];
|
||||
}
|
||||
return $generators;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of user defined functions (UDF).
|
||||
* @return array
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
$res = $this->query("
|
||||
SELECT TRIM(RDB\$FUNCTION_NAME)
|
||||
FROM RDB\$FUNCTIONS
|
||||
WHERE RDB\$SYSTEM_FLAG = 0;"
|
||||
);
|
||||
$functions = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$functions[] = $row[0];
|
||||
}
|
||||
return $functions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Database procedure exception.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
@@ -1,377 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
require_once dirname(__FILE__) . '/DibiMsSql2005Reflector.php';
|
||||
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* - charset => character encoding to set (default is UTF-8)
|
||||
* - resource (resource) => existing connection resource
|
||||
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
|
||||
{
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var int|FALSE Affected rows */
|
||||
private $affectedRows = FALSE;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('sqlsrv')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
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 {
|
||||
// Default values
|
||||
if (!isset($config['options']['CharacterSet'])) {
|
||||
$config['options']['CharacterSet'] = 'UTF-8';
|
||||
}
|
||||
|
||||
$this->connection = sqlsrv_connect($config['host'], (array) $config['options']);
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
$info = sqlsrv_errors();
|
||||
throw new DibiDriverException($info[0]['message'], $info[0]['code']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
sqlsrv_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return IDibiResultDriver|NULL
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$this->affectedRows = FALSE;
|
||||
$res = sqlsrv_query($this->connection, $sql);
|
||||
|
||||
if ($res === FALSE) {
|
||||
$info = sqlsrv_errors();
|
||||
throw new DibiDriverException($info[0]['message'], $info[0]['code'], $sql);
|
||||
|
||||
} elseif (is_resource($res)) {
|
||||
$this->affectedRows = sqlsrv_rows_affected($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 $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)
|
||||
{
|
||||
$res = sqlsrv_query($this->connection, 'SELECT @@IDENTITY');
|
||||
if (is_resource($res)) {
|
||||
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
|
||||
return $row[0];
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
sqlsrv_begin_transaction($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
sqlsrv_commit($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
sqlsrv_rollback($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 DibiMssql2005Reflector($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 "'" . str_replace("'", "''", $value) . "'";
|
||||
|
||||
case dibi::IDENTIFIER:
|
||||
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
|
||||
return '[' . str_replace(']', ']]', $value) . ']';
|
||||
|
||||
case dibi::BOOL:
|
||||
return $value ? 1 : 0;
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
|
||||
|
||||
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)
|
||||
{
|
||||
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// offset support is missing
|
||||
if ($limit >= 0) {
|
||||
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') AS T ';
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
throw new DibiNotImplementedException('Offset is not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && $this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 sqlsrv_fetch_array($this->resultSet, $assoc ? SQLSRV_FETCH_ASSOC : SQLSRV_FETCH_NUMERIC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 DibiNotSupportedException('Cannot seek an unbuffered result set.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
sqlsrv_free_stmt($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$columns = array();
|
||||
foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) {
|
||||
$columns[] = array(
|
||||
'name' => $fieldMetadata['Name'],
|
||||
'fullname' => $fieldMetadata['Name'],
|
||||
'nativetype' => $fieldMetadata['Type'],
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : NULL;
|
||||
}
|
||||
|
||||
}
|
@@ -1,132 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi reflector for MSSQL2005 databases.
|
||||
*
|
||||
* @package dibi\drivers
|
||||
* @internal
|
||||
*/
|
||||
class DibiMsSql2005Reflector 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()
|
||||
{
|
||||
$res = $this->driver->query('SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.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)
|
||||
{
|
||||
$res = $this->driver->query("
|
||||
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->escape($table, dibi::TEXT)}
|
||||
");
|
||||
|
||||
$autoIncrements = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT'];
|
||||
}
|
||||
|
||||
$res = $this->driver->query("
|
||||
SELECT C.COLUMN_NAME, C.DATA_TYPE, C.CHARACTER_MAXIMUM_LENGTH , C.COLUMN_DEFAULT , C.NUMERIC_PRECISION, C.NUMERIC_SCALE , C.IS_NULLABLE, Case When Z.CONSTRAINT_NAME Is Null Then 0 Else 1 End As IsPartOfPrimaryKey
|
||||
FROM INFORMATION_SCHEMA.COLUMNS As C
|
||||
Outer Apply (
|
||||
SELECT CCU.CONSTRAINT_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS As TC
|
||||
Join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE As CCU
|
||||
On CCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
|
||||
WHERE TC.TABLE_SCHEMA = C.TABLE_SCHEMA
|
||||
And TC.TABLE_NAME = C.TABLE_NAME
|
||||
And TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
|
||||
And CCU.COLUMN_NAME = C.COLUMN_NAME
|
||||
) As Z
|
||||
WHERE C.TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
|
||||
");
|
||||
$columns = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$columns[] = array(
|
||||
'name' => $row['COLUMN_NAME'],
|
||||
'table' => $table,
|
||||
'nativetype' => strtoupper($row['DATA_TYPE']),
|
||||
'size' => $row['CHARACTER_MAXIMUM_LENGTH'],
|
||||
'unsigned' => TRUE,
|
||||
'nullable' => $row['IS_NULLABLE'] === 'YES',
|
||||
'default' => $row['COLUMN_DEFAULT'],
|
||||
'autoincrement' => $autoIncrements[$row['COLUMN_NAME']],
|
||||
'vendor' => $row,
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
$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 * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}");
|
||||
$indexes = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
throw new DibiNotImplementedException;
|
||||
}
|
||||
|
||||
}
|
@@ -1,365 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/DibiMsSqlReflector.php';
|
||||
|
||||
/**
|
||||
* The dibi driver for MS SQL 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
|
||||
* - persistent (bool) => try to find a persistent link?
|
||||
* - resource (resource) => existing connection resource
|
||||
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
|
||||
{
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('mssql')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'mssql' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
} elseif (empty($config['persistent'])) {
|
||||
$this->connection = @mssql_connect($config['host'], $config['username'], $config['password'], TRUE); // intentionally @
|
||||
} else {
|
||||
$this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
throw new DibiDriverException("Can't connect to DB.");
|
||||
}
|
||||
|
||||
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]'.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
mssql_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return IDibiResultDriver|NULL
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$res = @mssql_query($sql, $this->connection); // intentionally @
|
||||
|
||||
if ($res === FALSE) {
|
||||
throw new DibiDriverException(mssql_get_last_message(), 0, $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 mssql_rows_affected($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)
|
||||
{
|
||||
$res = mssql_query('SELECT @@IDENTITY', $this->connection);
|
||||
if (is_resource($res)) {
|
||||
$row = mssql_fetch_row($res);
|
||||
return $row[0];
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->query('BEGIN TRANSACTION');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 DibiMsSqlReflector($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 "'" . str_replace("'", "''", $value) . "'";
|
||||
|
||||
case dibi::IDENTIFIER:
|
||||
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
|
||||
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
|
||||
|
||||
case dibi::BOOL:
|
||||
return $value ? 1 : 0;
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
|
||||
|
||||
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)
|
||||
{
|
||||
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// offset support is missing
|
||||
if ($limit >= 0) {
|
||||
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
throw new DibiNotImplementedException('Offset is not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && $this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
return mssql_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)
|
||||
{
|
||||
return mssql_fetch_array($this->resultSet, $assoc ? MSSQL_ASSOC : MSSQL_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)
|
||||
{
|
||||
return mssql_data_seek($this->resultSet, $row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
mssql_free_result($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$count = mssql_num_fields($this->resultSet);
|
||||
$columns = array();
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$row = (array) mssql_fetch_field($this->resultSet, $i);
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : NULL;
|
||||
}
|
||||
|
||||
}
|
@@ -1,212 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi reflector for MsSQL databases.
|
||||
*
|
||||
* @package dibi\drivers
|
||||
* @internal
|
||||
*/
|
||||
class DibiMsSqlReflector 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()
|
||||
{
|
||||
$res = $this->driver->query('
|
||||
SELECT TABLE_NAME, TABLE_TYPE
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
');
|
||||
$tables = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$tables[] = array(
|
||||
'name' => $row[0],
|
||||
'view' => isset($row[1]) && $row[1] === 'VIEW',
|
||||
);
|
||||
}
|
||||
return $tables;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns count of rows in a table
|
||||
* @param string
|
||||
* @return int
|
||||
*/
|
||||
public function getTableCount($table, $fallback = TRUE)
|
||||
{
|
||||
if (empty($table)) {
|
||||
return FALSE;
|
||||
}
|
||||
$result = $this->driver->query("
|
||||
SELECT MAX(rowcnt)
|
||||
FROM sys.sysindexes
|
||||
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->escape($table, dibi::IDENTIFIER)}")->fetch(FALSE);
|
||||
$count = intval($row[0]);
|
||||
} else {
|
||||
$count = FALSE;
|
||||
}
|
||||
} else {
|
||||
$count = intval($row[0]);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
$res = $this->driver->query("
|
||||
SELECT * FROM
|
||||
INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
|
||||
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
||||
");
|
||||
$columns = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$size = FALSE;
|
||||
$type = strtoupper($row['DATA_TYPE']);
|
||||
|
||||
$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]) {
|
||||
$size = $row[$size_cols[$type]];
|
||||
}
|
||||
}
|
||||
|
||||
$columns[] = array(
|
||||
'name' => $row['COLUMN_NAME'],
|
||||
'table' => $table,
|
||||
'nativetype' => $type,
|
||||
'size' => $size,
|
||||
'unsigned' => NULL,
|
||||
'nullable' => $row['IS_NULLABLE'] === 'YES',
|
||||
'default' => $row['COLUMN_DEFAULT'],
|
||||
'autoincrement' => FALSE,
|
||||
'vendor' => $row,
|
||||
);
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
$res = $this->driver->query(
|
||||
"SELECT ind.name index_name, ind.index_id, ic.index_column_id,
|
||||
col.name column_name, ind.is_unique, ind.is_primary_key
|
||||
FROM sys.indexes ind
|
||||
INNER JOIN sys.index_columns ic ON
|
||||
(ind.object_id = ic.object_id AND ind.index_id = ic.index_id)
|
||||
INNER JOIN sys.columns col ON
|
||||
(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->escape($table, dibi::TEXT)}
|
||||
AND t.is_ms_shipped = 0
|
||||
ORDER BY
|
||||
t.name, ind.name, ind.index_id, ic.index_column_id
|
||||
");
|
||||
|
||||
$indexes = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$index_name = $row['index_name'];
|
||||
|
||||
if (!isset($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'] = array();
|
||||
}
|
||||
$indexes[$index_name]['columns'][] = $row['column_name'];
|
||||
}
|
||||
|
||||
return array_values($indexes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
$res = $this->driver->query("
|
||||
SELECT f.name AS foreign_key,
|
||||
OBJECT_NAME(f.parent_object_id) AS table_name,
|
||||
COL_NAME(fc.parent_object_id,
|
||||
fc.parent_column_id) AS column_name,
|
||||
OBJECT_NAME (f.referenced_object_id) AS reference_table_name,
|
||||
COL_NAME(fc.referenced_object_id,
|
||||
fc.referenced_column_id) AS reference_column_name,
|
||||
fc.*
|
||||
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->escape($table, dibi::TEXT)}
|
||||
");
|
||||
|
||||
$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'] = array($row['column_name']); // local columns
|
||||
$keys[$key_name]['table'] = $row['reference_table_name']; // referenced table
|
||||
$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
|
||||
}
|
||||
}
|
||||
return array_values($keys);
|
||||
}
|
||||
|
||||
}
|
@@ -1,465 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver for MySQL database.
|
||||
*
|
||||
* Driver options:
|
||||
* - host => the MySQL server host name
|
||||
* - port (int) => the port number to attempt to connect to the MySQL server
|
||||
* - socket => the socket or named pipe
|
||||
* - username (or user)
|
||||
* - password (or pass)
|
||||
* - database => the database name to select
|
||||
* - flags (int) => driver specific constants (MYSQL_CLIENT_*)
|
||||
* - charset => character encoding to set (default is utf8)
|
||||
* - persistent (bool) => try to find a persistent link?
|
||||
* - 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 DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
|
||||
{
|
||||
const ERROR_ACCESS_DENIED = 1045;
|
||||
const ERROR_DUPLICATE_ENTRY = 1062;
|
||||
const ERROR_DATA_TRUNCATED = 1265;
|
||||
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var bool Is buffered (seekable and countable)? */
|
||||
private $buffered;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('mysql')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
} else {
|
||||
// default values
|
||||
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) {
|
||||
$config['host'] = $host;
|
||||
$config['port'] = ini_get('mysql.default_port');
|
||||
} else {
|
||||
if (!isset($config['socket'])) {
|
||||
$config['socket'] = ini_get('mysql.default_socket');
|
||||
}
|
||||
$config['host'] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($config['socket'])) {
|
||||
$host = $config['host'] . (empty($config['port']) ? '' : ':' . $config['port']);
|
||||
} else {
|
||||
$host = ':' . $config['socket'];
|
||||
}
|
||||
|
||||
if (empty($config['persistent'])) {
|
||||
$this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['flags']); // intentionally @
|
||||
} else {
|
||||
$this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['flags']); // intentionally @
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
throw new DibiDriverException(mysql_error(), mysql_errno());
|
||||
}
|
||||
|
||||
if (isset($config['charset'])) {
|
||||
$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 DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['sqlmode'])) {
|
||||
$this->query("SET sql_mode='$config[sqlmode]'");
|
||||
}
|
||||
|
||||
if (isset($config['timezone'])) {
|
||||
$this->query("SET time_zone='$config[timezone]'");
|
||||
}
|
||||
|
||||
$this->buffered = empty($config['unbuffered']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
mysql_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return IDibiResultDriver|NULL
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
if ($this->buffered) {
|
||||
$res = @mysql_query($sql, $this->connection); // intentionally @
|
||||
} else {
|
||||
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves information about the most recently executed query.
|
||||
* @return array
|
||||
*/
|
||||
public function getInfo()
|
||||
{
|
||||
$res = array();
|
||||
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
|
||||
if (preg_last_error()) {
|
||||
throw new DibiPcreException;
|
||||
}
|
||||
|
||||
foreach ($matches as $m) {
|
||||
$res[$m[1]] = (int) $m[2];
|
||||
}
|
||||
return $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 mysql_affected_rows($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 mysql_insert_id($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : '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 DibiMySqlReflector($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:
|
||||
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:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
|
||||
|
||||
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)
|
||||
{
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
|
||||
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
|
||||
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && $this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 mysql_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)
|
||||
{
|
||||
return mysql_fetch_array($this->resultSet, $assoc ? MYSQL_ASSOC : MYSQL_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
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function seek($row)
|
||||
{
|
||||
if (!$this->buffered) {
|
||||
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
|
||||
}
|
||||
|
||||
return mysql_data_seek($this->resultSet, $row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
mysql_free_result($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$count = mysql_num_fields($this->resultSet);
|
||||
$columns = array();
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$row = (array) mysql_fetch_field($this->resultSet, $i);
|
||||
$columns[] = array(
|
||||
'name' => $row['name'],
|
||||
'table' => $row['table'],
|
||||
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
||||
'nativetype' => strtoupper($row['type']),
|
||||
'vendor' => $row,
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : NULL;
|
||||
}
|
||||
|
||||
}
|
@@ -1,149 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi reflector for MySQL databases.
|
||||
*
|
||||
* @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
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
$data = $this->driver->query("SELECT `ENGINE` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}")->fetch(TRUE);
|
||||
if ($data['ENGINE'] !== 'InnoDB') {
|
||||
throw new DibiNotSupportedException("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->escape($table, dibi::TEXT)}
|
||||
GROUP BY rc.CONSTRAINT_NAME
|
||||
");
|
||||
|
||||
$foreignKeys = array();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@@ -1,459 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver for MySQL database via improved extension.
|
||||
*
|
||||
* Driver options:
|
||||
* - host => the MySQL server host name
|
||||
* - port (int) => the port number to attempt to connect to the MySQL server
|
||||
* - socket => the socket or named pipe
|
||||
* - username (or user)
|
||||
* - password (or pass)
|
||||
* - database => the database name to select
|
||||
* - options (array) => array of driver specific constants (MYSQLI_*) and values {@see mysqli_options}
|
||||
* - flags (int) => driver specific constants (MYSQLI_CLIENT_*) {@see mysqli_real_connect}
|
||||
* - charset => character encoding to set (default is utf8)
|
||||
* - persistent (bool) => try to find a persistent link?
|
||||
* - 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 DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
|
||||
{
|
||||
const ERROR_ACCESS_DENIED = 1045;
|
||||
const ERROR_DUPLICATE_ENTRY = 1062;
|
||||
const ERROR_DATA_TRUNCATED = 1265;
|
||||
|
||||
/** @var mysqli Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var mysqli_result Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var bool Is buffered (seekable and countable)? */
|
||||
private $buffered;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('mysqli')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
mysqli_report(MYSQLI_REPORT_OFF);
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
} else {
|
||||
// default values
|
||||
$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'),
|
||||
'port' => NULL,
|
||||
);
|
||||
if (!isset($config['host'])) {
|
||||
$host = ini_get('mysqli.default_host');
|
||||
if ($host) {
|
||||
$config['host'] = $host;
|
||||
$config['port'] = ini_get('mysqli.default_port');
|
||||
} else {
|
||||
$config['host'] = NULL;
|
||||
$config['port'] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
$foo = & $config['flags'];
|
||||
$foo = & $config['database'];
|
||||
|
||||
$this->connection = mysqli_init();
|
||||
if (isset($config['options'])) {
|
||||
if (is_scalar($config['options'])) {
|
||||
$config['flags'] = $config['options']; // back compatibility
|
||||
trigger_error(__CLASS__ . ": configuration item 'options' must be array; for constants MYSQLI_CLIENT_* use 'flags'.", E_USER_NOTICE);
|
||||
} else {
|
||||
foreach ((array) $config['options'] as $key => $value) {
|
||||
mysqli_options($this->connection, $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@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 DibiDriverException(mysqli_connect_error(), $errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['charset'])) {
|
||||
if (!@mysqli_set_charset($this->connection, $config['charset'])) {
|
||||
$this->query("SET NAMES '$config[charset]'");
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['sqlmode'])) {
|
||||
$this->query("SET sql_mode='$config[sqlmode]'");
|
||||
}
|
||||
|
||||
if (isset($config['timezone'])) {
|
||||
$this->query("SET time_zone='$config[timezone]'");
|
||||
}
|
||||
|
||||
$this->buffered = empty($config['unbuffered']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
mysqli_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @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 (mysqli_errno($this->connection)) {
|
||||
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
|
||||
|
||||
} elseif (is_object($res)) {
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves information about the most recently executed query.
|
||||
* @return array
|
||||
*/
|
||||
public function getInfo()
|
||||
{
|
||||
$res = array();
|
||||
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
|
||||
if (preg_last_error()) {
|
||||
throw new DibiPcreException;
|
||||
}
|
||||
|
||||
foreach ($matches as $m) {
|
||||
$res[$m[1]] = (int) $m[2];
|
||||
}
|
||||
return $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 mysqli_affected_rows($this->connection) === -1 ? FALSE : mysqli_affected_rows($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 mysqli_insert_id($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection resource.
|
||||
* @return mysqli
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
return @$this->connection->thread_id ? $this->connection : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection reflector.
|
||||
* @return IDibiReflector
|
||||
*/
|
||||
public function getReflector()
|
||||
{
|
||||
return new DibiMySqlReflector($this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result set driver factory.
|
||||
* @param mysqli_result
|
||||
* @return IDibiResultDriver
|
||||
*/
|
||||
public function createResultDriver(mysqli_result $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 "'" . mysqli_real_escape_string($this->connection, $value) . "'";
|
||||
|
||||
case dibi::BINARY:
|
||||
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
|
||||
|
||||
case dibi::IDENTIFIER:
|
||||
return '`' . str_replace('`', '``', $value) . '`';
|
||||
|
||||
case dibi::BOOL:
|
||||
return $value ? 1 : 0;
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
|
||||
|
||||
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)
|
||||
{
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
|
||||
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
|
||||
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && @$this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 mysqli_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)
|
||||
{
|
||||
return mysqli_fetch_array($this->resultSet, $assoc ? MYSQLI_ASSOC : MYSQLI_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
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function seek($row)
|
||||
{
|
||||
if (!$this->buffered) {
|
||||
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
|
||||
}
|
||||
return mysqli_data_seek($this->resultSet, $row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
mysqli_free_result($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
static $types;
|
||||
if ($types === NULL) {
|
||||
$consts = get_defined_constants(TRUE);
|
||||
$types = array();
|
||||
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : array() as $key => $value) {
|
||||
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
|
||||
$types[$value] = substr($key, 12);
|
||||
}
|
||||
}
|
||||
$types[MYSQLI_TYPE_TINY] = $types[MYSQLI_TYPE_SHORT] = $types[MYSQLI_TYPE_LONG] = 'INT';
|
||||
}
|
||||
|
||||
$count = mysqli_num_fields($this->resultSet);
|
||||
$columns = array();
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
|
||||
$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'],
|
||||
'vendor' => $row,
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mysqli_result
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return $this->resultSet;
|
||||
}
|
||||
|
||||
}
|
@@ -1,474 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver interacting with databases via ODBC connections.
|
||||
*
|
||||
* Driver options:
|
||||
* - dsn => driver specific DSN
|
||||
* - username (or user)
|
||||
* - password (or pass)
|
||||
* - persistent (bool) => try to find a persistent link?
|
||||
* - resource (resource) => existing connection resource
|
||||
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
|
||||
{
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var int|FALSE Affected rows */
|
||||
private $affectedRows = FALSE;
|
||||
|
||||
/** @var int Cursor */
|
||||
private $row = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('odbc')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'odbc' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
} else {
|
||||
// default values
|
||||
$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 @
|
||||
} else {
|
||||
$this->connection = @odbc_pconnect($config['dsn'], $config['username'], $config['password']); // intentionally @
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
odbc_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return IDibiResultDriver|NULL
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$this->affectedRows = FALSE;
|
||||
$res = @odbc_exec($this->connection, $sql); // intentionally @
|
||||
|
||||
if ($res === FALSE) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
throw new DibiNotSupportedException('ODBC does not support autoincrementing.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
if (!odbc_autocommit($this->connection, FALSE)) {
|
||||
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
if (!odbc_commit($this->connection)) {
|
||||
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||
}
|
||||
odbc_autocommit($this->connection, TRUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
if (!odbc_rollback($this->connection)) {
|
||||
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||
}
|
||||
odbc_autocommit($this->connection, TRUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is in transaction?
|
||||
* @return bool
|
||||
*/
|
||||
public function inTransaction()
|
||||
{
|
||||
return !odbc_autocommit($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 $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 "'" . str_replace("'", "''", $value) . "'";
|
||||
|
||||
case dibi::IDENTIFIER:
|
||||
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
|
||||
|
||||
case dibi::BOOL:
|
||||
return $value ? 1 : 0;
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? "#m/d/Y H:i:s#" : "#m/d/Y#");
|
||||
|
||||
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)
|
||||
{
|
||||
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// offset support is missing
|
||||
if ($limit >= 0) {
|
||||
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && $this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
// will return -1 with many drivers :-(
|
||||
return odbc_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)
|
||||
{
|
||||
if ($assoc) {
|
||||
return odbc_fetch_array($this->resultSet, ++$this->row);
|
||||
} else {
|
||||
$set = $this->resultSet;
|
||||
if (!odbc_fetch_row($set, ++$this->row)) {
|
||||
return FALSE;
|
||||
}
|
||||
$count = odbc_num_fields($set);
|
||||
$cols = array();
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
$cols[] = odbc_result($set, $i);
|
||||
}
|
||||
return $cols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->row = $row;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
odbc_free_result($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$count = odbc_num_fields($this->resultSet);
|
||||
$columns = array();
|
||||
for ($i = 1; $i <= $count; $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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************* IDibiReflector ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of tables.
|
||||
* @return array
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
$res = odbc_tables($this->connection);
|
||||
$tables = array();
|
||||
while ($row = odbc_fetch_array($res)) {
|
||||
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
|
||||
$tables[] = array(
|
||||
'name' => $row['TABLE_NAME'],
|
||||
'view' => $row['TABLE_TYPE'] === 'VIEW',
|
||||
);
|
||||
}
|
||||
}
|
||||
odbc_free_result($res);
|
||||
return $tables;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
$res = odbc_columns($this->connection);
|
||||
$columns = array();
|
||||
while ($row = odbc_fetch_array($res)) {
|
||||
if ($row['TABLE_NAME'] === $table) {
|
||||
$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);
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
throw new DibiNotImplementedException;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
throw new DibiNotImplementedException;
|
||||
}
|
||||
|
||||
}
|
@@ -1,445 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver for Oracle database.
|
||||
*
|
||||
* Driver options:
|
||||
* - database => the name of the local Oracle instance or the name of the entry in tnsnames.ora
|
||||
* - 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 DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
|
||||
{
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var bool */
|
||||
private $autocommit = TRUE;
|
||||
|
||||
/** @var string Date and datetime format */
|
||||
private $fmtDate, $fmtDateTime;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('oci8')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'oci8' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
$foo = & $config['charset'];
|
||||
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
|
||||
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
|
||||
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
} elseif (empty($config['persistent'])) {
|
||||
$this->connection = @oci_new_connect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @
|
||||
} else {
|
||||
$this->connection = @oci_pconnect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @
|
||||
}
|
||||
|
||||
if (!$this->connection) {
|
||||
$err = oci_error();
|
||||
throw new DibiDriverException($err['message'], $err['code']);
|
||||
}
|
||||
|
||||
if (isset($config['schema'])) {
|
||||
$this->query('ALTER SESSION SET CURRENT_SCHEMA=' . $config['schema']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
oci_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return IDibiResultDriver|NULL
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$res = oci_parse($this->connection, $sql);
|
||||
if ($res) {
|
||||
@oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
|
||||
$err = oci_error($res);
|
||||
if ($err) {
|
||||
throw new DibiDriverException($err['message'], $err['code'], $sql);
|
||||
|
||||
} elseif (is_resource($res)) {
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
} else {
|
||||
$err = oci_error($this->connection);
|
||||
throw new DibiDriverException($err['message'], $err['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()
|
||||
{
|
||||
throw new DibiNotImplementedException;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$row = $this->query("SELECT $sequence.CURRVAL AS ID FROM DUAL")->fetch(TRUE);
|
||||
return isset($row['ID']) ? (int) $row['ID'] : FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->autocommit = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
if (!oci_commit($this->connection)) {
|
||||
$err = oci_error($this->connection);
|
||||
throw new DibiDriverException($err['message'], $err['code']);
|
||||
}
|
||||
$this->autocommit = TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
if (!oci_rollback($this->connection)) {
|
||||
$err = oci_error($this->connection);
|
||||
throw new DibiDriverException($err['message'], $err['code']);
|
||||
}
|
||||
$this->autocommit = TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 $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 "'" . 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) . '"';
|
||||
|
||||
case dibi::BOOL:
|
||||
return $value ? 1 : 0;
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? $this->fmtDateTime : $this->fmtDate);
|
||||
|
||||
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)
|
||||
{
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
|
||||
$value = str_replace("'", "''", $value);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 ($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 >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
|
||||
. ') WHERE "__rnum" > '. (int) $offset;
|
||||
|
||||
} elseif ($limit >= 0) {
|
||||
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && $this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 oci_fetch_array($this->resultSet, ($assoc ? OCI_ASSOC : OCI_NUM) | OCI_RETURN_NULLS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 DibiNotImplementedException;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
oci_free_statement($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$count = oci_num_fields($this->resultSet);
|
||||
$columns = array();
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
$type = oci_field_type($this->resultSet, $i);
|
||||
$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,
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************* IDibiReflector ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of tables.
|
||||
* @return array
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
$res = $this->query('SELECT * FROM cat');
|
||||
$tables = array();
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
|
||||
$tables[] = array(
|
||||
'name' => $row[0],
|
||||
'view' => $row[1] === 'VIEW',
|
||||
);
|
||||
}
|
||||
}
|
||||
return $tables;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
throw new DibiNotImplementedException;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
throw new DibiNotImplementedException;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
throw new DibiNotImplementedException;
|
||||
}
|
||||
|
||||
}
|
@@ -1,520 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://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
|
||||
* - version
|
||||
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
|
||||
*
|
||||
* @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;
|
||||
|
||||
/** @var string */
|
||||
private $serverVersion;
|
||||
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
if ($e->getMessage() === 'could not find driver') {
|
||||
throw new DibiNotSupportedException('PHP extension for PDO is not loaded.');
|
||||
}
|
||||
throw new DibiDriverException($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 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:
|
||||
case dibi::BINARY:
|
||||
if ($this->driverName === 'odbc') {
|
||||
return "'" . str_replace("'", "''", $value) . "'";
|
||||
} else {
|
||||
return $this->connection->quote($value, $type === dibi::TEXT ? PDO::PARAM_STR : PDO::PARAM_LOB);
|
||||
}
|
||||
|
||||
case dibi::IDENTIFIER:
|
||||
switch ($this->driverName) {
|
||||
case 'mysql':
|
||||
return '`' . str_replace('`', '``', $value) . '`';
|
||||
|
||||
case 'oci':
|
||||
case 'pgsql':
|
||||
return '"' . str_replace('"', '""', $value) . '"';
|
||||
|
||||
case 'sqlite':
|
||||
case 'sqlite2':
|
||||
return '[' . strtr($value, '[]', ' ') . ']';
|
||||
|
||||
case 'odbc':
|
||||
case 'mssql':
|
||||
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
|
||||
|
||||
case 'dblib':
|
||||
case 'sqlsrv':
|
||||
return '[' . str_replace(']', ']]', $value) . ']';
|
||||
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
|
||||
case dibi::BOOL:
|
||||
if ($this->driverName === 'pgsql') {
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
} else {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
if ($this->driverName === 'odbc') {
|
||||
return $value->format($type === dibi::DATETIME ? '#m/d/Y H:i:s#' : '#m/d/Y#');
|
||||
} else {
|
||||
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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, array('%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\'));
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
|
||||
case 'sqlite':
|
||||
case 'sqlite2':
|
||||
$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, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
|
||||
default:
|
||||
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 'mssql':
|
||||
case 'sqlsrv':
|
||||
case 'dblib':
|
||||
if (version_compare($this->serverVersion, '11.0') >= 0) {
|
||||
if ($offset >= 0 || $limit >= 0) {
|
||||
$sql .= ' OFFSET ' . (int) $offset . ' ROWS'
|
||||
. ($limit > 0 ? ' FETCH NEXT ' . (int) $limit . ' ROWS ONLY' : '');
|
||||
}
|
||||
break;
|
||||
}
|
||||
// intentionally break omitted
|
||||
|
||||
case 'odbc':
|
||||
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 bool 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;
|
||||
}
|
||||
|
||||
}
|
@@ -1,672 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver for PostgreSQL database.
|
||||
*
|
||||
* Driver options:
|
||||
* - host, hostaddr, port, dbname, user, password, connect_timeout, options, sslmode, service => see PostgreSQL API
|
||||
* - string => or use connection string
|
||||
* - schema => the schema search path
|
||||
* - 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 DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
|
||||
{
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var int|FALSE Affected rows */
|
||||
private $affectedRows = FALSE;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('pgsql')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'pgsql' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function connect(array & $config)
|
||||
{
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
} else {
|
||||
$config += array(
|
||||
'charset' => 'utf8',
|
||||
);
|
||||
if (isset($config['string'])) {
|
||||
$string = $config['string'];
|
||||
} else {
|
||||
$string = '';
|
||||
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] . ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (DibiDriverException::catchError($msg)) {
|
||||
throw new DibiDriverException($msg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
throw new DibiDriverException('Connecting error.');
|
||||
}
|
||||
|
||||
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 "' . $config['schema'] . '"');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
pg_close($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pings database.
|
||||
* @return bool
|
||||
*/
|
||||
public function ping()
|
||||
{
|
||||
return pg_ping($this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return IDibiResultDriver|NULL
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$this->affectedRows = FALSE;
|
||||
$res = @pg_query($this->connection, $sql); // intentionally @
|
||||
|
||||
if ($res === FALSE) {
|
||||
throw new DibiDriverException(pg_last_error($this->connection), 0, $sql);
|
||||
|
||||
} elseif (is_resource($res)) {
|
||||
$this->affectedRows = pg_affected_rows($res);
|
||||
if (pg_num_fields($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 $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)
|
||||
{
|
||||
if ($sequence === NULL) {
|
||||
// PostgreSQL 8.1 is needed
|
||||
$res = $this->query('SELECT LASTVAL()');
|
||||
} else {
|
||||
$res = $this->query("SELECT CURRVAL('$sequence')");
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$row = $res->fetch(FALSE);
|
||||
return is_array($row) ? $row[0] : FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is in transaction?
|
||||
* @return bool
|
||||
*/
|
||||
public function inTransaction()
|
||||
{
|
||||
return !in_array(pg_transaction_status($this->connection), array(PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE), TRUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 $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:
|
||||
if (!is_resource($this->connection)) {
|
||||
throw new DibiException('Lost connection to server.');
|
||||
}
|
||||
return "'" . pg_escape_string($this->connection, $value) . "'";
|
||||
|
||||
case dibi::BINARY:
|
||||
if (!is_resource($this->connection)) {
|
||||
throw new DibiException('Lost connection to server.');
|
||||
}
|
||||
return "'" . pg_escape_bytea($this->connection, $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:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
|
||||
|
||||
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)
|
||||
{
|
||||
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
|
||||
$value = pg_escape_string($this->connection, $value);
|
||||
$value = strtr($value, array('%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\'));
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 pg_unescape_bytea($value);
|
||||
}
|
||||
throw new InvalidArgumentException('Unsupported type.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injects LIMIT/OFFSET to the SQL query.
|
||||
* @return void
|
||||
*/
|
||||
public function applyLimit(& $sql, $limit, $offset)
|
||||
{
|
||||
if ($limit >= 0) {
|
||||
$sql .= ' LIMIT ' . (int) $limit;
|
||||
}
|
||||
|
||||
if ($offset > 0) {
|
||||
$sql .= ' OFFSET ' . (int) $offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* result set ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->getResultResource() && $this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
return pg_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)
|
||||
{
|
||||
return pg_fetch_array($this->resultSet, NULL, $assoc ? PGSQL_ASSOC : PGSQL_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)
|
||||
{
|
||||
return pg_result_seek($this->resultSet, $row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
pg_free_result($this->resultSet);
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$count = pg_num_fields($this->resultSet);
|
||||
$columns = array();
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$row = array(
|
||||
'name' => pg_field_name($this->resultSet, $i),
|
||||
'table' => pg_field_table($this->resultSet, $i),
|
||||
'nativetype' => pg_field_type($this->resultSet, $i),
|
||||
);
|
||||
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
|
||||
$columns[] = $row;
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************* IDibiReflector ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of tables.
|
||||
* @return array
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
$version = pg_parameter_status($this->resource, 'server_version');
|
||||
if ($version < 7.4) {
|
||||
throw new DibiDriverException('Reflection requires PostgreSQL 7.4 and newer.');
|
||||
}
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
table_name AS name,
|
||||
CASE table_type
|
||||
WHEN 'VIEW' THEN 1
|
||||
ELSE 0
|
||||
END AS view
|
||||
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);
|
||||
$tables = pg_fetch_all($res->resultSet);
|
||||
return $tables ? $tables : array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), 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
|
||||
");
|
||||
$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
|
||||
");
|
||||
|
||||
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 = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
|
||||
$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',
|
||||
'default' => $row['column_default'],
|
||||
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
|
||||
'vendor' => $row,
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), 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
|
||||
ORDER BY ordinal_position
|
||||
");
|
||||
|
||||
$columns = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$columns[$row['ordinal_position']] = $row['column_name'];
|
||||
}
|
||||
|
||||
$res = $this->query("
|
||||
SELECT pg_class2.relname, indisunique, indisprimary, indkey
|
||||
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
|
||||
");
|
||||
|
||||
$indexes = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$indexes[$row['relname']]['name'] = $row['relname'];
|
||||
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
|
||||
$indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't';
|
||||
foreach (explode(' ', $row['indkey']) as $index) {
|
||||
$indexes[$row['relname']]['columns'][] = $columns[$index];
|
||||
}
|
||||
}
|
||||
return array_values($indexes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
|
||||
|
||||
$res = $this->query("
|
||||
SELECT
|
||||
c.conname AS name,
|
||||
lt.attname AS local,
|
||||
c.confrelid::regclass AS table,
|
||||
ft.attname AS foreign,
|
||||
|
||||
CASE c.confupdtype
|
||||
WHEN 'a' THEN 'NO ACTION'
|
||||
WHEN 'r' THEN 'RESTRICT'
|
||||
WHEN 'c' THEN 'CASCADE'
|
||||
WHEN 'n' THEN 'SET NULL'
|
||||
WHEN 'd' THEN 'SET DEFAULT'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS \"onUpdate\",
|
||||
|
||||
CASE c.confdeltype
|
||||
WHEN 'a' THEN 'NO ACTION'
|
||||
WHEN 'r' THEN 'RESTRICT'
|
||||
WHEN 'c' THEN 'CASCADE'
|
||||
WHEN 'n' THEN 'SET NULL'
|
||||
WHEN 'd' THEN 'SET DEFAULT'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS \"onDelete\",
|
||||
|
||||
c.conkey,
|
||||
lt.attnum AS lnum,
|
||||
c.confkey,
|
||||
ft.attnum AS fnum
|
||||
FROM
|
||||
pg_constraint c
|
||||
JOIN pg_attribute lt ON c.conrelid = lt.attrelid AND lt.attnum = ANY (c.conkey)
|
||||
JOIN pg_attribute ft ON c.confrelid = ft.attrelid AND ft.attnum = ANY (c.confkey)
|
||||
WHERE
|
||||
c.contype = 'f'
|
||||
AND
|
||||
c.conrelid = $_table::regclass
|
||||
");
|
||||
|
||||
$fKeys = $references = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
if (!isset($fKeys[$row['name']])) {
|
||||
$fKeys[$row['name']] = array(
|
||||
'name' => $row['name'],
|
||||
'table' => $row['table'],
|
||||
'local' => array(),
|
||||
'foreign' => array(),
|
||||
'onUpdate' => $row['onUpdate'],
|
||||
'onDelete' => $row['onDelete'],
|
||||
);
|
||||
|
||||
$l = explode(',', trim($row['conkey'], '{}'));
|
||||
$f = explode(',', trim($row['confkey'], '{}'));
|
||||
|
||||
$references[$row['name']] = array_combine($l, $f);
|
||||
}
|
||||
|
||||
if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) {
|
||||
$fKeys[$row['name']]['local'][] = $row['local'];
|
||||
$fKeys[$row['name']]['foreign'][] = $row['foreign'];
|
||||
}
|
||||
}
|
||||
|
||||
return $fKeys;
|
||||
}
|
||||
|
||||
}
|
@@ -1,421 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver for SQLite3 database.
|
||||
*
|
||||
* Driver options:
|
||||
* - database (or file) => the filename of the SQLite3 database
|
||||
* - 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 (SQLite3) => existing connection resource
|
||||
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
|
||||
*
|
||||
* @package dibi\drivers
|
||||
*/
|
||||
class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
|
||||
{
|
||||
/** @var SQLite3 Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var SQLite3Result Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
/** @var string Date and datetime format */
|
||||
private $fmtDate, $fmtDateTime;
|
||||
|
||||
/** @var string character encoding */
|
||||
private $dbcharset, $charset;
|
||||
|
||||
|
||||
/**
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('sqlite3')) {
|
||||
throw new DibiNotSupportedException("PHP extension 'sqlite3' 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';
|
||||
|
||||
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 DibiDriverException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$this->connection->close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
$res = @$this->connection->query($sql); // intentionally @
|
||||
if ($this->connection->lastErrorCode()) {
|
||||
throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
|
||||
|
||||
} elseif ($res instanceof SQLite3Result) {
|
||||
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->connection->changes();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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->lastInsertRowID();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection reflector.
|
||||
* @return IDibiReflector
|
||||
*/
|
||||
public function getReflector()
|
||||
{
|
||||
return new DibiSqliteReflector($this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result set driver factory.
|
||||
* @param SQLite3Result
|
||||
* @return IDibiResultDriver
|
||||
*/
|
||||
public function createResultDriver(SQLite3Result $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->escapeString($value) . "'";
|
||||
|
||||
case dibi::BINARY:
|
||||
return "X'" . bin2hex((string) $value) . "'";
|
||||
|
||||
case dibi::IDENTIFIER:
|
||||
return '[' . strtr($value, '[]', ' ') . ']';
|
||||
|
||||
case dibi::BOOL:
|
||||
return $value ? 1 : 0;
|
||||
|
||||
case dibi::DATE:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? $this->fmtDateTime : $this->fmtDate);
|
||||
|
||||
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)
|
||||
{
|
||||
$value = addcslashes($this->connection->escapeString($value), '%_\\');
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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**/
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->autoFree && $this->resultSet && @$this->free();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_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 bool TRUE on success, FALSE if unable to seek to specified record
|
||||
* @throws DibiNotSupportedException
|
||||
*/
|
||||
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->finalize();
|
||||
$this->resultSet = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array
|
||||
*/
|
||||
public function getResultColumns()
|
||||
{
|
||||
$count = $this->resultSet->numColumns();
|
||||
$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[] = array(
|
||||
'name' => $this->resultSet->columnName($i),
|
||||
'table' => NULL,
|
||||
'fullname' => $this->resultSet->columnName($i),
|
||||
'nativetype' => $types[$this->resultSet->columnType($i)],
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = FALSE;
|
||||
return $this->resultSet;
|
||||
}
|
||||
|
||||
|
||||
/********************* 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)
|
||||
{
|
||||
$this->connection->createFunction($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)
|
||||
{
|
||||
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
|
||||
}
|
||||
|
||||
}
|
@@ -1,419 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://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:
|
||||
case dibi::DATETIME:
|
||||
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
|
||||
$value = new DibiDateTime($value);
|
||||
}
|
||||
return $value->format($type === dibi::DATETIME ? $this->fmtDateTime : $this->fmtDate);
|
||||
|
||||
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 bool 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);
|
||||
}
|
||||
|
||||
}
|
@@ -1,159 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The dibi reflector for SQLite database.
|
||||
*
|
||||
* @package dibi\drivers
|
||||
* @internal
|
||||
*/
|
||||
class DibiSqliteReflector 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()
|
||||
{
|
||||
$res = $this->driver->query("
|
||||
SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view')
|
||||
UNION ALL
|
||||
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
|
||||
ORDER BY name
|
||||
");
|
||||
$tables = array();
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$tables[] = $row;
|
||||
}
|
||||
return $tables;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
$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'];
|
||||
$type = explode('(', $row['type']);
|
||||
$columns[] = array(
|
||||
'name' => $column,
|
||||
'table' => $table,
|
||||
'fullname' => "$table.$column",
|
||||
'nativetype' => strtoupper($type[0]),
|
||||
'size' => isset($type[1]) ? (int) $type[1] : NULL,
|
||||
'nullable' => $row['notnull'] == '0',
|
||||
'default' => $row['dflt_value'],
|
||||
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
|
||||
'vendor' => $row,
|
||||
);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
$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->escape($index, dibi::IDENTIFIER)})");
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
|
||||
}
|
||||
}
|
||||
|
||||
$columns = $this->getColumns($table);
|
||||
foreach ($indexes as $index => $values) {
|
||||
$column = $indexes[$index]['columns'][0];
|
||||
$primary = FALSE;
|
||||
foreach ($columns as $info) {
|
||||
if ($column == $info['name']) {
|
||||
$primary = $info['vendor']['pk'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$indexes[$index]['primary'] = (bool) $primary;
|
||||
}
|
||||
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
|
||||
foreach ($columns as $column) {
|
||||
if ($column['vendor']['pk']) {
|
||||
$indexes[] = array(
|
||||
'name' => 'ROWID',
|
||||
'unique' => TRUE,
|
||||
'primary' => TRUE,
|
||||
'columns' => array($column['name']),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($indexes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getForeignKeys($table)
|
||||
{
|
||||
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
|
||||
$keys[$row['id']]['table'] = $row['table']; // referenced table
|
||||
$keys[$row['id']]['foreign'][$row['seq']] = $row['to']; // referenced columns
|
||||
$keys[$row['id']]['onDelete'] = $row['on_delete'];
|
||||
$keys[$row['id']]['onUpdate'] = $row['on_update'];
|
||||
|
||||
if ($keys[$row['id']]['foreign'][0] == NULL) {
|
||||
$keys[$row['id']]['foreign'] = NULL;
|
||||
}
|
||||
}
|
||||
return array_values($keys);
|
||||
}
|
||||
|
||||
}
|
@@ -1,540 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* This class is static container class for creating DB objects and
|
||||
* store connections info.
|
||||
*
|
||||
* @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',
|
||||
AFFECTED_ROWS = 'a';
|
||||
|
||||
/** @deprecated */
|
||||
const FIELD_TEXT = self::TEXT,
|
||||
FIELD_BINARY = self::BINARY,
|
||||
FIELD_BOOL = self::BOOL,
|
||||
FIELD_INTEGER = self::INTEGER,
|
||||
FIELD_FLOAT = self::FLOAT,
|
||||
FIELD_DATE = self::DATE,
|
||||
FIELD_DATETIME = self::DATETIME,
|
||||
FIELD_TIME = self::TIME;
|
||||
|
||||
/** version */
|
||||
const VERSION = '2.4-dev',
|
||||
REVISION = 'released on 2015-10-08';
|
||||
|
||||
/** 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 = '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 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
|
||||
/********************* substitutions ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns substitution hashmap - Monostate for DibiConnection::getSubstitutes().
|
||||
* @return DibiHashMap
|
||||
*/
|
||||
public static function getSubstitutes()
|
||||
{
|
||||
return self::getConnection()->getSubstitutes();
|
||||
}
|
||||
|
||||
|
||||
/********************* 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|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, 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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,690 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* dibi connection.
|
||||
*
|
||||
* @package dibi
|
||||
*
|
||||
* @property-read int $affectedRows
|
||||
* @property-read int $insertId
|
||||
*/
|
||||
class DibiConnection extends DibiObject
|
||||
{
|
||||
/** @var array of function (DibiEvent $event); Occurs after query is executed */
|
||||
public $onEvent;
|
||||
|
||||
/** @var array Current connection configuration */
|
||||
private $config;
|
||||
|
||||
/** @var IDibiDriver */
|
||||
private $driver;
|
||||
|
||||
/** @var DibiTranslator */
|
||||
private $translator;
|
||||
|
||||
/** @var bool Is connected? */
|
||||
private $connected = FALSE;
|
||||
|
||||
/** @var DibiHashMap Substitutes for identifiers */
|
||||
private $substitutes;
|
||||
|
||||
|
||||
/**
|
||||
* Connection options: (see driver-specific options too)
|
||||
* - lazy (bool) => if TRUE, connection will be established only when required
|
||||
* - result (array) => result set options
|
||||
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
|
||||
* - profiler (array or bool)
|
||||
* - run (bool) => enable profiler?
|
||||
* - file => file to log
|
||||
* - substitutes (array) => map of driver specific substitutes (under development)
|
||||
|
||||
* @param mixed connection parameters
|
||||
* @param string connection name
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function __construct($config, $name = NULL)
|
||||
{
|
||||
if (is_string($config)) {
|
||||
parse_str($config, $config);
|
||||
|
||||
} elseif ($config instanceof Traversable) {
|
||||
$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.');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$class = preg_replace(array('#\W#', '#sql#'), array('_', 'Sql'), ucfirst(strtolower($config['driver'])));
|
||||
$class = "Dibi{$class}Driver";
|
||||
if (!class_exists($class)) {
|
||||
include_once dirname(__FILE__) . "/../drivers/$class.php";
|
||||
|
||||
if (!class_exists($class, FALSE)) {
|
||||
throw new DibiException("Unable to create instance of dibi driver '$class'.");
|
||||
}
|
||||
}
|
||||
|
||||
$config['name'] = $name;
|
||||
$this->config = $config;
|
||||
$this->driver = new $class;
|
||||
$this->translator = new DibiTranslator($this);
|
||||
|
||||
// profiler
|
||||
$profilerCfg = & $config['profiler'];
|
||||
if (is_scalar($profilerCfg)) {
|
||||
$profilerCfg = array('run' => (bool) $profilerCfg);
|
||||
}
|
||||
if (!empty($profilerCfg['run'])) {
|
||||
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
|
||||
|
||||
if (isset($profilerCfg['file'])) {
|
||||
$this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
|
||||
}
|
||||
|
||||
if (DibiFirePhpLogger::isAvailable()) {
|
||||
$this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
|
||||
}
|
||||
|
||||
if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('DibiNettePanel')) {
|
||||
$panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
|
||||
$panel->register($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
|
||||
if (!empty($config['substitutes'])) {
|
||||
foreach ($config['substitutes'] as $key => $value) {
|
||||
$this->substitutes->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($config['lazy'])) {
|
||||
$this->connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Automatically frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
// disconnects and rolls back transaction - do not rely on auto-disconnect and rollback!
|
||||
$this->connected && $this->driver->getResource() && $this->disconnect();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
*/
|
||||
final public function connect()
|
||||
{
|
||||
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
|
||||
try {
|
||||
$this->driver->connect($this->config);
|
||||
$this->connected = TRUE;
|
||||
$event && $this->onEvent($event->done());
|
||||
|
||||
} catch (DibiException $e) {
|
||||
$event && $this->onEvent($event->done($e));
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
final public function disconnect()
|
||||
{
|
||||
$this->driver->disconnect();
|
||||
$this->connected = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns TRUE when connection was established.
|
||||
* @return bool
|
||||
*/
|
||||
final public function isConnected()
|
||||
{
|
||||
return $this->connected;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns configuration variable. If no $key is passed, returns the entire array.
|
||||
* @see self::__construct
|
||||
* @param string
|
||||
* @param mixed default value to use if key not found
|
||||
* @return mixed
|
||||
*/
|
||||
final public function getConfig($key = NULL, $default = NULL)
|
||||
{
|
||||
if ($key === NULL) {
|
||||
return $this->config;
|
||||
|
||||
} elseif (isset($this->config[$key])) {
|
||||
return $this->config[$key];
|
||||
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the driver and connects to a database in lazy mode.
|
||||
* @return IDibiDriver
|
||||
*/
|
||||
final public function getDriver()
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
return $this->driver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates (translates) and executes SQL query.
|
||||
* @param array|mixed one or more arguments
|
||||
* @return DibiResult|int result set object (if any)
|
||||
* @throws DibiException
|
||||
*/
|
||||
final public function query($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return $this->nativeQuery($this->translateArgs($args));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates SQL query.
|
||||
* @param array|mixed one or more arguments
|
||||
* @return string
|
||||
* @throws DibiException
|
||||
*/
|
||||
final public function translate($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return $this->translateArgs($args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates and prints SQL query.
|
||||
* @param array|mixed one or more arguments
|
||||
* @return bool
|
||||
*/
|
||||
final public function test($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
try {
|
||||
dibi::dump($this->translateArgs($args));
|
||||
return TRUE;
|
||||
|
||||
} catch (DibiException $e) {
|
||||
if ($e->getSql()) {
|
||||
dibi::dump($e->getSql());
|
||||
} else {
|
||||
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates (translates) and returns SQL query as DibiDataSource.
|
||||
* @param array|mixed one or more arguments
|
||||
* @return DibiDataSource
|
||||
* @throws DibiException
|
||||
*/
|
||||
final public function dataSource($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return new DibiDataSource($this->translateArgs($args), $this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates SQL query.
|
||||
* @param array
|
||||
* @return string
|
||||
*/
|
||||
private function translateArgs($args)
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
return $this->translator->translate($args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @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 DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
|
||||
try {
|
||||
$res = $this->driver->query($sql);
|
||||
|
||||
} catch (DibiException $e) {
|
||||
$event && $this->onEvent($event->done($e));
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($res) {
|
||||
$res = $this->createResultSet($res);
|
||||
} else {
|
||||
$res = $this->driver->getAffectedRows();
|
||||
}
|
||||
|
||||
$event && $this->onEvent($event->done($res));
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
|
||||
* @return int number of rows
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function getAffectedRows()
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
$rows = $this->driver->getAffectedRows();
|
||||
if (!is_int($rows) || $rows < 0) {
|
||||
throw new DibiException('Cannot retrieve number of affected rows.');
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of affected rows. Alias for getAffectedRows().
|
||||
* @return int number of rows
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
return $this->getAffectedRows();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
||||
* @param string optional sequence name
|
||||
* @return int
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function getInsertId($sequence = NULL)
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
$id = $this->driver->getInsertId($sequence);
|
||||
if ($id < 1) {
|
||||
throw new DibiException('Cannot retrieve last generated ID.');
|
||||
}
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
|
||||
* @param string optional sequence name
|
||||
* @return int
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function insertId($sequence = NULL)
|
||||
{
|
||||
return $this->getInsertId($sequence);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL;
|
||||
try {
|
||||
$this->driver->begin($savepoint);
|
||||
$event && $this->onEvent($event->done());
|
||||
|
||||
} catch (DibiException $e) {
|
||||
$event && $this->onEvent($event->done($e));
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL;
|
||||
try {
|
||||
$this->driver->commit($savepoint);
|
||||
$event && $this->onEvent($event->done());
|
||||
|
||||
} catch (DibiException $e) {
|
||||
$event && $this->onEvent($event->done($e));
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
*/
|
||||
public function rollback($savepoint = NULL)
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL;
|
||||
try {
|
||||
$this->driver->rollback($savepoint);
|
||||
$event && $this->onEvent($event->done());
|
||||
|
||||
} catch (DibiException $e) {
|
||||
$event && $this->onEvent($event->done($e));
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result set factory.
|
||||
* @param IDibiResultDriver
|
||||
* @return DibiResult
|
||||
*/
|
||||
public function createResultSet(IDibiResultDriver $resultDriver)
|
||||
{
|
||||
$res = new DibiResult($resultDriver);
|
||||
return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
|
||||
->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
|
||||
}
|
||||
|
||||
|
||||
/********************* fluent SQL builders ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* @return DibiFluent
|
||||
*/
|
||||
public function command()
|
||||
{
|
||||
return new DibiFluent($this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string column name
|
||||
* @return DibiFluent
|
||||
*/
|
||||
public function select($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return $this->command()->__call('select', $args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string table
|
||||
* @param array
|
||||
* @return DibiFluent
|
||||
*/
|
||||
public function update($table, $args)
|
||||
{
|
||||
if (!(is_array($args) || $args instanceof Traversable)) {
|
||||
throw new InvalidArgumentException('Arguments must be array or Traversable.');
|
||||
}
|
||||
return $this->command()->update('%n', $table)->set($args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string table
|
||||
* @param array
|
||||
* @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.');
|
||||
}
|
||||
return $this->command()->insert()
|
||||
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string table
|
||||
* @return DibiFluent
|
||||
*/
|
||||
public function delete($table)
|
||||
{
|
||||
return $this->command()->delete()->from('%n', $table);
|
||||
}
|
||||
|
||||
|
||||
/********************* substitutions ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns substitution hashmap.
|
||||
* @return DibiHashMap
|
||||
*/
|
||||
public function getSubstitutes()
|
||||
{
|
||||
return $this->substitutes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides substitution.
|
||||
* @return string
|
||||
*/
|
||||
public function substitute($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]};
|
||||
}
|
||||
|
||||
|
||||
/********************* shortcuts ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
||||
* @param array|mixed one or more arguments
|
||||
* @return DibiRow
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function fetch($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return $this->query($args)->fetch();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
|
||||
* @param array|mixed one or more arguments
|
||||
* @return DibiRow[]
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function fetchAll($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return $this->query($args)->fetchAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
||||
* @param array|mixed one or more arguments
|
||||
* @return string
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function fetchSingle($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return $this->query($args)->fetchSingle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
||||
* @param array|mixed one or more arguments
|
||||
* @return string
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function fetchPairs($args)
|
||||
{
|
||||
$args = func_get_args();
|
||||
return $this->query($args)->fetchPairs();
|
||||
}
|
||||
|
||||
|
||||
/********************* misc ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Import SQL dump from file - extreme fast!
|
||||
* @param string filename
|
||||
* @return int count of sql commands
|
||||
*/
|
||||
public function loadFile($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;
|
||||
$delimiter = ';';
|
||||
$sql = '';
|
||||
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));
|
||||
$this->driver->query($sql);
|
||||
$sql = '';
|
||||
$count++;
|
||||
|
||||
} else {
|
||||
$sql .= $s . "\n";
|
||||
}
|
||||
}
|
||||
if (trim($sql) !== '') {
|
||||
$this->driver->query($sql);
|
||||
$count++;
|
||||
}
|
||||
fclose($handle);
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a information about the current database.
|
||||
* @return DibiDatabaseInfo
|
||||
*/
|
||||
public function getDatabaseInfo()
|
||||
{
|
||||
$this->connected || $this->connect();
|
||||
return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevents unserialization.
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevents serialization.
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
|
||||
}
|
||||
|
||||
}
|
@@ -1,306 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of IDataSource for dibi.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiDataSource extends DibiObject implements IDataSource
|
||||
{
|
||||
/** @var DibiConnection */
|
||||
private $connection;
|
||||
|
||||
/** @var string */
|
||||
private $sql;
|
||||
|
||||
/** @var DibiResult */
|
||||
private $result;
|
||||
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/** @var int */
|
||||
private $totalCount;
|
||||
|
||||
/** @var array */
|
||||
private $cols = array();
|
||||
|
||||
/** @var array */
|
||||
private $sorting = array();
|
||||
|
||||
/** @var array */
|
||||
private $conds = array();
|
||||
|
||||
/** @var int */
|
||||
private $offset;
|
||||
|
||||
/** @var int */
|
||||
private $limit;
|
||||
|
||||
|
||||
/**
|
||||
* @param string SQL command or table or view name, as data source
|
||||
* @param DibiConnection connection
|
||||
*/
|
||||
public function __construct($sql, DibiConnection $connection)
|
||||
{
|
||||
if (strpbrk($sql, " \t\r\n") === FALSE) {
|
||||
$this->sql = $connection->getDriver()->escape($sql, dibi::IDENTIFIER); // table name
|
||||
} else {
|
||||
$this->sql = '(' . $sql . ') t'; // SQL command
|
||||
}
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Selects columns to query.
|
||||
* @param string|array column name or array of column names
|
||||
* @param string column alias
|
||||
* @return self
|
||||
*/
|
||||
public function select($col, $as = NULL)
|
||||
{
|
||||
if (is_array($col)) {
|
||||
$this->cols = $col;
|
||||
} else {
|
||||
$this->cols[$col] = $as;
|
||||
}
|
||||
$this->result = NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds conditions to query.
|
||||
* @param mixed conditions
|
||||
* @return self
|
||||
*/
|
||||
public function where($cond)
|
||||
{
|
||||
if (is_array($cond)) {
|
||||
// TODO: not consistent with select and orderBy
|
||||
$this->conds[] = $cond;
|
||||
} else {
|
||||
$this->conds[] = func_get_args();
|
||||
}
|
||||
$this->result = $this->count = NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Selects columns to order by.
|
||||
* @param string|array column name or array of column names
|
||||
* @param string sorting direction
|
||||
* @return self
|
||||
*/
|
||||
public function orderBy($row, $sorting = 'ASC')
|
||||
{
|
||||
if (is_array($row)) {
|
||||
$this->sorting = $row;
|
||||
} else {
|
||||
$this->sorting[$row] = $sorting;
|
||||
}
|
||||
$this->result = NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Limits number of rows.
|
||||
* @param int limit
|
||||
* @param int offset
|
||||
* @return self
|
||||
*/
|
||||
public function applyLimit($limit, $offset = NULL)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
$this->offset = $offset;
|
||||
$this->result = $this->count = NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the dibi connection.
|
||||
* @return DibiConnection
|
||||
*/
|
||||
final public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
|
||||
/********************* executing ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns (and queries) DibiResult.
|
||||
* @return DibiResult
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
if ($this->result === NULL) {
|
||||
$this->result = $this->connection->nativeQuery($this->__toString());
|
||||
}
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return DibiResultIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return $this->getResult()->getIterator();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates, executes SQL query and fetches the single row.
|
||||
* @return DibiRow|FALSE array on success, FALSE if no next record
|
||||
*/
|
||||
public function fetch()
|
||||
{
|
||||
return $this->getResult()->fetch();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Like fetch(), but returns only first field.
|
||||
* @return mixed value on success, FALSE if no next record
|
||||
*/
|
||||
public function fetchSingle()
|
||||
{
|
||||
return $this->getResult()->fetchSingle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table.
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll()
|
||||
{
|
||||
return $this->getResult()->fetchAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table and returns associative tree.
|
||||
* @param string associative descriptor
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAssoc($assoc)
|
||||
{
|
||||
return $this->getResult()->fetchAssoc($assoc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table like $key => $value pairs.
|
||||
* @param string associative key
|
||||
* @param string value
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs($key = NULL, $value = NULL)
|
||||
{
|
||||
return $this->getResult()->fetchPairs($key, $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Discards the internal cache.
|
||||
* @return void
|
||||
*/
|
||||
public function release()
|
||||
{
|
||||
$this->result = $this->count = $this->totalCount = NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************* exporting ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns this data source wrapped in DibiFluent object.
|
||||
* @return DibiFluent
|
||||
*/
|
||||
public function toFluent()
|
||||
{
|
||||
return $this->connection->select('*')->from('(%SQL) t', $this->__toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns this data source wrapped in DibiDataSource object.
|
||||
* @return DibiDataSource
|
||||
*/
|
||||
public function toDataSource()
|
||||
{
|
||||
return new self($this->__toString(), $this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns SQL query.
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->connection->translate('
|
||||
SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
|
||||
FROM %SQL', $this->sql, '
|
||||
%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) {
|
||||
trigger_error($e->getMessage(), E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* counting ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a given data source.
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if ($this->count === NULL) {
|
||||
$this->count = $this->conds || $this->offset || $this->limit
|
||||
? (int) $this->connection->nativeQuery(
|
||||
'SELECT COUNT(*) FROM (' . $this->__toString() . ') t'
|
||||
)->fetchSingle()
|
||||
: $this->getTotalCount();
|
||||
}
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a given data source.
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalCount()
|
||||
{
|
||||
if ($this->totalCount === NULL) {
|
||||
$this->totalCount = (int) $this->connection->nativeQuery(
|
||||
'SELECT COUNT(*) FROM ' . $this->sql
|
||||
)->fetchSingle();
|
||||
}
|
||||
return $this->totalCount;
|
||||
}
|
||||
|
||||
}
|
@@ -1,702 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Reflection metadata class for a database.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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 ? $this->info['table'] : NULL; // intentionally ==
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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']);
|
||||
}
|
||||
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* DateTime with serialization and timestamp support for PHP 5.2.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiDateTime extends DateTime
|
||||
{
|
||||
|
||||
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 modify($modify)
|
||||
{
|
||||
parent::modify($modify);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function setTimestamp($timestamp)
|
||||
{
|
||||
$zone = PHP_VERSION_ID === 50206 ? new DateTimeZone($this->getTimezone()->getName()) : $this->getTimezone();
|
||||
$this->__construct('@' . $timestamp);
|
||||
$this->setTimeZone($zone);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
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 __sleep()
|
||||
{
|
||||
$zone = $this->getTimezone()->getName();
|
||||
if ($zone[0] === '+') {
|
||||
$this->fix = array($this->format('Y-m-d H:i:sP'));
|
||||
} else {
|
||||
$this->fix = array($this->format('Y-m-d H:i:s'), $zone);
|
||||
}
|
||||
return array('fix');
|
||||
}
|
||||
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
if (isset($this->fix[1])) {
|
||||
$this->__construct($this->fix[0], new DateTimeZone($this->fix[1]));
|
||||
} else {
|
||||
$this->__construct($this->fix[0]);
|
||||
}
|
||||
unset($this->fix);
|
||||
}
|
||||
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Profiler & logger event.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiEvent
|
||||
{
|
||||
/** event type */
|
||||
const CONNECT = 1,
|
||||
SELECT = 4,
|
||||
INSERT = 8,
|
||||
DELETE = 16,
|
||||
UPDATE = 32,
|
||||
QUERY = 60, // SELECT | INSERT | DELETE | UPDATE
|
||||
BEGIN = 64,
|
||||
COMMIT = 128,
|
||||
ROLLBACK = 256,
|
||||
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
|
||||
ALL = 1023;
|
||||
|
||||
/** @var DibiConnection */
|
||||
public $connection;
|
||||
|
||||
/** @var int */
|
||||
public $type;
|
||||
|
||||
/** @var string */
|
||||
public $sql;
|
||||
|
||||
/** @var DibiResult|DibiDriverException|NULL */
|
||||
public $result;
|
||||
|
||||
/** @var float */
|
||||
public $time;
|
||||
|
||||
/** @var int */
|
||||
public $count;
|
||||
|
||||
/** @var array */
|
||||
public $source;
|
||||
|
||||
|
||||
public function __construct(DibiConnection $connection, $type, $sql = NULL)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->type = $type;
|
||||
$this->sql = trim($sql);
|
||||
$this->time = -microtime(TRUE);
|
||||
|
||||
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
|
||||
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');
|
||||
$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 = array($row['file'], (int) $row['line']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dibi::$elapsedTime = FALSE;
|
||||
dibi::$numOfQueries++;
|
||||
dibi::$sql = $sql;
|
||||
}
|
||||
|
||||
|
||||
public function done($result = NULL)
|
||||
{
|
||||
$this->result = $result;
|
||||
try {
|
||||
$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;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
@@ -1,145 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* dibi common exception.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
{}
|
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* dibi file logger.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiFileLogger extends DibiObject
|
||||
{
|
||||
/** @var string Name of the file where SQL errors should be logged */
|
||||
public $file;
|
||||
|
||||
/** @var int */
|
||||
public $filter;
|
||||
|
||||
|
||||
public function __construct($file, $filter = NULL)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After event notification.
|
||||
* @return void
|
||||
*/
|
||||
public function logEvent(DibiEvent $event)
|
||||
{
|
||||
if (($event->type & $this->filter) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$handle = fopen($this->file, 'a');
|
||||
if (!$handle) {
|
||||
return; // or throw exception?
|
||||
}
|
||||
flock($handle, LOCK_EX);
|
||||
|
||||
if ($event->result instanceof Exception) {
|
||||
$message = $event->result->getMessage();
|
||||
if ($code = $event->result->getCode()) {
|
||||
$message = "[$code] $message";
|
||||
}
|
||||
fwrite($handle,
|
||||
"ERROR: $message"
|
||||
. "\n-- SQL: " . $event->sql
|
||||
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
|
||||
. ";\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
} else {
|
||||
fwrite($handle,
|
||||
'OK: ' . $event->sql
|
||||
. ($event->count ? ";\n-- rows: " . $event->count : '')
|
||||
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
|
||||
. "\n-- source: " . implode(':', $event->source)
|
||||
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
|
||||
. "\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
}
|
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* dibi FirePHP logger.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiFirePhpLogger extends DibiObject
|
||||
{
|
||||
/** maximum number of rows */
|
||||
static public $maxQueries = 30;
|
||||
|
||||
/** maximum SQL length */
|
||||
static public $maxLength = 1000;
|
||||
|
||||
/** size of json stream chunk */
|
||||
static public $streamChunkSize = 4990;
|
||||
|
||||
/** @var int */
|
||||
public $filter;
|
||||
|
||||
/** @var int Elapsed time for all queries */
|
||||
public $totalTime = 0;
|
||||
|
||||
/** @var int Number of all queries */
|
||||
public $numOfQueries = 0;
|
||||
|
||||
/** @var array */
|
||||
private static $fireTable = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAvailable()
|
||||
{
|
||||
return isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
|
||||
}
|
||||
|
||||
|
||||
public function __construct($filter = NULL)
|
||||
{
|
||||
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After event notification.
|
||||
* @return void
|
||||
*/
|
||||
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[] = 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'),
|
||||
);
|
||||
|
||||
$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) {
|
||||
$num++;
|
||||
header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index
|
||||
}
|
||||
header("X-Wf-dibi-1-1-d$num: |$s|");
|
||||
}
|
||||
|
||||
}
|
@@ -1,509 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* dibi SQL builder via fluent interfaces. EXPERIMENTAL!
|
||||
*
|
||||
* @package dibi
|
||||
*
|
||||
* @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)
|
||||
* @method DibiFluent leftJoin($table)
|
||||
* @method DibiFluent on($cond)
|
||||
*/
|
||||
class DibiFluent extends DibiObject implements IDataSource
|
||||
{
|
||||
const REMOVE = FALSE;
|
||||
|
||||
/** @var array */
|
||||
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 = array(
|
||||
'SELECT' => '%n',
|
||||
'FROM' => '%n',
|
||||
'IN' => '%in',
|
||||
'VALUES' => '%l',
|
||||
'SET' => '%a',
|
||||
'WHERE' => '%and',
|
||||
'HAVING' => '%and',
|
||||
'ORDER BY' => '%by',
|
||||
'GROUP BY' => '%by',
|
||||
);
|
||||
|
||||
/** @var array clauses separators */
|
||||
public static $separators = array(
|
||||
'SELECT' => ',',
|
||||
'FROM' => ',',
|
||||
'WHERE' => 'AND',
|
||||
'GROUP BY' => ',',
|
||||
'HAVING' => 'AND',
|
||||
'ORDER BY' => ',',
|
||||
'LIMIT' => FALSE,
|
||||
'OFFSET' => FALSE,
|
||||
'SET' => ',',
|
||||
'VALUES' => ',',
|
||||
'INTO' => FALSE,
|
||||
);
|
||||
|
||||
/** @var array clauses */
|
||||
public static $clauseSwitches = array(
|
||||
'JOIN' => 'FROM',
|
||||
'INNER JOIN' => 'FROM',
|
||||
'LEFT JOIN' => 'FROM',
|
||||
'RIGHT JOIN' => 'FROM',
|
||||
);
|
||||
|
||||
/** @var DibiConnection */
|
||||
private $connection;
|
||||
|
||||
/** @var array */
|
||||
private $setups = array();
|
||||
|
||||
/** @var string */
|
||||
private $command;
|
||||
|
||||
/** @var array */
|
||||
private $clauses = array();
|
||||
|
||||
/** @var array */
|
||||
private $flags = array();
|
||||
|
||||
/** @var array */
|
||||
private $cursor;
|
||||
|
||||
/** @var DibiHashMap normalized clauses */
|
||||
private static $normalizer;
|
||||
|
||||
|
||||
/**
|
||||
* @param DibiConnection
|
||||
*/
|
||||
public function __construct(DibiConnection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
if (self::$normalizer === NULL) {
|
||||
self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends new argument to the clause.
|
||||
* @param string clause name
|
||||
* @param array arguments
|
||||
* @return self
|
||||
*/
|
||||
public function __call($clause, $args)
|
||||
{
|
||||
$clause = self::$normalizer->$clause;
|
||||
|
||||
// lazy initialization
|
||||
if ($this->command === NULL) {
|
||||
if (isset(self::$masks[$clause])) {
|
||||
$this->clauses = array_fill_keys(self::$masks[$clause], NULL);
|
||||
}
|
||||
$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]];
|
||||
}
|
||||
|
||||
if (array_key_exists($clause, $this->clauses)) {
|
||||
// append to clause
|
||||
$this->cursor = & $this->clauses[$clause];
|
||||
|
||||
// TODO: really delete?
|
||||
if ($args === array(self::REMOVE)) {
|
||||
$this->cursor = NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (isset(self::$separators[$clause])) {
|
||||
$sep = self::$separators[$clause];
|
||||
if ($sep === FALSE) { // means: replace
|
||||
$this->cursor = array();
|
||||
|
||||
} elseif (!empty($this->cursor)) {
|
||||
$this->cursor[] = $sep;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// append to currect flow
|
||||
if ($args === array(self::REMOVE)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->cursor[] = $clause;
|
||||
}
|
||||
|
||||
if ($this->cursor === NULL) {
|
||||
$this->cursor = array();
|
||||
}
|
||||
|
||||
// special types or argument
|
||||
if (count($args) === 1) {
|
||||
$arg = $args[0];
|
||||
// TODO: really ignore TRUE?
|
||||
if ($arg === TRUE) { // flag
|
||||
return $this;
|
||||
|
||||
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
|
||||
$args = array('%n', $arg);
|
||||
|
||||
} elseif (is_array($arg) || ($arg instanceof Traversable && !$arg instanceof self)) { // any array
|
||||
if (isset(self::$modifiers[$clause])) {
|
||||
$args = array(self::$modifiers[$clause], $arg);
|
||||
|
||||
} elseif (is_string(key($arg))) { // associative array
|
||||
$args = array('%a', $arg);
|
||||
}
|
||||
} // case $arg === FALSE is handled above
|
||||
}
|
||||
|
||||
foreach ($args as $arg) {
|
||||
if ($arg instanceof self) {
|
||||
$arg = "($arg)";
|
||||
}
|
||||
$this->cursor[] = $arg;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switch to a clause.
|
||||
* @param string clause name
|
||||
* @return self
|
||||
*/
|
||||
public function clause($clause)
|
||||
{
|
||||
$this->cursor = & $this->clauses[self::$normalizer->$clause];
|
||||
if ($this->cursor === NULL) {
|
||||
$this->cursor = array();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a clause.
|
||||
* @param string clause name
|
||||
* @return self
|
||||
*/
|
||||
public function removeClause($clause)
|
||||
{
|
||||
$this->clauses[self::$normalizer->$clause] = NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Change a SQL flag.
|
||||
* @param string flag name
|
||||
* @param bool value
|
||||
* @return self
|
||||
*/
|
||||
public function setFlag($flag, $value = TRUE)
|
||||
{
|
||||
$flag = strtoupper($flag);
|
||||
if ($value) {
|
||||
$this->flags[$flag] = TRUE;
|
||||
} else {
|
||||
unset($this->flags[$flag]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is a flag set?
|
||||
* @param string flag name
|
||||
* @return bool
|
||||
*/
|
||||
final public function getFlag($flag)
|
||||
{
|
||||
return isset($this->flags[strtoupper($flag)]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns SQL command.
|
||||
* @return string
|
||||
*/
|
||||
final public function getCommand()
|
||||
{
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the dibi connection.
|
||||
* @return DibiConnection
|
||||
*/
|
||||
final public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds DibiResult setup.
|
||||
* @param string method
|
||||
* @param mixed args
|
||||
* @return self
|
||||
*/
|
||||
public function setupResult($method)
|
||||
{
|
||||
$this->setups[] = func_get_args();
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/********************* executing ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Generates and executes SQL query.
|
||||
* @param mixed what to return?
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates, executes SQL query and fetches the single row.
|
||||
* @return DibiRow|FALSE array on success, FALSE if no next record
|
||||
*/
|
||||
public function fetch()
|
||||
{
|
||||
if ($this->command === 'SELECT') {
|
||||
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch();
|
||||
} else {
|
||||
return $this->query($this->_export())->fetch();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Like fetch(), but returns only first field.
|
||||
* @return mixed value on success, FALSE if no next record
|
||||
*/
|
||||
public function fetchSingle()
|
||||
{
|
||||
if ($this->command === 'SELECT') {
|
||||
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
|
||||
} else {
|
||||
return $this->query($this->_export())->fetchSingle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table.
|
||||
* @param int offset
|
||||
* @param int limit
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll($offset = NULL, $limit = NULL)
|
||||
{
|
||||
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table and returns associative tree.
|
||||
* @param string associative descriptor
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAssoc($assoc)
|
||||
{
|
||||
return $this->query($this->_export())->fetchAssoc($assoc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table like $key => $value pairs.
|
||||
* @param string associative key
|
||||
* @param string value
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs($key = NULL, $value = NULL)
|
||||
{
|
||||
return $this->query($this->_export())->fetchPairs($key, $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Required by the IteratorAggregate interface.
|
||||
* @param int offset
|
||||
* @param int limit
|
||||
* @return DibiResultIterator
|
||||
*/
|
||||
public function getIterator($offset = NULL, $limit = NULL)
|
||||
{
|
||||
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates and prints SQL query or it's part.
|
||||
* @param string clause name
|
||||
* @return bool
|
||||
*/
|
||||
public function test($clause = NULL)
|
||||
{
|
||||
return $this->connection->test($this->_export($clause));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return (int) $this->query(array(
|
||||
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') AS [data]',
|
||||
))->fetchSingle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return DibiResult
|
||||
*/
|
||||
private function query($args)
|
||||
{
|
||||
$res = $this->connection->query($args);
|
||||
foreach ($this->setups as $setup) {
|
||||
call_user_func_array(array($res, array_shift($setup)), $setup);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/********************* exporting ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* @return DibiDataSource
|
||||
*/
|
||||
public function toDataSource()
|
||||
{
|
||||
return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns SQL query.
|
||||
* @return string
|
||||
*/
|
||||
final public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->connection->translate($this->_export());
|
||||
} catch (Exception $e) {
|
||||
trigger_error($e->getMessage(), E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates parameters for DibiTranslator.
|
||||
* @param string clause name
|
||||
* @return array
|
||||
*/
|
||||
protected function _export($clause = NULL, $args = array())
|
||||
{
|
||||
if ($clause === NULL) {
|
||||
$data = $this->clauses;
|
||||
|
||||
} else {
|
||||
$clause = self::$normalizer->$clause;
|
||||
if (array_key_exists($clause, $this->clauses)) {
|
||||
$data = array($clause => $this->clauses[$clause]);
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $clause => $statement) {
|
||||
if ($statement !== NULL) {
|
||||
$args[] = $clause;
|
||||
if ($clause === $this->command && $this->flags) {
|
||||
$args[] = implode(' ', array_keys($this->flags));
|
||||
}
|
||||
foreach ($statement as $arg) {
|
||||
$args[] = $arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format camelCase clause name to UPPER CASE.
|
||||
* @param string
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public static function _formatClause($s)
|
||||
{
|
||||
if ($s === 'order' || $s === 'group') {
|
||||
$s .= 'By';
|
||||
trigger_error("Did you mean '$s'?", E_USER_NOTICE);
|
||||
}
|
||||
return strtoupper(preg_replace('#[a-z](?=[A-Z])#', '$0 ', $s));
|
||||
}
|
||||
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
// remove references
|
||||
foreach ($this->clauses as $clause => $val) {
|
||||
$this->clauses[$clause] = & $val;
|
||||
unset($val);
|
||||
}
|
||||
$this->cursor = & $foo;
|
||||
}
|
||||
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Lazy cached storage.
|
||||
*
|
||||
* @package dibi
|
||||
* @internal
|
||||
*/
|
||||
abstract class DibiHashMapBase
|
||||
{
|
||||
private $callback;
|
||||
|
||||
|
||||
public function __construct($callback)
|
||||
{
|
||||
$this->setCallback($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;
|
||||
}
|
||||
|
||||
|
||||
public function getCallback()
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lazy cached storage.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class DibiHashMap extends DibiHashMapBase
|
||||
{
|
||||
|
||||
public function __set($nm, $val)
|
||||
{
|
||||
if ($nm == '') {
|
||||
$nm = "\xFF";
|
||||
}
|
||||
$this->$nm = $val;
|
||||
}
|
||||
|
||||
|
||||
public function __get($nm)
|
||||
{
|
||||
if ($nm == '') {
|
||||
$nm = "\xFF";
|
||||
return isset($this->$nm) ? $this->$nm : $this->$nm = call_user_func($this->getCallback(), '');
|
||||
} else {
|
||||
return $this->$nm = call_user_func($this->getCallback(), $nm);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* SQL literal value.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiLiteral extends DibiObject
|
||||
{
|
||||
/** @var string */
|
||||
private $value;
|
||||
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = (string) $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
}
|
@@ -1,307 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* DibiObject is the ultimate ancestor of all instantiable classes.
|
||||
*
|
||||
* DibiObject is copy of Nette\Object from Nette Framework (https://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>
|
||||
*
|
||||
* @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
|
||||
$uname = ucfirst($name);
|
||||
$m = 'get' . $uname;
|
||||
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' . $uname;
|
||||
if (self::hasAccessor($class, $m)) {
|
||||
$val = $this->$m();
|
||||
return $val;
|
||||
}
|
||||
|
||||
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
|
||||
$uname = ucfirst($name);
|
||||
if (self::hasAccessor($class, 'get' . $uname) || self::hasAccessor($class, 'is' . $uname)) {
|
||||
$m = 'set' . $name;
|
||||
if (self::hasAccessor($class, $m)) {
|
||||
$this->$m($value);
|
||||
return;
|
||||
|
||||
} else {
|
||||
throw new LogicException("Cannot assign to a read-only property $class::\$$name.");
|
||||
}
|
||||
}
|
||||
|
||||
throw new LogicException("Cannot assign to an undeclared property $class::\$$name.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is property defined?
|
||||
* @param string property name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return $name !== '' && self::hasAccessor(get_class($this), 'get' . ucfirst($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]);
|
||||
}
|
||||
|
||||
}
|
@@ -1,657 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* dibi result set.
|
||||
*
|
||||
* <code>
|
||||
* $result = dibi::query('SELECT * FROM [table]');
|
||||
*
|
||||
* $row = $result->fetch();
|
||||
* $value = $result->fetchSingle();
|
||||
* $table = $result->fetchAll();
|
||||
* $pairs = $result->fetchPairs();
|
||||
* $assoc = $result->fetchAssoc('id');
|
||||
* $assoc = $result->fetchAssoc('active,#,id');
|
||||
*
|
||||
* unset($result);
|
||||
* </code>
|
||||
*
|
||||
* @package dibi
|
||||
*
|
||||
* @property-read int $rowCount
|
||||
*/
|
||||
class DibiResult extends DibiObject implements IDataSource
|
||||
{
|
||||
/** @var array IDibiResultDriver */
|
||||
private $driver;
|
||||
|
||||
/** @var array Translate table */
|
||||
private $types = array();
|
||||
|
||||
/** @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 = 'DibiRow';
|
||||
|
||||
/** @var Callback returned object factory*/
|
||||
private $rowFactory;
|
||||
|
||||
/** @var array format */
|
||||
private $formats = array();
|
||||
|
||||
|
||||
/**
|
||||
* @param IDibiResultDriver
|
||||
*/
|
||||
public function __construct($driver)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
$this->detectTypes();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
final public function getResource()
|
||||
{
|
||||
return $this->getResultDriver()->getResultResource();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return void
|
||||
*/
|
||||
final public function free()
|
||||
{
|
||||
if ($this->driver !== NULL) {
|
||||
$this->driver->free();
|
||||
$this->driver = $this->meta = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Safe access to property $driver.
|
||||
* @return IDibiResultDriver
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
final public function getResultDriver()
|
||||
{
|
||||
if ($this->driver === NULL) {
|
||||
throw new RuntimeException('Result-set was released from memory.');
|
||||
}
|
||||
|
||||
return $this->driver;
|
||||
}
|
||||
|
||||
|
||||
/********************* rows ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* 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 DibiException
|
||||
*/
|
||||
final public function seek($row)
|
||||
{
|
||||
return ($row !== 0 || $this->fetched) ? (bool) $this->getResultDriver()->seek($row) : TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Required by the Countable interface.
|
||||
* @return int
|
||||
*/
|
||||
final public function count()
|
||||
{
|
||||
return $this->getResultDriver()->getRowCount();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
final public function getRowCount()
|
||||
{
|
||||
return $this->getResultDriver()->getRowCount();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Required by the IteratorAggregate interface.
|
||||
* @return DibiResultIterator
|
||||
*/
|
||||
final public function getIterator()
|
||||
{
|
||||
return new DibiResultIterator($this);
|
||||
}
|
||||
|
||||
|
||||
/********************* fetching rows ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Set fetched object class. This class should extend the DibiRow class.
|
||||
* @param string
|
||||
* @return self
|
||||
*/
|
||||
public function setRowClass($class)
|
||||
{
|
||||
$this->rowClass = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns fetched object class name.
|
||||
* @return string
|
||||
*/
|
||||
public function getRowClass()
|
||||
{
|
||||
return $this->rowClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a factory to create fetched object instances. These should extend the DibiRow class.
|
||||
* @param callback
|
||||
* @return self
|
||||
*/
|
||||
public function setRowFactory($callback)
|
||||
{
|
||||
$this->rowFactory = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches the row at current position, process optional type conversion.
|
||||
* and moves the internal cursor to the next position
|
||||
* @return DibiRow|FALSE array on success, FALSE if no next record
|
||||
*/
|
||||
final public function fetch()
|
||||
{
|
||||
$row = $this->getResultDriver()->fetch(TRUE);
|
||||
if (!is_array($row)) {
|
||||
return FALSE;
|
||||
}
|
||||
$this->fetched = TRUE;
|
||||
$this->normalize($row);
|
||||
if ($this->rowFactory) {
|
||||
return call_user_func($this->rowFactory, $row);
|
||||
} elseif ($this->rowClass) {
|
||||
$row = new $this->rowClass($row);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Like fetch(), but returns only first field.
|
||||
* @return mixed value on success, FALSE if no next record
|
||||
*/
|
||||
final public function fetchSingle()
|
||||
{
|
||||
$row = $this->getResultDriver()->fetch(TRUE);
|
||||
if (!is_array($row)) {
|
||||
return FALSE;
|
||||
}
|
||||
$this->fetched = TRUE;
|
||||
$this->normalize($row);
|
||||
return reset($row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table.
|
||||
* @param int offset
|
||||
* @param int limit
|
||||
* @return DibiRow[]
|
||||
*/
|
||||
final public function fetchAll($offset = NULL, $limit = NULL)
|
||||
{
|
||||
$limit = $limit === NULL ? -1 : (int) $limit;
|
||||
$this->seek((int) $offset);
|
||||
$row = $this->fetch();
|
||||
if (!$row) {
|
||||
return array(); // empty result set
|
||||
}
|
||||
|
||||
$data = array();
|
||||
do {
|
||||
if ($limit === 0) {
|
||||
break;
|
||||
}
|
||||
$limit--;
|
||||
$data[] = $row;
|
||||
} while ($row = $this->fetch());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table and returns associative tree.
|
||||
* Examples:
|
||||
* - associative descriptor: col1[]col2->col3
|
||||
* builds a tree: $tree[$val1][$index][$val2]->col3[$val3] = {record}
|
||||
* - associative descriptor: col1|col2->col3=col4
|
||||
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
|
||||
* @param string associative descriptor
|
||||
* @return DibiRow
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
final public function fetchAssoc($assoc)
|
||||
{
|
||||
if (strpos($assoc, ',') !== FALSE) {
|
||||
return $this->oldFetchAssoc($assoc);
|
||||
}
|
||||
|
||||
$this->seek(0);
|
||||
$row = $this->fetch();
|
||||
if (!$row) {
|
||||
return array(); // empty result set
|
||||
}
|
||||
|
||||
$data = NULL;
|
||||
$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.");
|
||||
}
|
||||
}
|
||||
|
||||
if ($as === '->') { // must not be last
|
||||
array_pop($assoc);
|
||||
}
|
||||
|
||||
if (empty($assoc)) {
|
||||
$assoc[] = '[]';
|
||||
}
|
||||
|
||||
// make associative tree
|
||||
do {
|
||||
$x = & $data;
|
||||
|
||||
// iterative deepening
|
||||
foreach ($assoc as $i => $as) {
|
||||
if ($as === '[]') { // indexed-array node
|
||||
$x = & $x[];
|
||||
|
||||
} elseif ($as === '=') { // "value" node
|
||||
$x = $row->{$assoc[$i + 1]};
|
||||
continue 2;
|
||||
|
||||
} elseif ($as === '->') { // "object" node
|
||||
if ($x === NULL) {
|
||||
$x = clone $row;
|
||||
$x = & $x->{$assoc[$i + 1]};
|
||||
$x = NULL; // prepare child node
|
||||
} else {
|
||||
$x = & $x->{$assoc[$i + 1]};
|
||||
}
|
||||
|
||||
} elseif ($as !== '|') { // associative-array node
|
||||
$x = & $x[$row->$as];
|
||||
}
|
||||
}
|
||||
|
||||
if ($x === NULL) { // build leaf
|
||||
$x = $row;
|
||||
}
|
||||
|
||||
} while ($row = $this->fetch());
|
||||
|
||||
unset($x);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private function oldFetchAssoc($assoc)
|
||||
{
|
||||
$this->seek(0);
|
||||
$row = $this->fetch();
|
||||
if (!$row) {
|
||||
return array(); // empty result set
|
||||
}
|
||||
|
||||
$data = NULL;
|
||||
$assoc = explode(',', $assoc);
|
||||
|
||||
// strip leading = and @
|
||||
$leaf = '@'; // gap
|
||||
$last = count($assoc) - 1;
|
||||
while ($assoc[$last] === '=' || $assoc[$last] === '@') {
|
||||
$leaf = $assoc[$last];
|
||||
unset($assoc[$last]);
|
||||
$last--;
|
||||
|
||||
if ($last < 0) {
|
||||
$assoc[] = '#';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
$x = & $data;
|
||||
|
||||
foreach ($assoc as $i => $as) {
|
||||
if ($as === '#') { // indexed-array node
|
||||
$x = & $x[];
|
||||
|
||||
} elseif ($as === '=') { // "record" node
|
||||
if ($x === NULL) {
|
||||
$x = $row->toArray();
|
||||
$x = & $x[ $assoc[$i + 1] ];
|
||||
$x = NULL; // prepare child node
|
||||
} else {
|
||||
$x = & $x[ $assoc[$i + 1] ];
|
||||
}
|
||||
|
||||
} elseif ($as === '@') { // "object" node
|
||||
if ($x === NULL) {
|
||||
$x = clone $row;
|
||||
$x = & $x->{$assoc[$i + 1]};
|
||||
$x = NULL; // prepare child node
|
||||
} else {
|
||||
$x = & $x->{$assoc[$i + 1]};
|
||||
}
|
||||
|
||||
} else { // associative-array node
|
||||
$x = & $x[$row->$as];
|
||||
}
|
||||
}
|
||||
|
||||
if ($x === NULL) { // build leaf
|
||||
if ($leaf === '=') {
|
||||
$x = $row->toArray();
|
||||
} else {
|
||||
$x = $row;
|
||||
}
|
||||
}
|
||||
|
||||
} while ($row = $this->fetch());
|
||||
|
||||
unset($x);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches all records from table like $key => $value pairs.
|
||||
* @param string associative key
|
||||
* @param string value
|
||||
* @return array
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
final public function fetchPairs($key = NULL, $value = NULL)
|
||||
{
|
||||
$this->seek(0);
|
||||
$row = $this->fetch();
|
||||
if (!$row) {
|
||||
return array(); // empty result set
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
if ($value === NULL) {
|
||||
if ($key !== NULL) {
|
||||
throw new InvalidArgumentException('Either none or both columns must be specified.');
|
||||
}
|
||||
|
||||
// autodetect
|
||||
$tmp = array_keys($row->toArray());
|
||||
$key = $tmp[0];
|
||||
if (count($row) < 2) { // indexed-array
|
||||
do {
|
||||
$data[] = $row[$key];
|
||||
} while ($row = $this->fetch());
|
||||
return $data;
|
||||
}
|
||||
|
||||
$value = $tmp[1];
|
||||
|
||||
} else {
|
||||
if (!property_exists($row, $value)) {
|
||||
throw new InvalidArgumentException("Unknown value column '$value'.");
|
||||
}
|
||||
|
||||
if ($key === NULL) { // indexed-array
|
||||
do {
|
||||
$data[] = $row[$value];
|
||||
} while ($row = $this->fetch());
|
||||
return $data;
|
||||
}
|
||||
|
||||
if (!property_exists($row, $key)) {
|
||||
throw new InvalidArgumentException("Unknown key column '$key'.");
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
$data[ (string) $row[$key] ] = $row[$value];
|
||||
} while ($row = $this->fetch());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/********************* column types ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Autodetect column types.
|
||||
* @return void
|
||||
*/
|
||||
private function detectTypes()
|
||||
{
|
||||
$cache = DibiColumnInfo::getTypeCache();
|
||||
try {
|
||||
foreach ($this->getResultDriver()->getResultColumns() as $col) {
|
||||
$this->types[$col['name']] = $cache->{$col['nativetype']};
|
||||
}
|
||||
} catch (DibiNotSupportedException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts values to specified type and format.
|
||||
* @param array
|
||||
* @return void
|
||||
*/
|
||||
private function normalize(array & $row)
|
||||
{
|
||||
foreach ($this->types as $key => $type) {
|
||||
if (!isset($row[$key])) { // NULL
|
||||
continue;
|
||||
}
|
||||
$value = $row[$key];
|
||||
if ($value === FALSE || $type === dibi::TEXT) {
|
||||
|
||||
} elseif ($type === dibi::INTEGER) {
|
||||
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
|
||||
|
||||
} elseif ($type === dibi::FLOAT) {
|
||||
$row[$key] = str_replace(',', '.', ltrim((string) ($tmp = (float) $value), '0')) === ltrim(rtrim(rtrim($value, '0'), '.'), '0') ? $tmp : $value;
|
||||
|
||||
} elseif ($type === dibi::BOOL) {
|
||||
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
|
||||
|
||||
} elseif ($type === dibi::DATE || $type === dibi::DATETIME) {
|
||||
if ((int) $value !== 0 || substr((string) $value, 0, 3) === '00:') { // '', NULL, FALSE, '0000-00-00', ...
|
||||
$value = new DibiDateTime($value);
|
||||
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
|
||||
}
|
||||
|
||||
} elseif ($type === dibi::BINARY) {
|
||||
$row[$key] = $this->getResultDriver()->unescape($value, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Define column type.
|
||||
* @param string column
|
||||
* @param string type (use constant Dibi::*)
|
||||
* @return self
|
||||
*/
|
||||
final public function setType($col, $type)
|
||||
{
|
||||
$this->types[$col] = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns column type.
|
||||
* @return string
|
||||
*/
|
||||
final public function getType($col)
|
||||
{
|
||||
return isset($this->types[$col]) ? $this->types[$col] : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets data format.
|
||||
* @param string type (use constant Dibi::*)
|
||||
* @param string format
|
||||
* @return self
|
||||
*/
|
||||
final public function setFormat($type, $format)
|
||||
{
|
||||
$this->formats[$type] = $format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns data format.
|
||||
* @return string
|
||||
*/
|
||||
final public function getFormat($type)
|
||||
{
|
||||
return isset($this->formats[$type]) ? $this->formats[$type] : NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************* meta info ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns a meta information about the current result set.
|
||||
* @return DibiResultInfo
|
||||
*/
|
||||
public function getInfo()
|
||||
{
|
||||
if ($this->meta === NULL) {
|
||||
$this->meta = new DibiResultInfo($this->getResultDriver());
|
||||
}
|
||||
return $this->meta;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
final public function getColumns()
|
||||
{
|
||||
return $this->getInfo()->getColumns();
|
||||
}
|
||||
|
||||
|
||||
/********************* misc tools ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Displays complete result set as HTML or text table for debug purposes.
|
||||
* @return void
|
||||
*/
|
||||
final public function dump()
|
||||
{
|
||||
$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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* External result set iterator.
|
||||
*
|
||||
* This can be returned by DibiResult::getIterator() method or using foreach
|
||||
* <code>
|
||||
* $result = dibi::query('SELECT * FROM table');
|
||||
* foreach ($result as $row) {
|
||||
* print_r($row);
|
||||
* }
|
||||
* unset($result);
|
||||
* </code>
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiResultIterator implements Iterator, Countable
|
||||
{
|
||||
/** @var DibiResult */
|
||||
private $result;
|
||||
|
||||
/** @var int */
|
||||
private $row;
|
||||
|
||||
/** @var int */
|
||||
private $pointer;
|
||||
|
||||
|
||||
/**
|
||||
* @param DibiResult
|
||||
*/
|
||||
public function __construct(DibiResult $result)
|
||||
{
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rewinds the iterator to the first element.
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->pointer = 0;
|
||||
$this->result->seek(0);
|
||||
$this->row = $this->result->fetch();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the key of the current element.
|
||||
* @return mixed
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->pointer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current element.
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->row;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Moves forward to next element.
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->row = $this->result->fetch();
|
||||
$this->pointer++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if there is a current element after calls to rewind() or next().
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return !empty($this->row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Required by the Countable interface.
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->result->getRowCount();
|
||||
}
|
||||
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Result set single row.
|
||||
*
|
||||
* @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($time);
|
||||
}
|
||||
return $format === NULL ? $time : $time->format($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);
|
||||
}
|
||||
|
||||
}
|
@@ -1,623 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* dibi SQL translator.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
final class DibiTranslator extends DibiObject
|
||||
{
|
||||
/** @var DibiConnection */
|
||||
private $connection;
|
||||
|
||||
/** @var IDibiDriver */
|
||||
private $driver;
|
||||
|
||||
/** @var int */
|
||||
private $cursor;
|
||||
|
||||
/** @var array */
|
||||
private $args;
|
||||
|
||||
/** @var bool */
|
||||
private $hasError;
|
||||
|
||||
/** @var bool */
|
||||
private $comment;
|
||||
|
||||
/** @var int */
|
||||
private $ifLevel;
|
||||
|
||||
/** @var int */
|
||||
private $ifLevelStart;
|
||||
|
||||
/** @var int */
|
||||
private $limit;
|
||||
|
||||
/** @var int */
|
||||
private $offset;
|
||||
|
||||
/** @var DibiHashMap */
|
||||
private $identifiers;
|
||||
|
||||
|
||||
public function __construct(DibiConnection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->identifiers = new DibiHashMap(array($this, 'delimite'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates SQL.
|
||||
* @param array
|
||||
* @return string
|
||||
* @throws DibiException
|
||||
*/
|
||||
public function translate(array $args)
|
||||
{
|
||||
if (!$this->driver) {
|
||||
$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;
|
||||
// shortcuts
|
||||
$cursor = & $this->cursor;
|
||||
$cursor = 0;
|
||||
|
||||
// conditional sql
|
||||
$this->ifLevel = $this->ifLevelStart = 0;
|
||||
$comment = & $this->comment;
|
||||
$comment = FALSE;
|
||||
|
||||
// iterate
|
||||
$sql = array();
|
||||
while ($cursor < count($this->args)) {
|
||||
$arg = $this->args[$cursor];
|
||||
$cursor++;
|
||||
|
||||
// simple string means SQL
|
||||
if (is_string($arg)) {
|
||||
// speed-up - is regexp required?
|
||||
$toSkip = strcspn($arg, '`[\'":%?');
|
||||
|
||||
if (strlen($arg) === $toSkip) { // needn't be translated
|
||||
$sql[] = $arg;
|
||||
} else {
|
||||
$sql[] = substr($arg, 0, $toSkip)
|
||||
/*
|
||||
. preg_replace_callback('/
|
||||
(?=[`[\'":%?]) ## speed-up
|
||||
(?:
|
||||
`(.+?)`| ## 1) `identifier`
|
||||
\[(.+?)\]| ## 2) [identifier]
|
||||
(\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string'
|
||||
(")((?:""|[^"])*)"| ## 5,6) "string"
|
||||
(\'|")| ## 7) lone quote
|
||||
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution:
|
||||
%([a-zA-Z~][a-zA-Z0-9~]{0,5})|## 10) modifier
|
||||
(\?) ## 11) placeholder
|
||||
)/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',
|
||||
array($this, 'cb'),
|
||||
substr($arg, $toSkip)
|
||||
);
|
||||
if (preg_last_error()) {
|
||||
throw new DibiPcreException;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($comment) {
|
||||
$sql[] = '...';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($arg instanceof Traversable) {
|
||||
$arg = iterator_to_array($arg);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
$lastArr = $cursor;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default processing
|
||||
$sql[] = $this->formatValue($arg, FALSE);
|
||||
} // while
|
||||
|
||||
|
||||
if ($comment) {
|
||||
$sql[] = '*/';
|
||||
}
|
||||
|
||||
$sql = implode(' ', $sql);
|
||||
|
||||
if ($this->hasError) {
|
||||
throw new DibiException('SQL translate error', 0, $sql);
|
||||
}
|
||||
|
||||
// apply limit
|
||||
if ($this->limit > -1 || $this->offset > 0) {
|
||||
$this->driver->applyLimit($sql, $this->limit, $this->offset);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply modifier to single value.
|
||||
* @param mixed
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public function formatValue($value, $modifier)
|
||||
{
|
||||
if ($this->comment) {
|
||||
return '...';
|
||||
}
|
||||
|
||||
if (!$this->driver) {
|
||||
$this->driver = $this->connection->getDriver();
|
||||
}
|
||||
|
||||
// array processing (with or without modifier)
|
||||
if ($value instanceof Traversable) {
|
||||
$value = iterator_to_array($value);
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$vx = $kx = array();
|
||||
switch ($modifier) {
|
||||
case 'and':
|
||||
case 'or': // key=val AND key IS NULL AND ...
|
||||
if (empty($value)) {
|
||||
return '1=1';
|
||||
}
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_string($k)) {
|
||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
||||
$k = $this->identifiers->{$pair[0]} . ' ';
|
||||
if (!isset($pair[1])) {
|
||||
$v = $this->formatValue($v, FALSE);
|
||||
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
|
||||
|
||||
} elseif ($pair[1] === 'ex') { // TODO: this will be removed
|
||||
$vx[] = $k . $this->formatValue($v, 'ex');
|
||||
|
||||
} else {
|
||||
$v = $this->formatValue($v, $pair[1]);
|
||||
if ($pair[1] === 'l' || $pair[1] === 'in') {
|
||||
$op = 'IN ';
|
||||
} elseif (strpos($pair[1], 'like') !== FALSE) {
|
||||
$op = 'LIKE ';
|
||||
} elseif ($v === 'NULL') {
|
||||
$op = 'IS ';
|
||||
} else {
|
||||
$op = '= ';
|
||||
}
|
||||
$vx[] = $k . $op . $v;
|
||||
}
|
||||
|
||||
} else {
|
||||
$vx[] = $this->formatValue($v, 'ex');
|
||||
}
|
||||
}
|
||||
return '(' . implode(') ' . strtoupper($modifier) . ' (', $vx) . ')';
|
||||
|
||||
case 'n': // key, key, ... identifier names
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_string($k)) {
|
||||
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->identifiers->$v);
|
||||
} else {
|
||||
$pair = explode('%', $v, 2); // split into identifier & modifier
|
||||
$vx[] = $this->identifiers->{$pair[0]};
|
||||
}
|
||||
}
|
||||
return implode(', ', $vx);
|
||||
|
||||
|
||||
case 'a': // key=val, key=val, ...
|
||||
foreach ($value as $k => $v) {
|
||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
||||
$vx[] = $this->identifiers->{$pair[0]} . '='
|
||||
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
|
||||
}
|
||||
return implode(', ', $vx);
|
||||
|
||||
|
||||
case 'in':// replaces scalar %in modifier!
|
||||
case 'l': // (val, val, ...)
|
||||
foreach ($value as $k => $v) {
|
||||
$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') . ')';
|
||||
|
||||
|
||||
case 'v': // (key, key, ...) VALUES (val, val, ...)
|
||||
foreach ($value as $k => $v) {
|
||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
||||
$kx[] = $this->identifiers->{$pair[0]};
|
||||
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
|
||||
}
|
||||
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
|
||||
|
||||
case 'm': // (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
if (isset($proto)) {
|
||||
if ($proto !== array_keys($v)) {
|
||||
$this->hasError = TRUE;
|
||||
return '**Multi-insert array "' . $k . '" is different.**';
|
||||
}
|
||||
} else {
|
||||
$proto = array_keys($v);
|
||||
}
|
||||
} else {
|
||||
$this->hasError = TRUE;
|
||||
return '**Unexpected type ' . gettype($v) . '**';
|
||||
}
|
||||
|
||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
||||
$kx[] = $this->identifiers->{$pair[0]};
|
||||
foreach ($v as $k2 => $v2) {
|
||||
$vx[$k2][] = $this->formatValue($v2, isset($pair[1]) ? $pair[1] : (is_array($v2) ? 'ex' : FALSE));
|
||||
}
|
||||
}
|
||||
foreach ($vx as $k => $v) {
|
||||
$vx[$k] = '(' . implode(', ', $v) . ')';
|
||||
}
|
||||
return '(' . implode(', ', $kx) . ') VALUES ' . implode(', ', $vx);
|
||||
|
||||
case 'by': // key ASC, key DESC
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
$vx[] = $this->formatValue($v, 'ex');
|
||||
} elseif (is_string($k)) {
|
||||
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
|
||||
$vx[] = $this->identifiers->$k . ' ' . $v;
|
||||
} else {
|
||||
$vx[] = $this->identifiers->$v;
|
||||
}
|
||||
}
|
||||
return implode(', ', $vx);
|
||||
|
||||
case 'ex':
|
||||
case 'sql':
|
||||
$translator = new self($this->connection);
|
||||
return $translator->translate($value);
|
||||
|
||||
default: // value, value, value - all with the same modifier
|
||||
foreach ($value as $v) {
|
||||
$vx[] = $this->formatValue($v, $modifier);
|
||||
}
|
||||
return implode(', ', $vx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// with modifier procession
|
||||
if ($modifier) {
|
||||
if ($value !== NULL && !is_scalar($value) && !$value instanceof DateTime && !$value instanceof DateTimeInterface) { // array is already processed
|
||||
$this->hasError = TRUE;
|
||||
return '**Unexpected type ' . gettype($value) . '**';
|
||||
}
|
||||
|
||||
switch ($modifier) {
|
||||
case 's': // string
|
||||
case 'bin':// binary
|
||||
case 'b': // boolean
|
||||
return $value === NULL ? 'NULL' : $this->driver->escape($value, $modifier);
|
||||
|
||||
case 'sN': // string or NULL
|
||||
case 'sn':
|
||||
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;
|
||||
}
|
||||
// intentionally break omitted
|
||||
|
||||
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);
|
||||
} else {
|
||||
return (string) (int) $value;
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
return rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
|
||||
}
|
||||
|
||||
case 'd': // date
|
||||
case 't': // datetime
|
||||
if ($value === NULL) {
|
||||
return 'NULL';
|
||||
} else {
|
||||
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': // identifier name
|
||||
return $this->identifiers->$value;
|
||||
|
||||
case 'ex':
|
||||
case 'sql': // preserve as dibi-SQL (TODO: leave only %ex)
|
||||
$value = (string) $value;
|
||||
// speed-up - is regexp required?
|
||||
$toSkip = strcspn($value, '`[\'":');
|
||||
if (strlen($value) !== $toSkip) {
|
||||
$value = substr($value, 0, $toSkip)
|
||||
. preg_replace_callback(
|
||||
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
|
||||
array($this, 'cb'),
|
||||
substr($value, $toSkip)
|
||||
);
|
||||
if (preg_last_error()) {
|
||||
throw new DibiPcreException;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
|
||||
case 'SQL': // preserve as real SQL (TODO: rename to %sql)
|
||||
return (string) $value;
|
||||
|
||||
case 'like~': // LIKE string%
|
||||
return $this->driver->escapeLike($value, 1);
|
||||
|
||||
case '~like': // LIKE %string
|
||||
return $this->driver->escapeLike($value, -1);
|
||||
|
||||
case '~like~': // LIKE %string%
|
||||
return $this->driver->escapeLike($value, 0);
|
||||
|
||||
case 'and':
|
||||
case 'or':
|
||||
case 'a':
|
||||
case 'l':
|
||||
case 'v':
|
||||
$this->hasError = TRUE;
|
||||
return '**Unexpected type ' . gettype($value) . '**';
|
||||
|
||||
default:
|
||||
$this->hasError = TRUE;
|
||||
return "**Unknown or invalid modifier %$modifier**";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// without modifier procession
|
||||
if (is_string($value)) {
|
||||
return $this->driver->escape($value, dibi::TEXT);
|
||||
|
||||
} elseif (is_int($value)) {
|
||||
return (string) $value;
|
||||
|
||||
} elseif (is_float($value)) {
|
||||
return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
|
||||
|
||||
} elseif (is_bool($value)) {
|
||||
return $this->driver->escape($value, dibi::BOOL);
|
||||
|
||||
} elseif ($value === NULL) {
|
||||
return 'NULL';
|
||||
|
||||
} elseif ($value instanceof DateTime || $value instanceof DateTimeInterface) {
|
||||
return $this->driver->escape($value, dibi::DATETIME);
|
||||
|
||||
} elseif ($value instanceof DibiLiteral) {
|
||||
return (string) $value;
|
||||
|
||||
} else {
|
||||
$this->hasError = TRUE;
|
||||
return '**Unexpected ' . gettype($value) . '**';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PREG callback from translate() or formatValue().
|
||||
* @param array
|
||||
* @return string
|
||||
*/
|
||||
private function cb($matches)
|
||||
{
|
||||
// [1] => `ident`
|
||||
// [2] => [ident]
|
||||
// [3] => '
|
||||
// [4] => string
|
||||
// [5] => "
|
||||
// [6] => string
|
||||
// [7] => lone-quote
|
||||
// [8] => substitution
|
||||
// [9] => substitution flag
|
||||
// [10] => modifier (when called from self::translate())
|
||||
// [11] => placeholder (when called from self::translate())
|
||||
|
||||
|
||||
if (!empty($matches[11])) { // placeholder
|
||||
$cursor = & $this->cursor;
|
||||
|
||||
if ($cursor >= count($this->args)) {
|
||||
$this->hasError = TRUE;
|
||||
return '**Extra placeholder**';
|
||||
}
|
||||
|
||||
$cursor++;
|
||||
return $this->formatValue($this->args[$cursor - 1], FALSE);
|
||||
}
|
||||
|
||||
if (!empty($matches[10])) { // modifier
|
||||
$mod = $matches[10];
|
||||
$cursor = & $this->cursor;
|
||||
|
||||
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
|
||||
$this->hasError = TRUE;
|
||||
return "**Extra modifier %$mod**";
|
||||
}
|
||||
|
||||
if ($mod === 'if') {
|
||||
$this->ifLevel++;
|
||||
$cursor++;
|
||||
if (!$this->comment && !$this->args[$cursor - 1]) {
|
||||
// open comment
|
||||
$this->ifLevelStart = $this->ifLevel;
|
||||
$this->comment = TRUE;
|
||||
return '/*';
|
||||
}
|
||||
return '';
|
||||
|
||||
} elseif ($mod === 'else') {
|
||||
if ($this->ifLevelStart === $this->ifLevel) {
|
||||
$this->ifLevelStart = 0;
|
||||
$this->comment = FALSE;
|
||||
return '*/';
|
||||
} elseif (!$this->comment) {
|
||||
$this->ifLevelStart = $this->ifLevel;
|
||||
$this->comment = TRUE;
|
||||
return '/*';
|
||||
}
|
||||
|
||||
} elseif ($mod === 'end') {
|
||||
$this->ifLevel--;
|
||||
if ($this->ifLevelStart === $this->ifLevel + 1) {
|
||||
// close comment
|
||||
$this->ifLevelStart = 0;
|
||||
$this->comment = FALSE;
|
||||
return '*/';
|
||||
}
|
||||
return '';
|
||||
|
||||
} elseif ($mod === 'ex') { // array expansion
|
||||
array_splice($this->args, $cursor, 1, $this->args[$cursor]);
|
||||
return '';
|
||||
|
||||
} elseif ($mod === 'lmt') { // apply limit
|
||||
$arg = $this->args[$cursor++];
|
||||
if ($arg === NULL) {
|
||||
} elseif ($this->comment) {
|
||||
return "(limit $arg)";
|
||||
} else {
|
||||
$this->limit = (int) $arg;
|
||||
}
|
||||
return '';
|
||||
|
||||
} elseif ($mod === 'ofs') { // apply offset
|
||||
$arg = $this->args[$cursor++];
|
||||
if ($arg === NULL) {
|
||||
} elseif ($this->comment) {
|
||||
return "(offset $arg)";
|
||||
} else {
|
||||
$this->offset = (int) $arg;
|
||||
}
|
||||
return '';
|
||||
|
||||
} else { // default processing
|
||||
$cursor++;
|
||||
return $this->formatValue($this->args[$cursor - 1], $mod);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->comment) {
|
||||
return '...';
|
||||
}
|
||||
|
||||
if ($matches[1]) { // SQL identifiers: `ident`
|
||||
return $this->identifiers->{$matches[1]};
|
||||
|
||||
} elseif ($matches[2]) { // SQL identifiers: [ident]
|
||||
return $this->identifiers->{$matches[2]};
|
||||
|
||||
} elseif ($matches[3]) { // SQL strings: '...'
|
||||
return $this->driver->escape(str_replace("''", "'", $matches[4]), dibi::TEXT);
|
||||
|
||||
} elseif ($matches[5]) { // SQL strings: "..."
|
||||
return $this->driver->escape(str_replace('""', '"', $matches[6]), dibi::TEXT);
|
||||
|
||||
} elseif ($matches[7]) { // string quote
|
||||
$this->hasError = TRUE;
|
||||
return '**Alone quote**';
|
||||
}
|
||||
|
||||
if ($matches[8]) { // SQL identifier substitution
|
||||
$m = substr($matches[8], 0, -1);
|
||||
$m = $this->connection->getSubstitutes()->$m;
|
||||
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
|
||||
}
|
||||
|
||||
die('this should be never executed');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply substitutions to indentifier and delimites it.
|
||||
* @param string indentifier
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function delimite($value)
|
||||
{
|
||||
$value = $this->connection->substitute($value);
|
||||
$parts = explode('.', $value);
|
||||
foreach ($parts as & $v) {
|
||||
if ($v !== '*') {
|
||||
$v = $this->driver->escape($v, dibi::IDENTIFIER);
|
||||
}
|
||||
}
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
}
|
@@ -1,219 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Provides an interface between a dataset and data-aware components.
|
||||
* @package dibi
|
||||
*/
|
||||
interface IDataSource extends Countable, IteratorAggregate
|
||||
{
|
||||
//function IteratorAggregate::getIterator();
|
||||
//function Countable::count();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dibi driver interface.
|
||||
* @package dibi
|
||||
*/
|
||||
interface IDibiDriver
|
||||
{
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @param array
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
function connect(array & $config);
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
* @throws DibiException
|
||||
*/
|
||||
function disconnect();
|
||||
|
||||
/**
|
||||
* Internal: Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return IDibiResultDriver|NULL
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
function query($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
|
||||
*/
|
||||
function getAffectedRows();
|
||||
|
||||
/**
|
||||
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
||||
* @return int|FALSE int on success or FALSE on failure
|
||||
*/
|
||||
function getInsertId($sequence);
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
function begin($savepoint = NULL);
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
function commit($savepoint = NULL);
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws DibiDriverException
|
||||
*/
|
||||
function rollback($savepoint = NULL);
|
||||
|
||||
/**
|
||||
* Returns the connection resource.
|
||||
* @return mixed
|
||||
*/
|
||||
function getResource();
|
||||
|
||||
/**
|
||||
* Returns the connection 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 escape($value, $type);
|
||||
|
||||
/**
|
||||
* Encodes string for use in a LIKE statement.
|
||||
* @param string
|
||||
* @param int
|
||||
* @return string
|
||||
*/
|
||||
function escapeLike($value, $pos);
|
||||
|
||||
/**
|
||||
* Injects LIMIT/OFFSET to the SQL query.
|
||||
* @return void
|
||||
*/
|
||||
function applyLimit(& $sql, $limit, $offset);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dibi result set driver interface.
|
||||
* @package dibi
|
||||
*/
|
||||
interface IDibiResultDriver
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a result set.
|
||||
* @return int
|
||||
*/
|
||||
function getRowCount();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
function seek($row);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @internal
|
||||
*/
|
||||
function fetch($type);
|
||||
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @param resource result set resource
|
||||
* @return void
|
||||
*/
|
||||
function free();
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a result set.
|
||||
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]}
|
||||
*/
|
||||
function getResultColumns();
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return mixed
|
||||
*/
|
||||
function getResultResource();
|
||||
|
||||
/**
|
||||
* Decodes data from result set.
|
||||
* @param string value
|
||||
* @param string type (dibi::BINARY)
|
||||
* @return string decoded value
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
function unescape($value, $type);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dibi driver reflection.
|
||||
*
|
||||
* @package dibi
|
||||
*/
|
||||
interface IDibiReflector
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns list of tables.
|
||||
* @return array of {name [, (bool) view ]}
|
||||
*/
|
||||
function getTables();
|
||||
|
||||
/**
|
||||
* Returns metadata for all columns in a table.
|
||||
* @param string
|
||||
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]}
|
||||
*/
|
||||
function getColumns($table);
|
||||
|
||||
/**
|
||||
* Returns metadata for all indexes in a table.
|
||||
* @param string
|
||||
* @return array of {name, (array of names) columns [, (bool) unique, (bool) primary ]}
|
||||
*/
|
||||
function getIndexes($table);
|
||||
|
||||
/**
|
||||
* Returns metadata for all foreign keys in a table.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
function getForeignKeys($table);
|
||||
|
||||
}
|
Reference in New Issue
Block a user