mirror of
https://github.com/dg/dibi.git
synced 2025-08-30 01:09:50 +02:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9b070bb737 | ||
|
917971992f | ||
|
bc564555f8 | ||
|
3e20a6b8fc | ||
|
c019e7cac2 | ||
|
2294c195f4 | ||
|
25246529f7 | ||
|
d405ec369b | ||
|
b7974fe192 | ||
|
80f1898e1b | ||
|
8e8e6dfdca | ||
|
6510fcce25 | ||
|
b7d84b90ef | ||
|
2571e54f3c | ||
|
15e6d9f738 | ||
|
ddfd4a0f1a | ||
|
4e838fc2b5 | ||
|
37487816db | ||
|
0c099bb2bc | ||
|
1786c861b9 | ||
|
bc0578928f | ||
|
12a07ff6ad | ||
|
b50f59c64c | ||
|
fdebf349f5 | ||
|
954c2f25d0 | ||
|
43dccb1ba2 | ||
|
352a683ec1 | ||
|
f4638796fb | ||
|
4659f4550e | ||
|
c3548465fb | ||
|
dda0bdbd67 | ||
|
7142be254b | ||
|
8948cc293c | ||
|
193252dc5f | ||
|
6b2a46bcb8 | ||
|
2c01d993d0 | ||
|
e415157206 | ||
|
8a7dbcba86 | ||
|
3b00115ee5 | ||
|
4f2a0dac97 | ||
|
a13eb6f3d1 | ||
|
e3f01c879f | ||
|
efae5808f0 | ||
|
6a8a136c0a | ||
|
bbb654fc27 | ||
|
b6933815c7 | ||
|
a8691eb8f5 | ||
|
0cce3b9916 | ||
|
98d1b2a519 |
13
.travis.yml
13
.travis.yml
@@ -4,14 +4,16 @@ php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- hhvm
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 7.1
|
||||
- php: hhvm
|
||||
|
||||
script:
|
||||
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini
|
||||
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini $COVERAGE
|
||||
- php temp/code-checker/src/code-checker.php --short-arrays
|
||||
|
||||
after_failure:
|
||||
@@ -22,6 +24,7 @@ before_script:
|
||||
# Install Nette Tester & Code Checker
|
||||
- travis_retry composer install --no-interaction
|
||||
- travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction
|
||||
- if [ $TRAVIS_PHP_VERSION == "7.0" ]; then COVERAGE="-p phpdbg --coverage ./coverage.xml --coverage-src ./src"; fi
|
||||
|
||||
# Create databases.ini
|
||||
- cp ./tests/databases.travis.ini ./tests/databases.ini
|
||||
@@ -29,6 +32,14 @@ before_script:
|
||||
# Create Postgre database
|
||||
- psql -c 'CREATE DATABASE dibi_test' -U postgres
|
||||
|
||||
after_script:
|
||||
# Report Code Coverage
|
||||
- >
|
||||
if [ "$COVERAGE" != "" ]; then
|
||||
wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar
|
||||
&& php coveralls.phar --verbose --config tests/.coveralls.yml
|
||||
|| true; fi
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
|
36
appveyor.yml
36
appveyor.yml
@@ -1,30 +1,49 @@
|
||||
build: off
|
||||
cache:
|
||||
- c:\php -> appveyor.yml
|
||||
- c:\php5 -> appveyor.yml
|
||||
- c:\php7 -> appveyor.yml
|
||||
- '%LOCALAPPDATA%\Composer\files -> appveyor.yml'
|
||||
|
||||
clone_folder: c:\projects\dibi
|
||||
|
||||
services:
|
||||
- mssql2012sp1
|
||||
- mssql2014
|
||||
- mysql
|
||||
|
||||
init:
|
||||
- SET PATH=c:\php;%PATH%
|
||||
- SET PHP=1
|
||||
- SET PATH=c:\php5;%PATH%
|
||||
- SET ANSICON=121x90 (121x90)
|
||||
|
||||
install:
|
||||
# Install PHP
|
||||
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
|
||||
- IF %PHP%==1 cd c:\php
|
||||
# Install PHP 5
|
||||
- IF EXIST c:\php5 (SET PHP=0) ELSE (SET PHP=1)
|
||||
- IF %PHP%==1 mkdir c:\php5
|
||||
- IF %PHP%==1 cd c:\php5
|
||||
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip
|
||||
- IF %PHP%==1 7z x php-5.6.14-Win32-VC11-x86.zip >nul
|
||||
- IF %PHP%==1 echo extension_dir=ext >> php.ini
|
||||
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
|
||||
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
|
||||
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
|
||||
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_56_ts.dll ext\php_sqlsrv_ts.dll
|
||||
- IF %PHP%==1 copy SQLSRV\php_pdo_sqlsrv_56_ts.dll ext\php_pdo_sqlsrv_ts.dll
|
||||
- IF %PHP%==1 del /Q *.zip
|
||||
|
||||
# Install PHP 7
|
||||
- IF EXIST c:\php7 (SET PHP=0) ELSE (SET PHP=1)
|
||||
- IF %PHP%==1 mkdir c:\php7
|
||||
- IF %PHP%==1 cd c:\php7
|
||||
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-7.0.3-Win32-VC14-x86.zip
|
||||
- IF %PHP%==1 7z x php-7.0.3-Win32-VC14-x86.zip >nul
|
||||
- IF %PHP%==1 echo extension_dir=ext >> php.ini
|
||||
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
|
||||
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
|
||||
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_7_ts.dll ext\php_sqlsrv_ts.dll
|
||||
- IF %PHP%==1 del /Q *.zip
|
||||
- cd c:\projects\dibi
|
||||
|
||||
# Install Nette Tester
|
||||
- cd c:\projects\dibi
|
||||
- appveyor DownloadFile https://getcomposer.org/composer.phar
|
||||
- php composer.phar install --prefer-dist --no-interaction --no-progress
|
||||
|
||||
@@ -32,7 +51,8 @@ install:
|
||||
- copy tests\databases.appveyor.ini tests\databases.ini
|
||||
|
||||
test_script:
|
||||
- vendor\bin\tester tests -s -p php -c tests\php-win.ini
|
||||
- vendor\bin\tester tests -s -p c:\php5\php -c tests\php5-win.ini
|
||||
- vendor\bin\tester tests -s -p c:\php7\php -c tests\php7-win.ini
|
||||
|
||||
on_failure:
|
||||
# Print *.actual content
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "dibi/dibi",
|
||||
"description": "Dibi is Database Abstraction Library for PHP",
|
||||
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"],
|
||||
"homepage": "http://dibiphp.com",
|
||||
"homepage": "https://dibiphp.com",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
@@ -15,10 +15,10 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"tracy/tracy": "~2.2",
|
||||
"nette/tester": "~1.3"
|
||||
"nette/tester": "~1.7"
|
||||
},
|
||||
"replace": {
|
||||
"dg/dibi": "self.version"
|
||||
"dg/dibi": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"],
|
||||
|
@@ -5,7 +5,7 @@ The issue tracker is the preferred channel for bug reports, features requests
|
||||
and submitting pull requests, but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests (use
|
||||
[dibi forum](http://forum.dibiphp.com) or [Stack Overflow](http://stackoverflow.com)).
|
||||
[dibi forum](https://forum.dibiphp.com) or [Stack Overflow](http://stackoverflow.com)).
|
||||
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
@@ -112,6 +112,22 @@ try {
|
||||
echo "</p>\n";
|
||||
|
||||
|
||||
// connects to MS SQL
|
||||
echo '<p>Connecting to MS SQL: ';
|
||||
try {
|
||||
dibi::connect([
|
||||
'driver' => 'mssql',
|
||||
'host' => 'localhost',
|
||||
'username' => 'root',
|
||||
'password' => 'xxx',
|
||||
]);
|
||||
echo 'OK';
|
||||
} catch (Dibi\Exception $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
echo "</p>\n";
|
||||
|
||||
|
||||
// connects to SQLSRV
|
||||
echo '<p>Connecting to Microsoft SQL Server: ';
|
||||
try {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
[Dibi](http://dibiphp.com) - smart database layer for PHP [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN)
|
||||
[Dibi](https://dibiphp.com) - smart database layer for PHP [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN)
|
||||
=========================================================
|
||||
|
||||
[](https://packagist.org/packages/dibi/dibi)
|
||||
@@ -14,7 +14,7 @@ The best way to install Dibi is to use a [Composer](https://getcomposer.org/down
|
||||
|
||||
php composer.phar require dibi/dibi
|
||||
|
||||
Or you can download the latest package from http://dibiphp.com. In this
|
||||
Or you can download the latest package from https://dibiphp.com. In this
|
||||
package is also `Dibi.minified`, shrinked single-file version of whole Dibi,
|
||||
useful when you don't want to modify the library, but just use it.
|
||||
|
||||
@@ -25,7 +25,7 @@ Examples
|
||||
--------
|
||||
|
||||
Refer to the `examples` directory for examples. Dibi documentation is
|
||||
available on the [homepage](http://dibiphp.com).
|
||||
available on the [homepage](https://dibiphp.com).
|
||||
|
||||
Connect to database:
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
|
||||
namespace Dibi\Bridges\Nette;
|
||||
|
||||
use Dibi;
|
||||
use Nette;
|
||||
|
||||
|
||||
@@ -47,7 +48,10 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||
}
|
||||
if ($useProfiler) {
|
||||
$panel = $container->addDefinition($this->prefix('panel'))
|
||||
->setClass('Dibi\Bridges\Tracy\Panel');
|
||||
->setClass('Dibi\Bridges\Tracy\Panel', [
|
||||
isset($config['explain']) ? $config['explain'] : TRUE,
|
||||
isset($config['filter']) && $config['filter'] === FALSE ? Dibi\Event::ALL : Dibi\Event::QUERY,
|
||||
]);
|
||||
$connection->addSetup([$panel, 'register'], [$connection]);
|
||||
}
|
||||
}
|
||||
|
@@ -110,7 +110,7 @@ class Panel implements Tracy\IBarPanel
|
||||
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
|
||||
$event->connection->onEvent = NULL;
|
||||
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
||||
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
|
||||
$explain = Helpers::dump(@$event->connection->nativeQuery("$cmd $event->sql"), TRUE);
|
||||
} catch (Dibi\Exception $e) {
|
||||
}
|
||||
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
|
||||
|
@@ -545,7 +545,7 @@ class Connection
|
||||
{
|
||||
return strpos($value, ':') === FALSE
|
||||
? $value
|
||||
: preg_replace_callback('#:([^:\s]*):#', function ($m) { $this->substitutes->{$m[1]}; }, $value);
|
||||
: preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -55,4 +55,18 @@ class DateTime extends \DateTime
|
||||
return $this->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
if (isset($this->fix, $this->fix[1])) {
|
||||
$this->__construct($this->fix[0], new \DateTimeZone($this->fix[1]));
|
||||
unset($this->fix);
|
||||
} elseif (isset($this->fix)) {
|
||||
$this->__construct($this->fix[0]);
|
||||
unset($this->fix);
|
||||
} else {
|
||||
parent::__wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -96,7 +96,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
ibase_close($this->connection);
|
||||
@ibase_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
|
||||
public function escapeIdentifier($value)
|
||||
{
|
||||
return $value;
|
||||
return '"' . str_replace('"', '""', $value). '"';
|
||||
}
|
||||
|
||||
|
||||
@@ -333,9 +333,9 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
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 . ')';
|
||||
if ($limit > 0 || $offset > 0) {
|
||||
// http://www.firebirdsql.org/refdocs/langrefupd20-select.html
|
||||
$sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . (int) $limit : '') . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
public function fetch($assoc)
|
||||
{
|
||||
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
|
||||
$result = $assoc ? @ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
|
||||
|
||||
if (ibase_errcode()) {
|
||||
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
|
||||
|
386
src/Dibi/Drivers/MsSqlDriver.php
Normal file
386
src/Dibi/Drivers/MsSqlDriver.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Dibi\Drivers;
|
||||
|
||||
use Dibi;
|
||||
|
||||
|
||||
/**
|
||||
* The dibi 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 Dibi\Connection options
|
||||
*/
|
||||
class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource Connection resource */
|
||||
private $connection;
|
||||
|
||||
/** @var resource Resultset resource */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
private $autoFree = TRUE;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('mssql')) {
|
||||
throw new Dibi\NotSupportedException("PHP extension 'mssql' is not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
* @return void
|
||||
* @throws Dibi\Exception
|
||||
*/
|
||||
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 Dibi\DriverException("Can't connect to DB.");
|
||||
}
|
||||
|
||||
if (isset($config['database']) && !@mssql_select_db($this->escapeIdentifier($config['database']), $this->connection)) { // intentionally @
|
||||
throw new Dibi\DriverException("Can't select DB '$config[database]'.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
@mssql_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @param string SQL statement.
|
||||
* @return Dibi\ResultDriver|NULL
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$res = @mssql_query($sql, $this->connection); // intentionally @
|
||||
|
||||
if ($res === FALSE) {
|
||||
throw new Dibi\DriverException(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 Dibi\DriverException
|
||||
*/
|
||||
public function begin($savepoint = NULL)
|
||||
{
|
||||
$this->query('BEGIN TRANSACTION');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit($savepoint = NULL)
|
||||
{
|
||||
$this->query('COMMIT');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @param string optional savepoint name
|
||||
* @return void
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
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 Dibi\Reflector
|
||||
*/
|
||||
public function getReflector()
|
||||
{
|
||||
return new MsSqlReflector($this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result set driver factory.
|
||||
* @param resource
|
||||
* @return Dibi\ResultDriver
|
||||
*/
|
||||
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
|
||||
* @return string encoded value
|
||||
*/
|
||||
public function escapeText($value)
|
||||
{
|
||||
return "'" . str_replace("'", "''", $value) . "'";
|
||||
}
|
||||
|
||||
|
||||
public function escapeBinary($value)
|
||||
{
|
||||
return "'" . str_replace("'", "''", $value) . "'";
|
||||
}
|
||||
|
||||
|
||||
public function escapeIdentifier($value)
|
||||
{
|
||||
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
|
||||
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
|
||||
}
|
||||
|
||||
|
||||
public function escapeBool($value)
|
||||
{
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
public function escapeDate($value)
|
||||
{
|
||||
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
|
||||
$value = new Dibi\DateTime($value);
|
||||
}
|
||||
return $value->format("'Y-m-d'");
|
||||
}
|
||||
|
||||
|
||||
public function escapeDateTime($value)
|
||||
{
|
||||
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
|
||||
$value = new Dibi\DateTime($value);
|
||||
}
|
||||
return $value->format("'Y-m-d H:i:s'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes string for use in a LIKE statement.
|
||||
* @param string
|
||||
* @param int
|
||||
* @return string
|
||||
*/
|
||||
public function escapeLike($value, $pos)
|
||||
{
|
||||
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes data from result set.
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public function unescapeBinary($value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/** @deprecated */
|
||||
public function escape($value, $type)
|
||||
{
|
||||
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
|
||||
return Dibi\Helpers::escape($this, $value, $type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injects LIMIT/OFFSET to the SQL query.
|
||||
* @return void
|
||||
*/
|
||||
public function applyLimit(& $sql, $limit, $offset)
|
||||
{
|
||||
if ($offset) {
|
||||
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
|
||||
|
||||
} elseif ($limit < 0) {
|
||||
throw new Dibi\NotSupportedException('Negative offset or limit.');
|
||||
|
||||
} elseif ($limit !== NULL) {
|
||||
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* 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 = [];
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$row = (array) mssql_fetch_field($this->resultSet, $i);
|
||||
$columns[] = [
|
||||
'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;
|
||||
}
|
||||
|
||||
}
|
216
src/Dibi/Drivers/MsSqlReflector.php
Normal file
216
src/Dibi/Drivers/MsSqlReflector.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Dibi\Drivers;
|
||||
|
||||
use Dibi;
|
||||
|
||||
|
||||
/**
|
||||
* The dibi reflector for MS SQL databases.
|
||||
* @internal
|
||||
*/
|
||||
class MsSqlReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var Dibi\Driver */
|
||||
private $driver;
|
||||
|
||||
|
||||
public function __construct(Dibi\Driver $driver)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of tables.
|
||||
* @return array
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
$res = $this->driver->query('
|
||||
SELECT TABLE_NAME, TABLE_TYPE
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
');
|
||||
$tables = [];
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$tables[] = [
|
||||
'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->escapeIdentifier($table)})
|
||||
");
|
||||
$row = $result->fetch(FALSE);
|
||||
|
||||
if (!is_array($row) || count($row) < 1) {
|
||||
if ($fallback) {
|
||||
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escapeIdentifier($table)}")->fetch(FALSE);
|
||||
$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->escapeText($table)}
|
||||
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
||||
");
|
||||
$columns = [];
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$size = FALSE;
|
||||
$type = strtoupper($row['DATA_TYPE']);
|
||||
|
||||
$size_cols = [
|
||||
'DATETIME' => 'DATETIME_PRECISION',
|
||||
'DECIMAL' => 'NUMERIC_PRECISION',
|
||||
'CHAR' => 'CHARACTER_MAXIMUM_LENGTH',
|
||||
'NCHAR' => 'CHARACTER_OCTET_LENGTH',
|
||||
'NVARCHAR' => 'CHARACTER_OCTET_LENGTH',
|
||||
'VARCHAR' => 'CHARACTER_OCTET_LENGTH',
|
||||
];
|
||||
|
||||
if (isset($size_cols[$type])) {
|
||||
if ($size_cols[$type]) {
|
||||
$size = $row[$size_cols[$type]];
|
||||
}
|
||||
}
|
||||
|
||||
$columns[] = [
|
||||
'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->escapeText($table)}
|
||||
AND t.is_ms_shipped = 0
|
||||
ORDER BY
|
||||
t.name, ind.name, ind.index_id, ic.index_column_id
|
||||
");
|
||||
|
||||
$indexes = [];
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$index_name = $row['index_name'];
|
||||
|
||||
if (!isset($indexes[$index_name])) {
|
||||
$indexes[$index_name] = [];
|
||||
$indexes[$index_name]['name'] = $index_name;
|
||||
$indexes[$index_name]['unique'] = (bool) $row['is_unique'];
|
||||
$indexes[$index_name]['primary'] = (bool) $row['is_primary_key'];
|
||||
$indexes[$index_name]['columns'] = [];
|
||||
}
|
||||
$indexes[$index_name]['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->escapeText($table)}
|
||||
");
|
||||
|
||||
$keys = [];
|
||||
while ($row = $res->fetch(TRUE)) {
|
||||
$key_name = $row['foreign_key'];
|
||||
|
||||
if (!isset($keys[$key_name])) {
|
||||
$keys[$key_name]['name'] = $row['foreign_key']; // foreign key name
|
||||
$keys[$key_name]['local'] = [$row['column_name']]; // local columns
|
||||
$keys[$key_name]['table'] = $row['reference_table_name']; // referenced table
|
||||
$keys[$key_name]['foreign'] = [$row['reference_column_name']]; // referenced columns
|
||||
$keys[$key_name]['onDelete'] = FALSE;
|
||||
$keys[$key_name]['onUpdate'] = FALSE;
|
||||
} else {
|
||||
$keys[$key_name]['local'][] = $row['column_name']; // local columns
|
||||
$keys[$key_name]['foreign'][] = $row['reference_column_name']; // referenced columns
|
||||
}
|
||||
}
|
||||
return array_values($keys);
|
||||
}
|
||||
|
||||
}
|
@@ -139,7 +139,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
mysql_close($this->connection);
|
||||
@mysql_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
|
@@ -138,7 +138,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
mysqli_close($this->connection);
|
||||
@mysqli_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
@@ -172,9 +172,6 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
} elseif (in_array($code, [1062, 1557, 1569, 1586], TRUE)) {
|
||||
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
|
||||
|
||||
} elseif ($code >= 2001 && $code <= 2028) {
|
||||
return new Dibi\ConnectionException($message, $code, $sql);
|
||||
|
||||
} elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], TRUE)) {
|
||||
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
|
||||
|
||||
|
@@ -88,7 +88,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
odbc_close($this->connection);
|
||||
@odbc_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
|
@@ -44,6 +44,9 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
/** @var string Date and datetime format */
|
||||
private $fmtDate, $fmtDateTime;
|
||||
|
||||
/** @var int|FALSE Number of affected rows */
|
||||
private $affectedRows = FALSE;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
@@ -92,7 +95,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
oci_close($this->connection);
|
||||
@oci_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +107,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$this->affectedRows = FALSE;
|
||||
$res = oci_parse($this->connection, $sql);
|
||||
if ($res) {
|
||||
@oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
|
||||
@@ -112,6 +116,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
throw self::createException($err['message'], $err['code'], $sql);
|
||||
|
||||
} elseif (is_resource($res)) {
|
||||
$this->affectedRows = oci_num_rows($res);
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
} else {
|
||||
@@ -147,7 +152,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
public function getAffectedRows()
|
||||
{
|
||||
throw new Dibi\NotImplementedException;
|
||||
return $this->affectedRows;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -67,7 +67,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
|
||||
if ($config['resource'] instanceof PDO) {
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
unset($config['resource'], $config['pdo']);
|
||||
} else {
|
||||
try {
|
||||
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
|
||||
@@ -291,6 +291,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
return '[' . strtr($value, '[]', ' ') . ']';
|
||||
|
||||
case 'odbc':
|
||||
case 'mssql':
|
||||
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
|
||||
|
||||
case 'dblib':
|
||||
@@ -360,6 +361,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
|
||||
case 'odbc':
|
||||
case 'mssql':
|
||||
case 'dblib':
|
||||
case 'sqlsrv':
|
||||
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
|
||||
@@ -437,13 +439,15 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mssql':
|
||||
case 'sqlsrv':
|
||||
case 'dblib':
|
||||
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012
|
||||
if ($limit !== NULL || $offset) {
|
||||
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
||||
$sql .= ' OFFSET ' . (int) $offset . ' ROWS '
|
||||
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY';
|
||||
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
||||
if ($limit !== NULL) {
|
||||
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
|
||||
} elseif ($offset) {
|
||||
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@@ -111,7 +111,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
pg_close($this->connection);
|
||||
@pg_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
|
@@ -72,12 +72,17 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
} else {
|
||||
// Default values
|
||||
if (!isset($config['options']['CharacterSet'])) {
|
||||
$config['options']['CharacterSet'] = 'UTF-8';
|
||||
}
|
||||
$options = $config['options'];
|
||||
|
||||
$this->connection = sqlsrv_connect($config['host'], (array) $config['options']);
|
||||
// Default values
|
||||
if (!isset($options['CharacterSet'])) {
|
||||
$options['CharacterSet'] = 'UTF-8';
|
||||
}
|
||||
$options['PWD'] = (string) $options['PWD'];
|
||||
$options['UID'] = (string) $options['UID'];
|
||||
$options['Database'] = (string) $options['Database'];
|
||||
|
||||
$this->connection = sqlsrv_connect($config['host'], $options);
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
@@ -94,7 +99,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
sqlsrv_close($this->connection);
|
||||
@sqlsrv_close($this->connection); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +141,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
*/
|
||||
public function getInsertId($sequence)
|
||||
{
|
||||
$res = sqlsrv_query($this->connection, 'SELECT @@IDENTITY');
|
||||
$res = sqlsrv_query($this->connection, 'SELECT SCOPE_IDENTITY()');
|
||||
if (is_resource($res)) {
|
||||
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
|
||||
return $row[0];
|
||||
@@ -311,13 +316,15 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
|
||||
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
|
||||
|
||||
} elseif ($limit !== NULL) {
|
||||
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
|
||||
$sql = sprintf('SELECT TOP (%d) * FROM (%s) t', $limit, $sql);
|
||||
}
|
||||
|
||||
} elseif ($limit !== NULL || $offset) {
|
||||
} elseif ($limit !== NULL) {
|
||||
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
||||
$sql .= ' OFFSET ' . (int) $offset . ' ROWS '
|
||||
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY';
|
||||
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
|
||||
} elseif ($offset) {
|
||||
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
||||
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -34,7 +34,7 @@ class SqlsrvReflector implements Dibi\Reflector
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
$res = $this->driver->query('SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES');
|
||||
$res = $this->driver->query("SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE [TABLE_SCHEMA] = 'dbo'");
|
||||
$tables = [];
|
||||
while ($row = $res->fetch(FALSE)) {
|
||||
$tables[] = [
|
||||
@@ -105,19 +105,19 @@ class SqlsrvReflector implements Dibi\Reflector
|
||||
*/
|
||||
public function getIndexes($table)
|
||||
{
|
||||
$keyUsagesRes = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = {$this->driver->escapeText($table)}");
|
||||
$keyUsagesRes = $this->driver->query(sprintf("EXEC [sys].[sp_helpindex] @objname = N%s", $this->driver->escapeText($table)));
|
||||
$keyUsages = [];
|
||||
while ($row = $keyUsagesRes->fetch(TRUE)) {
|
||||
$keyUsages[$row['CONSTRAINT_NAME']][(int) $row['ORDINAL_POSITION'] - 1] = $row['COLUMN_NAME'];
|
||||
$keyUsages[$row['index_name']] = explode(',', $row['index_keys']);
|
||||
}
|
||||
|
||||
$res = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = {$this->driver->escapeText($table)}");
|
||||
$res = $this->driver->query("SELECT [i].* FROM [sys].[indexes] [i] INNER JOIN [sys].[tables] [t] ON [i].[object_id] = [t].[object_id] WHERE [t].[name] = {$this->driver->escapeText($table)}");
|
||||
$indexes = [];
|
||||
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']] : [];
|
||||
$indexes[$row['name']]['name'] = $row['name'];
|
||||
$indexes[$row['name']]['unique'] = $row['is_unique'] === 1;
|
||||
$indexes[$row['name']]['primary'] = $row['is_primary_key'] === 1;
|
||||
$indexes[$row['name']]['columns'] = isset($keyUsages[$row['name']]) ? $keyUsages[$row['name']] : [];
|
||||
}
|
||||
return array_values($indexes);
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ namespace Dibi;
|
||||
* @method Fluent innerJoin(...$table)
|
||||
* @method Fluent rightJoin(...$table)
|
||||
* @method Fluent outerJoin(...$table)
|
||||
* @method Fluent as(...$field)
|
||||
* @method Fluent on(...$cond)
|
||||
* @method Fluent using(...$cond)
|
||||
*/
|
||||
@@ -180,7 +181,7 @@ class Fluent implements IDataSource
|
||||
return $this;
|
||||
|
||||
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
|
||||
$args = ['%n', $arg];
|
||||
$args = [$clause === 'AS' ? '%N' : '%n', $arg];
|
||||
|
||||
} elseif (is_array($arg) || ($arg instanceof \Traversable && !$arg instanceof self)) { // any array
|
||||
if (isset(self::$modifiers[$clause])) {
|
||||
|
@@ -183,8 +183,8 @@ class Helpers
|
||||
static $patterns = [
|
||||
'^_' => Type::TEXT, // PostgreSQL arrays
|
||||
'BYTEA|BLOB|BIN' => Type::BINARY,
|
||||
'TEXT|CHAR|POINT|INTERVAL' => Type::TEXT,
|
||||
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT' => Type::INTEGER,
|
||||
'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::TEXT,
|
||||
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER,
|
||||
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT,
|
||||
'^TIME$' => Type::TIME,
|
||||
'TIME' => Type::DATETIME, // DATETIME, TIMESTAMP
|
||||
|
@@ -116,6 +116,19 @@ trait Strict
|
||||
list($class, $name) = explode('::', $name);
|
||||
$class = (new ReflectionClass($class))->getName();
|
||||
}
|
||||
|
||||
if (self::$extMethods === NULL) { // for backwards compatibility
|
||||
$list = get_defined_functions();
|
||||
foreach ($list['user'] as $fce) {
|
||||
$pair = explode('_prototype_', $fce);
|
||||
if (count($pair) === 2) {
|
||||
trigger_error("Extension method defined as $fce() is deprecated, use $class::extensionMethod('$name', ...).", E_USER_DEPRECATED);
|
||||
self::$extMethods[$pair[1]][(new ReflectionClass($pair[0]))->getName()] = $fce;
|
||||
self::$extMethods[$pair[1]][''] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$list = & self::$extMethods[strtolower($name)];
|
||||
if ($callback === NULL) { // getter
|
||||
$cache = & $list[''][$class];
|
||||
|
@@ -225,7 +225,7 @@ final class Translator
|
||||
case 'n': // key, key, ... identifier names
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_string($k)) {
|
||||
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->identifiers->$v);
|
||||
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->driver->escapeIdentifier($v));
|
||||
} else {
|
||||
$pair = explode('%', $v, 2); // split into identifier & modifier
|
||||
$vx[] = $this->identifiers->{$pair[0]};
|
||||
@@ -372,6 +372,7 @@ final class Translator
|
||||
|
||||
case 'd': // date
|
||||
case 't': // datetime
|
||||
case 'dt': // datetime
|
||||
if ($value === NULL) {
|
||||
return 'NULL';
|
||||
} else {
|
||||
@@ -379,9 +380,12 @@ final class Translator
|
||||
}
|
||||
|
||||
case 'by':
|
||||
case 'n': // identifier name
|
||||
case 'n': // composed identifier name
|
||||
return $this->identifiers->$value;
|
||||
|
||||
case 'N': // identifier name
|
||||
return $this->driver->escapeIdentifier($value);
|
||||
|
||||
case 'ex':
|
||||
case 'sql': // preserve as dibi-SQL (TODO: leave only %ex)
|
||||
$value = (string) $value;
|
||||
|
@@ -22,8 +22,8 @@ class dibi
|
||||
|
||||
/** version */
|
||||
const
|
||||
VERSION = '3.0.0',
|
||||
REVISION = 'released on 2015-11-07';
|
||||
VERSION = '3.0.6',
|
||||
REVISION = 'released on 2016-07-31';
|
||||
|
||||
/** sorting order */
|
||||
const
|
||||
|
@@ -13,8 +13,6 @@ namespace Dibi;
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** @var string|NULL */
|
||||
private $sql;
|
||||
|
||||
@@ -66,7 +64,6 @@ class DriverException extends Exception
|
||||
*/
|
||||
class PcreException extends Exception
|
||||
{
|
||||
use Strict;
|
||||
|
||||
public function __construct($message = '%msg.')
|
||||
{
|
||||
|
@@ -26,6 +26,8 @@ spl_autoload_register(function ($class) {
|
||||
'Dibi\Drivers\FirebirdDriver' => 'Drivers/FirebirdDriver.php',
|
||||
'Dibi\Drivers\SqlsrvDriver' => 'Drivers/SqlsrvDriver.php',
|
||||
'Dibi\Drivers\SqlsrvReflector' => 'Drivers/SqlsrvReflector.php',
|
||||
'Dibi\Drivers\MsSqlDriver' => 'Drivers/MsSqlDriver.php',
|
||||
'Dibi\Drivers\MsSqlReflector' => 'Drivers/MsSqlReflector.php',
|
||||
'Dibi\Drivers\MySqlDriver' => 'Drivers/MySqlDriver.php',
|
||||
'Dibi\Drivers\MySqliDriver' => 'Drivers/MySqliDriver.php',
|
||||
'Dibi\Drivers\MySqlReflector' => 'Drivers/MySqlReflector.php',
|
||||
@@ -84,6 +86,8 @@ spl_autoload_register(function ($class) {
|
||||
'DibiLiteral' => 'Dibi\Literal',
|
||||
'DibiMsSql2005Driver' => 'Dibi\Drivers\SqlsrvDriver',
|
||||
'DibiMsSql2005Reflector' => 'Dibi\Drivers\SqlsrvReflector',
|
||||
'DibiMsSqlDriver' => 'Dibi\Drivers\MsSqlDriver',
|
||||
'DibiMsSqlReflector' => 'Dibi\Drivers\MsSqlReflector',
|
||||
'DibiMySqlDriver' => 'Dibi\Drivers\MySqlDriver',
|
||||
'DibiMySqliDriver' => 'Dibi\Drivers\MySqliDriver',
|
||||
'DibiMySqlReflector' => 'Dibi\Drivers\MySqlReflector',
|
||||
|
4
tests/.coveralls.yml
Normal file
4
tests/.coveralls.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
# for php-coveralls
|
||||
service_name: travis-ci
|
||||
coverage_clover: coverage.xml
|
||||
json_path: coverage.json
|
@@ -42,3 +42,48 @@ dsn = "odbc:Driver={Microsoft Access Driver (*.mdb)};Dbq=data/odbc.mdb"
|
||||
username =
|
||||
password =
|
||||
system = odbc
|
||||
|
||||
;[sqlsrv 2008]
|
||||
;driver = sqlsrv
|
||||
;host = "(local)\SQL2008R2SP2"
|
||||
;database = master
|
||||
;username = sa
|
||||
;password = "Password12!"
|
||||
;system = sqlsrv
|
||||
|
||||
;[sqlsrv 2008-pdo]
|
||||
;driver = pdo
|
||||
;dsn = "sqlsrv:Server=(local)\SQL2008R2SP2;Database=master"
|
||||
;username = sa
|
||||
;password = "Password12!"
|
||||
;system = sqlsrv
|
||||
|
||||
[sqlsrv 2012]
|
||||
driver = sqlsrv
|
||||
host = "(local)\SQL2012SP1"
|
||||
database = master
|
||||
username = sa
|
||||
password = "Password12!"
|
||||
system = sqlsrv
|
||||
|
||||
;[sqlsrv 2012-pdo]
|
||||
;driver = pdo
|
||||
;dsn = "sqlsrv:Server=(local)\SQL2012SP1;Database=master"
|
||||
;username = sa
|
||||
;password = "Password12!"
|
||||
;system = sqlsrv
|
||||
|
||||
[sqlsrv 2014]
|
||||
driver = sqlsrv
|
||||
host = "(local)\SQL2014"
|
||||
database = master
|
||||
username = sa
|
||||
password = "Password12!"
|
||||
system = sqlsrv
|
||||
|
||||
;[sqlsrv 2014-pdo]
|
||||
;driver = pdo
|
||||
;dsn = "sqlsrv:Server=(local)\SQL2014;Database=master"
|
||||
;username = sa
|
||||
;password = "Password12!"
|
||||
;system = sqlsrv
|
||||
|
@@ -57,6 +57,20 @@ username =
|
||||
password =
|
||||
system = odbc
|
||||
|
||||
[mssql]
|
||||
driver = mssql
|
||||
host = 127.0.0.1
|
||||
username = dibi
|
||||
password =
|
||||
system = mssql
|
||||
|
||||
[mssql pdo]
|
||||
driver = pdo
|
||||
host = mssql:host=127.0.0.1;dbname=dibi_test
|
||||
username = dibi
|
||||
password =
|
||||
system = mssql
|
||||
|
||||
[sqlsrv]
|
||||
driver = sqlsrv
|
||||
host = (local)
|
||||
|
@@ -36,3 +36,15 @@ test(function () use ($config) { // query string
|
||||
Assert::same($config['driver'], $conn->getConfig('driver'));
|
||||
Assert::type('Dibi\Driver', $conn->getDriver());
|
||||
});
|
||||
|
||||
|
||||
test(function () use ($config) {
|
||||
$conn = new Connection($config);
|
||||
Assert::true($conn->isConnected());
|
||||
|
||||
$conn->disconnect();
|
||||
Assert::false($conn->isConnected());
|
||||
|
||||
$conn->disconnect();
|
||||
Assert::false($conn->isConnected());
|
||||
});
|
||||
|
@@ -14,7 +14,7 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||
|
||||
|
||||
// fetch a single value
|
||||
$res = $conn->query('SELECT [title] FROM [products]');
|
||||
$res = $conn->query('SELECT [title] FROM [products] ORDER BY [product_id]');
|
||||
Assert::same('Chair', $res->fetchSingle());
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ Assert::equal([
|
||||
// more complex association array
|
||||
function query($conn) {
|
||||
|
||||
return $conn->query($conn->getConfig('system') === 'odbc' ? '
|
||||
return $conn->query(in_array($conn->getConfig('system'), ['odbc', 'sqlsrv']) ? '
|
||||
SELECT products.title, customers.name, orders.amount
|
||||
FROM ([products]
|
||||
INNER JOIN [orders] ON [products.product_id] = [orders.product_id])
|
||||
|
@@ -11,16 +11,46 @@ $conn = new Dibi\Connection($config);
|
||||
$conn->getSubstitutes()->blog = 'wp_';
|
||||
|
||||
Assert::same(
|
||||
reformat('UPDATE wp_items SET [text]=\'Hello World\''),
|
||||
$conn->translate("UPDATE :blog:items SET [text]='Hello World'")
|
||||
reformat('UPDATE wp_items SET [val]=1'),
|
||||
$conn->translate('UPDATE :blog:items SET [val]=1')
|
||||
);
|
||||
|
||||
Assert::same(
|
||||
reformat('UPDATE \'wp_\' SET [text]=\'Hello World\''),
|
||||
$conn->translate("UPDATE :blog: SET [text]='Hello World'")
|
||||
reformat('UPDATE [wp_items] SET [val]=1'),
|
||||
$conn->translate('UPDATE [:blog:items] SET [val]=1')
|
||||
);
|
||||
|
||||
Assert::same(
|
||||
reformat('UPDATE \':blg:\' SET [text]=\'Hello World\''),
|
||||
$conn->translate("UPDATE :blg: SET [text]='Hello World'")
|
||||
reformat("UPDATE 'wp_' SET [val]=1"),
|
||||
$conn->translate('UPDATE :blog: SET [val]=1')
|
||||
);
|
||||
|
||||
Assert::same(
|
||||
reformat("UPDATE ':blg:' SET [val]=1"),
|
||||
$conn->translate('UPDATE :blg: SET [val]=1')
|
||||
);
|
||||
|
||||
Assert::same(
|
||||
reformat("UPDATE table SET [text]=':blog:a'"),
|
||||
$conn->translate("UPDATE table SET [text]=':blog:a'")
|
||||
);
|
||||
|
||||
|
||||
// create new substitution :: (empty) ==> my_
|
||||
$conn->getSubstitutes()->{''} = 'my_';
|
||||
|
||||
Assert::same(
|
||||
reformat('UPDATE my_table SET [val]=1'),
|
||||
$conn->translate('UPDATE ::table SET [val]=1')
|
||||
);
|
||||
|
||||
|
||||
// create substitutions using fallback callback
|
||||
$conn->getSubstitutes()->setCallback(function ($expr) {
|
||||
return '_' . $expr . '_';
|
||||
});
|
||||
|
||||
Assert::same(
|
||||
reformat('UPDATE _account_user SET [val]=1'),
|
||||
$conn->translate('UPDATE :account:user SET [val]=1')
|
||||
);
|
||||
|
@@ -40,28 +40,28 @@ $fluent = $conn->select('*')
|
||||
->orderBy('customer_id');
|
||||
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 1 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
(string) $fluent
|
||||
);
|
||||
|
||||
|
||||
$fluent->fetch();
|
||||
Assert::same(
|
||||
'SELECT TOP 1 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t',
|
||||
'SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t',
|
||||
dibi::$sql
|
||||
);
|
||||
$fluent->fetchSingle();
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 1 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
dibi::$sql
|
||||
);
|
||||
$fluent->fetchAll(0, 3);
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 3 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (3) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
dibi::$sql
|
||||
);
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 1 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
(string) $fluent
|
||||
);
|
||||
|
||||
@@ -69,16 +69,16 @@ Assert::same(
|
||||
$fluent->limit(0);
|
||||
$fluent->fetch();
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 0 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
dibi::$sql
|
||||
);
|
||||
$fluent->fetchSingle();
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 0 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
dibi::$sql
|
||||
);
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 0 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
(string) $fluent
|
||||
);
|
||||
|
||||
@@ -87,12 +87,12 @@ $fluent->removeClause('limit');
|
||||
$fluent->removeClause('offset');
|
||||
$fluent->fetch();
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 1 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
dibi::$sql
|
||||
);
|
||||
$fluent->fetchSingle();
|
||||
Assert::same(
|
||||
reformat('SELECT TOP 1 * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
||||
dibi::$sql
|
||||
);
|
||||
Assert::same(
|
||||
|
@@ -28,7 +28,7 @@ Assert::equal([
|
||||
|
||||
|
||||
// more complex association array
|
||||
if ($config['system'] !== 'odbc') {
|
||||
if (!in_array($config['system'], ['odbc', 'sqlsrv'])) {
|
||||
$res = $conn->select(['products.title' => 'title', 'customers.name' => 'name'])->select('orders.amount')->as('amount')
|
||||
->from('products')
|
||||
->innerJoin('orders')->using('(product_id)')
|
||||
|
@@ -26,19 +26,19 @@ Assert::same(
|
||||
(string) $fluent
|
||||
);
|
||||
|
||||
$fluent->from('table')->as('tableAlias')
|
||||
$fluent->from('table')->as('table.Alias')
|
||||
->innerJoin('table1')->on('table.col = table1.col')
|
||||
->innerJoin('table2')->on('table.col = table2.col');
|
||||
|
||||
Assert::same(
|
||||
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [table] AS [tableAlias] INNER JOIN [table1] ON table.col = table1.col INNER JOIN [table2] ON table.col = table2.col'),
|
||||
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [table] AS [table.Alias] INNER JOIN [table1] ON table.col = table1.col INNER JOIN [table2] ON table.col = table2.col'),
|
||||
(string) $fluent
|
||||
);
|
||||
|
||||
$fluent->from('anotherTable');
|
||||
|
||||
Assert::same(
|
||||
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [table] AS [tableAlias] INNER JOIN [table1] ON table.col = table1.col INNER JOIN [table2] ON table.col = table2.col , [anotherTable]'),
|
||||
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [table] AS [table.Alias] INNER JOIN [table1] ON table.col = table1.col INNER JOIN [table2] ON table.col = table2.col , [anotherTable]'),
|
||||
(string) $fluent
|
||||
);
|
||||
|
||||
@@ -94,6 +94,7 @@ $fluent = $conn->select('*')
|
||||
Assert::same(
|
||||
reformat([
|
||||
'odbc' => 'SELECT TOP 1 * FROM ( SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123) t',
|
||||
'sqlsrv' => ' SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY',
|
||||
' SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 LIMIT 1',
|
||||
]),
|
||||
(string) $fluent
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @dataProvider ../databases.ini !=odbc
|
||||
* @dataProvider ../databases.ini !=odbc, !=sqlsrv
|
||||
*/
|
||||
|
||||
use Tester\Assert;
|
||||
@@ -13,9 +13,9 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||
|
||||
$info = $conn->query('
|
||||
SELECT products.product_id, orders.order_id, customers.name, products.product_id + 1 AS [xXx]
|
||||
FROM products
|
||||
INNER JOIN orders USING (product_id)
|
||||
INNER JOIN customers USING (customer_id)
|
||||
FROM ([products]
|
||||
INNER JOIN [orders] ON [products.product_id] = [orders.product_id])
|
||||
INNER JOIN [customers] ON [orders.customer_id] = [customers.customer_id]
|
||||
')->getInfo();
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Assert::same(
|
||||
);
|
||||
|
||||
|
||||
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
|
||||
if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'])) {
|
||||
Assert::same(
|
||||
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
|
||||
$info->getColumnNames(TRUE)
|
||||
@@ -36,18 +36,18 @@ if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
|
||||
$columns = $info->getColumns();
|
||||
|
||||
Assert::same('product_id', $columns[0]->getName());
|
||||
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
|
||||
if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'])) {
|
||||
Assert::same('products', $columns[0]->getTableName());
|
||||
}
|
||||
Assert::null($columns[0]->getVendorInfo('xxx'));
|
||||
if ($config['system'] !== 'sqlite') {
|
||||
if (!in_array($config['system'], ['sqlite', 'sqlsrv'])) {
|
||||
Assert::same('i', $columns[0]->getType());
|
||||
}
|
||||
Assert::null($columns[0]->isNullable());
|
||||
|
||||
Assert::same('xXx', $columns[3]->getName());
|
||||
Assert::null($columns[3]->getTableName());
|
||||
if ($config['system'] !== 'sqlite') {
|
||||
if (!in_array($config['system'], ['sqlite', 'sqlsrv'])) {
|
||||
Assert::same('i', $columns[0]->getType());
|
||||
}
|
||||
Assert::null($columns[3]->isNullable());
|
||||
|
@@ -139,7 +139,7 @@ test(function () {
|
||||
Assert::same(['col' => 1], $result->test(['col' => TRUE]));
|
||||
Assert::same(['col' => 0], $result->test(['col' => FALSE]));
|
||||
|
||||
Assert::same(['col' => 0], $result->test(['col' => '']));
|
||||
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning in PHP 7.1
|
||||
Assert::same(['col' => 0], $result->test(['col' => '0']));
|
||||
Assert::same(['col' => 1], $result->test(['col' => '1']));
|
||||
Assert::same(['col' => 10], $result->test(['col' => '10']));
|
||||
|
28
tests/dibi/Sqlsrv.insert.phpt
Normal file
28
tests/dibi/Sqlsrv.insert.phpt
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @dataProvider? ../databases.ini sqlsrv
|
||||
*/
|
||||
|
||||
use Tester\Assert;
|
||||
|
||||
require __DIR__ . '/bootstrap.php';
|
||||
|
||||
$conn = new Dibi\Connection($config);
|
||||
$conn->loadFile(__DIR__ . "/data/sqlsrv.insert.sql");
|
||||
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$conn->query('INSERT INTO %n DEFAULT VALUES', 'aaa');
|
||||
Assert::equal($i, $conn->getInsertId());
|
||||
}
|
||||
|
||||
$conn->query('INSERT INTO %n DEFAULT VALUES', 'aab');
|
||||
Assert::equal(1, $conn->getInsertId());
|
||||
|
||||
$conn->query(
|
||||
'CREATE TRIGGER %n ON %n AFTER INSERT AS INSERT INTO %n DEFAULT VALUES',
|
||||
'UpdAAB', 'aab', 'aaa'
|
||||
);
|
||||
|
||||
$conn->query('INSERT INTO %n DEFAULT VALUES', 'aab');
|
||||
Assert::equal(2, $conn->getInsertId());
|
@@ -9,7 +9,10 @@ use Tester\Assert;
|
||||
require __DIR__ . '/bootstrap.php';
|
||||
|
||||
$tests = function ($conn) {
|
||||
$version = $conn->getDriver()->getResource()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
$resource = $conn->getDriver()->getResource();
|
||||
$version = is_resource($resource)
|
||||
? sqlsrv_server_info($resource)['SQLServerVersion']
|
||||
: $resource->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
|
||||
// MsSQL2012+
|
||||
if (version_compare($version, '11.0') >= 0) {
|
||||
@@ -32,31 +35,43 @@ $tests = function ($conn) {
|
||||
);
|
||||
|
||||
// Offset invalid
|
||||
Assert::same(
|
||||
'SELECT 1',
|
||||
$conn->translate('SELECT 1 %ofs', -10)
|
||||
Assert::error(
|
||||
function () use ($conn) {
|
||||
$conn->translate('SELECT 1 %ofs', -10);
|
||||
},
|
||||
'Dibi\NotSupportedException',
|
||||
'Negative offset or limit.'
|
||||
);
|
||||
|
||||
// Limit invalid
|
||||
Assert::same(
|
||||
'SELECT 1',
|
||||
$conn->translate('SELECT 1 %lmt', -10)
|
||||
Assert::error(
|
||||
function () use ($conn) {
|
||||
$conn->translate('SELECT 1 %lmt', -10);
|
||||
},
|
||||
'Dibi\NotSupportedException',
|
||||
'Negative offset or limit.'
|
||||
);
|
||||
|
||||
// Limit invalid, offset valid
|
||||
Assert::same(
|
||||
'SELECT 1',
|
||||
$conn->translate('SELECT 1 %ofs %lmt', 10, -10)
|
||||
Assert::error(
|
||||
function () use ($conn) {
|
||||
$conn->translate('SELECT 1 %ofs %lmt', 10, -10);
|
||||
},
|
||||
'Dibi\NotSupportedException',
|
||||
'Negative offset or limit.'
|
||||
);
|
||||
|
||||
// Limit valid, offset invalid
|
||||
Assert::same(
|
||||
'SELECT 1',
|
||||
$conn->translate('SELECT 1 %ofs %lmt', -10, 10)
|
||||
Assert::error(
|
||||
function () use ($conn) {
|
||||
$conn->translate('SELECT 1 %ofs %lmt', -10, 10);
|
||||
},
|
||||
'Dibi\NotSupportedException',
|
||||
'Negative offset or limit.'
|
||||
);
|
||||
} else {
|
||||
Assert::same(
|
||||
'SELECT TOP 1 * FROM (SELECT 1) t',
|
||||
'SELECT TOP (1) * FROM (SELECT 1) t',
|
||||
$conn->translate('SELECT 1 %lmt', 1)
|
||||
);
|
||||
|
||||
|
@@ -141,6 +141,7 @@ Assert::same(
|
||||
Assert::same(
|
||||
reformat([
|
||||
'odbc' => 'SELECT TOP 2 * FROM (SELECT * FROM [products] ) t',
|
||||
'sqlsrv' => 'SELECT * FROM [products] OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY',
|
||||
'SELECT * FROM [products] LIMIT 2',
|
||||
]),
|
||||
$conn->translate('SELECT * FROM [products] %lmt', 2)
|
||||
@@ -153,7 +154,10 @@ if ($config['system'] === 'odbc') {
|
||||
} else {
|
||||
// with limit = 2, offset = 1
|
||||
Assert::same(
|
||||
reformat('SELECT * FROM [products] LIMIT 2 OFFSET 1'),
|
||||
reformat([
|
||||
'sqlsrv' => 'SELECT * FROM [products] OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY',
|
||||
'SELECT * FROM [products] LIMIT 2 OFFSET 1'
|
||||
]),
|
||||
$conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1)
|
||||
);
|
||||
|
||||
@@ -162,6 +166,7 @@ if ($config['system'] === 'odbc') {
|
||||
reformat([
|
||||
'mysql' => 'SELECT * FROM `products` LIMIT 18446744073709551615 OFFSET 50',
|
||||
'postgre' => 'SELECT * FROM "products" OFFSET 50',
|
||||
'sqlsrv' => 'SELECT * FROM [products] OFFSET 50 ROWS',
|
||||
'SELECT * FROM [products] LIMIT -1 OFFSET 50',
|
||||
]),
|
||||
$conn->translate('SELECT * FROM [products] %ofs', 50)
|
||||
@@ -224,6 +229,7 @@ if ($config['system'] === 'postgre') {
|
||||
reformat([
|
||||
'sqlite' => "SELECT * FROM products WHERE (title LIKE 'C%' ESCAPE '\\' AND title LIKE '%r' ESCAPE '\\') OR title LIKE '%a\n\\%\\_\\\\''\"%' ESCAPE '\\'",
|
||||
'odbc' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
|
||||
'sqlsrv' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
|
||||
"SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\\n\\%\\_\\\\\\\\\'\"%'",
|
||||
]),
|
||||
$conn->translate($args[0], $args[1], $args[2], $args[3])
|
||||
@@ -274,7 +280,7 @@ $array2 = ['one', 'two', 'three'];
|
||||
$array3 = [
|
||||
'col1' => 'one',
|
||||
'col2' => 'two',
|
||||
'col3' => 'three',
|
||||
'col3' => 'thr.ee',
|
||||
];
|
||||
$array4 = [
|
||||
'a' => 12,
|
||||
@@ -295,8 +301,8 @@ WHERE (`test`.`a` LIKE '1995-03-01'
|
||||
OR `b2` IN ('1', '2', '3' )
|
||||
OR `b3` IN ( )
|
||||
OR `b4` IN ( 'one', 'two', 'three' )
|
||||
OR `b5` IN (`col1` AS `one`, `col2` AS `two`, `col3` AS `three` )
|
||||
OR `b6` IN ('one', 'two', 'three')
|
||||
OR `b5` IN (`col1` AS `one`, `col2` AS `two`, `col3` AS `thr.ee` )
|
||||
OR `b6` IN ('one', 'two', 'thr.ee')
|
||||
OR `b7` IN (NULL)
|
||||
OR `b8` IN (RAND() `col1` > `col2` )
|
||||
OR `b9` IN (RAND(), [col1] > [col2] )
|
||||
@@ -316,8 +322,8 @@ WHERE ("test"."a" LIKE \'1995-03-01\'
|
||||
OR "b2" IN (\'1\', \'2\', \'3\' )
|
||||
OR "b3" IN ( )
|
||||
OR "b4" IN ( \'one\', \'two\', \'three\' )
|
||||
OR "b5" IN ("col1" AS "one", "col2" AS "two", "col3" AS "three" )
|
||||
OR "b6" IN (\'one\', \'two\', \'three\')
|
||||
OR "b5" IN ("col1" AS "one", "col2" AS "two", "col3" AS "thr.ee" )
|
||||
OR "b6" IN (\'one\', \'two\', \'thr.ee\')
|
||||
OR "b7" IN (NULL)
|
||||
OR "b8" IN (RAND() "col1" > "col2" )
|
||||
OR "b9" IN (RAND(), [col1] > [col2] )
|
||||
@@ -337,8 +343,8 @@ WHERE ([test].[a] LIKE #03/01/1995#
|
||||
OR [b2] IN ('1', '2', '3' )
|
||||
OR [b3] IN ( )
|
||||
OR [b4] IN ( 'one', 'two', 'three' )
|
||||
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [three] )
|
||||
OR [b6] IN ('one', 'two', 'three')
|
||||
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [thr.ee] )
|
||||
OR [b6] IN ('one', 'two', 'thr.ee')
|
||||
OR [b7] IN (NULL)
|
||||
OR [b8] IN (RAND() [col1] > [col2] )
|
||||
OR [b9] IN (RAND(), [col1] > [col2] )
|
||||
@@ -358,8 +364,8 @@ WHERE ([test].[a] LIKE '1995-03-01'
|
||||
OR [b2] IN ('1', '2', '3' )
|
||||
OR [b3] IN ( )
|
||||
OR [b4] IN ( 'one', 'two', 'three' )
|
||||
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [three] )
|
||||
OR [b6] IN ('one', 'two', 'three')
|
||||
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [thr.ee] )
|
||||
OR [b6] IN ('one', 'two', 'thr.ee')
|
||||
OR [b7] IN (NULL)
|
||||
OR [b8] IN (RAND() [col1] > [col2] )
|
||||
OR [b9] IN (RAND(), [col1] > [col2] )
|
||||
@@ -425,6 +431,7 @@ Assert::same(
|
||||
Assert::same(
|
||||
reformat([
|
||||
'odbc' => 'SELECT TOP 10 * FROM (SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' ) t',
|
||||
'sqlsrv' => 'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
|
||||
'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' LIMIT 10',
|
||||
]),
|
||||
$conn->translate("SELECT * FROM [test] WHERE %n LIKE '%d%t' %lmt", 'id', 10)
|
||||
@@ -515,7 +522,7 @@ Assert::same(
|
||||
|
||||
Assert::same(
|
||||
reformat('INSERT INTO 0'),
|
||||
$conn->translate('INSERT INTO %f', 'ahoj')
|
||||
@$conn->translate('INSERT INTO %f', 'ahoj') // triggers warning in PHP 7.1
|
||||
);
|
||||
|
||||
|
||||
@@ -535,6 +542,12 @@ Assert::same(
|
||||
);
|
||||
|
||||
|
||||
Assert::same(
|
||||
reformat('SELECT [a].[b] AS [c.d]'),
|
||||
$conn->translate('SELECT %n AS %N', 'a.b', 'c.d')
|
||||
);
|
||||
|
||||
|
||||
setLocale(LC_ALL, 'czech');
|
||||
|
||||
Assert::same(
|
||||
|
@@ -1,5 +1,3 @@
|
||||
/*!40102 SET storage_engine = InnoDB */;
|
||||
|
||||
DROP DATABASE IF EXISTS dibi_test;
|
||||
CREATE DATABASE dibi_test;
|
||||
USE dibi_test;
|
||||
|
5
tests/dibi/data/sqlsrv.insert.sql
Normal file
5
tests/dibi/data/sqlsrv.insert.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
IF OBJECT_ID('aaa', 'U') IS NOT NULL DROP TABLE aaa;
|
||||
IF OBJECT_ID('aab', 'U') IS NOT NULL DROP TABLE aab;
|
||||
|
||||
CREATE TABLE aaa ( [id] int NOT NULL IDENTITY PRIMARY KEY )
|
||||
CREATE TABLE aab ( [id] int NOT NULL IDENTITY PRIMARY KEY )
|
@@ -8,6 +8,7 @@ CREATE TABLE products (
|
||||
title varchar(50) NOT NULL,
|
||||
PRIMARY KEY(product_id)
|
||||
);
|
||||
CREATE INDEX [title] ON [dbo].[products] ([title]);
|
||||
|
||||
SET IDENTITY_INSERT products ON;
|
||||
INSERT INTO products (product_id, title) VALUES (1, 'Chair');
|
||||
|
@@ -17,11 +17,12 @@ try {
|
||||
Tester\Environment::skip($e->getMessage());
|
||||
}
|
||||
|
||||
Assert::same(3, count($meta->getTables()));
|
||||
|
||||
$names = $meta->getTableNames();
|
||||
sort($names);
|
||||
Assert::equal(['customers', 'orders', 'products'], $names);
|
||||
if ($config['system'] !== 'sqlsrv') {
|
||||
Assert::same(3, count($meta->getTables()));
|
||||
$names = $meta->getTableNames();
|
||||
sort($names);
|
||||
Assert::equal(['customers', 'orders', 'products'], $names);
|
||||
}
|
||||
|
||||
Assert::false($meta->hasTable('xxxx'));
|
||||
|
||||
@@ -48,7 +49,7 @@ Assert::same('title', $column->getName());
|
||||
Assert::same('products', $column->getTable()->getName());
|
||||
Assert::same('s', $column->getType());
|
||||
Assert::type('string', $column->getNativeType());
|
||||
Assert::same(100, $column->getSize());
|
||||
Assert::same($config['system'] === 'sqlsrv' ? 50 : 100, $column->getSize());
|
||||
Assert::false($column->isNullable());
|
||||
Assert::false($column->isAutoIncrement());
|
||||
//Assert::null($column->getDefault());
|
||||
|
18
tests/php5-win.ini
Normal file
18
tests/php5-win.ini
Normal file
@@ -0,0 +1,18 @@
|
||||
[PHP]
|
||||
extension_dir = "./ext"
|
||||
;extension=php_mssql.dll
|
||||
extension=php_mysql.dll
|
||||
extension=php_mysqli.dll
|
||||
;extension=php_oci8.dll
|
||||
;extension=php_oci8_11g.dll
|
||||
;extension=php_pdo_firebird.dll
|
||||
;extension=php_pdo_mssql.dll
|
||||
extension=php_pdo_mysql.dll
|
||||
;extension=php_pdo_oci.dll
|
||||
extension=php_pdo_odbc.dll
|
||||
extension=php_pdo_pgsql.dll
|
||||
extension=php_pdo_sqlite.dll
|
||||
extension=php_pgsql.dll
|
||||
extension=php_sqlite3.dll
|
||||
extension=php_sqlsrv_ts.dll
|
||||
extension=php_pdo_sqlsrv_ts.dll
|
@@ -1,10 +1,11 @@
|
||||
[PHP]
|
||||
extension_dir = "./ext"
|
||||
extension=php_mysql.dll
|
||||
;extension=php_mssql.dll
|
||||
extension=php_mysqli.dll
|
||||
;extension=php_oci8.dll
|
||||
;extension=php_oci8_11g.dll
|
||||
;extension=php_pdo_firebird.dll
|
||||
;extension=php_pdo_mssql.dll
|
||||
extension=php_pdo_mysql.dll
|
||||
;extension=php_pdo_oci.dll
|
||||
extension=php_pdo_odbc.dll
|
||||
@@ -12,4 +13,5 @@ extension=php_pdo_pgsql.dll
|
||||
extension=php_pdo_sqlite.dll
|
||||
extension=php_pgsql.dll
|
||||
extension=php_sqlite3.dll
|
||||
;extension=php_sqlsrv_ts.dll
|
||||
extension=php_sqlsrv_ts.dll
|
||||
extension=php_odbc.dll
|
Reference in New Issue
Block a user