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

Compare commits

...

102 Commits
v3.0.1 ... v3.1

Author SHA1 Message Date
David Grudl
1809885d30 travis: uses NCS 2 2018-09-17 13:29:16 +02:00
David Grudl
e1256eca87 cs 2018-09-17 13:29:16 +02:00
David Grudl
f26bb27504 loader: added missing classes 2018-09-17 13:23:09 +02:00
David Grudl
504b7725c7 Released version 3.1.1 2018-03-09 13:00:25 +01:00
David Grudl
822405b478 appveyor: PHP is downloaded using cURL
"03/mar/2018: We've upgraded the server bandwidth. This is however still not sufficient to handle all empty user agent connections. Please update the user agent in your scripts accordingly or contact us so we can discuss it."
2018-03-08 13:52:54 +01:00
David Grudl
43d3b57a8d Microsoft SQL Server and MSSQL support for microseconds fix cont. 2018-02-25 16:59:37 +01:00
hubipe
4f0566ece7 Microsoft SQL Server and MSSQL support for microseconds fix (#249) 2018-02-25 16:59:36 +01:00
Korney Czukowski
67521084d9 Previous exception can now be passed to Dibi\Exception constructor (#276) 2018-02-16 14:12:33 +01:00
David Grudl
22ac601502 loader: fixed missing class 'dibi.php' 2018-02-15 11:53:57 +01:00
Petr Bugyík
b740b70f18 drivers: changed self:: to static:: 2018-02-14 13:05:35 +01:00
David Grudl
44fc3f4553 coding style 2018-02-11 22:47:22 +01:00
David Grudl
fa35c0cf7d Released version 3.1.0 2017-09-25 17:57:54 +02:00
David Grudl
8a25700c8c travis: fixed PHP versions 2017-09-25 17:57:45 +02:00
David Grudl
1352437e08 added Dibi\Expression [Closes #264] 2017-09-25 17:57:45 +02:00
David Grudl
f0d08a990c whitespace 2017-09-21 14:22:22 +02:00
David Grudl
4472fb3a3d Translator: fixed %dt with DateTimeInterface object [Closes #263] 2017-09-21 14:04:30 +02:00
Miloslav Hůla
d4b87490a1 Fluent: fixed phpDoc, query() may return Result|int 2017-09-07 21:26:43 +02:00
Miloslav Hůla
9486b65b84 OdbcDriver, OracleDriver, SqlsrvDriver, Sqlite3Driver: query() returns ResultSet only when contains columns 2017-09-07 21:26:43 +02:00
David Grudl
f47ad15af0 MySqliDriver: removed deprecated stuff 2017-09-07 21:26:43 +02:00
David Grudl
6c147f2ea5 Connection, dibi: deprecated insertId() & affectedRows() 2017-09-07 21:26:43 +02:00
David Grudl
0af64b5f9b Revert "loader: uses only Composer's autoloader"
This reverts commit 53475ba05a.
2017-07-26 15:02:03 +02:00
David Grudl
03c6eeeb16 Revert "examples: uses composer autoload"
This reverts commit 6a4cc4f36f.
2017-07-26 15:01:44 +02:00
David Grudl
ad3a9c50f9 travis: use stages 2017-07-24 17:04:29 +02:00
David Grudl
d31f2e3dd4 Translator: %i %f throws exception when value is not numeric (BC break) 2017-07-22 00:41:39 +02:00
David Grudl
3253a5b092 support for 64bit numbers on 32bit platform [Closes #253] (BC break)
throws exception when given argument is not a number
2017-07-22 00:41:35 +02:00
David Grudl
be3a0aa57d __toString() returns always string 2017-07-22 00:40:33 +02:00
David Grudl
6a4cc4f36f examples: uses composer autoload 2017-07-21 22:46:09 +02:00
David Grudl
1815b214ee travis: tested using Nette Coding Standard 2017-07-21 22:46:09 +02:00
David Grudl
4f75637b63 coding style: fixes in code 2017-07-21 22:46:09 +02:00
David Grudl
ebf0be1fd0 coding style: TRUE/FALSE/NULL -> true/false/null 2017-07-21 22:33:41 +02:00
David Grudl
b439ee9df1 coding style: fixed spaces & use statements order 2017-07-21 22:25:33 +02:00
David Grudl
e45a86d58c Released version 3.1.0 2017-06-10 03:08:16 +02:00
David Grudl
e891bdd862 DibiExtension22: added $debugMode to constructor 2017-06-10 03:08:16 +02:00
David Grudl
8dc5567bdd Helpers::loadFromFile added $onProgress 2017-06-10 03:08:16 +02:00
David Grudl
e3bfac2316 Helpers::loadFromFile improved 2017-06-10 03:08:16 +02:00
David Grudl
53475ba05a loader: uses only Composer's autoloader 2017-06-10 03:08:14 +02:00
David Grudl
c219726914 removed bridges for Nette < 2.2 2017-06-10 03:06:01 +02:00
hubipe
ed0cb63df0 Support for microseconds (#246) 2017-06-10 03:06:01 +02:00
Aleš Culek
1ab69f3576 OracleDriver: added 'nativeDate' option, formatDate & formatDateTime are deprecated (#232) 2017-06-10 03:06:01 +02:00
David Grudl
70f29f6857 removed folder /dibi 2017-06-10 03:06:01 +02:00
David Grudl
d365077319 opened 3.1-dev 2017-06-10 03:05:55 +02:00
David Grudl
0e5d951dfb Released version 3.0.8 2017-06-10 03:05:18 +02:00
David Grudl
126422ad7e strict fixes 2017-06-10 03:05:18 +02:00
David Grudl
277f52c928 appveyor: is unable to start MSSQL 2012 & 2014 together 2017-06-10 02:58:47 +02:00
David Grudl
80ac569621 fixed phpDoc 2017-06-10 02:58:47 +02:00
David Grudl
d9628f933d coding style: removed space after reference & 2017-06-10 02:58:47 +02:00
David Grudl
f7009f3e0c Tracy\Panel: better typography 2017-06-10 02:58:47 +02:00
Jiří Trávníček
36d30c1fcf Dibi\Bridges\Tracy\Panel: host added to tracy panel [closes #250] (#251) 2017-06-10 02:58:44 +02:00
David Grudl
ae6c8756b6 Tracy\Panel: one panel is used per connection 2017-06-10 02:58:06 +02:00
David Grudl
6b2c996b16 Tracy\Panel: dump() may fails
related: https://forum.nette.org/cs/26790-error-dibi-bridges-tracy-panel-oracle
917971992f (commitcomment-18444224)
2017-06-09 12:12:07 +02:00
David Grudl
718c617764 travis: removed HHVM 2017-06-09 12:12:07 +02:00
David Grudl
6194152e67 examples: tracy is loaded before output [Closes #248] 2017-06-09 12:12:07 +02:00
David Grudl
d39603e23d updated .gitattributes 2017-06-09 12:12:07 +02:00
David Grudl
22e6ea4e40 Released version 3.0.7 2017-01-04 15:18:17 +01:00
David Grudl
f0d2c3f414 travis: removed PHP 7.1 from allowed failures 2017-01-04 15:18:17 +01:00
David Grudl
873ed3115d Connection::translateArgs() is protected [Closes #237] 2017-01-04 15:18:17 +01:00
Martin Dzíbela
73289d0569 PostgreSQL: add support for more than one schema in search_path (#239) 2017-01-04 15:18:17 +01:00
David Grudl
7b899ddda5 updated contributing.md, added GitHub templates 2017-01-04 15:18:17 +01:00
Ondřej Mirtes
8860268791 typos (#243) 2016-12-03 19:29:29 +01:00
Honza Machala
551b576271 Typos (#242) 2016-11-14 13:47:45 +01:00
David Grudl
74d0a78ec2 Result, Row: added support for datetime like 'Jun 17 2015 12:00:00:000AM' [Closes #180] 2016-09-02 17:36:37 +02:00
Aleš Culek
be7c3f095d Implemented OracleDriver::getColumns() (#233) 2016-08-09 22:37:45 +02:00
Radovan Kepák
3a6dc07da8 FirebirdDriver: Fixed DriverException throw (#231)
Fixed fetch Dibi\DriverException throw and more PHP warnings silenced
2016-08-09 22:36:44 +02:00
David Grudl
9b070bb737 Released version 3.0.6 2016-07-31 16:50:07 +02:00
David Grudl
917971992f Bridges\Tracy\Panel: silenced PHP warning
https://forum.nette.org/cs/26790-error-dibi-bridges-tracy-panel-oracle
2016-07-27 14:01:46 +02:00
Radovan Kepák
bc564555f8 FirebirdDriver: silenced PHP warning
If fetch have error, PHP show Warning, this is error by my opinion as Dibi Throw Exception with information, so the Warning is useless, and even more, if we catch exception, we still have warning.
2016-07-27 13:55:15 +02:00
David Grudl
3e20a6b8fc Dibi\DateTime::__wakeup() doesn't call parent after 8e8e6dfd [Closes #228] 2016-07-27 13:28:23 +02:00
David Grudl
c019e7cac2 tests: fixed compatibility with PHP 7.1 2016-07-21 14:38:56 +02:00
David Grudl
2294c195f4 Released version 3.0.5 2016-07-20 16:15:13 +02:00
David Grudl
25246529f7 Translator, Fluent: preserve dot in name after AS [Closes #224] 2016-07-20 16:13:33 +02:00
David Grudl
d405ec369b Translator: added %N 2016-07-20 16:13:32 +02:00
David Grudl
b7974fe192 travis: added PHP 7.1 2016-07-20 15:54:58 +02:00
David Grudl
80f1898e1b tests: removed deprecated 'storage_engine' 2016-07-20 15:50:33 +02:00
Roman Pavlík
8e8e6dfdca Dibi\DateTime: provides BC for serialized older versions [Closes #226] (#227) 2016-07-20 15:50:33 +02:00
Milan Pála
6510fcce25 Disconnect on not connected driver not fail (#222)
Sometimes database go away and Connection::isConnected() is returning TRUE. Prevent this should be posibble to disconnect on closed connection without error.
2016-07-20 15:50:32 +02:00
aldria
b7d84b90ef Correct limit and offset for Firebird Driver (#221)
SELECT [FIRST (<int-expr>)] [SKIP (<int-expr>)] <columns> FROM ...
2016-05-27 03:48:06 +02:00
David Grudl
2571e54f3c composer.json: replaces all dg/dibi versions 2016-05-04 20:13:50 +02:00
Aleš Culek
15e6d9f738 Implemented OracleDriver::getAffectedRows() 2016-05-03 08:55:22 +02:00
David Grudl
ddfd4a0f1a tests/travis: reports code coverage to Coveralls 2016-04-21 11:29:10 +02:00
David Grudl
4e838fc2b5 Released version 3.0.4 2016-04-06 19:09:24 +02:00
David Grudl
37487816db removed Strict from exceptions [Closes #216] 2016-04-06 18:52:42 +02:00
David Grudl
0c099bb2bc tests: a different .ini for PHP 5 and PHP 7 2016-03-20 19:23:57 +01:00
David Grudl
1786c861b9 Merge pull request #213 from castamir/php7sqlsrv
SqlsrvDriver: php7 compatibility
2016-03-20 16:34:36 +01:00
Mira Paulik
bc0578928f SqlsrvDriver: php7 compatibility 2016-03-20 13:50:10 +01:00
David Grudl
12a07ff6ad Merge pull request #212 from soukiii/master
Fluent: missing annotation
2016-03-18 17:16:23 +01:00
Petr Soukup
b50f59c64c Fluent: missing annotation 2016-03-18 15:40:16 +01:00
David Grudl
fdebf349f5 appveyor: test under PHP 7 2016-03-18 15:07:45 +01:00
David Grudl
954c2f25d0 Merge pull request #209 from castamir/sqlsrv_insert
SqlsrvDriver::getInsertId() last inserted id is from last statement instead of last inserted row
2016-02-29 23:37:17 +01:00
Mira Paulik
43dccb1ba2 SqlsrvDriver::getInsertId() last inserted id is from last statement instead of last inserted row regardless of the table that produced the value 2016-02-29 16:10:59 +01:00
David Grudl
352a683ec1 Released version 3.0.3 2016-02-21 02:07:19 +01:00
David Grudl
f4638796fb Helpers::detectType() detects VAR_STRING as Type::TEXT 2016-02-20 21:08:01 +01:00
David Grudl
4659f4550e DibiExtension22: added options 'explain' & 'filter' [Closes #203] 2016-02-10 00:08:27 +01:00
David Grudl
c3548465fb Merge pull request #204 from castamir/sqlsrv
SqlsrvDriver: fixed: sql server does not respond on non-string password
2016-02-09 23:19:53 +01:00
David Grudl
dda0bdbd67 Merge pull request #208 from ondrej-tuhacek/master
FirebirdDriver::delimite - Quotation marks for escaping identifiers
2016-02-04 16:08:25 +01:00
Ondřej Tuháček
7142be254b FirebirdDriver::delimite - Quotation marks for escaping identifiers 2016-02-04 13:29:06 +01:00
David Grudl
8948cc293c Released version 3.0.2 2016-01-29 15:46:23 +01:00
David Grudl
193252dc5f PdoDriver::applyLimit() is the same as SqlsrvDriver 2016-01-28 20:13:49 +01:00
Mira Paulik
6b2a46bcb8 appveyor: testing with SQL Server 2012 & 2014 2016-01-28 19:55:55 +01:00
Mira Paulik
2c01d993d0 SqlsrvReflector::getTables(): gets list of all tables from dbo schema only 2016-01-28 19:54:46 +01:00
Mira Paulik
e415157206 SqlsrvDriver::applyLimit(): fixed limit and offset behaviour for odbc 11+
SqlsrvReflector: changed constrains metadata loading from INFORMATION_SCHEMA to sys schema to get complete list of all constraints, not PK only
2016-01-28 19:48:49 +01:00
David Grudl
8a7dbcba86 Revert "removed MsSqlDriver (is not available with PHP 5.3 or later; replaced with SqlsrvDriver)"
This reverts commit ac1ab26e7a.
2016-01-24 22:52:18 +01:00
Mira Paulik
3b00115ee5 SqlsrvDriver: fixed: sql server does not respond on non-string credentials 2016-01-22 17:14:12 +01:00
99 changed files with 2495 additions and 1400 deletions

2
.gitattributes vendored
View File

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

16
.github/issue_template.md vendored Normal file
View File

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

11
.github/pull_request_template.md vendored Normal file
View File

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

View File

@@ -4,24 +4,12 @@ php:
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0 - 7.0
- hhvm - 7.1
- 7.2
matrix: before_install:
allow_failures: # turn off XDebug
- php: hhvm - phpenv config-rm xdebug.ini || return 0
script:
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini
- php temp/code-checker/src/code-checker.php --short-arrays
after_failure:
# Print *.actual content
- for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done
before_script:
# Install Nette Tester & Code Checker
- travis_retry composer install --no-interaction
- travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction
# Create databases.ini # Create databases.ini
- cp ./tests/databases.travis.ini ./tests/databases.ini - cp ./tests/databases.travis.ini ./tests/databases.ini
@@ -29,8 +17,49 @@ before_script:
# Create Postgre database # Create Postgre database
- psql -c 'CREATE DATABASE dibi_test' -U postgres - psql -c 'CREATE DATABASE dibi_test' -U postgres
install:
- travis_retry composer install --no-progress --prefer-dist
script:
- vendor/bin/tester tests -s
after_failure:
# Print *.actual content
- for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done
jobs:
include:
- stage: Code Standard Checker
php: 7.1
install:
# Install Nette Code Checker
- travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress
# Install Nette Coding Standard
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
script:
- php temp/code-checker/code-checker
- php temp/coding-standard/ecs check src tests examples --config temp/coding-standard/coding-standard-php56.yml
- stage: Code Coverage
php: 7.1
script:
- vendor/bin/tester -p phpdbg tests -s --coverage ./coverage.xml --coverage-src ./src
after_script:
- wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar
- php coveralls.phar --verbose --config tests/.coveralls.yml
allow_failures:
- php: 7.2
- stage: Code Coverage
sudo: false sudo: false
cache: cache:
directories: directories:
- $HOME/.composer/cache - $HOME/.composer/cache
notifications:
email: false

View File

@@ -1,30 +1,49 @@
build: off build: off
cache: cache:
- c:\php -> appveyor.yml - c:\php5 -> appveyor.yml
- c:\php7 -> appveyor.yml
- '%LOCALAPPDATA%\Composer\files -> appveyor.yml' - '%LOCALAPPDATA%\Composer\files -> appveyor.yml'
clone_folder: c:\projects\dibi clone_folder: c:\projects\dibi
services: services:
- mssql2012sp1
# - mssql2014
- mysql - mysql
init: init:
- SET PATH=c:\php;%PATH% - SET PATH=c:\php5;%PATH%
- SET PHP=1
- SET ANSICON=121x90 (121x90) - SET ANSICON=121x90 (121x90)
install: install:
# Install PHP # Install PHP 5
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) - IF EXIST c:\php5 (SET PHP=0) ELSE (SET PHP=1)
- IF %PHP%==1 cd c:\php - IF %PHP%==1 mkdir 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 cd c:\php5
- IF %PHP%==1 7z x php-5.6.14-Win32-VC11-x86.zip >nul - IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip --output php.zip
- IF %PHP%==1 7z x php.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini - IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 echo extension=php_openssl.dll >> 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 curl https://windows.php.net/downloads/releases/archives/php-7.0.3-Win32-VC14-x86.zip --output php.zip
- IF %PHP%==1 7z x php.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 - IF %PHP%==1 del /Q *.zip
- cd c:\projects\dibi
# Install Nette Tester # Install Nette Tester
- cd c:\projects\dibi
- appveyor DownloadFile https://getcomposer.org/composer.phar - appveyor DownloadFile https://getcomposer.org/composer.phar
- php composer.phar install --prefer-dist --no-interaction --no-progress - php composer.phar install --prefer-dist --no-interaction --no-progress
@@ -32,7 +51,8 @@ install:
- copy tests\databases.appveyor.ini tests\databases.ini - copy tests\databases.appveyor.ini tests\databases.ini
test_script: 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: on_failure:
# Print *.actual content # Print *.actual content

View File

@@ -15,10 +15,10 @@
}, },
"require-dev": { "require-dev": {
"tracy/tracy": "~2.2", "tracy/tracy": "~2.2",
"nette/tester": "~1.3" "nette/tester": "~1.7"
}, },
"replace": { "replace": {
"dg/dibi": "self.version" "dg/dibi": "*"
}, },
"autoload": { "autoload": {
"classmap": ["src/"], "classmap": ["src/"],
@@ -26,7 +26,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.0-dev" "dev-master": "3.1-dev"
} }
} }
} }

View File

@@ -1,27 +1,31 @@
How to contribute & use the issue tracker How to contribute & use the issue tracker
========================================= =========================================
The issue tracker is the preferred channel for bug reports, features requests Dibi welcomes your contributions. There are several ways to help out:
and submitting pull requests, but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use * Create an issue on GitHub, if you have found a bug
[dibi forum](https://forum.dibiphp.com) or [Stack Overflow](http://stackoverflow.com)). * Write test cases for open bug issues
* Write fixes for open bug/feature issues, preferably with test cases included
* Contribute to the documentation
* Please **do not** derail or troll issues. Keep the discussion on topic and Issues
respect the opinions of others. ------
* Use the GitHub **issue search** &mdash; check if the issue has already been Please **do not use the issue tracker to ask questions**. We will be happy to help you
reported. on [Dibi forum](https://forum.dibiphp.com).
A good **bug report** shouldn't leave others needing to chase you up for more A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report. information. Please try to be as detailed as possible in your report.
**Feature requests** are welcome. But take a moment to find out whether your idea **Feature requests** are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature. case to convince the project's developers of the merits of this feature.
We welcome **pull requests**. If you'd like to contribute, please take a moment Contributing
to [read the guidelines](https://nette.org/en/contributing) in order to make ------------
the contribution process easy and effective for everyone involved.
Thanks! The best way to propose a feature is to discuss your ideas on [Dibi forum](https://forum.dibiphp.com) before implementing them.
Please do not fix whitespace, format code, or make a purely cosmetic patch.
Thanks! :heart:

View File

@@ -1,3 +0,0 @@
<?php
trigger_error('Dibi was moved to /src/loader.php', E_USER_WARNING);
require __DIR__ . '/../src/loader.php';

View File

@@ -74,7 +74,7 @@ try {
'driver' => 'odbc', 'driver' => 'odbc',
'username' => 'root', 'username' => 'root',
'password' => '***', 'password' => '***',
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.__DIR__.'/data/sample.mdb', 'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq=' . __DIR__ . '/data/sample.mdb',
]); ]);
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (Dibi\Exception $e) {
@@ -89,7 +89,7 @@ try {
dibi::connect([ dibi::connect([
'driver' => 'postgre', 'driver' => 'postgre',
'string' => 'host=localhost port=5432 dbname=mary', 'string' => 'host=localhost port=5432 dbname=mary',
'persistent' => TRUE, 'persistent' => true,
]); ]);
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (Dibi\Exception $e) {
@@ -112,6 +112,22 @@ try {
echo "</p>\n"; 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 // connects to SQLSRV
echo '<p>Connecting to Microsoft SQL Server: '; echo '<p>Connecting to Microsoft SQL Server: ';
try { try {

View File

@@ -1,7 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Fetching Examples | dibi</h1>
<?php <?php
if (@!include __DIR__ . '/../vendor/autoload.php') { if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -10,6 +6,12 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable(); Tracy\Debugger::enable();
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Fetching Examples | dibi</h1>
<?php
dibi::connect([ dibi::connect([
'driver' => 'sqlite3', 'driver' => 'sqlite3',

View File

@@ -14,13 +14,13 @@ dibi::connect([
// some variables // some variables
$cond1 = TRUE; $cond1 = true;
$cond2 = FALSE; $cond2 = false;
$foo = -1; $foo = -1;
$bar = 2; $bar = 2;
// conditional variable // conditional variable
$name = $cond1 ? 'K%' : NULL; $name = $cond1 ? 'K%' : null;
// if & end // if & end
dibi::test(' dibi::test('

View File

@@ -33,7 +33,7 @@ dibi::test('
REPLACE INTO products', [ REPLACE INTO products', [
'title' => 'Super product', 'title' => 'Super product',
'price' => 318, 'price' => 318,
'active' => TRUE, 'active' => true,
]); ]);
// -> REPLACE INTO products ([title], [price], [active]) VALUES ('Super product', 318, 1) // -> REPLACE INTO products ([title], [price], [active]) VALUES ('Super product', 318, 1)
@@ -42,7 +42,7 @@ dibi::test('
$array = [ $array = [
'title' => 'Super Product', 'title' => 'Super Product',
'price' => 12, 'price' => 12,
'brand' => NULL, 'brand' => null,
'created' => new DateTime, 'created' => new DateTime,
]; ];
dibi::test('INSERT INTO products', $array, $array, $array); dibi::test('INSERT INTO products', $array, $array, $array);

View File

@@ -1,7 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Result Set Data Types | dibi</h1>
<?php <?php
use Dibi\Type; use Dibi\Type;
@@ -14,6 +10,12 @@ Tracy\Debugger::enable();
date_default_timezone_set('Europe/Prague'); date_default_timezone_set('Europe/Prague');
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Result Set Data Types | dibi</h1>
<?php
dibi::connect([ dibi::connect([
'driver' => 'sqlite3', 'driver' => 'sqlite3',

View File

@@ -1,9 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Tracy & SQL Exceptions | dibi</h1>
<p>Dibi can display and log exceptions via <a href="https://tracy.nette.org">Tracy</a>.</p>
<?php <?php
if (@!include __DIR__ . '/../vendor/autoload.php') { if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -19,7 +13,7 @@ $connection = dibi::connect([
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
'profiler' => [ 'profiler' => [
'run' => TRUE, 'run' => true,
], ],
]); ]);
@@ -31,3 +25,9 @@ $panel->register($connection);
// throws error because SQL is bad // throws error because SQL is bad
dibi::query('SELECT FROM customers WHERE customer_id < ?', 38); dibi::query('SELECT FROM customers WHERE customer_id < ?', 38);
?><!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Tracy & SQL Exceptions | dibi</h1>
<p>Dibi can display and log exceptions via <a href="https://tracy.nette.org">Tracy</a>.</p>

View File

@@ -1,11 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<style> html { background: url(data/arrow.png) no-repeat bottom right; height: 100%; } </style>
<h1>Tracy | dibi</h1>
<p>Dibi can log queries and dump variables to the <a href="https://tracy.nette.org">Tracy</a>.</p>
<?php <?php
if (@!include __DIR__ . '/../vendor/autoload.php') { if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -21,7 +13,7 @@ $connection = dibi::connect([
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
'profiler' => [ 'profiler' => [
'run' => TRUE, 'run' => true,
], ],
]); ]);
@@ -36,3 +28,13 @@ dibi::query('SELECT 123');
// result set will be dumped // result set will be dumped
Tracy\Debugger::barDump(dibi::fetchAll('SELECT * FROM customers WHERE customer_id < ?', 38), '[customers]'); Tracy\Debugger::barDump(dibi::fetchAll('SELECT * FROM customers WHERE customer_id < ?', 38), '[customers]');
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<style> html { background: url(data/arrow.png) no-repeat bottom right; height: 100%; } </style>
<h1>Tracy | dibi</h1>
<p>Dibi can log queries and dump variables to the <a href="https://tracy.nette.org">Tracy</a>.</p>

View File

@@ -1,7 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Extension Methods | dibi</h1>
<?php <?php
if (@!include __DIR__ . '/../vendor/autoload.php') { if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -10,6 +6,12 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable(); Tracy\Debugger::enable();
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Extension Methods | dibi</h1>
<?php
dibi::connect([ dibi::connect([
'driver' => 'sqlite3', 'driver' => 'sqlite3',

View File

@@ -19,7 +19,7 @@ $id = 10;
$record = [ $record = [
'title' => 'Super product', 'title' => 'Super product',
'price' => 318, 'price' => 318,
'active' => TRUE, 'active' => true,
]; ];
// SELECT ... // SELECT ...

View File

@@ -14,7 +14,7 @@ dibi::connect([
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
// enable query logging to this file // enable query logging to this file
'profiler' => [ 'profiler' => [
'run' => TRUE, 'run' => true,
'file' => 'data/log.sql', 'file' => 'data/log.sql',
], ],
]); ]);

View File

@@ -13,7 +13,7 @@ dibi::connect([
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
'profiler' => [ 'profiler' => [
'run' => TRUE, 'run' => true,
], ],
]); ]);

View File

@@ -38,6 +38,7 @@ function substFallBack($expr)
} }
} }
// define callback // define callback
dibi::getSubstitutes()->setCallback('substFallBack'); dibi::getSubstitutes()->setCallback('substFallBack');

View File

@@ -49,7 +49,7 @@ dibi::query('SELECT * FROM users WHERE id = ?', $id);
$arr = [ $arr = [
'name' => 'John', 'name' => 'John',
'is_admin' => TRUE, 'is_admin' => true,
]; ];
dibi::query('INSERT INTO users', $arr); dibi::query('INSERT INTO users', $arr);
// INSERT INTO users (`name`, `is_admin`) VALUES ('John', 1) // INSERT INTO users (`name`, `is_admin`) VALUES ('John', 1)

View File

@@ -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 Nette;
/**
* Dibi extension for Nette Framework 2.1. Creates 'connection' service.
*/
class DibiExtension21 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('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Nette\Panel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', ['@self'])
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', ['Dibi\Bridges\Nette\Panel::renderException']);
$connection->addSetup('$service->onEvent[] = ?', [[$panel, 'logEvent']]);
}
}
}

View File

@@ -7,6 +7,7 @@
namespace Dibi\Bridges\Nette; namespace Dibi\Bridges\Nette;
use Dibi;
use Nette; use Nette;
@@ -15,15 +16,28 @@ use Nette;
*/ */
class DibiExtension22 extends Nette\DI\CompilerExtension class DibiExtension22 extends Nette\DI\CompilerExtension
{ {
/** @var bool */
private $debugMode;
public function __construct($debugMode = null)
{
$this->debugMode = $debugMode;
}
public function loadConfiguration() public function loadConfiguration()
{ {
$container = $this->getContainerBuilder(); $container = $this->getContainerBuilder();
$config = $this->getConfig(); $config = $this->getConfig();
if ($this->debugMode === null) {
$this->debugMode = $container->parameters['debugMode'];
}
$useProfiler = isset($config['profiler']) $useProfiler = isset($config['profiler'])
? $config['profiler'] ? $config['profiler']
: class_exists('Tracy\Debugger') && $container->parameters['debugMode']; : class_exists('Tracy\Debugger') && $this->debugMode;
unset($config['profiler']); unset($config['profiler']);
@@ -37,7 +51,7 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
$connection = $container->addDefinition($this->prefix('connection')) $connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config]) ->setClass('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE); ->setAutowired(isset($config['autowired']) ? $config['autowired'] : true);
if (class_exists('Tracy\Debugger')) { if (class_exists('Tracy\Debugger')) {
$connection->addSetup( $connection->addSetup(
@@ -47,9 +61,11 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
} }
if ($useProfiler) { if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel')) $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]); $connection->addSetup([$panel, 'register'], [$connection]);
} }
} }
} }

View File

@@ -1,153 +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 Dibi\Event;
use Dibi\Helpers;
use Nette;
use Nette\Diagnostics\Debugger;
/**
* Dibi panel for Nette\Diagnostics.
*/
class Panel implements Nette\Diagnostics\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
public static $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
/** @var int */
public $filter;
/** @var array */
private $events = [];
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain;
}
public function register(Dibi\Connection $connection)
{
Debugger::getBar()->addPanel($this);
Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
/**
* After event notification.
* @return void
*/
public function logEvent(Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$this->events[] = $event;
}
/**
* Returns blue-screen custom tab.
* @return mixed
*/
public static function renderException($e)
{
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE),
];
}
}
/**
* Returns HTML code for custom tab. (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 === Event::SELECT) {
try {
$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);
} catch (Dibi\Exception $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">' . Helpers::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([$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&nbsp;ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . '
</table>
</div>';
}
}

View File

@@ -1,5 +1,5 @@
# This will create service named 'dibi.connection'. # This will create service named 'dibi.connection'.
# Requires Nette Framework 2.2 # Requires Nette Framework 2.2 or later
extensions: extensions:
dibi: Dibi\Bridges\Nette\DibiExtension22 dibi: Dibi\Bridges\Nette\DibiExtension22
@@ -9,4 +9,4 @@ dibi:
username: root username: root
password: *** password: ***
database: foo database: foo
lazy: TRUE lazy: true

View File

@@ -33,7 +33,7 @@ class Panel implements Tracy\IBarPanel
private $events = []; private $events = [];
public function __construct($explain = TRUE, $filter = NULL) public function __construct($explain = true, $filter = null)
{ {
$this->filter = $filter ? (int) $filter : Event::QUERY; $this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain; $this->explain = $explain;
@@ -63,14 +63,14 @@ class Panel implements Tracy\IBarPanel
/** /**
* Returns blue-screen custom tab. * Returns blue-screen custom tab.
* @return mixed * @return array|null
*/ */
public static function renderException($e) public static function renderException($e)
{ {
if ($e instanceof Dibi\Exception && $e->getSql()) { if ($e instanceof Dibi\Exception && $e->getSql()) {
return [ return [
'tab' => 'SQL', 'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE), 'panel' => Helpers::dump($e->getSql(), true),
]; ];
} }
} }
@@ -78,7 +78,7 @@ class Panel implements Tracy\IBarPanel
/** /**
* Returns HTML code for custom tab. (Tracy\IBarPanel) * Returns HTML code for custom tab. (Tracy\IBarPanel)
* @return mixed * @return string
*/ */
public function getTab() public function getTab()
{ {
@@ -88,42 +88,46 @@ class Panel implements Tracy\IBarPanel
$totalTime += $event->time; $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">' 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' . $count . ' queries'
. ($totalTime ? sprintf(' / %0.1f ms', $totalTime * 1000) : '') . ($totalTime ? ' / ' . number_format($totalTime * 1000, 1, '.', '') . 'ms' : '')
. '</span></span>'; . '</span></span>';
} }
/** /**
* Returns HTML code for custom panel. (Tracy\IBarPanel) * Returns HTML code for custom panel. (Tracy\IBarPanel)
* @return mixed * @return string|null
*/ */
public function getPanel() public function getPanel()
{ {
$totalTime = $s = NULL; if (!$this->events) {
$h = 'htmlSpecialChars'; return null;
}
$totalTime = $s = null;
foreach ($this->events as $event) { foreach ($this->events as $event) {
$totalTime += $event->time; $totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS() $connection = $event->connection;
$explain = null; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === Event::SELECT) { if ($this->explain && $event->type === Event::SELECT) {
try { try {
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime]; $backup = [$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$event->connection->onEvent = NULL; $connection->onEvent = null;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN'); $cmd = is_string($this->explain) ? $this->explain : ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE); $explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
} catch (Dibi\Exception $e) { } catch (Dibi\Exception $e) {
} }
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup; list($connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
} }
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000); $s .= '<tr><td>' . number_format($event->time * 1000, 3, '.', '');
if ($explain) { if ($explain) {
static $counter; static $counter;
$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 .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>";
} }
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE); $s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, true);
if ($explain) { if ($explain) {
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>"; $s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
} }
@@ -131,19 +135,20 @@ class Panel implements Tracy\IBarPanel
$s .= Tracy\Helpers::editorLink($event->source[0], $event->source[1]);//->class('tracy-DibiProfiler-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>"; $s .= "</td><td>{$event->count}</td></tr>";
} }
return empty($this->events) ? '' : return '<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
'<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important } #tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style> #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> <h1>Queries: ' . count($this->events)
. ($totalTime === null ? '' : ', time: ' . number_format($totalTime * 1000, 1, '.', '') . 'ms') . ', '
. htmlspecialchars($connection->getConfig('driver') . ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
. ($connection->getConfig('host') ? '@' . $connection->getConfig('host') : '')) . '</h1>
<div class="tracy-inner tracy-DibiProfiler"> <div class="tracy-inner tracy-DibiProfiler">
<table> <table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . ' <tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th></tr>' . $s . '
</table> </table>
</div>'; </div>';
} }
} }

View File

@@ -33,7 +33,7 @@ class Connection
private $translator; private $translator;
/** @var bool Is connected? */ /** @var bool Is connected? */
private $connected = FALSE; private $connected = false;
/** @var HashMap Substitutes for identifiers */ /** @var HashMap Substitutes for identifiers */
private $substitutes; private $substitutes;
@@ -41,19 +41,18 @@ class Connection
/** /**
* Connection options: (see driver-specific options too) * Connection options: (see driver-specific options too)
* - lazy (bool) => if TRUE, connection will be established only when required * - lazy (bool) => if true, connection will be established only when required
* - result (array) => result set options * - result (array) => result set options
* - formatDateTime => date-time format (if empty, DateTime objects will be returned) * - formatDateTime => date-time format (if empty, DateTime objects will be returned)
* - profiler (array or bool) * - profiler (array or bool)
* - run (bool) => enable profiler? * - run (bool) => enable profiler?
* - file => file to log * - file => file to log
* - substitutes (array) => map of driver specific substitutes (under development) * - substitutes (array) => map of driver specific substitutes (under development)
* @param mixed connection parameters * @param mixed connection parameters
* @param string connection name * @param string connection name
* @throws Exception * @throws Exception
*/ */
public function __construct($config, $name = NULL) public function __construct($config, $name = null)
{ {
if (is_string($config)) { if (is_string($config)) {
parse_str($config, $config); parse_str($config, $config);
@@ -97,7 +96,7 @@ class Connection
$this->config = $config; $this->config = $config;
// profiler // profiler
$profilerCfg = & $config['profiler']; $profilerCfg = &$config['profiler'];
if (is_scalar($profilerCfg)) { if (is_scalar($profilerCfg)) {
$profilerCfg = ['run' => (bool) $profilerCfg]; $profilerCfg = ['run' => (bool) $profilerCfg];
} }
@@ -111,11 +110,6 @@ class Connection
if (Loggers\FirePhpLogger::isAvailable()) { if (Loggers\FirePhpLogger::isAvailable()) {
$this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent']; $this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent'];
} }
if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('Dibi\Bridges\Nette\Panel')) {
$panel = new Bridges\Nette\Panel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this);
}
} }
$this->substitutes = new HashMap(function ($expr) { return ":$expr:"; }); $this->substitutes = new HashMap(function ($expr) { return ":$expr:"; });
@@ -148,10 +142,10 @@ class Connection
*/ */
final public function connect() final public function connect()
{ {
$event = $this->onEvent ? new Event($this, Event::CONNECT) : NULL; $event = $this->onEvent ? new Event($this, Event::CONNECT) : null;
try { try {
$this->driver->connect($this->config); $this->driver->connect($this->config);
$this->connected = TRUE; $this->connected = true;
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
} catch (Exception $e) { } catch (Exception $e) {
@@ -168,12 +162,12 @@ class Connection
final public function disconnect() final public function disconnect()
{ {
$this->driver->disconnect(); $this->driver->disconnect();
$this->connected = FALSE; $this->connected = false;
} }
/** /**
* Returns TRUE when connection was established. * Returns true when connection was established.
* @return bool * @return bool
*/ */
final public function isConnected() final public function isConnected()
@@ -189,9 +183,9 @@ class Connection
* @param mixed default value to use if key not found * @param mixed default value to use if key not found
* @return mixed * @return mixed
*/ */
final public function getConfig($key = NULL, $default = NULL) final public function getConfig($key = null, $default = null)
{ {
if ($key === NULL) { if ($key === null) {
return $this->config; return $this->config;
} elseif (isset($this->config[$key])) { } elseif (isset($this->config[$key])) {
@@ -204,7 +198,7 @@ class Connection
/** @deprecated */ /** @deprecated */
public static function alias(& $config, $key, $alias) public static function alias(&$config, $key, $alias)
{ {
trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED); trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED);
Helpers::alias($config, $key, $alias); Helpers::alias($config, $key, $alias);
@@ -225,7 +219,7 @@ class Connection
/** /**
* Generates (translates) and executes SQL query. * Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return Result|int result set object (if any) * @return Result|int result set or number of affected rows
* @throws Exception * @throws Exception
*/ */
final public function query($args) final public function query($args)
@@ -258,7 +252,7 @@ class Connection
$args = func_get_args(); $args = func_get_args();
try { try {
Helpers::dump($this->translateArgs($args)); Helpers::dump($this->translateArgs($args));
return TRUE; return true;
} catch (Exception $e) { } catch (Exception $e) {
if ($e->getSql()) { if ($e->getSql()) {
@@ -266,7 +260,7 @@ class Connection
} else { } else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>'); echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
} }
return FALSE; return false;
} }
} }
@@ -289,7 +283,7 @@ class Connection
* @param array * @param array
* @return string * @return string
*/ */
private function translateArgs($args) protected function translateArgs($args)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
if (!$this->translator) { if (!$this->translator) {
@@ -303,7 +297,7 @@ class Connection
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Result|int result set object (if any) * @return Result|int result set or number of affected rows
* @throws Exception * @throws Exception
*/ */
final public function nativeQuery($sql) final public function nativeQuery($sql)
@@ -311,7 +305,7 @@ class Connection
$this->connected || $this->connect(); $this->connected || $this->connect();
\dibi::$sql = $sql; \dibi::$sql = $sql;
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : NULL; $event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : null;
try { try {
$res = $this->driver->query($sql); $res = $this->driver->query($sql);
@@ -348,12 +342,11 @@ class Connection
/** /**
* Gets the number of affected rows. Alias for getAffectedRows(). * @deprecated
* @return int number of rows
* @throws Exception
*/ */
public function affectedRows() public function affectedRows()
{ {
trigger_error(__METHOD__ . '() is deprecated, use getAffectedRows()', E_USER_DEPRECATED);
return $this->getAffectedRows(); return $this->getAffectedRows();
} }
@@ -364,25 +357,23 @@ class Connection
* @return int * @return int
* @throws Exception * @throws Exception
*/ */
public function getInsertId($sequence = NULL) public function getInsertId($sequence = null)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$id = $this->driver->getInsertId($sequence); $id = $this->driver->getInsertId($sequence);
if ($id < 1) { if ($id < 1) {
throw new Exception('Cannot retrieve last generated ID.'); throw new Exception('Cannot retrieve last generated ID.');
} }
return (int) $id; return Helpers::intVal($id);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId(). * @deprecated
* @param string optional sequence name
* @return int
* @throws Exception
*/ */
public function insertId($sequence = NULL) public function insertId($sequence = null)
{ {
trigger_error(__METHOD__ . '() is deprecated, use getInsertId()', E_USER_DEPRECATED);
return $this->getInsertId($sequence); return $this->getInsertId($sequence);
} }
@@ -392,10 +383,10 @@ class Connection
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : NULL; $event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : null;
try { try {
$this->driver->begin($savepoint); $this->driver->begin($savepoint);
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
@@ -412,10 +403,10 @@ class Connection
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : NULL; $event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : null;
try { try {
$this->driver->commit($savepoint); $this->driver->commit($savepoint);
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
@@ -432,10 +423,10 @@ class Connection
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : NULL; $event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : null;
try { try {
$this->driver->rollback($savepoint); $this->driver->rollback($savepoint);
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
@@ -473,7 +464,7 @@ class Connection
/** /**
* @param string column name * @param mixed column name
* @return Fluent * @return Fluent
*/ */
public function select($args) public function select($args)
@@ -543,7 +534,7 @@ class Connection
*/ */
public function substitute($value) public function substitute($value)
{ {
return strpos($value, ':') === FALSE return strpos($value, ':') === false
? $value ? $value
: preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value); : preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
} }
@@ -555,7 +546,7 @@ class Connection
/** /**
* Executes SQL query and fetch result - shortcut for query() & fetch(). * Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return Row * @return Row|false
* @throws Exception * @throws Exception
*/ */
public function fetch($args) public function fetch($args)
@@ -581,7 +572,7 @@ class Connection
/** /**
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle(). * Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return string * @return mixed
* @throws Exception * @throws Exception
*/ */
public function fetchSingle($args) public function fetchSingle($args)
@@ -594,7 +585,7 @@ class Connection
/** /**
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs(). * Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return string * @return array
* @throws Exception * @throws Exception
*/ */
public function fetchPairs($args) public function fetchPairs($args)
@@ -619,11 +610,12 @@ class Connection
/** /**
* Import SQL dump from file. * Import SQL dump from file.
* @param string filename * @param string filename
* @param callable function (int $count, ?float $percent): void
* @return int count of sql commands * @return int count of sql commands
*/ */
public function loadFile($file) public function loadFile($file, callable $onProgress = null)
{ {
return Helpers::loadFromFile($this, $file); return Helpers::loadFromFile($this, $file, $onProgress);
} }
@@ -634,7 +626,7 @@ class Connection
public function getDatabaseInfo() public function getDatabaseInfo()
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL); return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : null);
} }
@@ -662,5 +654,4 @@ class Connection
call_user_func($handler, $arg); call_user_func($handler, $arg);
} }
} }
} }

View File

@@ -10,7 +10,6 @@ namespace Dibi;
/** /**
* Default implementation of IDataSource for dibi. * Default implementation of IDataSource for dibi.
*
*/ */
class DataSource implements IDataSource class DataSource implements IDataSource
{ {
@@ -40,10 +39,10 @@ class DataSource implements IDataSource
/** @var array */ /** @var array */
private $conds = []; private $conds = [];
/** @var int */ /** @var int|null */
private $offset; private $offset;
/** @var int */ /** @var int|null */
private $limit; private $limit;
@@ -53,7 +52,7 @@ class DataSource implements IDataSource
*/ */
public function __construct($sql, Connection $connection) public function __construct($sql, Connection $connection)
{ {
if (strpbrk($sql, " \t\r\n") === FALSE) { if (strpbrk($sql, " \t\r\n") === false) {
$this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name $this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name
} else { } else {
$this->sql = '(' . $sql . ') t'; // SQL command $this->sql = '(' . $sql . ') t'; // SQL command
@@ -68,14 +67,14 @@ class DataSource implements IDataSource
* @param string column alias * @param string column alias
* @return self * @return self
*/ */
public function select($col, $as = NULL) public function select($col, $as = null)
{ {
if (is_array($col)) { if (is_array($col)) {
$this->cols = $col; $this->cols = $col;
} else { } else {
$this->cols[$col] = $as; $this->cols[$col] = $as;
} }
$this->result = NULL; $this->result = null;
return $this; return $this;
} }
@@ -93,7 +92,7 @@ class DataSource implements IDataSource
} else { } else {
$this->conds[] = func_get_args(); $this->conds[] = func_get_args();
} }
$this->result = $this->count = NULL; $this->result = $this->count = null;
return $this; return $this;
} }
@@ -111,22 +110,22 @@ class DataSource implements IDataSource
} else { } else {
$this->sorting[$row] = $sorting; $this->sorting[$row] = $sorting;
} }
$this->result = NULL; $this->result = null;
return $this; return $this;
} }
/** /**
* Limits number of rows. * Limits number of rows.
* @param int limit * @param int|null limit
* @param int offset * @param int offset
* @return self * @return self
*/ */
public function applyLimit($limit, $offset = NULL) public function applyLimit($limit, $offset = null)
{ {
$this->limit = $limit; $this->limit = $limit;
$this->offset = $offset; $this->offset = $offset;
$this->result = $this->count = NULL; $this->result = $this->count = null;
return $this; return $this;
} }
@@ -150,7 +149,7 @@ class DataSource implements IDataSource
*/ */
public function getResult() public function getResult()
{ {
if ($this->result === NULL) { if ($this->result === null) {
$this->result = $this->connection->nativeQuery($this->__toString()); $this->result = $this->connection->nativeQuery($this->__toString());
} }
return $this->result; return $this->result;
@@ -168,7 +167,7 @@ class DataSource implements IDataSource
/** /**
* Generates, executes SQL query and fetches the single row. * Generates, executes SQL query and fetches the single row.
* @return Row|FALSE array on success, FALSE if no next record * @return Row|false
*/ */
public function fetch() public function fetch()
{ {
@@ -178,7 +177,7 @@ class DataSource implements IDataSource
/** /**
* Like fetch(), but returns only first field. * Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record * @return mixed value on success, false if no next record
*/ */
public function fetchSingle() public function fetchSingle()
{ {
@@ -213,7 +212,7 @@ class DataSource implements IDataSource
* @param string value * @param string value
* @return array * @return array
*/ */
public function fetchPairs($key = NULL, $value = NULL) public function fetchPairs($key = null, $value = null)
{ {
return $this->getResult()->fetchPairs($key, $value); return $this->getResult()->fetchPairs($key, $value);
} }
@@ -225,7 +224,7 @@ class DataSource implements IDataSource
*/ */
public function release() public function release()
{ {
$this->result = $this->count = $this->totalCount = NULL; $this->result = $this->count = $this->totalCount = null;
} }
@@ -262,12 +261,13 @@ class DataSource implements IDataSource
return $this->connection->translate(' return $this->connection->translate('
SELECT %n', (empty($this->cols) ? '*' : $this->cols), ' SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
FROM %SQL', $this->sql, ' FROM %SQL', $this->sql, '
%ex', $this->conds ? ['WHERE %and', $this->conds] : NULL, ' %ex', $this->conds ? ['WHERE %and', $this->conds] : null, '
%ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : NULL, ' %ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : null, '
%ofs %lmt', $this->offset, $this->limit %ofs %lmt', $this->offset, $this->limit
); );
} catch (\Exception $e) { } catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR); trigger_error($e->getMessage(), E_USER_ERROR);
return '';
} }
} }
@@ -281,11 +281,11 @@ FROM %SQL', $this->sql, '
*/ */
public function count() public function count()
{ {
if ($this->count === NULL) { if ($this->count === null) {
$this->count = $this->conds || $this->offset || $this->limit $this->count = $this->conds || $this->offset || $this->limit
? (int) $this->connection->nativeQuery( ? Helpers::intVal($this->connection->nativeQuery(
'SELECT COUNT(*) FROM (' . $this->__toString() . ') t' 'SELECT COUNT(*) FROM (' . $this->__toString() . ') t'
)->fetchSingle() )->fetchSingle())
: $this->getTotalCount(); : $this->getTotalCount();
} }
return $this->count; return $this->count;
@@ -298,12 +298,11 @@ FROM %SQL', $this->sql, '
*/ */
public function getTotalCount() public function getTotalCount()
{ {
if ($this->totalCount === NULL) { if ($this->totalCount === null) {
$this->totalCount = (int) $this->connection->nativeQuery( $this->totalCount = Helpers::intVal($this->connection->nativeQuery(
'SELECT COUNT(*) FROM ' . $this->sql 'SELECT COUNT(*) FROM ' . $this->sql
)->fetchSingle(); )->fetchSingle());
} }
return $this->totalCount; return $this->totalCount;
} }
} }

View File

@@ -15,12 +15,15 @@ class DateTime extends \DateTime
{ {
use Strict; use Strict;
public function __construct($time = 'now', \DateTimeZone $timezone = NULL) /**
* @param string|int
*/
public function __construct($time = 'now', \DateTimeZone $timezone = null)
{ {
if (is_numeric($time)) { if (is_numeric($time)) {
parent::__construct('@' . $time); parent::__construct('@' . $time);
$this->setTimeZone($timezone ? $timezone : new \DateTimeZone(date_default_timezone_get())); $this->setTimeZone($timezone ?: new \DateTimeZone(date_default_timezone_get()));
} elseif ($timezone === NULL) { } elseif ($timezone === null) {
parent::__construct($time); parent::__construct($time);
} else { } else {
parent::__construct($time, $timezone); parent::__construct($time, $timezone);
@@ -30,7 +33,7 @@ class DateTime extends \DateTime
public function modifyClone($modify = '') public function modifyClone($modify = '')
{ {
$dolly = clone($this); $dolly = clone $this;
return $modify ? $dolly->modify($modify) : $dolly; return $modify ? $dolly->modify($modify) : $dolly;
} }
@@ -52,7 +55,20 @@ class DateTime extends \DateTime
public function __toString() public function __toString()
{ {
return $this->format('Y-m-d H:i:s'); return $this->format('Y-m-d H:i:s.u');
} }
public function __wakeup()
{
if (isset($this->fix, $this->fix[1])) {
$this->__construct($this->fix[0], new \DateTimeZone($this->fix[1]));
unset($this->fix);
} elseif (isset($this->fix)) {
$this->__construct($this->fix[0]);
unset($this->fix);
} else {
parent::__wakeup();
}
}
} }

View File

@@ -28,20 +28,20 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
const ERROR_EXCEPTION_THROWN = -836; const ERROR_EXCEPTION_THROWN = -836;
/** @var resource Connection resource */ /** @var resource|null */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var resource Resultset resource */ /** @var resource|null */
private $transaction; private $transaction;
/** @var bool */ /** @var bool */
private $inTransaction = FALSE; private $inTransaction = false;
/** /**
@@ -60,7 +60,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
Dibi\Helpers::alias($config, 'database', 'db'); Dibi\Helpers::alias($config, 'database', 'db');
@@ -78,9 +78,9 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
]; ];
if (empty($config['persistent'])) { if (empty($config['persistent'])) {
$this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @ $this->connection = @ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else { } else {
$this->connection = ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @ $this->connection = @ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} }
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
@@ -96,14 +96,14 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function disconnect() public function disconnect()
{ {
ibase_close($this->connection); @ibase_close($this->connection); // @ - connection can be already disconnected
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException|Dibi\Exception * @throws Dibi\DriverException|Dibi\Exception
*/ */
public function query($sql) public function query($sql)
@@ -111,7 +111,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$resource = $this->inTransaction ? $this->transaction : $this->connection; $resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql); $res = ibase_query($resource, $sql);
if ($res === FALSE) { if ($res === false) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) { if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match); preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql); throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);
@@ -123,12 +123,13 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
} }
return null;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
@@ -139,7 +140,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string generator name * @param string generator name
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
@@ -153,13 +154,13 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
if ($savepoint !== NULL) { if ($savepoint !== null) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.'); throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
} }
$this->transaction = ibase_trans($this->getResource()); $this->transaction = ibase_trans($this->getResource());
$this->inTransaction = TRUE; $this->inTransaction = true;
} }
@@ -169,9 +170,9 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
if ($savepoint !== NULL) { if ($savepoint !== null) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.'); throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
} }
@@ -179,7 +180,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
throw new Dibi\DriverException('Unable to handle operation - failure when commiting transaction.'); throw new Dibi\DriverException('Unable to handle operation - failure when commiting transaction.');
} }
$this->inTransaction = FALSE; $this->inTransaction = false;
} }
@@ -189,9 +190,9 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
if ($savepoint !== NULL) { if ($savepoint !== null) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.'); throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
} }
@@ -199,7 +200,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
throw new Dibi\DriverException('Unable to handle operation - failure when rolbacking transaction.'); throw new Dibi\DriverException('Unable to handle operation - failure when rolbacking transaction.');
} }
$this->inTransaction = FALSE; $this->inTransaction = false;
} }
@@ -215,11 +216,11 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return resource * @return resource|null
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return is_resource($this->connection) ? $this->connection : null;
} }
@@ -260,24 +261,40 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
return "'" . str_replace("'", "''", $value) . "'"; return "'" . str_replace("'", "''", $value) . "'";
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
return $value; return '"' . str_replace('"', '""', $value) . '"';
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 1 : 0; return $value ? '1' : '0';
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -287,12 +304,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d H:i:s'"); return $value->format("'Y-m-d H:i:s.u'");
} }
@@ -329,13 +350,18 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit >= 0 && $offset > 0) { if ($limit > 0 || $offset > 0) {
// see http://scott.yang.id.au/2004/01/limit-in-select-statements-in-firebird/ // http://www.firebirdsql.org/refdocs/langrefupd20-select.html
$sql = 'SELECT FIRST ' . (int) $limit . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')'; $sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . Dibi\Helpers::intVal($limit) : '')
. ($offset > 0 ? ' SKIP ' . Dibi\Helpers::intVal($offset) : '')
. ' * FROM (' . $sql . ')';
} }
} }
@@ -365,12 +391,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) 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()) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) { if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
@@ -378,7 +404,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]); throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);
} else { } else {
throw new Dibi\DriverException($msg, ibase_errcode()); throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
} }
} }
@@ -389,7 +415,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function seek($row) public function seek($row)
@@ -405,18 +431,18 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function free() public function free()
{ {
ibase_free_result($this->resultSet); ibase_free_result($this->resultSet);
$this->resultSet = NULL; $this->resultSet = null;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return resource * @return resource|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return is_resource($this->resultSet) ? $this->resultSet : NULL; return is_resource($this->resultSet) ? $this->resultSet : null;
} }
@@ -457,7 +483,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE RDB\$SYSTEM_FLAG = 0;" WHERE RDB\$SYSTEM_FLAG = 0;"
); );
$tables = []; $tables = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$tables[] = [ $tables[] = [
'name' => $row[0], 'name' => $row[0],
'view' => $row[1] === 'TRUE', 'view' => $row[1] === 'TRUE',
@@ -506,7 +532,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
); );
$columns = []; $columns = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$key = $row['FIELD_NAME']; $key = $row['FIELD_NAME'];
$columns[$key] = [ $columns[$key] = [
'name' => $key, 'name' => $key,
@@ -515,7 +541,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
'size' => $row['FIELD_LENGTH'], 'size' => $row['FIELD_LENGTH'],
'nullable' => $row['NULLABLE'] === 'TRUE', 'nullable' => $row['NULLABLE'] === 'TRUE',
'default' => $row['DEFAULT_VALUE'], 'default' => $row['DEFAULT_VALUE'],
'autoincrement' => FALSE, 'autoincrement' => false,
]; ];
} }
return $columns; return $columns;
@@ -544,7 +570,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY s.RDB\$FIELD_POSITION" ORDER BY s.RDB\$FIELD_POSITION"
); );
$indexes = []; $indexes = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$key = $row['INDEX_NAME']; $key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key; $indexes[$key]['name'] = $key;
$indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1; $indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1;
@@ -574,7 +600,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY s.RDB\$FIELD_POSITION" ORDER BY s.RDB\$FIELD_POSITION"
); );
$keys = []; $keys = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$key = $row['INDEX_NAME']; $key = $row['INDEX_NAME'];
$keys[$key] = [ $keys[$key] = [
'name' => $key, 'name' => $key,
@@ -601,7 +627,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
AND RDB\$FOREIGN_KEY IS NULL;" AND RDB\$FOREIGN_KEY IS NULL;"
); );
$indices = []; $indices = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$indices[] = $row[0]; $indices[] = $row[0];
} }
return $indices; return $indices;
@@ -625,7 +651,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
);" );"
); );
$constraints = []; $constraints = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$constraints[] = $row[0]; $constraints[] = $row[0];
} }
return $constraints; return $constraints;
@@ -639,7 +665,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @param string * @param string
* @return array * @return array
*/ */
public function getTriggersMeta($table = NULL) public function getTriggersMeta($table = null)
{ {
$res = $this->query(" $res = $this->query("
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME, SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
@@ -665,10 +691,10 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
END AS TRIGGER_ENABLED END AS TRIGGER_ENABLED
FROM RDB\$TRIGGERS FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0" WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');") . ($table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
); );
$triggers = []; $triggers = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$triggers[$row['TRIGGER_NAME']] = [ $triggers[$row['TRIGGER_NAME']] = [
'name' => $row['TRIGGER_NAME'], 'name' => $row['TRIGGER_NAME'],
'table' => $row['TABLE_NAME'], 'table' => $row['TABLE_NAME'],
@@ -687,16 +713,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @param string * @param string
* @return array * @return array
*/ */
public function getTriggers($table = NULL) public function getTriggers($table = null)
{ {
$q = "SELECT TRIM(RDB\$TRIGGER_NAME) $q = 'SELECT TRIM(RDB$TRIGGER_NAME)
FROM RDB\$TRIGGERS FROM RDB$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0"; WHERE RDB$SYSTEM_FLAG = 0';
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')"; $q .= $table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q); $res = $this->query($q);
$triggers = []; $triggers = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$triggers[] = $row[0]; $triggers[] = $row[0];
} }
return $triggers; return $triggers;
@@ -743,7 +769,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;" ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
); );
$procedures = []; $procedures = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$key = $row['PROCEDURE_NAME']; $key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']); $io = trim($row['PARAMETER_TYPE']);
$num = $row['PARAMETER_NUMBER']; $num = $row['PARAMETER_NUMBER'];
@@ -762,12 +788,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getProcedures() public function getProcedures()
{ {
$res = $this->query(" $res = $this->query('
SELECT TRIM(RDB\$PROCEDURE_NAME) SELECT TRIM(RDB$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;" FROM RDB$PROCEDURES;'
); );
$procedures = []; $procedures = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$procedures[] = $row[0]; $procedures[] = $row[0];
} }
return $procedures; return $procedures;
@@ -780,13 +806,13 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getGenerators() public function getGenerators()
{ {
$res = $this->query(" $res = $this->query('
SELECT TRIM(RDB\$GENERATOR_NAME) SELECT TRIM(RDB$GENERATOR_NAME)
FROM RDB\$GENERATORS FROM RDB$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;" WHERE RDB$SYSTEM_FLAG = 0;'
); );
$generators = []; $generators = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$generators[] = $row[0]; $generators[] = $row[0];
} }
return $generators; return $generators;
@@ -799,16 +825,15 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getFunctions() public function getFunctions()
{ {
$res = $this->query(" $res = $this->query('
SELECT TRIM(RDB\$FUNCTION_NAME) SELECT TRIM(RDB$FUNCTION_NAME)
FROM RDB\$FUNCTIONS FROM RDB$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;" WHERE RDB$SYSTEM_FLAG = 0;'
); );
$functions = []; $functions = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$functions[] = $row[0]; $functions[] = $row[0];
} }
return $functions; return $functions;
} }
} }

View File

@@ -0,0 +1,409 @@
<?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|null */
private $connection;
/** @var resource|null */
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);
}
return null;
}
/**
* 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 resource|null
*/
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 string value
* @return string encoded value
*/
public function escapeText($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')';
}
/**
* 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.
* @param string
* @param int|null
* @param int|null
* @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 ' . Dibi\Helpers::intVal($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 bool 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 resource|null
*/
public function getResultResource()
{
$this->autoFree = false;
return is_resource($this->resultSet) ? $this->resultSet : null;
}
}

View File

@@ -0,0 +1,215 @@
<?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 = Dibi\Helpers::intVal($row[0]);
} else {
$count = false;
}
} else {
$count = Dibi\Helpers::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);
}
}

View File

@@ -33,17 +33,19 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
use Dibi\Strict; use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045; const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062; const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265; const ERROR_DATA_TRUNCATED = 1265;
/** @var resource Connection resource */ /** @var resource|null */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var bool Is buffered (seekable and countable)? */ /** @var bool Is buffered (seekable and countable)? */
private $buffered; private $buffered;
@@ -65,7 +67,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
if (isset($config['resource'])) { if (isset($config['resource'])) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
@@ -88,7 +90,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
if (!isset($config['socket'])) { if (!isset($config['socket'])) {
$config['socket'] = ini_get('mysql.default_socket'); $config['socket'] = ini_get('mysql.default_socket');
} }
$config['host'] = NULL; $config['host'] = null;
} }
} }
@@ -99,7 +101,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} }
if (empty($config['persistent'])) { if (empty($config['persistent'])) {
$this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['flags']); // intentionally @ $this->connection = @mysql_connect($host, $config['username'], $config['password'], true, $config['flags']); // intentionally @
} else { } else {
$this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['flags']); // intentionally @ $this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['flags']); // intentionally @
} }
@@ -139,14 +141,14 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function disconnect() public function disconnect()
{ {
mysql_close($this->connection); @mysql_close($this->connection); // @ - connection can be already disconnected
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
@@ -187,7 +189,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
@@ -197,7 +199,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
@@ -211,7 +213,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION'); $this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
} }
@@ -223,7 +225,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT'); $this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
} }
@@ -235,7 +237,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK'); $this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
} }
@@ -243,11 +245,11 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return resource|null
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return is_resource($this->connection) ? $this->connection : null;
} }
@@ -279,7 +281,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -291,6 +293,10 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
@@ -300,6 +306,10 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
@@ -307,12 +317,20 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 1 : 0; return $value ? 1 : 0;
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -322,12 +340,16 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d H:i:s'"); return $value->format("'Y-m-d H:i:s.u'");
} }
@@ -365,17 +387,20 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) { } elseif ($limit !== null || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html // see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit) $sql .= ' LIMIT ' . ($limit === null ? '18446744073709551615' : Dibi\Helpers::intVal($limit))
. ($offset ? ' OFFSET ' . (int) $offset : ''); . ($offset ? ' OFFSET ' . Dibi\Helpers::intVal($offset) : '');
} }
} }
@@ -408,7 +433,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
@@ -420,7 +445,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function seek($row) public function seek($row)
@@ -440,7 +465,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function free() public function free()
{ {
mysql_free_result($this->resultSet); mysql_free_result($this->resultSet);
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -459,7 +484,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
'table' => $row['table'], 'table' => $row['table'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'], 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => strtoupper($row['type']), 'nativetype' => strtoupper($row['type']),
'type' => $row['type'] === 'time' ? Dibi\Type::TIME_INTERVAL : NULL, 'type' => $row['type'] === 'time' ? Dibi\Type::TIME_INTERVAL : null,
'vendor' => $row, 'vendor' => $row,
]; ];
} }
@@ -469,12 +494,11 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return resource|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return is_resource($this->resultSet) ? $this->resultSet : NULL; return is_resource($this->resultSet) ? $this->resultSet : null;
} }
} }

View File

@@ -36,7 +36,7 @@ class MySqlReflector implements Dibi\Reflector
{ {
$res = $this->driver->query('SHOW FULL TABLES'); $res = $this->driver->query('SHOW FULL TABLES');
$tables = []; $tables = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$tables[] = [ $tables[] = [
'name' => $row[0], 'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW', 'view' => isset($row[1]) && $row[1] === 'VIEW',
@@ -55,13 +55,13 @@ class MySqlReflector implements Dibi\Reflector
{ {
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}"); $res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}");
$columns = []; $columns = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$type = explode('(', $row['Type']); $type = explode('(', $row['Type']);
$columns[] = [ $columns[] = [
'name' => $row['Field'], 'name' => $row['Field'],
'table' => $table, 'table' => $table,
'nativetype' => strtoupper($type[0]), 'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL, 'size' => isset($type[1]) ? (int) $type[1] : null,
'unsigned' => (bool) strstr($row['Type'], 'unsigned'), 'unsigned' => (bool) strstr($row['Type'], 'unsigned'),
'nullable' => $row['Null'] === 'YES', 'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'], 'default' => $row['Default'],
@@ -82,7 +82,7 @@ class MySqlReflector implements Dibi\Reflector
{ {
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}"); $res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}");
$indexes = []; $indexes = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$indexes[$row['Key_name']]['name'] = $row['Key_name']; $indexes[$row['Key_name']]['name'] = $row['Key_name'];
$indexes[$row['Key_name']]['unique'] = !$row['Non_unique']; $indexes[$row['Key_name']]['unique'] = !$row['Non_unique'];
$indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY'; $indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
@@ -100,7 +100,7 @@ class MySqlReflector implements Dibi\Reflector
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
$data = $this->driver->query("SELECT `ENGINE` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = {$this->driver->escapeText($table)}")->fetch(TRUE); $data = $this->driver->query("SELECT `ENGINE` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = {$this->driver->escapeText($table)}")->fetch(true);
if ($data['ENGINE'] !== 'InnoDB') { if ($data['ENGINE'] !== 'InnoDB') {
throw new Dibi\NotSupportedException("Foreign keys are not supported in {$data['ENGINE']} tables."); throw new Dibi\NotSupportedException("Foreign keys are not supported in {$data['ENGINE']} tables.");
} }
@@ -119,7 +119,7 @@ class MySqlReflector implements Dibi\Reflector
"); ");
$foreignKeys = []; $foreignKeys = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$keyName = $row['CONSTRAINT_NAME']; $keyName = $row['CONSTRAINT_NAME'];
$foreignKeys[$keyName]['name'] = $keyName; $foreignKeys[$keyName]['name'] = $keyName;
@@ -131,5 +131,4 @@ class MySqlReflector implements Dibi\Reflector
} }
return array_values($foreignKeys); return array_values($foreignKeys);
} }
} }

View File

@@ -34,17 +34,19 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
use Dibi\Strict; use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045; const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062; const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265; const ERROR_DATA_TRUNCATED = 1265;
/** @var mysqli Connection resource */ /** @var \mysqli|null */
private $connection; private $connection;
/** @var mysqli_result Resultset resource */ /** @var \mysqli_result|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var bool Is buffered (seekable and countable)? */ /** @var bool Is buffered (seekable and countable)? */
private $buffered; private $buffered;
@@ -66,7 +68,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
mysqli_report(MYSQLI_REPORT_OFF); mysqli_report(MYSQLI_REPORT_OFF);
if (isset($config['resource'])) { if (isset($config['resource'])) {
@@ -80,7 +82,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
'username' => ini_get('mysqli.default_user'), 'username' => ini_get('mysqli.default_user'),
'password' => ini_get('mysqli.default_pw'), 'password' => ini_get('mysqli.default_pw'),
'socket' => (string) ini_get('mysqli.default_socket'), 'socket' => (string) ini_get('mysqli.default_socket'),
'port' => NULL, 'port' => null,
]; ];
if (!isset($config['host'])) { if (!isset($config['host'])) {
$host = ini_get('mysqli.default_host'); $host = ini_get('mysqli.default_host');
@@ -88,23 +90,18 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
$config['host'] = $host; $config['host'] = $host;
$config['port'] = ini_get('mysqli.default_port'); $config['port'] = ini_get('mysqli.default_port');
} else { } else {
$config['host'] = NULL; $config['host'] = null;
$config['port'] = NULL; $config['port'] = null;
} }
} }
$foo = & $config['flags']; $foo = &$config['flags'];
$foo = & $config['database']; $foo = &$config['database'];
$this->connection = mysqli_init(); $this->connection = mysqli_init();
if (isset($config['options'])) { if (isset($config['options'])) {
if (is_scalar($config['options'])) { foreach ($config['options'] as $key => $value) {
$config['flags'] = $config['options']; // back compatibility mysqli_options($this->connection, $key, $value);
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 @ @mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @
@@ -138,14 +135,14 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function disconnect() public function disconnect()
{ {
mysqli_close($this->connection); @mysqli_close($this->connection); // @ - connection can be already disconnected
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
@@ -153,11 +150,12 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
$res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @ $res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if ($code = mysqli_errno($this->connection)) { if ($code = mysqli_errno($this->connection)) {
throw self::createException(mysqli_error($this->connection), $code, $sql); throw static::createException(mysqli_error($this->connection), $code, $sql);
} elseif (is_object($res)) { } elseif (is_object($res)) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
} }
return null;
} }
@@ -166,13 +164,13 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public static function createException($message, $code, $sql) public static function createException($message, $code, $sql)
{ {
if (in_array($code, [1216, 1217, 1451, 1452, 1701], TRUE)) { if (in_array($code, [1216, 1217, 1451, 1452, 1701], true)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql); return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1062, 1557, 1569, 1586], TRUE)) { } elseif (in_array($code, [1062, 1557, 1569, 1586], true)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql); return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], TRUE)) { } elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], true)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql); return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} else { } else {
@@ -202,17 +200,17 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
return mysqli_affected_rows($this->connection) === -1 ? FALSE : mysqli_affected_rows($this->connection); 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. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
@@ -226,7 +224,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION'); $this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
} }
@@ -238,7 +236,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT'); $this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
} }
@@ -250,7 +248,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK'); $this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
} }
@@ -258,11 +256,11 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mysqli * @return \mysqli
*/ */
public function getResource() public function getResource()
{ {
return @$this->connection->thread_id ? $this->connection : NULL; return @$this->connection->thread_id ? $this->connection : null;
} }
@@ -278,7 +276,6 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Result set driver factory. * Result set driver factory.
* @param mysqli_result
* @return Dibi\ResultDriver * @return Dibi\ResultDriver
*/ */
public function createResultDriver(\mysqli_result $resource) public function createResultDriver(\mysqli_result $resource)
@@ -294,7 +291,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -303,24 +300,40 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'"; return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
return '`' . str_replace('`', '``', $value) . '`'; return '`' . str_replace('`', '``', $value) . '`';
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 1 : 0; return $value ? '1' : '0';
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -330,12 +343,16 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d H:i:s'"); return $value->format("'Y-m-d H:i:s.u'");
} }
@@ -373,17 +390,20 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) { } elseif ($limit !== null || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html // see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit) $sql .= ' LIMIT ' . ($limit === null ? '18446744073709551615' : Dibi\Helpers::intVal($limit))
. ($offset ? ' OFFSET ' . (int) $offset : ''); . ($offset ? ' OFFSET ' . Dibi\Helpers::intVal($offset) : '');
} }
} }
@@ -416,7 +436,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
@@ -428,7 +448,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function seek($row) public function seek($row)
@@ -447,7 +467,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function free() public function free()
{ {
mysqli_free_result($this->resultSet); mysqli_free_result($this->resultSet);
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -458,8 +478,8 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns() public function getResultColumns()
{ {
static $types; static $types;
if ($types === NULL) { if ($types === null) {
$consts = get_defined_constants(TRUE); $consts = get_defined_constants(true);
$types = []; $types = [];
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) { foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) {
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) { if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
@@ -478,7 +498,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
'table' => $row['orgtable'], 'table' => $row['orgtable'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'], 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => isset($types[$row['type']]) ? $types[$row['type']] : $row['type'], 'nativetype' => isset($types[$row['type']]) ? $types[$row['type']] : $row['type'],
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : NULL, 'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : null,
'vendor' => $row, 'vendor' => $row,
]; ];
} }
@@ -488,12 +508,11 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mysqli_result * @return \mysqli_result|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return $this->resultSet; return $this->resultSet;
} }
} }

View File

@@ -25,17 +25,17 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{ {
use Dibi\Strict; use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource|null */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var int|FALSE Affected rows */ /** @var int|false Affected rows */
private $affectedRows = FALSE; private $affectedRows = false;
/** @var int Cursor */ /** @var int Cursor */
private $row = 0; private $row = 0;
@@ -57,7 +57,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
if (isset($config['resource'])) { if (isset($config['resource'])) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
@@ -88,34 +88,35 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function disconnect() public function disconnect()
{ {
odbc_close($this->connection); @odbc_close($this->connection); // @ - connection can be already disconnected
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
{ {
$this->affectedRows = FALSE; $this->affectedRows = false;
$res = @odbc_exec($this->connection, $sql); // intentionally @ $res = @odbc_exec($this->connection, $sql); // intentionally @
if ($res === FALSE) { if ($res === false) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql); throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res); $this->affectedRows = odbc_num_rows($res);
return $this->createResultDriver($res); return odbc_num_fields($res) ? $this->createResultDriver($res) : null;
} }
return null;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
@@ -125,7 +126,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
@@ -139,9 +140,9 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
if (!odbc_autocommit($this->connection, FALSE)) { if (!odbc_autocommit($this->connection, false)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
} }
} }
@@ -153,12 +154,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
if (!odbc_commit($this->connection)) { if (!odbc_commit($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
} }
odbc_autocommit($this->connection, TRUE); odbc_autocommit($this->connection, true);
} }
@@ -168,12 +169,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
if (!odbc_rollback($this->connection)) { if (!odbc_rollback($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
} }
odbc_autocommit($this->connection, TRUE); odbc_autocommit($this->connection, true);
} }
@@ -189,11 +190,11 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return resource|null
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return is_resource($this->connection) ? $this->connection : null;
} }
@@ -225,7 +226,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -234,39 +235,59 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
return "'" . str_replace("'", "''", $value) . "'"; return "'" . str_replace("'", "''", $value) . "'";
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']'; return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 1 : 0; return $value ? '1' : '0';
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format("#m/d/Y#"); return $value->format('#m/d/Y#');
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format("#m/d/Y H:i:s#"); return $value->format('#m/d/Y H:i:s.u#');
} }
@@ -304,9 +325,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($offset) { if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.'); throw new Dibi\NotSupportedException('Offset is not supported by this database.');
@@ -314,8 +338,8 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} elseif ($limit < 0) { } elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) { } elseif ($limit !== null) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; $sql = 'SELECT TOP ' . Dibi\Helpers::intVal($limit) . ' * FROM (' . $sql . ') t';
} }
} }
@@ -346,7 +370,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
@@ -356,7 +380,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} else { } else {
$set = $this->resultSet; $set = $this->resultSet;
if (!odbc_fetch_row($set, ++$this->row)) { if (!odbc_fetch_row($set, ++$this->row)) {
return FALSE; return false;
} }
$count = odbc_num_fields($set); $count = odbc_num_fields($set);
$cols = []; $cols = [];
@@ -371,12 +395,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
*/ */
public function seek($row) public function seek($row)
{ {
$this->row = $row; $this->row = $row;
return TRUE; return true;
} }
@@ -387,7 +411,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function free() public function free()
{ {
odbc_free_result($this->resultSet); odbc_free_result($this->resultSet);
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -402,7 +426,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
for ($i = 1; $i <= $count; $i++) { for ($i = 1; $i <= $count; $i++) {
$columns[] = [ $columns[] = [
'name' => odbc_field_name($this->resultSet, $i), 'name' => odbc_field_name($this->resultSet, $i),
'table' => NULL, 'table' => null,
'fullname' => odbc_field_name($this->resultSet, $i), 'fullname' => odbc_field_name($this->resultSet, $i),
'nativetype' => odbc_field_type($this->resultSet, $i), 'nativetype' => odbc_field_type($this->resultSet, $i),
]; ];
@@ -413,12 +437,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return resource|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return is_resource($this->resultSet) ? $this->resultSet : NULL; return is_resource($this->resultSet) ? $this->resultSet : null;
} }
@@ -492,5 +516,4 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{ {
throw new Dibi\NotImplementedException; throw new Dibi\NotImplementedException;
} }
} }

View File

@@ -19,8 +19,7 @@ use Dibi;
* - password (or pass) * - password (or pass)
* - charset => character encoding to set * - charset => character encoding to set
* - schema => alters session schema * - schema => alters session schema
* - formatDate => how to format date in SQL (@see date) * - nativeDate => use native date format (defaults to false)
* - formatDateTime => how to format datetime in SQL (@see date)
* - resource (resource) => existing connection resource * - resource (resource) => existing connection resource
* - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect * - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
@@ -29,20 +28,25 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{ {
use Dibi\Strict; use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource|null */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var bool */ /** @var bool */
private $autocommit = TRUE; private $autocommit = true;
/** @var string Date and datetime format */ /** @var string Date and datetime format */
private $fmtDate, $fmtDateTime; private $fmtDate;
private $fmtDateTime;
/** @var int|false Number of affected rows */
private $affectedRows = false;
/** /**
@@ -61,11 +65,17 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
$foo = & $config['charset']; $foo = &$config['charset'];
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U'; if (isset($config['formatDate']) || isset($config['formatDateTime'])) {
trigger_error('OracleDriver: options formatDate and formatDateTime are deprecated.', E_USER_DEPRECATED);
}
if (empty($config['nativeDate'])) {
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
}
if (isset($config['resource'])) { if (isset($config['resource'])) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
@@ -92,32 +102,35 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function disconnect() public function disconnect()
{ {
oci_close($this->connection); @oci_close($this->connection); // @ - connection can be already disconnected
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
{ {
$this->affectedRows = false;
$res = oci_parse($this->connection, $sql); $res = oci_parse($this->connection, $sql);
if ($res) { if ($res) {
@oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); @oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($res); $err = oci_error($res);
if ($err) { if ($err) {
throw self::createException($err['message'], $err['code'], $sql); throw static::createException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
return $this->createResultDriver($res); $this->affectedRows = oci_num_rows($res);
return oci_num_fields($res) ? $this->createResultDriver($res) : null;
} }
} else { } else {
$err = oci_error($this->connection); $err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code'], $sql); throw new Dibi\DriverException($err['message'], $err['code'], $sql);
} }
return null;
} }
@@ -126,13 +139,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public static function createException($message, $code, $sql) public static function createException($message, $code, $sql)
{ {
if (in_array($code, [1, 2299, 38911], TRUE)) { if (in_array($code, [1, 2299, 38911], true)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql); return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1400], TRUE)) { } elseif (in_array($code, [1400], true)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql); return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [2266, 2291, 2292], TRUE)) { } elseif (in_array($code, [2266, 2291, 2292], true)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql); return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else { } else {
@@ -143,22 +156,22 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
throw new Dibi\NotImplementedException; return $this->affectedRows;
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
$row = $this->query("SELECT $sequence.CURRVAL AS ID FROM DUAL")->fetch(TRUE); $row = $this->query("SELECT $sequence.CURRVAL AS ID FROM DUAL")->fetch(true);
return isset($row['ID']) ? (int) $row['ID'] : FALSE; return isset($row['ID']) ? Dibi\Helpers::intVal($row['ID']) : false;
} }
@@ -167,9 +180,9 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
$this->autocommit = FALSE; $this->autocommit = false;
} }
@@ -179,13 +192,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
if (!oci_commit($this->connection)) { if (!oci_commit($this->connection)) {
$err = oci_error($this->connection); $err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']); throw new Dibi\DriverException($err['message'], $err['code']);
} }
$this->autocommit = TRUE; $this->autocommit = true;
} }
@@ -195,23 +208,23 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
if (!oci_rollback($this->connection)) { if (!oci_rollback($this->connection)) {
$err = oci_error($this->connection); $err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']); throw new Dibi\DriverException($err['message'], $err['code']);
} }
$this->autocommit = TRUE; $this->autocommit = true;
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return resource|null
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return is_resource($this->connection) ? $this->connection : null;
} }
@@ -243,7 +256,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -252,12 +265,20 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
@@ -265,27 +286,43 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 1 : 0; return $value ? '1' : '0';
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format($this->fmtDate); return $this->fmtDate
? $value->format($this->fmtDate)
: "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')";
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format($this->fmtDateTime); return $this->fmtDateTime
? $value->format($this->fmtDateTime)
: "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')";
} }
@@ -324,9 +361,12 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
@@ -334,11 +374,11 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} elseif ($offset) { } elseif ($offset) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ($limit !== null ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset; . ') WHERE "__rnum" > ' . $offset;
} elseif ($limit !== NULL) { } elseif ($limit !== null) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit; $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . Dibi\Helpers::intVal($limit);
} }
} }
@@ -368,7 +408,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
@@ -380,7 +420,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
*/ */
public function seek($row) public function seek($row)
{ {
@@ -395,7 +435,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function free() public function free()
{ {
oci_free_statement($this->resultSet); oci_free_statement($this->resultSet);
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -411,7 +451,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$type = oci_field_type($this->resultSet, $i); $type = oci_field_type($this->resultSet, $i);
$columns[] = [ $columns[] = [
'name' => oci_field_name($this->resultSet, $i), 'name' => oci_field_name($this->resultSet, $i),
'table' => NULL, 'table' => null,
'fullname' => oci_field_name($this->resultSet, $i), 'fullname' => oci_field_name($this->resultSet, $i),
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type, 'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
]; ];
@@ -422,12 +462,12 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return resource|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return is_resource($this->resultSet) ? $this->resultSet : NULL; return is_resource($this->resultSet) ? $this->resultSet : null;
} }
@@ -442,7 +482,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{ {
$res = $this->query('SELECT * FROM cat'); $res = $this->query('SELECT * FROM cat');
$tables = []; $tables = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = [ $tables[] = [
'name' => $row[0], 'name' => $row[0],
@@ -461,7 +501,20 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getColumns($table) public function getColumns($table)
{ {
throw new Dibi\NotImplementedException; $res = $this->query('SELECT * FROM "ALL_TAB_COLUMNS" WHERE "TABLE_NAME" = ' . $this->escapeText($table));
$columns = [];
while ($row = $res->fetch(true)) {
$columns[] = [
'table' => $row['TABLE_NAME'],
'name' => $row['COLUMN_NAME'],
'nativetype' => $row['DATA_TYPE'],
'size' => isset($row['DATA_LENGTH']) ? $row['DATA_LENGTH'] : null,
'nullable' => $row['NULLABLE'] === 'Y',
'default' => $row['DATA_DEFAULT'],
'vendor' => $row,
];
}
return $columns;
} }
@@ -485,5 +538,4 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{ {
throw new Dibi\NotImplementedException; throw new Dibi\NotImplementedException;
} }
} }

View File

@@ -30,17 +30,17 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** @var PDO Connection resource */ /** @var PDO Connection resource */
private $connection; private $connection;
/** @var \PDOStatement Resultset resource */ /** @var \PDOStatement|null Resultset resource */
private $resultSet; private $resultSet;
/** @var int|FALSE Affected rows */ /** @var int|false Affected rows */
private $affectedRows = FALSE; private $affectedRows = false;
/** @var string */ /** @var string */
private $driverName; private $driverName;
/** @var string */ /** @var string */
private $serverVersion; private $serverVersion = '';
/** /**
@@ -59,10 +59,10 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
$foo = & $config['dsn']; $foo = &$config['dsn'];
$foo = & $config['options']; $foo = &$config['options'];
Dibi\Helpers::alias($config, 'resource', 'pdo'); Dibi\Helpers::alias($config, 'resource', 'pdo');
if ($config['resource'] instanceof PDO) { if ($config['resource'] instanceof PDO) {
@@ -92,14 +92,14 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function disconnect() public function disconnect()
{ {
$this->connection = NULL; $this->connection = null;
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
@@ -107,12 +107,12 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
// must detect if SQL returns result set or num of affected rows // must detect if SQL returns result set or num of affected rows
$cmd = strtoupper(substr(ltrim($sql), 0, 6)); $cmd = strtoupper(substr(ltrim($sql), 0, 6));
static $list = ['UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1]; static $list = ['UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1];
$this->affectedRows = FALSE; $this->affectedRows = false;
if (isset($list[$cmd])) { if (isset($list[$cmd])) {
$this->affectedRows = $this->connection->exec($sql); $this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows !== FALSE) { if ($this->affectedRows !== false) {
return; return null;
} }
} else { } else {
$res = $this->connection->query($sql); $res = $this->connection->query($sql);
@@ -144,7 +144,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
@@ -154,7 +154,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
@@ -168,7 +168,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
if (!$this->connection->beginTransaction()) { if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
@@ -183,7 +183,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
if (!$this->connection->commit()) { if (!$this->connection->commit()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
@@ -198,7 +198,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
if (!$this->connection->rollBack()) { if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
@@ -254,7 +254,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -267,6 +267,10 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
if ($this->driverName === 'odbc') { if ($this->driverName === 'odbc') {
@@ -277,6 +281,10 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
switch ($this->driverName) { switch ($this->driverName) {
@@ -291,6 +299,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
return '[' . strtr($value, '[]', ' ') . ']'; return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc': case 'odbc':
case 'mssql':
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']'; return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
case 'dblib': case 'dblib':
@@ -303,16 +312,24 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
if ($this->driverName === 'pgsql') { if ($this->driverName === 'pgsql') {
return $value ? 'TRUE' : 'FALSE'; return $value ? 'TRUE' : 'FALSE';
} else { } else {
return $value ? 1 : 0; return $value ? '1' : '0';
} }
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -322,12 +339,24 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format($this->driverName === 'odbc' ? "#m/d/Y H:i:s#" : "'Y-m-d H:i:s'"); switch ($this->driverName) {
case 'odbc':
return $value->format('#m/d/Y H:i:s.u#');
case 'mssql':
case 'sqlsrv':
return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')';
default:
return $value->format("'Y-m-d H:i:s.u'");
}
} }
@@ -360,6 +389,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'"; return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
case 'odbc': case 'odbc':
case 'mssql':
case 'dblib': case 'dblib':
case 'sqlsrv': case 'sqlsrv':
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
@@ -392,9 +422,12 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
@@ -402,26 +435,26 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
switch ($this->driverName) { switch ($this->driverName) {
case 'mysql': case 'mysql':
if ($limit !== NULL || $offset) { if ($limit !== null || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html // see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit) $sql .= ' LIMIT ' . ($limit === null ? '18446744073709551615' : Dibi\Helpers::intVal($limit))
. ($offset ? ' OFFSET ' . (int) $offset : ''); . ($offset ? ' OFFSET ' . Dibi\Helpers::intVal($offset) : '');
} }
break; break;
case 'pgsql': case 'pgsql':
if ($limit !== NULL) { if ($limit !== null) {
$sql .= ' LIMIT ' . (int) $limit; $sql .= ' LIMIT ' . Dibi\Helpers::intVal($limit);
} }
if ($offset) { if ($offset) {
$sql .= ' OFFSET ' . (int) $offset; $sql .= ' OFFSET ' . Dibi\Helpers::intVal($offset);
} }
break; break;
case 'sqlite': case 'sqlite':
if ($limit !== NULL || $offset) { if ($limit !== null || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit) $sql .= ' LIMIT ' . ($limit === null ? '-1' : Dibi\Helpers::intVal($limit))
. ($offset ? ' OFFSET ' . (int) $offset : ''); . ($offset ? ' OFFSET ' . Dibi\Helpers::intVal($offset) : '');
} }
break; break;
@@ -429,36 +462,36 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
if ($offset) { if ($offset) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ($limit !== null ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset; . ') WHERE "__rnum" > ' . $offset;
} elseif ($limit !== NULL) { } elseif ($limit !== null) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit; $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . Dibi\Helpers::intVal($limit);
} }
break; break;
case 'mssql':
case 'sqlsrv': case 'sqlsrv':
case 'dblib': case 'dblib':
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012 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
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx if ($limit !== null) {
$sql .= ' OFFSET ' . (int) $offset . ' ROWS ' $sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY'; } elseif ($offset) {
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
} }
break; break;
} }
// intentionally break omitted // break omitted
case 'odbc': case 'odbc':
if ($offset) { if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.'); throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) { } elseif ($limit !== null) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; $sql = 'SELECT TOP ' . Dibi\Helpers::intVal($limit) . ' * FROM (' . $sql . ') t';
break; break;
} }
// intentionally break omitted // break omitted
default: default:
throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.'); throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.');
} }
@@ -480,7 +513,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
@@ -492,7 +525,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
*/ */
public function seek($row) public function seek($row)
{ {
@@ -506,7 +539,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function free() public function free()
{ {
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -521,11 +554,11 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
$columns = []; $columns = [];
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @ $row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) { if ($row === false) {
throw new Dibi\NotSupportedException('Driver does not support meta data.'); throw new Dibi\NotSupportedException('Driver does not support meta data.');
} }
$row = $row + [ $row = $row + [
'table' => NULL, 'table' => null,
'native_type' => 'VAR_STRING', 'native_type' => 'VAR_STRING',
]; ];
@@ -533,7 +566,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
'name' => $row['name'], 'name' => $row['name'],
'table' => $row['table'], 'table' => $row['table'],
'nativetype' => $row['native_type'], 'nativetype' => $row['native_type'],
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TIME_INTERVAL : NULL, 'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TIME_INTERVAL : null,
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'], 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'vendor' => $row, 'vendor' => $row,
]; ];
@@ -544,11 +577,10 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return \PDOStatement * @return \PDOStatement|null
*/ */
public function getResultResource() public function getResultResource()
{ {
return $this->resultSet; return $this->resultSet;
} }
} }

View File

@@ -26,17 +26,17 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{ {
use Dibi\Strict; use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource|null */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var int|FALSE Affected rows */ /** @var int|false Affected rows */
private $affectedRows = FALSE; private $affectedRows = false;
/** /**
@@ -55,9 +55,9 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
$error = NULL; $error = null;
if (isset($config['resource'])) { if (isset($config['resource'])) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
@@ -78,7 +78,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
} }
set_error_handler(function($severity, $message) use (& $error) { set_error_handler(function ($severity, $message) use (&$error) {
$error = $message; $error = $message;
}); });
if (empty($config['persistent'])) { if (empty($config['persistent'])) {
@@ -96,11 +96,11 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE); pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE);
if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) { if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) {
throw self::createException(pg_last_error($this->connection)); throw static::createException(pg_last_error($this->connection));
} }
if (isset($config['schema'])) { if (isset($config['schema'])) {
$this->query('SET search_path TO "' . $config['schema'] . '"'); $this->query('SET search_path TO "' . implode('", "', (array) $config['schema']) . '"');
} }
} }
@@ -111,7 +111,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function disconnect() public function disconnect()
{ {
pg_close($this->connection); @pg_close($this->connection); // @ - connection can be already disconnected
} }
@@ -128,16 +128,16 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
{ {
$this->affectedRows = FALSE; $this->affectedRows = false;
$res = @pg_query($this->connection, $sql); // intentionally @ $res = @pg_query($this->connection, $sql); // intentionally @
if ($res === FALSE) { if ($res === false) {
throw self::createException(pg_last_error($this->connection), NULL, $sql); throw static::createException(pg_last_error($this->connection), null, $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res); $this->affectedRows = pg_affected_rows($res);
@@ -145,20 +145,21 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
return $this->createResultDriver($res); return $this->createResultDriver($res);
} }
} }
return null;
} }
/** /**
* @return Dibi\DriverException * @return Dibi\DriverException
*/ */
public static function createException($message, $code = NULL, $sql = NULL) public static function createException($message, $code = null, $sql = null)
{ {
if ($code === NULL && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) { if ($code === null && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) {
$code = $m[1]; $code = $m[1];
$message = substr($message, strlen($m[0])); $message = substr($message, strlen($m[0]));
} }
if ($code === '0A000' && strpos($message, 'truncate') !== FALSE) { if ($code === '0A000' && strpos($message, 'truncate') !== false) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql); return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23502') { } elseif ($code === '23502') {
@@ -178,7 +179,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
@@ -188,11 +189,11 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
if ($sequence === NULL) { if ($sequence === null) {
// PostgreSQL 8.1 is needed // PostgreSQL 8.1 is needed
$res = $this->query('SELECT LASTVAL()'); $res = $this->query('SELECT LASTVAL()');
} else { } else {
@@ -200,11 +201,11 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
if (!$res) { if (!$res) {
return FALSE; return false;
} }
$row = $res->fetch(FALSE); $row = $res->fetch(false);
return is_array($row) ? $row[0] : FALSE; return is_array($row) ? $row[0] : false;
} }
@@ -214,7 +215,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION'); $this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
} }
@@ -226,7 +227,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT'); $this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
} }
@@ -238,7 +239,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK'); $this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
} }
@@ -250,17 +251,17 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function inTransaction() public function inTransaction()
{ {
return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], TRUE); return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], true);
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return resource|null
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return is_resource($this->connection) ? $this->connection : null;
} }
@@ -292,7 +293,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -304,6 +305,10 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
@@ -313,6 +318,10 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
@@ -320,12 +329,20 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 'TRUE' : 'FALSE'; return $value ? 'TRUE' : 'FALSE';
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -335,12 +352,16 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d H:i:s'"); return $value->format("'Y-m-d H:i:s.u'");
} }
@@ -380,18 +401,21 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
} }
if ($limit !== NULL) { if ($limit !== null) {
$sql .= ' LIMIT ' . (int) $limit; $sql .= ' LIMIT ' . Dibi\Helpers::intVal($limit);
} }
if ($offset) { if ($offset) {
$sql .= ' OFFSET ' . (int) $offset; $sql .= ' OFFSET ' . Dibi\Helpers::intVal($offset);
} }
} }
@@ -421,19 +445,19 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
return pg_fetch_array($this->resultSet, NULL, $assoc ? PGSQL_ASSOC : PGSQL_NUM); return pg_fetch_array($this->resultSet, null, $assoc ? PGSQL_ASSOC : PGSQL_NUM);
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
*/ */
public function seek($row) public function seek($row)
{ {
@@ -448,7 +472,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function free() public function free()
{ {
pg_free_result($this->resultSet); pg_free_result($this->resultSet);
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -475,12 +499,12 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return resource|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return is_resource($this->resultSet) ? $this->resultSet : NULL; return is_resource($this->resultSet) ? $this->resultSet : null;
} }
@@ -523,7 +547,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = $this->query($query); $res = $this->query($query);
$tables = pg_fetch_all($res->resultSet); $tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : []; return $tables ?: [];
} }
@@ -577,13 +601,13 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
$columns = []; $columns = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']); $size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$columns[] = [ $columns[] = [
'name' => $row['column_name'], 'name' => $row['column_name'],
'table' => $table, 'table' => $table,
'nativetype' => strtoupper($row['udt_name']), 'nativetype' => strtoupper($row['udt_name']),
'size' => $size > 0 ? $size : NULL, 'size' => $size > 0 ? $size : null,
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't', 'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't',
'default' => $row['column_default'], 'default' => $row['column_default'],
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval', 'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
@@ -617,7 +641,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
"); ");
$columns = []; $columns = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$columns[$row['ordinal_position']] = $row['column_name']; $columns[$row['ordinal_position']] = $row['column_name'];
} }
@@ -630,7 +654,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
"); ");
$indexes = []; $indexes = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$indexes[$row['relname']]['name'] = $row['relname']; $indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't'; $indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
$indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't'; $indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't';
@@ -691,7 +715,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
"); ");
$fKeys = $references = []; $fKeys = $references = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
if (!isset($fKeys[$row['name']])) { if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = [ $fKeys[$row['name']] = [
'name' => $row['name'], 'name' => $row['name'],
@@ -716,5 +740,4 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
return $fKeys; return $fKeys;
} }
} }

View File

@@ -27,20 +27,24 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
{ {
use Dibi\Strict; use Dibi\Strict;
/** @var SQLite3 Connection resource */ /** @var SQLite3|null */
private $connection; private $connection;
/** @var \SQLite3Result Resultset resource */ /** @var \SQLite3Result|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var string Date and datetime format */ /** @var string Date and datetime format */
private $fmtDate, $fmtDateTime; private $fmtDate;
private $fmtDateTime;
/** @var string character encoding */ /** @var string character encoding */
private $dbcharset, $charset; private $dbcharset;
private $charset;
/** /**
@@ -59,7 +63,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
Dibi\Helpers::alias($config, 'database', 'file'); Dibi\Helpers::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U'; $this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
@@ -78,7 +82,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
$this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset']; $this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
$this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset']; $this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
if (strcasecmp($this->dbcharset, $this->charset) === 0) { if (strcasecmp($this->dbcharset, $this->charset) === 0) {
$this->dbcharset = $this->charset = NULL; $this->dbcharset = $this->charset = null;
} }
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced) // enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
@@ -102,22 +106,23 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
{ {
if ($this->dbcharset !== NULL) { if ($this->dbcharset !== null) {
$sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql); $sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql);
} }
$res = @$this->connection->query($sql); // intentionally @ $res = @$this->connection->query($sql); // intentionally @
if ($code = $this->connection->lastErrorCode()) { if ($code = $this->connection->lastErrorCode()) {
throw self::createException($this->connection->lastErrorMsg(), $code, $sql); throw static::createException($this->connection->lastErrorMsg(), $code, $sql);
} elseif ($res instanceof \SQLite3Result) { } elseif ($res instanceof \SQLite3Result && $res->numColumns()) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
} }
return null;
} }
@@ -129,19 +134,19 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
if ($code !== 19) { if ($code !== 19) {
return new Dibi\DriverException($message, $code, $sql); return new Dibi\DriverException($message, $code, $sql);
} elseif (strpos($message, 'must be unique') !== FALSE } elseif (strpos($message, 'must be unique') !== false
|| strpos($message, 'is not unique') !== FALSE || strpos($message, 'is not unique') !== false
|| strpos($message, 'UNIQUE constraint failed') !== FALSE || strpos($message, 'UNIQUE constraint failed') !== false
) { ) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql); return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'may not be NULL') !== FALSE } elseif (strpos($message, 'may not be NULL') !== false
|| strpos($message, 'NOT NULL constraint failed') !== FALSE || strpos($message, 'NOT NULL constraint failed') !== false
) { ) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql); return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'foreign key constraint failed') !== FALSE } elseif (strpos($message, 'foreign key constraint failed') !== false
|| strpos($message, 'FOREIGN KEY constraint failed') !== FALSE || strpos($message, 'FOREIGN KEY constraint failed') !== false
) { ) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql); return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
@@ -153,7 +158,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
@@ -163,7 +168,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
@@ -177,7 +182,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN'); $this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
} }
@@ -189,7 +194,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT'); $this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
} }
@@ -201,7 +206,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK'); $this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
} }
@@ -209,7 +214,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return SQLite3
*/ */
public function getResource() public function getResource()
{ {
@@ -245,7 +250,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -254,24 +259,40 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
return "X'" . bin2hex((string) $value) . "'"; return "X'" . bin2hex((string) $value) . "'";
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
return '[' . strtr($value, '[]', ' ') . ']'; return '[' . strtr($value, '[]', ' ') . ']';
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 1 : 0; return $value ? '1' : '0';
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -281,6 +302,10 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -324,16 +349,19 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) { } elseif ($limit !== null || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit) $sql .= ' LIMIT ' . ($limit === null ? '-1' : Dibi\Helpers::intVal($limit))
. ($offset ? ' OFFSET ' . (int) $offset : ''); . ($offset ? ' OFFSET ' . Dibi\Helpers::intVal($offset) : '');
} }
} }
@@ -364,17 +392,17 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM); $row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT'; $charset = $this->charset === null ? null : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) { if ($row && ($assoc || $charset)) {
$tmp = []; $tmp = [];
foreach ($row as $k => $v) { foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) { if ($charset !== null && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v); $v = iconv($this->dbcharset, $charset, $v);
} }
$tmp[str_replace(['[', ']'], '', $k)] = $v; $tmp[str_replace(['[', ']'], '', $k)] = $v;
@@ -388,7 +416,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
* @throws Dibi\NotSupportedException * @throws Dibi\NotSupportedException
*/ */
public function seek($row) public function seek($row)
@@ -404,7 +432,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
public function free() public function free()
{ {
$this->resultSet->finalize(); $this->resultSet->finalize();
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -420,7 +448,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$columns[] = [ $columns[] = [
'name' => $this->resultSet->columnName($i), 'name' => $this->resultSet->columnName($i),
'table' => NULL, 'table' => null,
'fullname' => $this->resultSet->columnName($i), 'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)], 'nativetype' => $types[$this->resultSet->columnType($i)],
]; ];
@@ -431,11 +459,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return \SQLite3Result|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return $this->resultSet; return $this->resultSet;
} }
@@ -468,5 +496,4 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
{ {
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs); $this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
} }
} }

View File

@@ -41,7 +41,7 @@ class SqliteReflector implements Dibi\Reflector
ORDER BY name ORDER BY name
"); ");
$tables = []; $tables = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$tables[] = $row; $tables[] = $row;
} }
return $tables; return $tables;
@@ -57,7 +57,7 @@ class SqliteReflector implements Dibi\Reflector
{ {
$res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})"); $res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})");
$columns = []; $columns = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$column = $row['name']; $column = $row['name'];
$type = explode('(', $row['type']); $type = explode('(', $row['type']);
$columns[] = [ $columns[] = [
@@ -65,7 +65,7 @@ class SqliteReflector implements Dibi\Reflector
'table' => $table, 'table' => $table,
'fullname' => "$table.$column", 'fullname' => "$table.$column",
'nativetype' => strtoupper($type[0]), 'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL, 'size' => isset($type[1]) ? (int) $type[1] : null,
'nullable' => $row['notnull'] == '0', 'nullable' => $row['notnull'] == '0',
'default' => $row['dflt_value'], 'default' => $row['dflt_value'],
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER', 'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
@@ -85,14 +85,14 @@ class SqliteReflector implements Dibi\Reflector
{ {
$res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})"); $res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})");
$indexes = []; $indexes = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$indexes[$row['name']]['name'] = $row['name']; $indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique']; $indexes[$row['name']]['unique'] = (bool) $row['unique'];
} }
foreach ($indexes as $index => $values) { foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})"); $res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})");
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name']; $indexes[$index]['columns'][$row['seqno']] = $row['name'];
} }
} }
@@ -100,7 +100,7 @@ class SqliteReflector implements Dibi\Reflector
$columns = $this->getColumns($table); $columns = $this->getColumns($table);
foreach ($indexes as $index => $values) { foreach ($indexes as $index => $values) {
$column = $indexes[$index]['columns'][0]; $column = $indexes[$index]['columns'][0];
$primary = FALSE; $primary = false;
foreach ($columns as $info) { foreach ($columns as $info) {
if ($column == $info['name']) { if ($column == $info['name']) {
$primary = $info['vendor']['pk']; $primary = $info['vendor']['pk'];
@@ -114,8 +114,8 @@ class SqliteReflector implements Dibi\Reflector
if ($column['vendor']['pk']) { if ($column['vendor']['pk']) {
$indexes[] = [ $indexes[] = [
'name' => 'ROWID', 'name' => 'ROWID',
'unique' => TRUE, 'unique' => true,
'primary' => TRUE, 'primary' => true,
'columns' => [$column['name']], 'columns' => [$column['name']],
]; ];
break; break;
@@ -136,7 +136,7 @@ class SqliteReflector implements Dibi\Reflector
{ {
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})"); $res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})");
$keys = []; $keys = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$keys[$row['id']]['name'] = $row['id']; // foreign key name $keys[$row['id']]['name'] = $row['id']; // foreign key name
$keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns $keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns
$keys[$row['id']]['table'] = $row['table']; // referenced table $keys[$row['id']]['table'] = $row['table']; // referenced table
@@ -144,11 +144,10 @@ class SqliteReflector implements Dibi\Reflector
$keys[$row['id']]['onDelete'] = $row['on_delete']; $keys[$row['id']]['onDelete'] = $row['on_delete'];
$keys[$row['id']]['onUpdate'] = $row['on_update']; $keys[$row['id']]['onUpdate'] = $row['on_update'];
if ($keys[$row['id']]['foreign'][0] == NULL) { if ($keys[$row['id']]['foreign'][0] == null) {
$keys[$row['id']]['foreign'] = NULL; $keys[$row['id']]['foreign'] = null;
} }
} }
return array_values($keys); return array_values($keys);
} }
} }

View File

@@ -8,7 +8,6 @@
namespace Dibi\Drivers; namespace Dibi\Drivers;
use Dibi; use Dibi;
use Dibi\Connection;
use Dibi\Helpers; use Dibi\Helpers;
@@ -29,20 +28,20 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
{ {
use Dibi\Strict; use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource|null */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource|null */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
private $autoFree = TRUE; private $autoFree = true;
/** @var int|FALSE Affected rows */ /** @var int|false Affected rows */
private $affectedRows = FALSE; private $affectedRows = false;
/** @var string */ /** @var string */
private $version; private $version = '';
/** /**
@@ -61,7 +60,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
Helpers::alias($config, 'options|UID', 'username'); Helpers::alias($config, 'options|UID', 'username');
Helpers::alias($config, 'options|PWD', 'password'); Helpers::alias($config, 'options|PWD', 'password');
@@ -72,12 +71,17 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
$this->connection = $config['resource']; $this->connection = $config['resource'];
} else { } else {
// Default values $options = $config['options'];
if (!isset($config['options']['CharacterSet'])) {
$config['options']['CharacterSet'] = 'UTF-8';
}
$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)) { if (!is_resource($this->connection)) {
@@ -94,35 +98,36 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function disconnect() public function disconnect()
{ {
sqlsrv_close($this->connection); @sqlsrv_close($this->connection); // @ - connection can be already disconnected
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return Dibi\ResultDriver|null
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function query($sql) public function query($sql)
{ {
$this->affectedRows = FALSE; $this->affectedRows = false;
$res = sqlsrv_query($this->connection, $sql); $res = sqlsrv_query($this->connection, $sql);
if ($res === FALSE) { if ($res === false) {
$info = sqlsrv_errors(); $info = sqlsrv_errors();
throw new Dibi\DriverException($info[0]['message'], $info[0]['code'], $sql); throw new Dibi\DriverException($info[0]['message'], $info[0]['code'], $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
$this->affectedRows = sqlsrv_rows_affected($res); $this->affectedRows = sqlsrv_rows_affected($res);
return $this->createResultDriver($res); return sqlsrv_num_fields($res) ? $this->createResultDriver($res) : null;
} }
return null;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
@@ -132,16 +137,16 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
$res = sqlsrv_query($this->connection, 'SELECT @@IDENTITY'); $res = sqlsrv_query($this->connection, 'SELECT SCOPE_IDENTITY()');
if (is_resource($res)) { if (is_resource($res)) {
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC); $row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
return $row[0]; return $row[0];
} }
return FALSE; return false;
} }
@@ -151,7 +156,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = null)
{ {
sqlsrv_begin_transaction($this->connection); sqlsrv_begin_transaction($this->connection);
} }
@@ -163,7 +168,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = null)
{ {
sqlsrv_commit($this->connection); sqlsrv_commit($this->connection);
} }
@@ -175,7 +180,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* @return void * @return void
* @throws Dibi\DriverException * @throws Dibi\DriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = null)
{ {
sqlsrv_rollback($this->connection); sqlsrv_rollback($this->connection);
} }
@@ -183,11 +188,11 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return resource|null
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return is_resource($this->connection) ? $this->connection : null;
} }
@@ -219,7 +224,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
public function escapeText($value) public function escapeText($value)
@@ -228,12 +233,20 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param string
* @return string
*/
public function escapeBinary($value) public function escapeBinary($value)
{ {
return "'" . str_replace("'", "''", $value) . "'"; return "'" . str_replace("'", "''", $value) . "'";
} }
/**
* @param string
* @return string
*/
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx // @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
@@ -241,12 +254,20 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param bool
* @return string
*/
public function escapeBool($value) public function escapeBool($value)
{ {
return $value ? 1 : 0; return $value ? '1' : '0';
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value) public function escapeDate($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
@@ -256,12 +277,16 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value) public function escapeDateTime($value)
{ {
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value); $value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d H:i:s'"); return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')';
} }
@@ -299,25 +324,30 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif (version_compare($this->version, 11, '<')) { // 11 == SQL Server 2012 } elseif (version_compare($this->version, '11', '<')) { // 11 == SQL Server 2012
if ($offset) { if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.'); throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) { } 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 // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql .= ' OFFSET ' . (int) $offset . ' ROWS ' $sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY'; } 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);
} }
} }
@@ -347,7 +377,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
*/ */
public function fetch($assoc) public function fetch($assoc)
@@ -359,7 +389,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
*/ */
public function seek($row) public function seek($row)
{ {
@@ -374,7 +404,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
public function free() public function free()
{ {
sqlsrv_free_stmt($this->resultSet); sqlsrv_free_stmt($this->resultSet);
$this->resultSet = NULL; $this->resultSet = null;
} }
@@ -398,12 +428,11 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return resource|null
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; $this->autoFree = false;
return is_resource($this->resultSet) ? $this->resultSet : NULL; return is_resource($this->resultSet) ? $this->resultSet : null;
} }
} }

View File

@@ -34,9 +34,9 @@ class SqlsrvReflector implements Dibi\Reflector
*/ */
public function getTables() 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 = []; $tables = [];
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(false)) {
$tables[] = [ $tables[] = [
'name' => $row[0], 'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW', 'view' => isset($row[1]) && $row[1] === 'VIEW',
@@ -61,7 +61,7 @@ class SqlsrvReflector implements Dibi\Reflector
"); ");
$autoIncrements = []; $autoIncrements = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT']; $autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT'];
} }
@@ -81,13 +81,13 @@ class SqlsrvReflector implements Dibi\Reflector
WHERE C.TABLE_NAME = {$this->driver->escapeText($table)} WHERE C.TABLE_NAME = {$this->driver->escapeText($table)}
"); ");
$columns = []; $columns = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$columns[] = [ $columns[] = [
'name' => $row['COLUMN_NAME'], 'name' => $row['COLUMN_NAME'],
'table' => $table, 'table' => $table,
'nativetype' => strtoupper($row['DATA_TYPE']), 'nativetype' => strtoupper($row['DATA_TYPE']),
'size' => $row['CHARACTER_MAXIMUM_LENGTH'], 'size' => $row['CHARACTER_MAXIMUM_LENGTH'],
'unsigned' => TRUE, 'unsigned' => true,
'nullable' => $row['IS_NULLABLE'] === 'YES', 'nullable' => $row['IS_NULLABLE'] === 'YES',
'default' => $row['COLUMN_DEFAULT'], 'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => $autoIncrements[$row['COLUMN_NAME']], 'autoincrement' => $autoIncrements[$row['COLUMN_NAME']],
@@ -105,19 +105,19 @@ class SqlsrvReflector implements Dibi\Reflector
*/ */
public function getIndexes($table) 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 = []; $keyUsages = [];
while ($row = $keyUsagesRes->fetch(TRUE)) { 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 = []; $indexes = [];
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(true)) {
$indexes[$row['CONSTRAINT_NAME']]['name'] = $row['CONSTRAINT_NAME']; $indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['CONSTRAINT_NAME']]['unique'] = $row['CONSTRAINT_TYPE'] === 'UNIQUE'; $indexes[$row['name']]['unique'] = $row['is_unique'] === 1;
$indexes[$row['CONSTRAINT_NAME']]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY'; $indexes[$row['name']]['primary'] = $row['is_primary_key'] === 1;
$indexes[$row['CONSTRAINT_NAME']]['columns'] = isset($keyUsages[$row['CONSTRAINT_NAME']]) ? $keyUsages[$row['CONSTRAINT_NAME']] : []; $indexes[$row['name']]['columns'] = isset($keyUsages[$row['name']]) ? $keyUsages[$row['name']] : [];
} }
return array_values($indexes); return array_values($indexes);
} }
@@ -132,5 +132,4 @@ class SqlsrvReflector implements Dibi\Reflector
{ {
throw new Dibi\NotImplementedException; throw new Dibi\NotImplementedException;
} }
} }

View File

@@ -37,7 +37,7 @@ class Event
/** @var string */ /** @var string */
public $sql; public $sql;
/** @var Result|DriverException|NULL */ /** @var Result|DriverException|null */
public $result; public $result;
/** @var float */ /** @var float */
@@ -50,12 +50,12 @@ class Event
public $source; public $source;
public function __construct(Connection $connection, $type, $sql = NULL) public function __construct(Connection $connection, $type, $sql = null)
{ {
$this->connection = $connection; $this->connection = $connection;
$this->type = $type; $this->type = $type;
$this->sql = trim($sql); $this->sql = trim($sql);
$this->time = -microtime(TRUE); $this->time = -microtime(true);
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) { if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
static $types = [ static $types = [
@@ -67,32 +67,31 @@ class Event
$rc = new \ReflectionClass('dibi'); $rc = new \ReflectionClass('dibi');
$dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR; $dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR;
foreach (debug_backtrace(FALSE) as $row) { foreach (debug_backtrace(false) as $row) {
if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) { if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) {
$this->source = [$row['file'], (int) $row['line']]; $this->source = [$row['file'], (int) $row['line']];
break; break;
} }
} }
\dibi::$elapsedTime = FALSE; \dibi::$elapsedTime = false;
\dibi::$numOfQueries++; \dibi::$numOfQueries++;
\dibi::$sql = $sql; \dibi::$sql = $sql;
} }
public function done($result = NULL) public function done($result = null)
{ {
$this->result = $result; $this->result = $result;
try { try {
$this->count = $result instanceof Result ? count($result) : NULL; $this->count = $result instanceof Result ? count($result) : null;
} catch (Exception $e) { } catch (Exception $e) {
$this->count = NULL; $this->count = null;
} }
$this->time += microtime(TRUE); $this->time += microtime(true);
\dibi::$elapsedTime = $this->time; \dibi::$elapsedTime = $this->time;
\dibi::$totalTime += $this->time; \dibi::$totalTime += $this->time;
return $this; return $this;
} }
} }

32
src/Dibi/Expression.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* SQL expression.
*/
class Expression
{
use Strict;
/** @var array */
private $values;
public function __construct()
{
$this->values = func_get_args();
}
public function getValues()
{
return $this->values;
}
}

View File

@@ -25,6 +25,7 @@ namespace Dibi;
* @method Fluent innerJoin(...$table) * @method Fluent innerJoin(...$table)
* @method Fluent rightJoin(...$table) * @method Fluent rightJoin(...$table)
* @method Fluent outerJoin(...$table) * @method Fluent outerJoin(...$table)
* @method Fluent as(...$field)
* @method Fluent on(...$cond) * @method Fluent on(...$cond)
* @method Fluent using(...$cond) * @method Fluent using(...$cond)
*/ */
@@ -32,12 +33,12 @@ class Fluent implements IDataSource
{ {
use Strict; use Strict;
const REMOVE = FALSE; const REMOVE = false;
/** @var array */ /** @var array */
public static $masks = [ public static $masks = [
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY', 'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'], 'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET', ],
'UPDATE' => ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'], 'UPDATE' => ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'],
'INSERT' => ['INSERT', 'INTO', 'VALUES', 'SELECT'], 'INSERT' => ['INSERT', 'INTO', 'VALUES', 'SELECT'],
'DELETE' => ['DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'], 'DELETE' => ['DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'],
@@ -64,11 +65,11 @@ class Fluent implements IDataSource
'GROUP BY' => ',', 'GROUP BY' => ',',
'HAVING' => 'AND', 'HAVING' => 'AND',
'ORDER BY' => ',', 'ORDER BY' => ',',
'LIMIT' => FALSE, 'LIMIT' => false,
'OFFSET' => FALSE, 'OFFSET' => false,
'SET' => ',', 'SET' => ',',
'VALUES' => ',', 'VALUES' => ',',
'INTO' => FALSE, 'INTO' => false,
]; ];
/** @var array clauses */ /** @var array clauses */
@@ -108,7 +109,7 @@ class Fluent implements IDataSource
{ {
$this->connection = $connection; $this->connection = $connection;
if (self::$normalizer === NULL) { if (self::$normalizer === null) {
self::$normalizer = new HashMap([__CLASS__, '_formatClause']); self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
} }
} }
@@ -125,33 +126,33 @@ class Fluent implements IDataSource
$clause = self::$normalizer->$clause; $clause = self::$normalizer->$clause;
// lazy initialization // lazy initialization
if ($this->command === NULL) { if ($this->command === null) {
if (isset(self::$masks[$clause])) { if (isset(self::$masks[$clause])) {
$this->clauses = array_fill_keys(self::$masks[$clause], NULL); $this->clauses = array_fill_keys(self::$masks[$clause], null);
} }
$this->cursor = & $this->clauses[$clause]; $this->cursor = &$this->clauses[$clause];
$this->cursor = []; $this->cursor = [];
$this->command = $clause; $this->command = $clause;
} }
// auto-switch to a clause // auto-switch to a clause
if (isset(self::$clauseSwitches[$clause])) { if (isset(self::$clauseSwitches[$clause])) {
$this->cursor = & $this->clauses[self::$clauseSwitches[$clause]]; $this->cursor = &$this->clauses[self::$clauseSwitches[$clause]];
} }
if (array_key_exists($clause, $this->clauses)) { if (array_key_exists($clause, $this->clauses)) {
// append to clause // append to clause
$this->cursor = & $this->clauses[$clause]; $this->cursor = &$this->clauses[$clause];
// TODO: really delete? // TODO: really delete?
if ($args === [self::REMOVE]) { if ($args === [self::REMOVE]) {
$this->cursor = NULL; $this->cursor = null;
return $this; return $this;
} }
if (isset(self::$separators[$clause])) { if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause]; $sep = self::$separators[$clause];
if ($sep === FALSE) { // means: replace if ($sep === false) { // means: replace
$this->cursor = []; $this->cursor = [];
} elseif (!empty($this->cursor)) { } elseif (!empty($this->cursor)) {
@@ -168,19 +169,19 @@ class Fluent implements IDataSource
$this->cursor[] = $clause; $this->cursor[] = $clause;
} }
if ($this->cursor === NULL) { if ($this->cursor === null) {
$this->cursor = []; $this->cursor = [];
} }
// special types or argument // special types or argument
if (count($args) === 1) { if (count($args) === 1) {
$arg = $args[0]; $arg = $args[0];
// TODO: really ignore TRUE? // TODO: really ignore true?
if ($arg === TRUE) { // flag if ($arg === true) { // flag
return $this; return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier } 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 } elseif (is_array($arg) || ($arg instanceof \Traversable && !$arg instanceof self)) { // any array
if (isset(self::$modifiers[$clause])) { if (isset(self::$modifiers[$clause])) {
@@ -189,7 +190,7 @@ class Fluent implements IDataSource
} elseif (is_string(key($arg))) { // associative array } elseif (is_string(key($arg))) { // associative array
$args = ['%a', $arg]; $args = ['%a', $arg];
} }
} // case $arg === FALSE is handled above } // case $arg === false is handled above
} }
foreach ($args as $arg) { foreach ($args as $arg) {
@@ -210,8 +211,8 @@ class Fluent implements IDataSource
*/ */
public function clause($clause) public function clause($clause)
{ {
$this->cursor = & $this->clauses[self::$normalizer->$clause]; $this->cursor = &$this->clauses[self::$normalizer->$clause];
if ($this->cursor === NULL) { if ($this->cursor === null) {
$this->cursor = []; $this->cursor = [];
} }
@@ -226,7 +227,7 @@ class Fluent implements IDataSource
*/ */
public function removeClause($clause) public function removeClause($clause)
{ {
$this->clauses[self::$normalizer->$clause] = NULL; $this->clauses[self::$normalizer->$clause] = null;
return $this; return $this;
} }
@@ -237,11 +238,11 @@ class Fluent implements IDataSource
* @param bool value * @param bool value
* @return self * @return self
*/ */
public function setFlag($flag, $value = TRUE) public function setFlag($flag, $value = true)
{ {
$flag = strtoupper($flag); $flag = strtoupper($flag);
if ($value) { if ($value) {
$this->flags[$flag] = TRUE; $this->flags[$flag] = true;
} else { } else {
unset($this->flags[$flag]); unset($this->flags[$flag]);
} }
@@ -299,10 +300,10 @@ class Fluent implements IDataSource
/** /**
* Generates and executes SQL query. * Generates and executes SQL query.
* @param mixed what to return? * @param mixed what to return?
* @return Result|int result set object (if any) * @return Result|int result set or number of affected rows
* @throws Exception * @throws Exception
*/ */
public function execute($return = NULL) public function execute($return = null)
{ {
$res = $this->query($this->_export()); $res = $this->query($this->_export());
switch ($return) { switch ($return) {
@@ -318,12 +319,12 @@ class Fluent implements IDataSource
/** /**
* Generates, executes SQL query and fetches the single row. * Generates, executes SQL query and fetches the single row.
* @return Row|FALSE array on success, FALSE if no next record * @return Row|false
*/ */
public function fetch() public function fetch()
{ {
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) { if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetch(); return $this->query($this->_export(null, ['%lmt', 1]))->fetch();
} else { } else {
return $this->query($this->_export())->fetch(); return $this->query($this->_export())->fetch();
} }
@@ -332,12 +333,12 @@ class Fluent implements IDataSource
/** /**
* Like fetch(), but returns only first field. * Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record * @return mixed value on success, false if no next record
*/ */
public function fetchSingle() public function fetchSingle()
{ {
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) { if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetchSingle(); return $this->query($this->_export(null, ['%lmt', 1]))->fetchSingle();
} else { } else {
return $this->query($this->_export())->fetchSingle(); return $this->query($this->_export())->fetchSingle();
} }
@@ -350,9 +351,9 @@ class Fluent implements IDataSource
* @param int limit * @param int limit
* @return array * @return array
*/ */
public function fetchAll($offset = NULL, $limit = NULL) public function fetchAll($offset = null, $limit = null)
{ {
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->fetchAll(); return $this->query($this->_export(null, ['%ofs %lmt', $offset, $limit]))->fetchAll();
} }
@@ -373,7 +374,7 @@ class Fluent implements IDataSource
* @param string value * @param string value
* @return array * @return array
*/ */
public function fetchPairs($key = NULL, $value = NULL) public function fetchPairs($key = null, $value = null)
{ {
return $this->query($this->_export())->fetchPairs($key, $value); return $this->query($this->_export())->fetchPairs($key, $value);
} }
@@ -385,9 +386,9 @@ class Fluent implements IDataSource
* @param int limit * @param int limit
* @return ResultIterator * @return ResultIterator
*/ */
public function getIterator($offset = NULL, $limit = NULL) public function getIterator($offset = null, $limit = null)
{ {
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->getIterator(); return $this->query($this->_export(null, ['%ofs %lmt', $offset, $limit]))->getIterator();
} }
@@ -396,7 +397,7 @@ class Fluent implements IDataSource
* @param string clause name * @param string clause name
* @return bool * @return bool
*/ */
public function test($clause = NULL) public function test($clause = null)
{ {
return $this->connection->test($this->_export($clause)); return $this->connection->test($this->_export($clause));
} }
@@ -407,14 +408,14 @@ class Fluent implements IDataSource
*/ */
public function count() public function count()
{ {
return (int) $this->query([ return Helpers::intVal($this->query([
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]', 'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]',
])->fetchSingle(); ])->fetchSingle());
} }
/** /**
* @return Result * @return Result|int
*/ */
private function query($args) private function query($args)
{ {
@@ -448,6 +449,7 @@ class Fluent implements IDataSource
return $this->connection->translate($this->_export()); return $this->connection->translate($this->_export());
} catch (\Exception $e) { } catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR); trigger_error($e->getMessage(), E_USER_ERROR);
return '';
} }
} }
@@ -457,9 +459,9 @@ class Fluent implements IDataSource
* @param string clause name * @param string clause name
* @return array * @return array
*/ */
protected function _export($clause = NULL, $args = []) protected function _export($clause = null, $args = [])
{ {
if ($clause === NULL) { if ($clause === null) {
$data = $this->clauses; $data = $this->clauses;
if ($this->command === 'SELECT' && ($data['LIMIT'] || $data['OFFSET'])) { if ($this->command === 'SELECT' && ($data['LIMIT'] || $data['OFFSET'])) {
$args = array_merge(['%lmt %ofs', $data['LIMIT'][0], $data['OFFSET'][0]], $args); $args = array_merge(['%lmt %ofs', $data['LIMIT'][0], $data['OFFSET'][0]], $args);
@@ -476,7 +478,7 @@ class Fluent implements IDataSource
} }
foreach ($data as $clause => $statement) { foreach ($data as $clause => $statement) {
if ($statement !== NULL) { if ($statement !== null) {
$args[] = $clause; $args[] = $clause;
if ($clause === $this->command && $this->flags) { if ($clause === $this->command && $this->flags) {
$args[] = implode(' ', array_keys($this->flags)); $args[] = implode(' ', array_keys($this->flags));
@@ -511,10 +513,9 @@ class Fluent implements IDataSource
{ {
// remove references // remove references
foreach ($this->clauses as $clause => $val) { foreach ($this->clauses as $clause => $val) {
$this->clauses[$clause] = & $val; $this->clauses[$clause] = &$val;
unset($val); unset($val);
} }
$this->cursor = & $foo; $this->cursor = &$foo;
} }
} }

View File

@@ -33,7 +33,6 @@ abstract class HashMapBase
{ {
return $this->callback; return $this->callback;
} }
} }
@@ -44,7 +43,6 @@ abstract class HashMapBase
*/ */
final class HashMap extends HashMapBase final class HashMap extends HashMapBase
{ {
public function __set($nm, $val) public function __set($nm, $val)
{ {
if ($nm == '') { if ($nm == '') {
@@ -63,5 +61,4 @@ final class HashMap extends HashMapBase
return $this->$nm = call_user_func($this->getCallback(), $nm); return $this->$nm = call_user_func($this->getCallback(), $nm);
} }
} }
} }

View File

@@ -15,13 +15,14 @@ class Helpers
/** @var array */ /** @var array */
private static $types; private static $types;
/** /**
* Prints out a syntax highlighted version of the SQL command or Result. * Prints out a syntax highlighted version of the SQL command or Result.
* @param string|Result * @param string|Result
* @param bool return output instead of printing it? * @param bool return output instead of printing it?
* @return string * @return string
*/ */
public static function dump($sql = NULL, $return = FALSE) public static function dump($sql = null, $return = false)
{ {
ob_start(); ob_start();
if ($sql instanceof Result && PHP_SAPI === 'cli') { if ($sql instanceof Result && PHP_SAPI === 'cli') {
@@ -38,7 +39,7 @@ class Helpers
echo $hasColors ? "\033[1;37m#row: $i\033[0m\n" : "#row: $i\n"; echo $hasColors ? "\033[1;37m#row: $i\033[0m\n" : "#row: $i\n";
foreach ($row as $col => $val) { foreach ($row as $col => $val) {
$spaces = $maxLen - mb_strlen($col) + 2; $spaces = $maxLen - mb_strlen($col) + 2;
echo "$col" . str_repeat(' ', $spaces) . "$val\n"; echo "$col" . str_repeat(' ', $spaces) . "$val\n";
} }
echo "\n"; echo "\n";
} }
@@ -50,14 +51,14 @@ class Helpers
if ($i === 0) { if ($i === 0) {
echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n"; echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
foreach ($row as $col => $foo) { foreach ($row as $col => $foo) {
echo "\t\t<th>" . htmlSpecialChars($col) . "</th>\n"; echo "\t\t<th>" . htmlspecialchars((string) $col) . "</th>\n";
} }
echo "\t</tr>\n</thead>\n<tbody>\n"; echo "\t</tr>\n</thead>\n<tbody>\n";
} }
echo "\t<tr>\n\t\t<th>", $i, "</th>\n"; echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
foreach ($row as $col) { foreach ($row as $col) {
echo "\t\t<td>", htmlSpecialChars($col), "</td>\n"; echo "\t\t<td>", htmlspecialchars((string) $col), "</td>\n";
} }
echo "\t</tr>\n"; echo "\t</tr>\n";
} }
@@ -67,7 +68,7 @@ class Helpers
: "</tbody>\n</table>\n"; : "</tbody>\n</table>\n";
} else { } else {
if ($sql === NULL) { if ($sql === null) {
$sql = \dibi::$sql; $sql = \dibi::$sql;
} }
@@ -106,7 +107,7 @@ class Helpers
echo trim($sql) . "\n\n"; echo trim($sql) . "\n\n";
} else { } else {
$sql = htmlSpecialChars($sql); $sql = htmlspecialchars($sql);
$sql = preg_replace_callback($highlighter, function ($m) { $sql = preg_replace_callback($highlighter, function ($m) {
if (!empty($m[1])) { // comment if (!empty($m[1])) { // comment
return '<em style="color:gray">' . $m[1] . '</em>'; return '<em style="color:gray">' . $m[1] . '</em>';
@@ -135,12 +136,12 @@ class Helpers
/** /**
* Finds the best suggestion. * Finds the best suggestion.
* @return string|NULL * @return string|null
* @internal * @internal
*/ */
public static function getSuggestion(array $items, $value) public static function getSuggestion(array $items, $value)
{ {
$best = NULL; $best = null;
$min = (strlen($value) / 4 + 1) * 10 + .1; $min = (strlen($value) / 4 + 1) * 10 + .1;
foreach (array_unique($items, SORT_REGULAR) as $item) { foreach (array_unique($items, SORT_REGULAR) as $item) {
$item = is_object($item) ? $item->getName() : $item; $item = is_object($item) ? $item->getName() : $item;
@@ -175,7 +176,7 @@ class Helpers
/** /**
* Heuristic type detection. * Heuristic type detection.
* @param string * @param string
* @return string|NULL * @return string|null
* @internal * @internal
*/ */
public static function detectType($type) public static function detectType($type)
@@ -183,7 +184,7 @@ class Helpers
static $patterns = [ static $patterns = [
'^_' => Type::TEXT, // PostgreSQL arrays '^_' => Type::TEXT, // PostgreSQL arrays
'BYTEA|BLOB|BIN' => Type::BINARY, 'BYTEA|BLOB|BIN' => Type::BINARY,
'TEXT|CHAR|POINT|INTERVAL' => Type::TEXT, 'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::TEXT,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER, 'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER,
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT, 'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT,
'^TIME$' => Type::TIME, '^TIME$' => Type::TIME,
@@ -197,7 +198,7 @@ class Helpers
return $val; return $val;
} }
} }
return NULL; return null;
} }
@@ -206,7 +207,7 @@ class Helpers
*/ */
public static function getTypeCache() public static function getTypeCache()
{ {
if (self::$types === NULL) { if (self::$types === null) {
self::$types = new HashMap([__CLASS__, 'detectType']); self::$types = new HashMap([__CLASS__, 'detectType']);
} }
return self::$types; return self::$types;
@@ -220,11 +221,11 @@ class Helpers
* @param string alias key * @param string alias key
* @return void * @return void
*/ */
public static function alias(& $config, $key, $alias) public static function alias(&$config, $key, $alias)
{ {
$foo = & $config; $foo = &$config;
foreach (explode('|', $key) as $key) { foreach (explode('|', $key) as $key) {
$foo = & $foo[$key]; $foo = &$foo[$key];
} }
if (!isset($foo) && isset($config[$alias])) { if (!isset($foo) && isset($config[$alias])) {
@@ -238,7 +239,7 @@ class Helpers
* Import SQL dump from file. * Import SQL dump from file.
* @return int count of sql commands * @return int count of sql commands
*/ */
public static function loadFromFile(Connection $connection, $file) public static function loadFromFile(Connection $connection, $file, callable $onProgress = null)
{ {
@set_time_limit(0); // intentionally @ @set_time_limit(0); // intentionally @
@@ -247,31 +248,55 @@ class Helpers
throw new \RuntimeException("Cannot open file '$file'."); throw new \RuntimeException("Cannot open file '$file'.");
} }
$count = 0; $stat = fstat($handle);
$count = $size = 0;
$delimiter = ';'; $delimiter = ';';
$sql = ''; $sql = '';
$driver = $connection->getDriver(); $driver = $connection->getDriver();
while (!feof($handle)) { while (($s = fgets($handle)) !== false) {
$s = rtrim(fgets($handle)); $size += strlen($s);
if (substr($s, 0, 10) === 'DELIMITER ') { if (strtoupper(substr($s, 0, 10)) === 'DELIMITER ') {
$delimiter = substr($s, 10); $delimiter = trim(substr($s, 10));
} elseif (substr($s, -strlen($delimiter)) === $delimiter) { } elseif (substr($ts = rtrim($s), -strlen($delimiter)) === $delimiter) {
$sql .= substr($s, 0, -strlen($delimiter)); $sql .= substr($ts, 0, -strlen($delimiter));
$driver->query($sql); $driver->query($sql);
$sql = ''; $sql = '';
$count++; $count++;
if ($onProgress) {
call_user_func($onProgress, $count, isset($stat['size']) ? $size * 100 / $stat['size'] : null);
}
} else { } else {
$sql .= $s . "\n"; $sql .= $s;
} }
} }
if (trim($sql) !== '') {
if (rtrim($sql) !== '') {
$driver->query($sql); $driver->query($sql);
$count++; $count++;
if ($onProgress) {
call_user_func($onProgress, $count, isset($stat['size']) ? 100 : null);
}
} }
fclose($handle); fclose($handle);
return $count; return $count;
} }
/**
* @internal
* @return string|int
*/
public static function intVal($value)
{
if (is_int($value)) {
return $value;
} elseif (is_string($value) && preg_match('#-?\d++\z#A', $value)) {
// support for long numbers - keep them unchanged
return is_float($number = $value * 1) ? $value : $number;
} else {
throw new Exception("Expected number, '$value' given.");
}
}
} }

View File

@@ -32,5 +32,4 @@ class Literal
{ {
return $this->value; return $this->value;
} }
} }

View File

@@ -24,7 +24,7 @@ class FileLogger
public $filter; public $filter;
public function __construct($file, $filter = NULL) public function __construct($file, $filter = null)
{ {
$this->file = $file; $this->file = $file;
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY; $this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
@@ -72,5 +72,4 @@ class FileLogger
} }
fclose($handle); fclose($handle);
} }
} }

View File

@@ -48,7 +48,7 @@ class FirePhpLogger
} }
public function __construct($filter = NULL) public function __construct($filter = null)
{ {
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY; $this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
} }
@@ -91,5 +91,4 @@ class FirePhpLogger
} }
header("X-Wf-dibi-1-1-d$num: |$s|"); header("X-Wf-dibi-1-1-d$num: |$s|");
} }
} }

View File

@@ -8,7 +8,6 @@
namespace Dibi\Reflection; namespace Dibi\Reflection;
use Dibi; use Dibi;
use Dibi\Type;
/** /**
@@ -19,24 +18,24 @@ use Dibi\Type;
* @property-read Table $table * @property-read Table $table
* @property-read string $type * @property-read string $type
* @property-read mixed $nativeType * @property-read mixed $nativeType
* @property-read int $size * @property-read int|null $size
* @property-read bool $unsigned * @property-read bool|null $unsigned
* @property-read bool $nullable * @property-read bool|null $nullable
* @property-read bool $autoIncrement * @property-read bool|null $autoIncrement
* @property-read mixed $default * @property-read mixed $default
*/ */
class Column class Column
{ {
use Dibi\Strict; use Dibi\Strict;
/** @var Dibi\Reflector|NULL when created by Result */ /** @var Dibi\Reflector|null when created by Result */
private $reflector; private $reflector;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */ /** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info; private $info;
public function __construct(Dibi\Reflector $reflector = NULL, array $info) public function __construct(Dibi\Reflector $reflector = null, array $info)
{ {
$this->reflector = $reflector; $this->reflector = $reflector;
$this->info = $info; $this->info = $info;
@@ -57,7 +56,7 @@ class Column
*/ */
public function getFullName() public function getFullName()
{ {
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL; return isset($this->info['fullname']) ? $this->info['fullname'] : null;
} }
@@ -76,18 +75,18 @@ class Column
public function getTable() public function getTable()
{ {
if (empty($this->info['table']) || !$this->reflector) { if (empty($this->info['table']) || !$this->reflector) {
throw new Dibi\Exception("Table is unknown or not available."); throw new Dibi\Exception('Table is unknown or not available.');
} }
return new Table($this->reflector, ['name' => $this->info['table']]); return new Table($this->reflector, ['name' => $this->info['table']]);
} }
/** /**
* @return string * @return string|null
*/ */
public function getTableName() public function getTableName()
{ {
return isset($this->info['table']) && $this->info['table'] != NULL ? $this->info['table'] : NULL; // intentionally == return isset($this->info['table']) && $this->info['table'] != null ? $this->info['table'] : null; // intentionally ==
} }
@@ -101,7 +100,7 @@ class Column
/** /**
* @return mixed * @return string
*/ */
public function getNativeType() public function getNativeType()
{ {
@@ -110,38 +109,38 @@ class Column
/** /**
* @return int * @return int|null
*/ */
public function getSize() public function getSize()
{ {
return isset($this->info['size']) ? (int) $this->info['size'] : NULL; return isset($this->info['size']) ? (int) $this->info['size'] : null;
} }
/** /**
* @return bool * @return bool|null
*/ */
public function isUnsigned() public function isUnsigned()
{ {
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL; return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : null;
} }
/** /**
* @return bool * @return bool|null
*/ */
public function isNullable() public function isNullable()
{ {
return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : NULL; return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : null;
} }
/** /**
* @return bool * @return bool|null
*/ */
public function isAutoIncrement() public function isAutoIncrement()
{ {
return isset($this->info['autoincrement']) ? (bool) $this->info['autoincrement'] : NULL; return isset($this->info['autoincrement']) ? (bool) $this->info['autoincrement'] : null;
} }
@@ -150,7 +149,7 @@ class Column
*/ */
public function getDefault() public function getDefault()
{ {
return isset($this->info['default']) ? $this->info['default'] : NULL; return isset($this->info['default']) ? $this->info['default'] : null;
} }
@@ -160,7 +159,6 @@ class Column
*/ */
public function getVendorInfo($key) public function getVendorInfo($key)
{ {
return isset($this->info['vendor'][$key]) ? $this->info['vendor'][$key] : NULL; return isset($this->info['vendor'][$key]) ? $this->info['vendor'][$key] : null;
} }
} }

View File

@@ -104,12 +104,11 @@ class Database
*/ */
protected function init() protected function init()
{ {
if ($this->tables === NULL) { if ($this->tables === null) {
$this->tables = []; $this->tables = [];
foreach ($this->reflector->getTables() as $info) { foreach ($this->reflector->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new Table($this->reflector, $info); $this->tables[strtolower($info['name'])] = new Table($this->reflector, $info);
} }
} }
} }
} }

View File

@@ -50,5 +50,4 @@ class ForeignKey
{ {
return $this->references; return $this->references;
} }
} }

View File

@@ -66,5 +66,4 @@ class Index
{ {
return !empty($this->info['primary']); return !empty($this->info['primary']);
} }
} }

View File

@@ -50,7 +50,7 @@ class Result
* @param bool * @param bool
* @return string[] * @return string[]
*/ */
public function getColumnNames($fullNames = FALSE) public function getColumnNames($fullNames = false)
{ {
$this->initColumns(); $this->initColumns();
$res = []; $res = [];
@@ -94,13 +94,12 @@ class Result
*/ */
protected function initColumns() protected function initColumns()
{ {
if ($this->columns === NULL) { if ($this->columns === null) {
$this->columns = []; $this->columns = [];
$reflector = $this->driver instanceof Dibi\Reflector ? $this->driver : NULL; $reflector = $this->driver instanceof Dibi\Reflector ? $this->driver : null;
foreach ($this->driver->getResultColumns() as $info) { foreach ($this->driver->getResultColumns() as $info) {
$this->columns[] = $this->names[strtolower($info['name'])] = new Column($reflector, $info); $this->columns[] = $this->names[strtolower($info['name'])] = new Column($reflector, $info);
} }
} }
} }
} }

View File

@@ -160,7 +160,7 @@ class Table
*/ */
protected function initColumns() protected function initColumns()
{ {
if ($this->columns === NULL) { if ($this->columns === null) {
$this->columns = []; $this->columns = [];
foreach ($this->reflector->getColumns($this->name) as $info) { foreach ($this->reflector->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new Column($this->reflector, $info); $this->columns[strtolower($info['name'])] = new Column($this->reflector, $info);
@@ -174,7 +174,7 @@ class Table
*/ */
protected function initIndexes() protected function initIndexes()
{ {
if ($this->indexes === NULL) { if ($this->indexes === null) {
$this->initColumns(); $this->initColumns();
$this->indexes = []; $this->indexes = [];
foreach ($this->reflector->getIndexes($this->name) as $info) { foreach ($this->reflector->getIndexes($this->name) as $info) {
@@ -197,5 +197,4 @@ class Table
{ {
throw new Dibi\NotImplementedException; throw new Dibi\NotImplementedException;
} }
} }

View File

@@ -40,7 +40,7 @@ class Result implements IDataSource
private $meta; private $meta;
/** @var bool Already fetched? Used for allowance for first seek(0) */ /** @var bool Already fetched? Used for allowance for first seek(0) */
private $fetched = FALSE; private $fetched = false;
/** @var string returned object class */ /** @var string returned object class */
private $rowClass = 'Dibi\Row'; private $rowClass = 'Dibi\Row';
@@ -78,9 +78,9 @@ class Result implements IDataSource
*/ */
final public function free() final public function free()
{ {
if ($this->driver !== NULL) { if ($this->driver !== null) {
$this->driver->free(); $this->driver->free();
$this->driver = $this->meta = NULL; $this->driver = $this->meta = null;
} }
} }
@@ -92,7 +92,7 @@ class Result implements IDataSource
*/ */
final public function getResultDriver() final public function getResultDriver()
{ {
if ($this->driver === NULL) { if ($this->driver === null) {
throw new \RuntimeException('Result-set was released from memory.'); throw new \RuntimeException('Result-set was released from memory.');
} }
@@ -106,12 +106,14 @@ class Result implements IDataSource
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
* @throws Exception * @throws Exception
*/ */
final public function seek($row) final public function seek($row)
{ {
return ($row !== 0 || $this->fetched) ? (bool) $this->getResultDriver()->seek($row) : TRUE; return ($row != 0 || $this->fetched) // intentionally ==
? (bool) $this->getResultDriver()->seek($row)
: true;
} }
@@ -184,15 +186,15 @@ class Result implements IDataSource
/** /**
* Fetches the row at current position, process optional type conversion. * Fetches the row at current position, process optional type conversion.
* and moves the internal cursor to the next position * and moves the internal cursor to the next position
* @return Row|FALSE array on success, FALSE if no next record * @return Row|false
*/ */
final public function fetch() final public function fetch()
{ {
$row = $this->getResultDriver()->fetch(TRUE); $row = $this->getResultDriver()->fetch(true);
if (!is_array($row)) { if (!is_array($row)) {
return FALSE; return false;
} }
$this->fetched = TRUE; $this->fetched = true;
$this->normalize($row); $this->normalize($row);
if ($this->rowFactory) { if ($this->rowFactory) {
return call_user_func($this->rowFactory, $row); return call_user_func($this->rowFactory, $row);
@@ -205,15 +207,15 @@ class Result implements IDataSource
/** /**
* Like fetch(), but returns only first field. * Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record * @return mixed value on success, false if no next record
*/ */
final public function fetchSingle() final public function fetchSingle()
{ {
$row = $this->getResultDriver()->fetch(TRUE); $row = $this->getResultDriver()->fetch(true);
if (!is_array($row)) { if (!is_array($row)) {
return FALSE; return false;
} }
$this->fetched = TRUE; $this->fetched = true;
$this->normalize($row); $this->normalize($row);
return reset($row); return reset($row);
} }
@@ -225,10 +227,10 @@ class Result implements IDataSource
* @param int limit * @param int limit
* @return Row[] * @return Row[]
*/ */
final public function fetchAll($offset = NULL, $limit = NULL) final public function fetchAll($offset = null, $limit = null)
{ {
$limit = $limit === NULL ? -1 : (int) $limit; $limit = $limit === null ? -1 : Helpers::intVal($limit);
$this->seek((int) $offset); $this->seek($offset);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) {
return []; // empty result set return []; // empty result set
@@ -260,7 +262,7 @@ class Result implements IDataSource
*/ */
final public function fetchAssoc($assoc) final public function fetchAssoc($assoc)
{ {
if (strpos($assoc, ',') !== FALSE) { if (strpos($assoc, ',') !== false) {
return $this->oldFetchAssoc($assoc); return $this->oldFetchAssoc($assoc);
} }
@@ -270,12 +272,12 @@ class Result implements IDataSource
return []; // empty result set return []; // empty result set
} }
$data = NULL; $data = null;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
// check columns // check columns
foreach ($assoc as $as) { foreach ($assoc as $as) {
// offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts // offsetExists ignores null in PHP 5.2.1, isset() surprisingly null accepts
if ($as !== '[]' && $as !== '=' && $as !== '->' && $as !== '|' && !property_exists($row, $as)) { if ($as !== '[]' && $as !== '=' && $as !== '->' && $as !== '|' && !property_exists($row, $as)) {
throw new \InvalidArgumentException("Unknown column '$as' in associative descriptor."); throw new \InvalidArgumentException("Unknown column '$as' in associative descriptor.");
} }
@@ -291,35 +293,34 @@ class Result implements IDataSource
// make associative tree // make associative tree
do { do {
$x = & $data; $x = &$data;
// iterative deepening // iterative deepening
foreach ($assoc as $i => $as) { foreach ($assoc as $i => $as) {
if ($as === '[]') { // indexed-array node if ($as === '[]') { // indexed-array node
$x = & $x[]; $x = &$x[];
} elseif ($as === '=') { // "value" node } elseif ($as === '=') { // "value" node
$x = $row->{$assoc[$i + 1]}; $x = $row->{$assoc[$i + 1]};
continue 2; continue 2;
} elseif ($as === '->') { // "object" node } elseif ($as === '->') { // "object" node
if ($x === NULL) { if ($x === null) {
$x = clone $row; $x = clone $row;
$x = & $x->{$assoc[$i + 1]}; $x = &$x->{$assoc[$i + 1]};
$x = NULL; // prepare child node $x = null; // prepare child node
} else { } else {
$x = & $x->{$assoc[$i + 1]}; $x = &$x->{$assoc[$i + 1]};
} }
} elseif ($as !== '|') { // associative-array node } elseif ($as !== '|') { // associative-array node
$x = & $x[$row->$as]; $x = &$x[$row->$as];
} }
} }
if ($x === NULL) { // build leaf if ($x === null) { // build leaf
$x = $row; $x = $row;
} }
} while ($row = $this->fetch()); } while ($row = $this->fetch());
unset($x); unset($x);
@@ -338,7 +339,7 @@ class Result implements IDataSource
return []; // empty result set return []; // empty result set
} }
$data = NULL; $data = null;
$assoc = explode(',', $assoc); $assoc = explode(',', $assoc);
// strip leading = and @ // strip leading = and @
@@ -356,43 +357,42 @@ class Result implements IDataSource
} }
do { do {
$x = & $data; $x = &$data;
foreach ($assoc as $i => $as) { foreach ($assoc as $i => $as) {
if ($as === '#') { // indexed-array node if ($as === '#') { // indexed-array node
$x = & $x[]; $x = &$x[];
} elseif ($as === '=') { // "record" node } elseif ($as === '=') { // "record" node
if ($x === NULL) { if ($x === null) {
$x = $row->toArray(); $x = $row->toArray();
$x = & $x[ $assoc[$i + 1] ]; $x = &$x[$assoc[$i + 1]];
$x = NULL; // prepare child node $x = null; // prepare child node
} else { } else {
$x = & $x[ $assoc[$i + 1] ]; $x = &$x[$assoc[$i + 1]];
} }
} elseif ($as === '@') { // "object" node } elseif ($as === '@') { // "object" node
if ($x === NULL) { if ($x === null) {
$x = clone $row; $x = clone $row;
$x = & $x->{$assoc[$i + 1]}; $x = &$x->{$assoc[$i + 1]};
$x = NULL; // prepare child node $x = null; // prepare child node
} else { } else {
$x = & $x->{$assoc[$i + 1]}; $x = &$x->{$assoc[$i + 1]};
} }
} else { // associative-array node } else { // associative-array node
$x = & $x[$row->$as]; $x = &$x[$row->$as];
} }
} }
if ($x === NULL) { // build leaf if ($x === null) { // build leaf
if ($leaf === '=') { if ($leaf === '=') {
$x = $row->toArray(); $x = $row->toArray();
} else { } else {
$x = $row; $x = $row;
} }
} }
} while ($row = $this->fetch()); } while ($row = $this->fetch());
unset($x); unset($x);
@@ -407,7 +407,7 @@ class Result implements IDataSource
* @return array * @return array
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
final public function fetchPairs($key = NULL, $value = NULL) final public function fetchPairs($key = null, $value = null)
{ {
$this->seek(0); $this->seek(0);
$row = $this->fetch(); $row = $this->fetch();
@@ -417,8 +417,8 @@ class Result implements IDataSource
$data = []; $data = [];
if ($value === NULL) { if ($value === null) {
if ($key !== NULL) { if ($key !== null) {
throw new \InvalidArgumentException('Either none or both columns must be specified.'); throw new \InvalidArgumentException('Either none or both columns must be specified.');
} }
@@ -439,7 +439,7 @@ class Result implements IDataSource
throw new \InvalidArgumentException("Unknown value column '$value'."); throw new \InvalidArgumentException("Unknown value column '$value'.");
} }
if ($key === NULL) { // indexed-array if ($key === null) { // indexed-array
do { do {
$data[] = $row[$value]; $data[] = $row[$value];
} while ($row = $this->fetch()); } while ($row = $this->fetch());
@@ -452,7 +452,7 @@ class Result implements IDataSource
} }
do { do {
$data[ (string) $row[$key] ] = $row[$value]; $data[(string) $row[$key]] = $row[$value];
} while ($row = $this->fetch()); } while ($row = $this->fetch());
return $data; return $data;
@@ -483,10 +483,10 @@ class Result implements IDataSource
* @param array * @param array
* @return void * @return void
*/ */
private function normalize(array & $row) private function normalize(array &$row)
{ {
foreach ($this->types as $key => $type) { foreach ($this->types as $key => $type) {
if (!isset($row[$key])) { // NULL if (!isset($row[$key])) { // null
continue; continue;
} }
$value = $row[$key]; $value = $row[$key];
@@ -499,9 +499,9 @@ class Result implements IDataSource
: $tmp; : $tmp;
} elseif ($type === Type::FLOAT) { } elseif ($type === Type::FLOAT) {
$value = ltrim($value, '0'); $value = ltrim((string) $value, '0');
$p = strpos($value, '.'); $p = strpos($value, '.');
if ($p !== FALSE) { if ($p !== false) {
$value = rtrim(rtrim($value, '0'), '.'); $value = rtrim(rtrim($value, '0'), '.');
} }
if ($value === '' || $value[0] === '.') { if ($value === '' || $value[0] === '.') {
@@ -515,11 +515,11 @@ class Result implements IDataSource
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) { } elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
if ((int) $value !== 0 || substr((string) $value, 0, 3) === '00:') { // '', NULL, FALSE, '0000-00-00', ... if ($value && substr((string) $value, 0, 3) !== '000') { // '', null, false, '0000-00-00', ...
$value = new DateTime($value); $value = new DateTime($value);
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]); $row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
} else { } else {
$row[$key] = NULL; $row[$key] = null;
} }
} elseif ($type === Type::TIME_INTERVAL) { } elseif ($type === Type::TIME_INTERVAL) {
@@ -553,14 +553,14 @@ class Result implements IDataSource
*/ */
final public function getType($col) final public function getType($col)
{ {
return isset($this->types[$col]) ? $this->types[$col] : NULL; return isset($this->types[$col]) ? $this->types[$col] : null;
} }
/** /**
* Sets data format. * Sets date format.
* @param string type (use constant Type::*) * @param string
* @param string format * @param string|null format
* @return self * @return self
*/ */
final public function setFormat($type, $format) final public function setFormat($type, $format)
@@ -572,11 +572,11 @@ class Result implements IDataSource
/** /**
* Returns data format. * Returns data format.
* @return string * @return string|null
*/ */
final public function getFormat($type) final public function getFormat($type)
{ {
return isset($this->formats[$type]) ? $this->formats[$type] : NULL; return isset($this->formats[$type]) ? $this->formats[$type] : null;
} }
@@ -589,7 +589,7 @@ class Result implements IDataSource
*/ */
public function getInfo() public function getInfo()
{ {
if ($this->meta === NULL) { if ($this->meta === null) {
$this->meta = new Reflection\Result($this->getResultDriver()); $this->meta = new Reflection\Result($this->getResultDriver());
} }
return $this->meta; return $this->meta;
@@ -616,5 +616,4 @@ class Result implements IDataSource
{ {
echo Helpers::dump($this); echo Helpers::dump($this);
} }
} }

View File

@@ -27,7 +27,7 @@ class ResultIterator implements \Iterator, \Countable
/** @var Result */ /** @var Result */
private $result; private $result;
/** @var int */ /** @var mixed */
private $row; private $row;
/** @var int */ /** @var int */
@@ -104,5 +104,4 @@ class ResultIterator implements \Iterator, \Countable
{ {
return $this->result->getRowCount(); return $this->result->getRowCount();
} }
} }

View File

@@ -13,7 +13,6 @@ namespace Dibi;
*/ */
class Row implements \ArrayAccess, \IteratorAggregate, \Countable class Row implements \ArrayAccess, \IteratorAggregate, \Countable
{ {
public function __construct($arr) public function __construct($arr)
{ {
foreach ($arr as $k => $v) { foreach ($arr as $k => $v) {
@@ -34,16 +33,16 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
* @param string format * @param string format
* @return \DateTime * @return \DateTime
*/ */
public function asDateTime($key, $format = NULL) public function asDateTime($key, $format = null)
{ {
$time = $this[$key]; $time = $this[$key];
if (!$time instanceof DateTime) { if (!$time instanceof DateTime) {
if ((int) $time === 0 && substr((string) $time, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ... if (!$time || substr((string) $time, 0, 3) === '000') { // '', null, false, '0000-00-00', ...
return NULL; return null;
} }
$time = new DateTime($time); $time = new DateTime($time);
} }
return $format === NULL ? $time : $time->format($format); return $format === null ? $time : $time->format($format);
} }
@@ -91,5 +90,4 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
{ {
unset($this->$nm); unset($this->$nm);
} }
} }

View File

@@ -88,7 +88,7 @@ trait Strict
*/ */
public function __isset($name) public function __isset($name)
{ {
return FALSE; return false;
} }
@@ -105,33 +105,33 @@ trait Strict
/** /**
* @param string method name * @param string method name
* @param callabke * @param callable
* @return mixed * @return mixed
*/ */
public static function extensionMethod($name, $callback = NULL) public static function extensionMethod($name, $callback = null)
{ {
if (strpos($name, '::') === FALSE) { if (strpos($name, '::') === false) {
$class = get_called_class(); $class = get_called_class();
} else { } else {
list($class, $name) = explode('::', $name); list($class, $name) = explode('::', $name);
$class = (new ReflectionClass($class))->getName(); $class = (new ReflectionClass($class))->getName();
} }
if (self::$extMethods === NULL) { // for backwards compatibility if (self::$extMethods === null) { // for backwards compatibility
$list = get_defined_functions(); $list = get_defined_functions();
foreach ($list['user'] as $fce) { foreach ($list['user'] as $fce) {
$pair = explode('_prototype_', $fce); $pair = explode('_prototype_', $fce);
if (count($pair) === 2) { if (count($pair) === 2) {
trigger_error("Extension method defined as $fce() is deprecated, use $class::extensionMethod('$name', ...).", E_USER_DEPRECATED); 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]][(new ReflectionClass($pair[0]))->getName()] = $fce;
self::$extMethods[$pair[1]][''] = NULL; self::$extMethods[$pair[1]][''] = null;
} }
} }
} }
$list = & self::$extMethods[strtolower($name)]; $list = &self::$extMethods[strtolower($name)];
if ($callback === NULL) { // getter if ($callback === null) { // getter
$cache = & $list[''][$class]; $cache = &$list[''][$class];
if (isset($cache)) { if (isset($cache)) {
return $cache; return $cache;
} }
@@ -141,12 +141,11 @@ trait Strict
return $cache = $list[$cl]; return $cache = $list[$cl];
} }
} }
return $cache = FALSE; return $cache = false;
} else { // setter } else { // setter
$list[$class] = $callback; $list[$class] = $callback;
$list[''] = NULL; $list[''] = null;
} }
} }
} }

View File

@@ -31,7 +31,7 @@ final class Translator
private $errors; private $errors;
/** @var bool */ /** @var bool */
private $comment = FALSE; private $comment = false;
/** @var int */ /** @var int */
private $ifLevel = 0; private $ifLevel = 0;
@@ -71,10 +71,10 @@ final class Translator
} }
$this->args = $args; $this->args = $args;
$commandIns = NULL; $commandIns = null;
$lastArr = NULL; $lastArr = null;
$cursor = & $this->cursor; $cursor = &$this->cursor;
$comment = & $this->comment; $comment = &$this->comment;
// iterate // iterate
$sql = []; $sql = [];
@@ -127,7 +127,7 @@ final class Translator
if (is_array($arg) && is_string(key($arg))) { if (is_array($arg) && is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST // associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) { if ($commandIns === null) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6)); $commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC'; $commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a'); $sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
@@ -142,7 +142,7 @@ final class Translator
} }
// default processing // default processing
$sql[] = $this->formatValue($arg, FALSE); $sql[] = $this->formatValue($arg, false);
} // while } // while
@@ -157,7 +157,7 @@ final class Translator
} }
// apply limit // apply limit
if ($this->limit !== NULL || $this->offset !== NULL) { if ($this->limit !== null || $this->offset !== null) {
$this->driver->applyLimit($sql, $this->limit, $this->offset); $this->driver->applyLimit($sql, $this->limit, $this->offset);
} }
@@ -196,7 +196,7 @@ final class Translator
$pair = explode('%', $k, 2); // split into identifier & modifier $pair = explode('%', $k, 2); // split into identifier & modifier
$k = $this->identifiers->{$pair[0]} . ' '; $k = $this->identifiers->{$pair[0]} . ' ';
if (!isset($pair[1])) { if (!isset($pair[1])) {
$v = $this->formatValue($v, FALSE); $v = $this->formatValue($v, false);
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v; $vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
} elseif ($pair[1] === 'ex') { } elseif ($pair[1] === 'ex') {
@@ -206,7 +206,7 @@ final class Translator
$v = $this->formatValue($v, $pair[1]); $v = $this->formatValue($v, $pair[1]);
if ($pair[1] === 'l' || $pair[1] === 'in') { if ($pair[1] === 'l' || $pair[1] === 'in') {
$op = 'IN '; $op = 'IN ';
} elseif (strpos($pair[1], 'like') !== FALSE) { } elseif (strpos($pair[1], 'like') !== false) {
$op = 'LIKE '; $op = 'LIKE ';
} elseif ($v === 'NULL') { } elseif ($v === 'NULL') {
$op = 'IS '; $op = 'IS ';
@@ -225,7 +225,7 @@ final class Translator
case 'n': // key, key, ... identifier names case 'n': // key, key, ... identifier names
foreach ($value as $k => $v) { foreach ($value as $k => $v) {
if (is_string($k)) { 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 { } else {
$pair = explode('%', $v, 2); // split into identifier & modifier $pair = explode('%', $v, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]}; $vx[] = $this->identifiers->{$pair[0]};
@@ -238,7 +238,7 @@ final class Translator
foreach ($value as $k => $v) { foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier $pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]} . '=' $vx[] = $this->identifiers->{$pair[0]} . '='
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE)); . $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : false));
} }
return implode(', ', $vx); return implode(', ', $vx);
@@ -246,8 +246,8 @@ final class Translator
case 'in':// replaces scalar %in modifier! case 'in':// replaces scalar %in modifier!
case 'l': // (val, val, ...) case 'l': // (val, val, ...)
foreach ($value as $k => $v) { foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier $pair = explode('%', (string) $k, 2); // split into identifier & modifier
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE)); $vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : false));
} }
return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')'; return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')';
@@ -256,7 +256,7 @@ final class Translator
foreach ($value as $k => $v) { foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier $pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]}; $kx[] = $this->identifiers->{$pair[0]};
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE)); $vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : false));
} }
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
@@ -277,7 +277,7 @@ final class Translator
$pair = explode('%', $k, 2); // split into identifier & modifier $pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]}; $kx[] = $this->identifiers->{$pair[0]};
foreach ($v as $k2 => $v2) { foreach ($v as $k2 => $v2) {
$vx[$k2][] = $this->formatValue($v2, isset($pair[1]) ? $pair[1] : (is_array($v2) ? 'ex' : FALSE)); $vx[$k2][] = $this->formatValue($v2, isset($pair[1]) ? $pair[1] : (is_array($v2) ? 'ex' : false));
} }
} }
foreach ($vx as $k => $v) { foreach ($vx as $k => $v) {
@@ -313,10 +313,10 @@ final class Translator
// with modifier procession // with modifier procession
if ($modifier) { if ($modifier) {
if ($value !== NULL && !is_scalar($value)) { // array is already processed if ($value !== null && !is_scalar($value)) { // array is already processed
if ($value instanceof Literal && ($modifier === 'sql' || $modifier === 'SQL')) { if ($value instanceof Literal && ($modifier === 'sql' || $modifier === 'SQL')) {
$modifier = 'SQL'; $modifier = 'SQL';
} elseif (($value instanceof \DateTime || $value instanceof \DateTimeInterface) && ($modifier === 'd' || $modifier === 't')) { } elseif (($value instanceof \DateTime || $value instanceof \DateTimeInterface) && ($modifier === 'd' || $modifier === 't' || $modifier === 'dt')) {
// continue // continue
} else { } else {
$type = is_object($value) ? get_class($value) : gettype($value); $type = is_object($value) ? get_class($value) : gettype($value);
@@ -326,63 +326,72 @@ final class Translator
switch ($modifier) { switch ($modifier) {
case 's': // string case 's': // string
return $value === NULL ? 'NULL' : $this->driver->escapeText($value); return $value === null ? 'NULL' : $this->driver->escapeText((string) $value);
case 'bin':// binary case 'bin':// binary
return $value === NULL ? 'NULL' : $this->driver->escapeBinary($value); return $value === null ? 'NULL' : $this->driver->escapeBinary($value);
case 'b': // boolean case 'b': // boolean
return $value === NULL ? 'NULL' : $this->driver->escapeBool($value); return $value === null ? 'NULL' : $this->driver->escapeBool($value);
case 'sN': // string or NULL case 'sN': // string or null
case 'sn': case 'sn':
return $value == '' ? 'NULL' : $this->driver->escapeText($value); // notice two equal signs return $value == '' ? 'NULL' : $this->driver->escapeText((string) $value); // notice two equal signs
case 'in': // deprecated case 'in': // deprecated
trigger_error('Modifier %in is deprecated, use %iN.', E_USER_DEPRECATED); trigger_error('Modifier %in is deprecated, use %iN.', E_USER_DEPRECATED);
// intentionally break omitted // break omitted
case 'iN': // signed int or null
case 'iN': // signed int or NULL
if ($value == '') { if ($value == '') {
$value = NULL; $value = null;
} }
// intentionally break omitted // break omitted
case 'i': // signed int case 'i': // signed int
case 'u': // unsigned int, ignored case 'u': // unsigned int, ignored
if ($value === NULL) { if ($value === null) {
return 'NULL'; return 'NULL';
} elseif (is_string($value) && preg_match('#[+-]?\d++(?:e\d+)?\z#A', $value)) { } elseif (is_string($value)) {
return $value; // support for long numbers - keep them unchanged if (preg_match('#[+-]?\d++(?:e\d+)?\z#A', $value)) {
} elseif (is_string($value) && substr($value, 1, 1) === 'x' && is_numeric($value)) { return $value; // support for long numbers - keep them unchanged
trigger_error('Support for hex strings has been deprecated.', E_USER_DEPRECATED); } elseif (substr($value, 1, 1) === 'x' && is_numeric($value)) {
return (string) hexdec($value); trigger_error('Support for hex strings has been deprecated.', E_USER_DEPRECATED);
return (string) hexdec($value);
} else {
throw new Exception("Expected number, '$value' given.");
}
} else { } else {
return (string) (int) $value; return (string) (int) $value;
} }
// break omitted
case 'f': // float case 'f': // float
if ($value === NULL) { if ($value === null) {
return 'NULL'; return 'NULL';
} elseif (is_string($value) && is_numeric($value) && substr($value, 1, 1) !== 'x') { } elseif (is_string($value)) {
return $value; // support for extreme numbers - keep them unchanged if (is_numeric($value) && substr($value, 1, 1) !== 'x') {
return $value; // support for long numbers - keep them unchanged
} else {
throw new Exception("Expected number, '$value' given.");
}
} else { } else {
return rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.'); return rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
} }
// break omitted
case 'd': // date case 'd': // date
case 't': // datetime case 't': // datetime
case 'dt': // datetime case 'dt': // datetime
if ($value === NULL) { if ($value === null) {
return 'NULL'; return 'NULL';
} else { } else {
return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value); return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value);
} }
// break omitted
case 'by': case 'by':
case 'n': // identifier name case 'n': // composed identifier name
return $this->identifiers->$value; return $this->identifiers->$value;
case 'N': // identifier name
return $this->driver->escapeIdentifier($value);
case 'ex': case 'ex':
case 'sql': // preserve as dibi-SQL (TODO: leave only %ex) case 'sql': // preserve as dibi-SQL (TODO: leave only %ex)
$value = (string) $value; $value = (string) $value;
@@ -440,7 +449,7 @@ final class Translator
} elseif (is_bool($value)) { } elseif (is_bool($value)) {
return $this->driver->escapeBool($value); return $this->driver->escapeBool($value);
} elseif ($value === NULL) { } elseif ($value === null) {
return 'NULL'; return 'NULL';
} elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { } elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) {
@@ -449,6 +458,9 @@ final class Translator
} elseif ($value instanceof Literal) { } elseif ($value instanceof Literal) {
return (string) $value; return (string) $value;
} elseif ($value instanceof Expression) {
return call_user_func_array([$this->connection, 'translate'], $value->getValues());
} else { } else {
$type = is_object($value) ? get_class($value) : gettype($value); $type = is_object($value) ? get_class($value) : gettype($value);
return $this->errors[] = "**Unexpected $type**"; return $this->errors[] = "**Unexpected $type**";
@@ -477,19 +489,19 @@ final class Translator
if (!empty($matches[11])) { // placeholder if (!empty($matches[11])) { // placeholder
$cursor = & $this->cursor; $cursor = &$this->cursor;
if ($cursor >= count($this->args)) { if ($cursor >= count($this->args)) {
return $this->errors[] = '**Extra placeholder**'; return $this->errors[] = '**Extra placeholder**';
} }
$cursor++; $cursor++;
return $this->formatValue($this->args[$cursor - 1], FALSE); return $this->formatValue($this->args[$cursor - 1], false);
} }
if (!empty($matches[10])) { // modifier if (!empty($matches[10])) { // modifier
$mod = $matches[10]; $mod = $matches[10];
$cursor = & $this->cursor; $cursor = &$this->cursor;
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') { if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
return $this->errors[] = "**Extra modifier %$mod**"; return $this->errors[] = "**Extra modifier %$mod**";
@@ -501,7 +513,7 @@ final class Translator
if (!$this->comment && !$this->args[$cursor - 1]) { if (!$this->comment && !$this->args[$cursor - 1]) {
// open comment // open comment
$this->ifLevelStart = $this->ifLevel; $this->ifLevelStart = $this->ifLevel;
$this->comment = TRUE; $this->comment = true;
return '/*'; return '/*';
} }
return ''; return '';
@@ -509,11 +521,11 @@ final class Translator
} elseif ($mod === 'else') { } elseif ($mod === 'else') {
if ($this->ifLevelStart === $this->ifLevel) { if ($this->ifLevelStart === $this->ifLevel) {
$this->ifLevelStart = 0; $this->ifLevelStart = 0;
$this->comment = FALSE; $this->comment = false;
return '*/'; return '*/';
} elseif (!$this->comment) { } elseif (!$this->comment) {
$this->ifLevelStart = $this->ifLevel; $this->ifLevelStart = $this->ifLevel;
$this->comment = TRUE; $this->comment = true;
return '/*'; return '/*';
} }
@@ -522,7 +534,7 @@ final class Translator
if ($this->ifLevelStart === $this->ifLevel + 1) { if ($this->ifLevelStart === $this->ifLevel + 1) {
// close comment // close comment
$this->ifLevelStart = 0; $this->ifLevelStart = 0;
$this->comment = FALSE; $this->comment = false;
return '*/'; return '*/';
} }
return ''; return '';
@@ -533,21 +545,21 @@ final class Translator
} elseif ($mod === 'lmt') { // apply limit } elseif ($mod === 'lmt') { // apply limit
$arg = $this->args[$cursor++]; $arg = $this->args[$cursor++];
if ($arg === NULL) { if ($arg === null) {
} elseif ($this->comment) { } elseif ($this->comment) {
return "(limit $arg)"; return "(limit $arg)";
} else { } else {
$this->limit = (int) $arg; $this->limit = Helpers::intVal($arg);
} }
return ''; return '';
} elseif ($mod === 'ofs') { // apply offset } elseif ($mod === 'ofs') { // apply offset
$arg = $this->args[$cursor++]; $arg = $this->args[$cursor++];
if ($arg === NULL) { if ($arg === null) {
} elseif ($this->comment) { } elseif ($this->comment) {
return "(offset $arg)"; return "(offset $arg)";
} else { } else {
$this->offset = (int) $arg; $this->offset = Helpers::intVal($arg);
} }
return ''; return '';
@@ -580,7 +592,7 @@ final class Translator
if ($matches[8]) { // SQL identifier substitution if ($matches[8]) { // SQL identifier substitution
$m = substr($matches[8], 0, -1); $m = substr($matches[8], 0, -1);
$m = $this->connection->getSubstitutes()->$m; $m = $this->connection->getSubstitutes()->$m;
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier return $matches[9] == '' ? $this->formatValue($m, false) : $m . $matches[9]; // value or identifier
} }
throw new \Exception('this should be never executed'); throw new \Exception('this should be never executed');
@@ -597,12 +609,11 @@ final class Translator
{ {
$value = $this->connection->substitute($value); $value = $this->connection->substitute($value);
$parts = explode('.', $value); $parts = explode('.', $value);
foreach ($parts as & $v) { foreach ($parts as &$v) {
if ($v !== '*') { if ($v !== '*') {
$v = $this->driver->escapeIdentifier($v); $v = $this->driver->escapeIdentifier($v);
} }
} }
return implode('.', $parts); return implode('.', $parts);
} }
} }

View File

@@ -24,9 +24,9 @@ class Type
TIME = 't', TIME = 't',
TIME_INTERVAL = 'ti'; TIME_INTERVAL = 'ti';
final public function __construct() final public function __construct()
{ {
throw new \LogicException('Cannot instantiate static class ' . __CLASS__); throw new \LogicException('Cannot instantiate static class ' . __CLASS__);
} }
} }

View File

@@ -22,8 +22,8 @@ class dibi
/** version */ /** version */
const const
VERSION = '3.0.1', VERSION = '3.1.1',
REVISION = 'released on 2015-12-16'; REVISION = 'released on 2018-03-09';
/** sorting order */ /** sorting order */
const const
@@ -49,12 +49,6 @@ class dibi
FIELD_DATETIME = Type::DATETIME, FIELD_DATETIME = Type::DATETIME,
FIELD_TIME = Type::TIME; FIELD_TIME = Type::TIME;
/** @var Dibi\Connection[] Connection registry storage for DibiConnection objects */
private static $registry = [];
/** @var Dibi\Connection Current connection */
private static $connection;
/** @var string Last SQL command @see dibi::query() */ /** @var string Last SQL command @see dibi::query() */
public static $sql; public static $sql;
@@ -70,6 +64,12 @@ class dibi
/** @var string Default dibi driver */ /** @var string Default dibi driver */
public static $defaultDriver = 'mysqli'; public static $defaultDriver = 'mysqli';
/** @var Dibi\Connection[] Connection registry storage for DibiConnection objects */
private static $registry = [];
/** @var Dibi\Connection Current connection */
private static $connection;
/** /**
* Static class - cannot be instantiated. * Static class - cannot be instantiated.
@@ -90,7 +90,7 @@ class dibi
* @return Dibi\Connection * @return Dibi\Connection
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function connect($config = [], $name = 0) public static function connect($config = [], $name = '0')
{ {
return self::$connection = self::$registry[$name] = new Dibi\Connection($config, $name); return self::$connection = self::$registry[$name] = new Dibi\Connection($config, $name);
} }
@@ -107,12 +107,12 @@ class dibi
/** /**
* Returns TRUE when connection was established. * Returns true when connection was established.
* @return bool * @return bool
*/ */
public static function isConnected() public static function isConnected()
{ {
return (self::$connection !== NULL) && self::$connection->isConnected(); return (self::$connection !== null) && self::$connection->isConnected();
} }
@@ -122,10 +122,10 @@ class dibi
* @return Dibi\Connection * @return Dibi\Connection
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function getConnection($name = NULL) public static function getConnection($name = null)
{ {
if ($name === NULL) { if ($name === null) {
if (self::$connection === NULL) { if (self::$connection === null) {
throw new Dibi\Exception('Dibi is not connected to database.'); throw new Dibi\Exception('Dibi is not connected to database.');
} }
@@ -167,7 +167,7 @@ class dibi
/** /**
* Generates and executes SQL query - Monostate for Dibi\Connection::query(). * Generates and executes SQL query - Monostate for Dibi\Connection::query().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return Dibi\Result|int result set object (if any) * @return Dibi\Result|int result set or number of affected rows
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function query($args) public static function query($args)
@@ -180,7 +180,7 @@ class dibi
/** /**
* Executes the SQL query - Monostate for Dibi\Connection::nativeQuery(). * Executes the SQL query - Monostate for Dibi\Connection::nativeQuery().
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\Result|int result set object (if any) * @return Dibi\Result|int result set or number of affected rows
*/ */
public static function nativeQuery($sql) public static function nativeQuery($sql)
{ {
@@ -241,7 +241,7 @@ class dibi
/** /**
* Executes SQL query and fetch first column - Monostate for Dibi\Connection::query() & fetchSingle(). * Executes SQL query and fetch first column - Monostate for Dibi\Connection::query() & fetchSingle().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return string * @return mixed
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function fetchSingle($args) public static function fetchSingle($args)
@@ -254,7 +254,7 @@ class dibi
/** /**
* Executes SQL query and fetch pairs - Monostate for Dibi\Connection::query() & fetchPairs(). * Executes SQL query and fetch pairs - Monostate for Dibi\Connection::query() & fetchPairs().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return string * @return array
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function fetchPairs($args) public static function fetchPairs($args)
@@ -277,12 +277,11 @@ class dibi
/** /**
* Gets the number of affected rows. Alias for getAffectedRows(). * @deprecated
* @return int number of rows
* @throws Dibi\Exception
*/ */
public static function affectedRows() public static function affectedRows()
{ {
trigger_error(__METHOD__ . '() is deprecated, use getAffectedRows()', E_USER_DEPRECATED);
return self::getConnection()->getAffectedRows(); return self::getConnection()->getAffectedRows();
} }
@@ -294,20 +293,18 @@ class dibi
* @return int * @return int
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function getInsertId($sequence = NULL) public static function getInsertId($sequence = null)
{ {
return self::getConnection()->getInsertId($sequence); return self::getConnection()->getInsertId($sequence);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId(). * @deprecated
* @param string optional sequence name
* @return int
* @throws Dibi\Exception
*/ */
public static function insertId($sequence = NULL) public static function insertId($sequence = null)
{ {
trigger_error(__METHOD__ . '() is deprecated, use getInsertId()', E_USER_DEPRECATED);
return self::getConnection()->getInsertId($sequence); return self::getConnection()->getInsertId($sequence);
} }
@@ -318,19 +315,19 @@ class dibi
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function begin($savepoint = NULL) public static function begin($savepoint = null)
{ {
self::getConnection()->begin($savepoint); self::getConnection()->begin($savepoint);
} }
/** /**
* Commits statements in a transaction - Monostate for Dibi\Connection::commit($savepoint = NULL). * Commits statements in a transaction - Monostate for Dibi\Connection::commit($savepoint = null).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function commit($savepoint = NULL) public static function commit($savepoint = null)
{ {
self::getConnection()->commit($savepoint); self::getConnection()->commit($savepoint);
} }
@@ -342,7 +339,7 @@ class dibi
* @return void * @return void
* @throws Dibi\Exception * @throws Dibi\Exception
*/ */
public static function rollback($savepoint = NULL) public static function rollback($savepoint = null)
{ {
self::getConnection()->rollback($savepoint); self::getConnection()->rollback($savepoint);
} }
@@ -382,7 +379,7 @@ class dibi
/** /**
* @param string column name * @param mixed column name
* @return Dibi\Fluent * @return Dibi\Fluent
*/ */
public static function select($args) public static function select($args)
@@ -446,9 +443,8 @@ class dibi
* @param bool return output instead of printing it? * @param bool return output instead of printing it?
* @return string * @return string
*/ */
public static function dump($sql = NULL, $return = FALSE) public static function dump($sql = null, $return = false)
{ {
return Dibi\Helpers::dump($sql, $return); return Dibi\Helpers::dump($sql, $return);
} }
} }

View File

@@ -13,9 +13,7 @@ namespace Dibi;
*/ */
class Exception extends \Exception class Exception extends \Exception
{ {
use Strict; /** @var string|null */
/** @var string|NULL */
private $sql; private $sql;
@@ -24,10 +22,11 @@ class Exception extends \Exception
* @param string Message describing the exception * @param string Message describing the exception
* @param mixed * @param mixed
* @param string SQL command * @param string SQL command
* @param \Exception
*/ */
public function __construct($message = NULL, $code = 0, $sql = NULL) public function __construct($message = '', $code = 0, $sql = null, \Exception $previous = null)
{ {
parent::__construct($message); parent::__construct($message, 0, $previous);
$this->code = $code; $this->code = $code;
$this->sql = $sql; $this->sql = $sql;
} }
@@ -49,7 +48,6 @@ class Exception extends \Exception
{ {
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : ''); return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
} }
} }
@@ -66,8 +64,6 @@ class DriverException extends Exception
*/ */
class PcreException extends Exception class PcreException extends Exception
{ {
use Strict;
public function __construct($message = '%msg.') public function __construct($message = '%msg.')
{ {
static $messages = [ static $messages = [
@@ -108,7 +104,7 @@ class ProcedureException extends Exception
* @param int Some code * @param int Some code
* @param string SQL command * @param string SQL command
*/ */
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL) public function __construct($message = null, $code = 0, $severity = null, $sql = null)
{ {
parent::__construct($message, (int) $code, $sql); parent::__construct($message, (int) $code, $sql);
$this->severity = $severity; $this->severity = $severity;
@@ -123,7 +119,6 @@ class ProcedureException extends Exception
{ {
$this->severity; $this->severity;
} }
} }

View File

@@ -30,7 +30,7 @@ interface Driver
* @return void * @return void
* @throws Exception * @throws Exception
*/ */
function connect(array & $config); function connect(array &$config);
/** /**
* Disconnects from a database. * Disconnects from a database.
@@ -42,20 +42,20 @@ interface Driver
/** /**
* Internal: Executes the SQL query. * Internal: Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return ResultDriver|NULL * @return ResultDriver|null
* @throws DriverException * @throws DriverException
*/ */
function query($sql); function query($sql);
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|false number of rows or false on error
*/ */
function getAffectedRows(); function getAffectedRows();
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|false int on success or false on failure
*/ */
function getInsertId($sequence); function getInsertId($sequence);
@@ -65,7 +65,7 @@ interface Driver
* @return void * @return void
* @throws DriverException * @throws DriverException
*/ */
function begin($savepoint = NULL); function begin($savepoint = null);
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
@@ -73,7 +73,7 @@ interface Driver
* @return void * @return void
* @throws DriverException * @throws DriverException
*/ */
function commit($savepoint = NULL); function commit($savepoint = null);
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
@@ -81,7 +81,7 @@ interface Driver
* @return void * @return void
* @throws DriverException * @throws DriverException
*/ */
function rollback($savepoint = NULL); function rollback($savepoint = null);
/** /**
* Returns the connection resource. * Returns the connection resource.
@@ -97,19 +97,39 @@ interface Driver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @return string encoded value * @return string encoded value
*/ */
function escapeText($value); function escapeText($value);
/**
* @param string
* @return string
*/
function escapeBinary($value); function escapeBinary($value);
/**
* @param string
* @return string
*/
function escapeIdentifier($value); function escapeIdentifier($value);
/**
* @param bool
* @return string
*/
function escapeBool($value); function escapeBool($value);
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
function escapeDate($value); function escapeDate($value);
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
function escapeDateTime($value); function escapeDateTime($value);
/** /**
@@ -122,10 +142,12 @@ interface Driver
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|null
* @param int|null
* @return void * @return void
*/ */
function applyLimit(& $sql, $limit, $offset); function applyLimit(&$sql, $limit, $offset);
} }
@@ -144,14 +166,14 @@ interface ResultDriver
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record * @return bool true on success, false if unable to seek to specified record
* @throws Exception * @throws Exception
*/ */
function seek($row); function seek($row);
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool true for associative array, false for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal * @internal
*/ */
@@ -182,7 +204,6 @@ interface ResultDriver
* @return string * @return string
*/ */
function unescapeBinary($value); function unescapeBinary($value);
} }
@@ -218,5 +239,4 @@ interface Reflector
* @return array * @return array
*/ */
function getForeignKeys($table); function getForeignKeys($table);
} }

View File

@@ -15,29 +15,32 @@ spl_autoload_register(function ($class) {
static $map = [ static $map = [
'dibi' => 'dibi.php', 'dibi' => 'dibi.php',
'Dibi\Bridges\Nette\DibiExtension22' => 'Bridges/Nette/DibiExtension22.php', 'Dibi\Bridges\Nette\DibiExtension22' => 'Bridges/Nette/DibiExtension22.php',
'Dibi\Bridges\Nette\DibiExtension21' => 'Bridges/Nette/DibiExtension21.php',
'Dibi\Bridges\Nette\Panel' => 'Bridges/Nette/Panel.php',
'Dibi\Bridges\Tracy\Panel' => 'Bridges/Tracy/Panel.php', 'Dibi\Bridges\Tracy\Panel' => 'Bridges/Tracy/Panel.php',
'Dibi\Connection' => 'Connection.php', 'Dibi\Connection' => 'Connection.php',
'Dibi\ConstraintViolationException' => 'exceptions.php',
'Dibi\DataSource' => 'DataSource.php', 'Dibi\DataSource' => 'DataSource.php',
'Dibi\DateTime' => 'DateTime.php', 'Dibi\DateTime' => 'DateTime.php',
'Dibi\Driver' => 'interfaces.php', 'Dibi\Driver' => 'interfaces.php',
'Dibi\DriverException' => 'exceptions.php', 'Dibi\DriverException' => 'exceptions.php',
'Dibi\Drivers\FirebirdDriver' => 'Drivers/FirebirdDriver.php', 'Dibi\Drivers\FirebirdDriver' => 'Drivers/FirebirdDriver.php',
'Dibi\Drivers\SqlsrvDriver' => 'Drivers/SqlsrvDriver.php', 'Dibi\Drivers\MsSqlDriver' => 'Drivers/MsSqlDriver.php',
'Dibi\Drivers\SqlsrvReflector' => 'Drivers/SqlsrvReflector.php', 'Dibi\Drivers\MsSqlReflector' => 'Drivers/MsSqlReflector.php',
'Dibi\Drivers\MySqlDriver' => 'Drivers/MySqlDriver.php', 'Dibi\Drivers\MySqlDriver' => 'Drivers/MySqlDriver.php',
'Dibi\Drivers\MySqliDriver' => 'Drivers/MySqliDriver.php',
'Dibi\Drivers\MySqlReflector' => 'Drivers/MySqlReflector.php', 'Dibi\Drivers\MySqlReflector' => 'Drivers/MySqlReflector.php',
'Dibi\Drivers\MySqliDriver' => 'Drivers/MySqliDriver.php',
'Dibi\Drivers\OdbcDriver' => 'Drivers/OdbcDriver.php', 'Dibi\Drivers\OdbcDriver' => 'Drivers/OdbcDriver.php',
'Dibi\Drivers\OracleDriver' => 'Drivers/OracleDriver.php', 'Dibi\Drivers\OracleDriver' => 'Drivers/OracleDriver.php',
'Dibi\Drivers\PdoDriver' => 'Drivers/PdoDriver.php', 'Dibi\Drivers\PdoDriver' => 'Drivers/PdoDriver.php',
'Dibi\Drivers\PostgreDriver' => 'Drivers/PostgreDriver.php', 'Dibi\Drivers\PostgreDriver' => 'Drivers/PostgreDriver.php',
'Dibi\Drivers\Sqlite3Driver' => 'Drivers/Sqlite3Driver.php', 'Dibi\Drivers\Sqlite3Driver' => 'Drivers/Sqlite3Driver.php',
'Dibi\Drivers\SqliteReflector' => 'Drivers/SqliteReflector.php', 'Dibi\Drivers\SqliteReflector' => 'Drivers/SqliteReflector.php',
'Dibi\Drivers\SqlsrvDriver' => 'Drivers/SqlsrvDriver.php',
'Dibi\Drivers\SqlsrvReflector' => 'Drivers/SqlsrvReflector.php',
'Dibi\Event' => 'Event.php', 'Dibi\Event' => 'Event.php',
'Dibi\Exception' => 'exceptions.php', 'Dibi\Exception' => 'exceptions.php',
'Dibi\Expression' => 'Expression.php',
'Dibi\Fluent' => 'Fluent.php', 'Dibi\Fluent' => 'Fluent.php',
'Dibi\ForeignKeyConstraintViolationException' => 'exceptions.php',
'Dibi\HashMap' => 'HashMap.php', 'Dibi\HashMap' => 'HashMap.php',
'Dibi\HashMapBase' => 'HashMap.php', 'Dibi\HashMapBase' => 'HashMap.php',
'Dibi\Helpers' => 'Helpers.php', 'Dibi\Helpers' => 'Helpers.php',
@@ -46,6 +49,7 @@ spl_autoload_register(function ($class) {
'Dibi\Loggers\FileLogger' => 'Loggers/FileLogger.php', 'Dibi\Loggers\FileLogger' => 'Loggers/FileLogger.php',
'Dibi\Loggers\FirePhpLogger' => 'Loggers/FirePhpLogger.php', 'Dibi\Loggers\FirePhpLogger' => 'Loggers/FirePhpLogger.php',
'Dibi\NotImplementedException' => 'exceptions.php', 'Dibi\NotImplementedException' => 'exceptions.php',
'Dibi\NotNullConstraintViolationException' => 'exceptions.php',
'Dibi\NotSupportedException' => 'exceptions.php', 'Dibi\NotSupportedException' => 'exceptions.php',
'Dibi\PcreException' => 'exceptions.php', 'Dibi\PcreException' => 'exceptions.php',
'Dibi\ProcedureException' => 'exceptions.php', 'Dibi\ProcedureException' => 'exceptions.php',
@@ -63,8 +67,8 @@ spl_autoload_register(function ($class) {
'Dibi\Strict' => 'Strict.php', 'Dibi\Strict' => 'Strict.php',
'Dibi\Translator' => 'Translator.php', 'Dibi\Translator' => 'Translator.php',
'Dibi\Type' => 'Type.php', 'Dibi\Type' => 'Type.php',
'Dibi\UniqueConstraintViolationException' => 'exceptions.php',
], $old2new = [ ], $old2new = [
'Dibi' => 'dibi.php',
'DibiColumnInfo' => 'Dibi\Reflection\Column', 'DibiColumnInfo' => 'Dibi\Reflection\Column',
'DibiConnection' => 'Dibi\Connection', 'DibiConnection' => 'Dibi\Connection',
'DibiDatabaseInfo' => 'Dibi\Reflection\Database', 'DibiDatabaseInfo' => 'Dibi\Reflection\Database',
@@ -84,11 +88,11 @@ spl_autoload_register(function ($class) {
'DibiLiteral' => 'Dibi\Literal', 'DibiLiteral' => 'Dibi\Literal',
'DibiMsSql2005Driver' => 'Dibi\Drivers\SqlsrvDriver', 'DibiMsSql2005Driver' => 'Dibi\Drivers\SqlsrvDriver',
'DibiMsSql2005Reflector' => 'Dibi\Drivers\SqlsrvReflector', 'DibiMsSql2005Reflector' => 'Dibi\Drivers\SqlsrvReflector',
'DibiMsSqlDriver' => 'Dibi\Drivers\MsSqlDriver',
'DibiMsSqlReflector' => 'Dibi\Drivers\MsSqlReflector',
'DibiMySqlDriver' => 'Dibi\Drivers\MySqlDriver', 'DibiMySqlDriver' => 'Dibi\Drivers\MySqlDriver',
'DibiMySqliDriver' => 'Dibi\Drivers\MySqliDriver', 'DibiMySqliDriver' => 'Dibi\Drivers\MySqliDriver',
'DibiMySqlReflector' => 'Dibi\Drivers\MySqlReflector', 'DibiMySqlReflector' => 'Dibi\Drivers\MySqlReflector',
'DibiNette21Extension' => 'Dibi\Bridges\Nette\DibiExtension21',
'DibiNettePanel' => 'Dibi\Bridges\Nette\Panel',
'DibiNotImplementedException' => 'Dibi\NotImplementedException', 'DibiNotImplementedException' => 'Dibi\NotImplementedException',
'DibiNotSupportedException' => 'Dibi\NotSupportedException', 'DibiNotSupportedException' => 'Dibi\NotSupportedException',
'DibiOdbcDriver' => 'Dibi\Drivers\OdbcDriver', 'DibiOdbcDriver' => 'Dibi\Drivers\OdbcDriver',

4
tests/.coveralls.yml Normal file
View File

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

View File

@@ -42,3 +42,48 @@ dsn = "odbc:Driver={Microsoft Access Driver (*.mdb)};Dbq=data/odbc.mdb"
username = username =
password = password =
system = odbc 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

View File

@@ -57,6 +57,20 @@ username =
password = password =
system = odbc 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] [sqlsrv]
driver = sqlsrv driver = sqlsrv
host = (local) host = (local)

View File

@@ -4,8 +4,8 @@
* @dataProvider ../databases.ini * @dataProvider ../databases.ini
*/ */
use Tester\Assert;
use Dibi\Connection; use Dibi\Connection;
use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
@@ -20,7 +20,7 @@ test(function () use ($config) {
test(function () use ($config) { // lazy test(function () use ($config) { // lazy
$conn = new Connection($config + ['lazy' => TRUE]); $conn = new Connection($config + ['lazy' => true]);
Assert::false($conn->isConnected()); Assert::false($conn->isConnected());
$conn->query('SELECT 1'); $conn->query('SELECT 1');
@@ -29,10 +29,22 @@ test(function () use ($config) { // lazy
test(function () use ($config) { // query string test(function () use ($config) { // query string
$conn = new Connection(http_build_query($config, NULL, '&')); $conn = new Connection(http_build_query($config, '', '&'));
Assert::true($conn->isConnected()); Assert::true($conn->isConnected());
Assert::null($conn->getConfig('lazy')); Assert::null($conn->getConfig('lazy'));
Assert::same($config['driver'], $conn->getConfig('driver')); Assert::same($config['driver'], $conn->getConfig('driver'));
Assert::type('Dibi\Driver', $conn->getDriver()); 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());
});

View File

@@ -4,8 +4,8 @@
* @dataProvider ../databases.ini * @dataProvider ../databases.ini
*/ */
use Tester\Assert;
use Dibi\Row; use Dibi\Row;
use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
@@ -14,7 +14,7 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
// fetch a single value // 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()); Assert::same('Chair', $res->fetchSingle());
@@ -61,9 +61,9 @@ Assert::equal([
// more complex association array // more complex association array
function query($conn) { function query($conn)
{
return $conn->query($conn->getConfig('system') === 'odbc' ? ' return $conn->query(in_array($conn->getConfig('system'), ['odbc', 'sqlsrv'], true) ? '
SELECT products.title, customers.name, orders.amount SELECT products.title, customers.name, orders.amount
FROM ([products] FROM ([products]
INNER JOIN [orders] ON [products.product_id] = [orders.product_id]) INNER JOIN [orders] ON [products.product_id] = [orders.product_id])
@@ -170,11 +170,11 @@ Assert::equal([
Assert::equal([ Assert::equal([
new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]), new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
new Row([ new Row([
'title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]), 'title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0), ]),
new Row([ new Row([
'title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]), 'title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0), ]),
new Row([ new Row([
'title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]), 'title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0), ]),
], query($conn)->fetchAssoc('@,=')); ], query($conn)->fetchAssoc('@,='));

View File

@@ -1,7 +1,7 @@
<?php <?php
use Tester\Assert;
use Dibi\Row; use Dibi\Row;
use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';

View File

@@ -1,7 +1,7 @@
<?php <?php
use Tester\Assert;
use Dibi\Fluent; use Dibi\Fluent;
use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';

View File

@@ -30,7 +30,7 @@ Assert::same(
(string) $fluent (string) $fluent
); );
$fluent->setFlag('IGNORE', FALSE); $fluent->setFlag('IGNORE', false);
Assert::same( Assert::same(
reformat('DELETE FROM [anotherTable] USING [thirdTable]'), reformat('DELETE FROM [anotherTable] USING [thirdTable]'),

View File

@@ -7,25 +7,31 @@ require __DIR__ . '/bootstrap.php';
class MockDriver extends Dibi\Drivers\SqlsrvDriver class MockDriver extends Dibi\Drivers\SqlsrvDriver
{ {
function __construct() public function __construct()
{} {
}
function connect(array & $config)
{}
function query($sql) public function connect(array &$config)
{
}
public function query($sql)
{ {
return $this; return $this;
} }
function getResultColumns()
public function getResultColumns()
{ {
return []; return [];
} }
function fetch($assoc)
public function fetch($assoc)
{ {
return FALSE; return false;
} }
} }
@@ -40,28 +46,28 @@ $fluent = $conn->select('*')
->orderBy('customer_id'); ->orderBy('customer_id');
Assert::same( 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 (string) $fluent
); );
$fluent->fetch(); $fluent->fetch();
Assert::same( 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 dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( 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 dibi::$sql
); );
$fluent->fetchAll(0, 3); $fluent->fetchAll(0, 3);
Assert::same( 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 dibi::$sql
); );
Assert::same( 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 (string) $fluent
); );
@@ -69,16 +75,16 @@ Assert::same(
$fluent->limit(0); $fluent->limit(0);
$fluent->fetch(); $fluent->fetch();
Assert::same( 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 dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( 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 dibi::$sql
); );
Assert::same( 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 (string) $fluent
); );
@@ -87,12 +93,12 @@ $fluent->removeClause('limit');
$fluent->removeClause('offset'); $fluent->removeClause('offset');
$fluent->fetch(); $fluent->fetch();
Assert::same( 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 dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( 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 dibi::$sql
); );
Assert::same( Assert::same(

View File

@@ -4,8 +4,8 @@
* @dataProvider ../databases.ini * @dataProvider ../databases.ini
*/ */
use Tester\Assert;
use Dibi\Row; use Dibi\Row;
use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
@@ -28,7 +28,7 @@ Assert::equal([
// more complex association array // more complex association array
if ($config['system'] !== 'odbc') { if (!in_array($config['system'], ['odbc', 'sqlsrv'], true)) {
$res = $conn->select(['products.title' => 'title', 'customers.name' => 'name'])->select('orders.amount')->as('amount') $res = $conn->select(['products.title' => 'title', 'customers.name' => 'name'])->select('orders.amount')->as('amount')
->from('products') ->from('products')
->innerJoin('orders')->using('(product_id)') ->innerJoin('orders')->using('(product_id)')
@@ -48,3 +48,8 @@ if ($config['system'] !== 'odbc') {
], ],
], $res->fetchAssoc('name,title')); ], $res->fetchAssoc('name,title'));
} }
// affected rows
$res = $conn->update('products', ['title' => 'new'])->execute();
Assert::same(3, $res);

View File

@@ -11,7 +11,7 @@ $conn = new Dibi\Connection($config);
$arr = [ $arr = [
'title' => 'Super Product', 'title' => 'Super Product',
'price' => 12, 'price' => 12,
'brand' => NULL, 'brand' => null,
]; ];
$fluent = $conn->insert('table', $arr) $fluent = $conn->insert('table', $arr)
@@ -22,7 +22,7 @@ Assert::same(
(string) $fluent (string) $fluent
); );
$fluent->setFlag('IGNORE', FALSE); $fluent->setFlag('IGNORE', false);
Assert::same( Assert::same(
reformat('INSERT DELAYED INTO [table] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL)'), reformat('INSERT DELAYED INTO [table] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL)'),

View File

@@ -26,19 +26,19 @@ Assert::same(
(string) $fluent (string) $fluent
); );
$fluent->from('table')->as('tableAlias') $fluent->from('table')->as('table.Alias')
->innerJoin('table1')->on('table.col = table1.col') ->innerJoin('table1')->on('table.col = table1.col')
->innerJoin('table2')->on('table.col = table2.col'); ->innerJoin('table2')->on('table.col = table2.col');
Assert::same( 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 (string) $fluent
); );
$fluent->from('anotherTable'); $fluent->from('anotherTable');
Assert::same( 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 (string) $fluent
); );
@@ -94,6 +94,7 @@ $fluent = $conn->select('*')
Assert::same( Assert::same(
reformat([ 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', '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', ' 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 (string) $fluent
@@ -141,10 +142,9 @@ if ($config['system'] === 'mysql') {
->limit(' 1; DROP TABLE users') ->limit(' 1; DROP TABLE users')
->offset(' 1; DROP TABLE users'); ->offset(' 1; DROP TABLE users');
Assert::same( Assert::error(function () use ($fluent) {
reformat(' SELECT * LIMIT 1 OFFSET 1'), (string) $fluent;
(string) $fluent }, E_USER_ERROR, "Expected number, ' 1; DROP TABLE users' given.");
);
} }

View File

@@ -11,7 +11,7 @@ $conn = new Dibi\Connection($config);
$arr = [ $arr = [
'title' => 'Super Product', 'title' => 'Super Product',
'price' => 12, 'price' => 12,
'brand' => NULL, 'brand' => null,
]; ];
$fluent = $conn->update('table', $arr) $fluent = $conn->update('table', $arr)

View File

@@ -6,19 +6,19 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
Assert::same(NULL, Helpers::getSuggestion([], '')); Assert::same(null, Helpers::getSuggestion([], ''));
Assert::same(NULL, Helpers::getSuggestion([], 'a')); Assert::same(null, Helpers::getSuggestion([], 'a'));
Assert::same(NULL, Helpers::getSuggestion(['a'], 'a')); Assert::same(null, Helpers::getSuggestion(['a'], 'a'));
Assert::same('a', Helpers::getSuggestion(['a', 'b'], '')); Assert::same('a', Helpers::getSuggestion(['a', 'b'], ''));
Assert::same('b', Helpers::getSuggestion(['a', 'b'], 'a')); // ignore 100% match Assert::same('b', Helpers::getSuggestion(['a', 'b'], 'a')); // ignore 100% match
Assert::same('a1', Helpers::getSuggestion(['a1', 'a2'], 'a')); // take first Assert::same('a1', Helpers::getSuggestion(['a1', 'a2'], 'a')); // take first
Assert::same(NULL, Helpers::getSuggestion(['aaa', 'bbb'], 'a')); Assert::same(null, Helpers::getSuggestion(['aaa', 'bbb'], 'a'));
Assert::same(NULL, Helpers::getSuggestion(['aaa', 'bbb'], 'ab')); Assert::same(null, Helpers::getSuggestion(['aaa', 'bbb'], 'ab'));
Assert::same(NULL, Helpers::getSuggestion(['aaa', 'bbb'], 'abc')); Assert::same(null, Helpers::getSuggestion(['aaa', 'bbb'], 'abc'));
Assert::same('bar', Helpers::getSuggestion(['foo', 'bar', 'baz'], 'baz')); Assert::same('bar', Helpers::getSuggestion(['foo', 'bar', 'baz'], 'baz'));
Assert::same('abcd', Helpers::getSuggestion(['abcd'], 'acbd')); Assert::same('abcd', Helpers::getSuggestion(['abcd'], 'acbd'));
Assert::same('abcd', Helpers::getSuggestion(['abcd'], 'axbd')); Assert::same('abcd', Helpers::getSuggestion(['abcd'], 'axbd'));
Assert::same(NULL, Helpers::getSuggestion(['abcd'], 'axyd')); Assert::same(null, Helpers::getSuggestion(['abcd'], 'axyd'));
/* /*

View File

@@ -0,0 +1,25 @@
<?php
use Dibi\Helpers;
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
Assert::same(0, Helpers::intVal(0));
Assert::same(0, Helpers::intVal('0'));
Assert::same(-10, Helpers::intVal('-10'));
Assert::same('12345678901234567890123456879', Helpers::intVal('12345678901234567890123456879'));
Assert::same('-12345678901234567890123456879', Helpers::intVal('-12345678901234567890123456879'));
Assert::exception(function () {
Helpers::intVal('');
}, 'Dibi\Exception', "Expected number, '' given.");
Assert::exception(function () {
Helpers::intVal('not number');
}, 'Dibi\Exception', "Expected number, 'not number' given.");
Assert::exception(function () {
Helpers::intVal(null);
}, 'Dibi\Exception', "Expected number, '' given.");

View File

@@ -1,7 +1,7 @@
<?php <?php
/** /**
* @dataProvider ../databases.ini !=odbc * @dataProvider ../databases.ini !=odbc, !=sqlsrv
*/ */
use Tester\Assert; use Tester\Assert;
@@ -13,9 +13,9 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
$info = $conn->query(' $info = $conn->query('
SELECT products.product_id, orders.order_id, customers.name, products.product_id + 1 AS [xXx] SELECT products.product_id, orders.order_id, customers.name, products.product_id + 1 AS [xXx]
FROM products FROM ([products]
INNER JOIN orders USING (product_id) INNER JOIN [orders] ON [products.product_id] = [orders.product_id])
INNER JOIN customers USING (customer_id) INNER JOIN [customers] ON [orders.customer_id] = [customers.customer_id]
')->getInfo(); ')->getInfo();
@@ -25,10 +25,10 @@ Assert::same(
); );
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') { if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'], true)) {
Assert::same( Assert::same(
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'], ['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
$info->getColumnNames(TRUE) $info->getColumnNames(true)
); );
} }
@@ -36,18 +36,18 @@ if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
$columns = $info->getColumns(); $columns = $info->getColumns();
Assert::same('product_id', $columns[0]->getName()); Assert::same('product_id', $columns[0]->getName());
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') { if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'], true)) {
Assert::same('products', $columns[0]->getTableName()); Assert::same('products', $columns[0]->getTableName());
} }
Assert::null($columns[0]->getVendorInfo('xxx')); Assert::null($columns[0]->getVendorInfo('xxx'));
if ($config['system'] !== 'sqlite') { if (!in_array($config['system'], ['sqlite', 'sqlsrv'], true)) {
Assert::same('i', $columns[0]->getType()); Assert::same('i', $columns[0]->getType());
} }
Assert::null($columns[0]->isNullable()); Assert::null($columns[0]->isNullable());
Assert::same('xXx', $columns[3]->getName()); Assert::same('xXx', $columns[3]->getName());
Assert::null($columns[3]->getTableName()); Assert::null($columns[3]->getTableName());
if ($config['system'] !== 'sqlite') { if (!in_array($config['system'], ['sqlite', 'sqlsrv'], true)) {
Assert::same('i', $columns[0]->getType()); Assert::same('i', $columns[0]->getType());
} }
Assert::null($columns[3]->isNullable()); Assert::null($columns[3]->isNullable());

View File

@@ -1,21 +1,23 @@
<?php <?php
use Tester\Assert;
use Dibi\Type; use Dibi\Type;
use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
class MockResult extends Dibi\Result class MockResult extends Dibi\Result
{ {
function __construct() public function __construct()
{} {
}
function test($row)
public function test($row)
{ {
$normalize = new ReflectionMethod('Dibi\Result', 'normalize'); $normalize = new ReflectionMethod('Dibi\Result', 'normalize');
$normalize->setAccessible(TRUE); $normalize->setAccessible(true);
$normalize->invokeArgs($this, [& $row]); $normalize->invokeArgs($this, [&$row]);
return $row; return $row;
} }
} }
@@ -25,21 +27,21 @@ test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::BOOL); $result->setType('col', Type::BOOL);
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::same(['col' => TRUE], $result->test(['col' => TRUE])); Assert::same(['col' => true], $result->test(['col' => true]));
Assert::same(['col' => FALSE], $result->test(['col' => FALSE])); Assert::same(['col' => false], $result->test(['col' => false]));
Assert::same(['col' => FALSE], $result->test(['col' => ''])); Assert::same(['col' => false], $result->test(['col' => '']));
Assert::same(['col' => FALSE], $result->test(['col' => '0'])); Assert::same(['col' => false], $result->test(['col' => '0']));
Assert::same(['col' => TRUE], $result->test(['col' => '1'])); Assert::same(['col' => true], $result->test(['col' => '1']));
Assert::same(['col' => TRUE], $result->test(['col' => 't'])); Assert::same(['col' => true], $result->test(['col' => 't']));
Assert::same(['col' => FALSE], $result->test(['col' => 'f'])); Assert::same(['col' => false], $result->test(['col' => 'f']));
Assert::same(['col' => TRUE], $result->test(['col' => 'T'])); Assert::same(['col' => true], $result->test(['col' => 'T']));
Assert::same(['col' => FALSE], $result->test(['col' => 'F'])); Assert::same(['col' => false], $result->test(['col' => 'F']));
Assert::same(['col' => FALSE], $result->test(['col' => 0])); Assert::same(['col' => false], $result->test(['col' => 0]));
Assert::same(['col' => FALSE], $result->test(['col' => 0.0])); Assert::same(['col' => false], $result->test(['col' => 0.0]));
Assert::same(['col' => TRUE], $result->test(['col' => 1])); Assert::same(['col' => true], $result->test(['col' => 1]));
Assert::same(['col' => TRUE], $result->test(['col' => 1.0])); Assert::same(['col' => true], $result->test(['col' => 1.0]));
}); });
@@ -47,9 +49,9 @@ test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::TEXT); $result->setType('col', Type::TEXT);
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::same(['col' => '1'], $result->test(['col' => TRUE])); Assert::same(['col' => '1'], $result->test(['col' => true]));
Assert::same(['col' => ''], $result->test(['col' => FALSE])); Assert::same(['col' => ''], $result->test(['col' => false]));
Assert::same(['col' => ''], $result->test(['col' => ''])); Assert::same(['col' => ''], $result->test(['col' => '']));
Assert::same(['col' => '0'], $result->test(['col' => '0'])); Assert::same(['col' => '0'], $result->test(['col' => '0']));
@@ -63,9 +65,9 @@ test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::FLOAT); $result->setType('col', Type::FLOAT);
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::same(['col' => 1.0], $result->test(['col' => TRUE])); Assert::same(['col' => 1.0], $result->test(['col' => true]));
Assert::same(['col' => 0.0], $result->test(['col' => FALSE])); Assert::same(['col' => 0.0], $result->test(['col' => false]));
Assert::same(['col' => 0.0], $result->test(['col' => ''])); Assert::same(['col' => 0.0], $result->test(['col' => '']));
Assert::same(['col' => 0.0], $result->test(['col' => '0'])); Assert::same(['col' => 0.0], $result->test(['col' => '0']));
@@ -135,11 +137,11 @@ test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::INTEGER); $result->setType('col', Type::INTEGER);
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::same(['col' => 1], $result->test(['col' => TRUE])); Assert::same(['col' => 1], $result->test(['col' => true]));
Assert::same(['col' => 0], $result->test(['col' => FALSE])); 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' => 0], $result->test(['col' => '0']));
Assert::same(['col' => 1], $result->test(['col' => '1'])); Assert::same(['col' => 1], $result->test(['col' => '1']));
Assert::same(['col' => 10], $result->test(['col' => '10'])); Assert::same(['col' => 10], $result->test(['col' => '10']));
@@ -161,14 +163,14 @@ test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::DATETIME); $result->setType('col', Type::DATETIME);
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::exception(function () use ($result) { Assert::exception(function () use ($result) {
$result->test(['col' => TRUE]); $result->test(['col' => true]);
}, 'Exception'); }, 'Exception');
Assert::same(['col' => NULL], $result->test(['col' => FALSE])); Assert::same(['col' => null], $result->test(['col' => false]));
Assert::same(['col' => NULL], $result->test(['col' => ''])); Assert::same(['col' => null], $result->test(['col' => '']));
Assert::same(['col' => NULL], $result->test(['col' => '0000-00-00'])); Assert::same(['col' => null], $result->test(['col' => '0000-00-00']));
Assert::equal(['col' => new Dibi\DateTime('00:00:00')], $result->test(['col' => '00:00:00'])); Assert::equal(['col' => new Dibi\DateTime('00:00:00')], $result->test(['col' => '00:00:00']));
Assert::equal(['col' => new Dibi\DateTime('2015-10-13')], $result->test(['col' => '2015-10-13'])); Assert::equal(['col' => new Dibi\DateTime('2015-10-13')], $result->test(['col' => '2015-10-13']));
Assert::equal(['col' => new Dibi\DateTime('2015-10-13 14:30')], $result->test(['col' => '2015-10-13 14:30'])); Assert::equal(['col' => new Dibi\DateTime('2015-10-13 14:30')], $result->test(['col' => '2015-10-13 14:30']));
@@ -180,14 +182,14 @@ test(function () {
$result->setType('col', Type::DATETIME); $result->setType('col', Type::DATETIME);
$result->setFormat(Type::DATETIME, 'Y-m-d H:i:s'); $result->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::exception(function () use ($result) { Assert::exception(function () use ($result) {
$result->test(['col' => TRUE]); $result->test(['col' => true]);
}, 'Exception'); }, 'Exception');
Assert::same(['col' => NULL], $result->test(['col' => FALSE])); Assert::same(['col' => null], $result->test(['col' => false]));
Assert::same(['col' => NULL], $result->test(['col' => ''])); Assert::same(['col' => null], $result->test(['col' => '']));
Assert::same(['col' => NULL], $result->test(['col' => '0000-00-00'])); Assert::same(['col' => null], $result->test(['col' => '0000-00-00']));
Assert::same(['col' => date('Y-m-d 00:00:00')], $result->test(['col' => '00:00:00'])); Assert::same(['col' => date('Y-m-d 00:00:00')], $result->test(['col' => '00:00:00']));
Assert::equal(['col' => '2015-10-13 00:00:00'], $result->test(['col' => '2015-10-13'])); Assert::equal(['col' => '2015-10-13 00:00:00'], $result->test(['col' => '2015-10-13']));
Assert::equal(['col' => '2015-10-13 14:30:00'], $result->test(['col' => '2015-10-13 14:30'])); Assert::equal(['col' => '2015-10-13 14:30:00'], $result->test(['col' => '2015-10-13 14:30']));
@@ -198,14 +200,14 @@ test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::DATE); $result->setType('col', Type::DATE);
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::exception(function () use ($result) { Assert::exception(function () use ($result) {
$result->test(['col' => TRUE]); $result->test(['col' => true]);
}, 'Exception'); }, 'Exception');
Assert::same(['col' => NULL], $result->test(['col' => FALSE])); Assert::same(['col' => null], $result->test(['col' => false]));
Assert::same(['col' => NULL], $result->test(['col' => ''])); Assert::same(['col' => null], $result->test(['col' => '']));
Assert::same(['col' => NULL], $result->test(['col' => '0000-00-00'])); Assert::same(['col' => null], $result->test(['col' => '0000-00-00']));
Assert::equal(['col' => new Dibi\DateTime('2015-10-13')], $result->test(['col' => '2015-10-13'])); Assert::equal(['col' => new Dibi\DateTime('2015-10-13')], $result->test(['col' => '2015-10-13']));
}); });
@@ -214,14 +216,14 @@ test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::TIME); $result->setType('col', Type::TIME);
Assert::same(['col' => NULL], $result->test(['col' => NULL])); Assert::same(['col' => null], $result->test(['col' => null]));
Assert::exception(function () use ($result) { Assert::exception(function () use ($result) {
$result->test(['col' => TRUE]); $result->test(['col' => true]);
}, 'Exception'); }, 'Exception');
Assert::same(['col' => NULL], $result->test(['col' => FALSE])); Assert::same(['col' => null], $result->test(['col' => false]));
Assert::same(['col' => NULL], $result->test(['col' => ''])); Assert::same(['col' => null], $result->test(['col' => '']));
Assert::same(['col' => NULL], $result->test(['col' => '0000-00-00'])); Assert::same(['col' => null], $result->test(['col' => '0000-00-00']));
Assert::equal(['col' => new Dibi\DateTime('00:00:00')], $result->test(['col' => '00:00:00'])); Assert::equal(['col' => new Dibi\DateTime('00:00:00')], $result->test(['col' => '00:00:00']));
Assert::equal(['col' => new Dibi\DateTime('14:30')], $result->test(['col' => '14:30'])); Assert::equal(['col' => new Dibi\DateTime('14:30')], $result->test(['col' => '14:30']));
}); });

View 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());

View File

@@ -9,7 +9,10 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$tests = function ($conn) { $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+ // MsSQL2012+
if (version_compare($version, '11.0') >= 0) { if (version_compare($version, '11.0') >= 0) {
@@ -32,31 +35,43 @@ $tests = function ($conn) {
); );
// Offset invalid // Offset invalid
Assert::same( Assert::error(
'SELECT 1', function () use ($conn) {
$conn->translate('SELECT 1 %ofs', -10) $conn->translate('SELECT 1 %ofs', -10);
},
'Dibi\NotSupportedException',
'Negative offset or limit.'
); );
// Limit invalid // Limit invalid
Assert::same( Assert::error(
'SELECT 1', function () use ($conn) {
$conn->translate('SELECT 1 %lmt', -10) $conn->translate('SELECT 1 %lmt', -10);
},
'Dibi\NotSupportedException',
'Negative offset or limit.'
); );
// Limit invalid, offset valid // Limit invalid, offset valid
Assert::same( Assert::error(
'SELECT 1', function () use ($conn) {
$conn->translate('SELECT 1 %ofs %lmt', 10, -10) $conn->translate('SELECT 1 %ofs %lmt', 10, -10);
},
'Dibi\NotSupportedException',
'Negative offset or limit.'
); );
// Limit valid, offset invalid // Limit valid, offset invalid
Assert::same( Assert::error(
'SELECT 1', function () use ($conn) {
$conn->translate('SELECT 1 %ofs %lmt', -10, 10) $conn->translate('SELECT 1 %ofs %lmt', -10, 10);
},
'Dibi\NotSupportedException',
'Negative offset or limit.'
); );
} else { } else {
Assert::same( Assert::same(
'SELECT TOP 1 * FROM (SELECT 1) t', 'SELECT TOP (1) * FROM (SELECT 1) t',
$conn->translate('SELECT 1 %lmt', 1) $conn->translate('SELECT 1 %lmt', 1)
); );

View File

@@ -11,27 +11,37 @@ class TestClass
public $public; public $public;
protected $protected;
public static $publicStatic; public static $publicStatic;
protected $protected;
public function publicMethod() public function publicMethod()
{} {
}
public static function publicMethodStatic() public static function publicMethodStatic()
{} {
}
protected function protectedMethod() protected function protectedMethod()
{} {
}
protected static function protectedMethodS() protected static function protectedMethodS()
{} {
}
public function getBar() public function getBar()
{ {
return 123; return 123;
} }
public function isFoo() public function isFoo()
{ {
return 456; return 456;

View File

@@ -13,5 +13,5 @@ $translator = new Dibi\Translator($conn);
$datetime = new DateTime('1978-01-23 00:00:00'); $datetime = new DateTime('1978-01-23 00:00:00');
Assert::equal($datetime->format('U'), $translator->formatValue(new DateTime($datetime->format('c')), NULL)); Assert::equal($datetime->format('U'), $translator->formatValue(new DateTime($datetime->format('c')), null));
Assert::equal($datetime->format('U'), $translator->formatValue(new DateTimeImmutable($datetime->format('c')), NULL)); Assert::equal($datetime->format('U'), $translator->formatValue(new DateTimeImmutable($datetime->format('c')), null));

View File

@@ -33,7 +33,7 @@ FROM [customers] /* ... */'),
$conn->translate(' $conn->translate('
SELECT * SELECT *
FROM %if', TRUE, '[customers] %else [products]' FROM %if', true, '[customers] %else [products]'
)); ));
@@ -51,8 +51,8 @@ WHERE [id] > 0
SELECT * SELECT *
FROM [people] FROM [people]
WHERE [id] > 0 WHERE [id] > 0
%if', FALSE, 'AND [foo]=%i', 1, ' %if', false, 'AND [foo]=%i', 1, '
%else %if', TRUE, 'AND [bar]=%i', 1, ' %else %if', true, 'AND [bar]=%i', 1, '
')); '));
@@ -70,8 +70,8 @@ WHERE
SELECT * SELECT *
FROM [customers] FROM [customers]
WHERE WHERE
%if', TRUE, '[name] LIKE %s', 'xxx', ' %if', true, '[name] LIKE %s', 'xxx', '
%if', FALSE, 'AND [admin]=1 %end %if', false, 'AND [admin]=1 %end
%else 1 LIMIT 10 %end' %else 1 LIMIT 10 %end'
)); ));
@@ -81,7 +81,7 @@ Assert::same(
'SELECT * FROM foo /* (limit 3) (offset 5) */', 'SELECT * FROM foo /* (limit 3) (offset 5) */',
$conn->translate( $conn->translate(
'SELECT * FROM foo', 'SELECT * FROM foo',
'%if', FALSE, '%if', false,
'%lmt', 3, '%lmt', 3,
'%ofs', 5, '%ofs', 5,
'%end' '%end'

View File

@@ -4,28 +4,29 @@
* @dataProvider ../databases.ini * @dataProvider ../databases.ini
*/ */
use Tester\Assert;
use Dibi\DateTime; use Dibi\DateTime;
use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config + ['formatDateTime' => "'Y-m-d H:i:s'", 'formatDate' => "'Y-m-d'"]); $conn = new Dibi\Connection($config + ['formatDateTime' => "'Y-m-d H:i:s.u'", 'formatDate' => "'Y-m-d'"]);
// dibi detects INSERT or REPLACE command & booleans // dibi detects INSERT or REPLACE command & booleans
Assert::same( Assert::same(
reformat("REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)"), reformat("REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)"),
$conn->translate('REPLACE INTO [products]', [ $conn->translate('REPLACE INTO [products]', [
'title' => 'Drticka', 'title' => 'Drticka',
'price' => 318, 'price' => 318,
])); ])
);
// multiple INSERT command // multiple INSERT command
$array = [ $array = [
'title' => 'Super Product', 'title' => 'Super Product',
'price' => 12, 'price' => 12,
'brand' => NULL, 'brand' => null,
]; ];
Assert::same( Assert::same(
reformat('INSERT INTO [products] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL)'), reformat('INSERT INTO [products] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL)'),
@@ -49,9 +50,10 @@ Assert::same(
Assert::same( Assert::same(
reformat("UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123"), reformat("UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123"),
$conn->translate('UPDATE [colors] SET', [ $conn->translate('UPDATE [colors] SET', [
'color' => 'blue', 'color' => 'blue',
'order' => 12, 'order' => 12,
], 'WHERE [id]=%i', 123)); ], 'WHERE [id]=%i', 123)
);
// IN array // IN array
@@ -95,7 +97,7 @@ Assert::same(
$conn->translate('TEST %and', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()']) $conn->translate('TEST %and', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'])
); );
//
$where = []; $where = [];
$where[] = '[age] > 20'; $where[] = '[age] > 20';
$where[] = '[email] IS NOT NULL'; $where[] = '[email] IS NOT NULL';
@@ -106,7 +108,7 @@ Assert::same(
$where = []; $where = [];
$where['age'] = NULL; $where['age'] = null;
$where['email'] = 'ahoj'; $where['email'] = 'ahoj';
$where['id%l'] = [10, 20, 30]; $where['id%l'] = [10, 20, 30];
Assert::same( Assert::same(
@@ -128,8 +130,8 @@ $order = [
'field2' => 'desc', 'field2' => 'desc',
'field3' => 1, 'field3' => 1,
'field4' => -1, 'field4' => -1,
'field5' => TRUE, 'field5' => true,
'field6' => FALSE, 'field6' => false,
]; ];
Assert::same( Assert::same(
reformat('SELECT * FROM [people] ORDER BY [field1] ASC, [field2] DESC, [field3] ASC, [field4] DESC, [field5] ASC, [field6] DESC'), reformat('SELECT * FROM [people] ORDER BY [field1] ASC, [field2] DESC, [field3] ASC, [field4] DESC, [field5] ASC, [field6] DESC'),
@@ -141,6 +143,7 @@ Assert::same(
Assert::same( Assert::same(
reformat([ reformat([
'odbc' => 'SELECT TOP 2 * FROM (SELECT * FROM [products] ) t', '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', 'SELECT * FROM [products] LIMIT 2',
]), ]),
$conn->translate('SELECT * FROM [products] %lmt', 2) $conn->translate('SELECT * FROM [products] %lmt', 2)
@@ -153,7 +156,10 @@ if ($config['system'] === 'odbc') {
} else { } else {
// with limit = 2, offset = 1 // with limit = 2, offset = 1
Assert::same( 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) $conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1)
); );
@@ -162,6 +168,7 @@ if ($config['system'] === 'odbc') {
reformat([ reformat([
'mysql' => 'SELECT * FROM `products` LIMIT 18446744073709551615 OFFSET 50', 'mysql' => 'SELECT * FROM `products` LIMIT 18446744073709551615 OFFSET 50',
'postgre' => 'SELECT * FROM "products" OFFSET 50', 'postgre' => 'SELECT * FROM "products" OFFSET 50',
'sqlsrv' => 'SELECT * FROM [products] OFFSET 50 ROWS',
'SELECT * FROM [products] LIMIT -1 OFFSET 50', 'SELECT * FROM [products] LIMIT -1 OFFSET 50',
]), ]),
$conn->translate('SELECT * FROM [products] %ofs', 50) $conn->translate('SELECT * FROM [products] %ofs', 50)
@@ -173,22 +180,26 @@ if ($config['system'] === 'odbc') {
Assert::same( Assert::same(
reformat([ reformat([
'odbc' => 'INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9]) VALUES (#09/26/1212 00:00:00#, #12/31/1969 22:13:20#, #09/26/1212#, #09/26/1212 00:00:00#, #12/31/1969#, #12/31/1969 22:13:20#, #09/26/1212 00:00:00#, #09/26/1212#, #09/26/1212 00:00:00#, NULL, NULL)', 'odbc' => 'INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES (#09/26/1212 00:00:00.000000#, #12/31/1969 22:13:20.000000#, #09/26/1212#, #09/26/1212 00:00:00.000000#, #12/31/1969#, #12/31/1969 22:13:20.000000#, #09/26/1212 00:00:00.000000#, #09/26/1212#, #09/26/1212 00:00:00.000000#, NULL, NULL, #09/26/1212 16:51:34.012400#)',
"INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9]) VALUES ('1212-09-26 00:00:00', '1969-12-31 22:13:20', '1212-09-26', '1212-09-26 00:00:00', '1969-12-31', '1969-12-31 22:13:20', '1212-09-26 00:00:00', '1212-09-26', '1212-09-26 00:00:00', NULL, NULL)", 'mssql' => "INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES (CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1969-12-31', CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), NULL, NULL, CONVERT(DATETIME2(7), '1212-09-26 16:51:34.012400'))",
'sqlsrv' => "INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES (CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1969-12-31', CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), NULL, NULL, CONVERT(DATETIME2(7), '1212-09-26 16:51:34.012400'))",
"INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES ('1212-09-26 00:00:00.000000', '1969-12-31 22:13:20.000000', '1212-09-26', '1212-09-26 00:00:00.000000', '1969-12-31', '1969-12-31 22:13:20.000000', '1212-09-26 00:00:00.000000', '1212-09-26', '1212-09-26 00:00:00.000000', NULL, NULL, '1212-09-26 16:51:34.012400')",
]), ]),
$conn->translate('INSERT INTO test', [ $conn->translate('INSERT INTO test', [
'a2' => new DateTime('1212-09-26'), 'a2' => new DateTime('1212-09-26'),
'a4' => new DateTime(-10000), 'a4' => new DateTime(-10000),
'b1%d' => '1212-09-26', 'b1%d' => '1212-09-26',
'b2%t' => '1212-09-26', 'b2%t' => '1212-09-26',
'b3%d' => -10000, 'b3%d' => -10000,
'b4%t' => -10000, 'b4%t' => -10000,
'b5' => new DateTime('1212-09-26'), 'b5' => new DateTime('1212-09-26'),
'b6%d' => new DateTime('1212-09-26'), 'b6%d' => new DateTime('1212-09-26'),
'b7%t' => new DateTime('1212-09-26'), 'b7%t' => new DateTime('1212-09-26'),
'b8%d' => NULL, 'b8%d' => null,
'b9%t' => NULL, 'b9%t' => null,
])); 'c1%t' => new DateTime('1212-09-26 16:51:34.0124'),
])
);
Assert::exception(function () use ($conn) { Assert::exception(function () use ($conn) {
$conn->translate('SELECT %s', new DateTime('1212-09-26')); $conn->translate('SELECT %s', new DateTime('1212-09-26'));
@@ -224,6 +235,7 @@ if ($config['system'] === 'postgre') {
reformat([ reformat([
'sqlite' => "SELECT * FROM products WHERE (title LIKE 'C%' ESCAPE '\\' AND title LIKE '%r' ESCAPE '\\') OR title LIKE '%a\n\\%\\_\\\\''\"%' ESCAPE '\\'", '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[%][_]\\''\"%'", '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\\%\\_\\\\\\\\\'\"%'", "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]) $conn->translate($args[0], $args[1], $args[2], $args[3])
@@ -274,11 +286,11 @@ $array2 = ['one', 'two', 'three'];
$array3 = [ $array3 = [
'col1' => 'one', 'col1' => 'one',
'col2' => 'two', 'col2' => 'two',
'col3' => 'three', 'col3' => 'thr.ee',
]; ];
$array4 = [ $array4 = [
'a' => 12, 'a' => 12,
'b' => NULL, 'b' => null,
'c' => new DateTime('12.3.2007'), 'c' => new DateTime('12.3.2007'),
'd' => 'any string', 'd' => 'any string',
]; ];
@@ -295,8 +307,8 @@ WHERE (`test`.`a` LIKE '1995-03-01'
OR `b2` IN ('1', '2', '3' ) OR `b2` IN ('1', '2', '3' )
OR `b3` IN ( ) OR `b3` IN ( )
OR `b4` IN ( 'one', 'two', 'three' ) OR `b4` IN ( 'one', 'two', 'three' )
OR `b5` IN (`col1` AS `one`, `col2` AS `two`, `col3` AS `three` ) OR `b5` IN (`col1` AS `one`, `col2` AS `two`, `col3` AS `thr.ee` )
OR `b6` IN ('one', 'two', 'three') OR `b6` IN ('one', 'two', 'thr.ee')
OR `b7` IN (NULL) OR `b7` IN (NULL)
OR `b8` IN (RAND() `col1` > `col2` ) OR `b8` IN (RAND() `col1` > `col2` )
OR `b9` IN (RAND(), [col1] > [col2] ) OR `b9` IN (RAND(), [col1] > [col2] )
@@ -316,8 +328,8 @@ WHERE ("test"."a" LIKE \'1995-03-01\'
OR "b2" IN (\'1\', \'2\', \'3\' ) OR "b2" IN (\'1\', \'2\', \'3\' )
OR "b3" IN ( ) OR "b3" IN ( )
OR "b4" IN ( \'one\', \'two\', \'three\' ) OR "b4" IN ( \'one\', \'two\', \'three\' )
OR "b5" IN ("col1" AS "one", "col2" AS "two", "col3" AS "three" ) OR "b5" IN ("col1" AS "one", "col2" AS "two", "col3" AS "thr.ee" )
OR "b6" IN (\'one\', \'two\', \'three\') OR "b6" IN (\'one\', \'two\', \'thr.ee\')
OR "b7" IN (NULL) OR "b7" IN (NULL)
OR "b8" IN (RAND() "col1" > "col2" ) OR "b8" IN (RAND() "col1" > "col2" )
OR "b9" IN (RAND(), [col1] > [col2] ) OR "b9" IN (RAND(), [col1] > [col2] )
@@ -337,8 +349,8 @@ WHERE ([test].[a] LIKE #03/01/1995#
OR [b2] IN ('1', '2', '3' ) OR [b2] IN ('1', '2', '3' )
OR [b3] IN ( ) OR [b3] IN ( )
OR [b4] IN ( 'one', 'two', 'three' ) OR [b4] IN ( 'one', 'two', 'three' )
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [three] ) OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [thr.ee] )
OR [b6] IN ('one', 'two', 'three') OR [b6] IN ('one', 'two', 'thr.ee')
OR [b7] IN (NULL) OR [b7] IN (NULL)
OR [b8] IN (RAND() [col1] > [col2] ) OR [b8] IN (RAND() [col1] > [col2] )
OR [b9] IN (RAND(), [col1] > [col2] ) OR [b9] IN (RAND(), [col1] > [col2] )
@@ -358,8 +370,8 @@ WHERE ([test].[a] LIKE '1995-03-01'
OR [b2] IN ('1', '2', '3' ) OR [b2] IN ('1', '2', '3' )
OR [b3] IN ( ) OR [b3] IN ( )
OR [b4] IN ( 'one', 'two', 'three' ) OR [b4] IN ( 'one', 'two', 'three' )
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [three] ) OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [thr.ee] )
OR [b6] IN ('one', 'two', 'three') OR [b6] IN ('one', 'two', 'thr.ee')
OR [b7] IN (NULL) OR [b7] IN (NULL)
OR [b8] IN (RAND() [col1] > [col2] ) OR [b8] IN (RAND() [col1] > [col2] )
OR [b9] IN (RAND(), [col1] > [col2] ) OR [b9] IN (RAND(), [col1] > [col2] )
@@ -389,9 +401,9 @@ WHERE ([test.a] LIKE %d', '1995-03-01', '
OR [b10] IN (', [], ") OR [b10] IN (', [], ")
AND [c] = 'embedded '' string' AND [c] = 'embedded '' string'
OR [d]=%i", 10.3, ' OR [d]=%i", 10.3, '
OR [e]=%i', NULL, ' OR [e]=%i', null, '
OR [true]=', TRUE, ' OR [true]=', true, '
OR [false]=', FALSE, ' OR [false]=', false, '
OR [str_null]=%sn', '', ' OR [str_null]=%sn', '', '
OR [str_not_null]=%sn', 'hello', ' OR [str_not_null]=%sn', 'hello', '
LIMIT 10') LIMIT 10')
@@ -425,6 +437,7 @@ Assert::same(
Assert::same( Assert::same(
reformat([ reformat([
'odbc' => 'SELECT TOP 10 * FROM (SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' ) t', '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', 'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' LIMIT 10',
]), ]),
$conn->translate("SELECT * FROM [test] WHERE %n LIKE '%d%t' %lmt", 'id', 10) $conn->translate("SELECT * FROM [test] WHERE %n LIKE '%d%t' %lmt", 'id', 10)
@@ -432,7 +445,7 @@ Assert::same(
$where = [ $where = [
'tablename.column' => 1, 'tablename.column' => 1,
]; ];
Assert::same( Assert::same(
reformat('SELECT * FROM [tablename] WHERE ([tablename].[column] = 1)'), reformat('SELECT * FROM [tablename] WHERE ([tablename].[column] = 1)'),
@@ -442,7 +455,7 @@ Assert::same(
Assert::same( Assert::same(
reformat('SELECT FROM ... '), reformat('SELECT FROM ... '),
$conn->translate('SELECT FROM ... %lmt', NULL) $conn->translate('SELECT FROM ... %lmt', null)
); );
Assert::same( Assert::same(
@@ -459,20 +472,36 @@ Assert::same(
Assert::same( Assert::same(
reformat('INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1(\'Test product\')) , (1, SHA1(\'Test product\'))'), reformat('INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1(\'Test product\')) , (1, SHA1(\'Test product\'))'),
$conn->translate('INSERT INTO [products]', [ $conn->translate('INSERT INTO [products]', [
'product_id' => 1, 'product_id' => 1,
'title' => ['SHA1(%s)', 'Test product'], 'title' => ['SHA1(%s)', 'Test product'],
], [ ], [
'product_id' => 1, 'product_id' => 1,
'title' => ['SHA1(%s)', 'Test product'], 'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
]) ])
); );
Assert::same( Assert::same(
reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'), reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
$conn->translate('UPDATE [products]', [ $conn->translate('UPDATE [products]', [
'product_id' => 1, 'product_id' => 1,
'title' => ['SHA1(%s)', 'Test product'], 'title' => ['SHA1(%s)', 'Test product'],
]) ])
);
Assert::same(
reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
$conn->translate('UPDATE [products]', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
])
);
Assert::same(
reformat('SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1(\'Test product\')'),
$conn->translate('SELECT * FROM [products] WHERE', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
])
); );
@@ -489,11 +518,11 @@ Assert::same('INSERT INTO test **Multi-insert array "num%i" is different**', $e-
$array6 = [ $array6 = [
'id' => [1, 2, 3, 4], 'id' => [1, 2, 3, 4],
'text' => ['ahoj', 'jak', 'se', ['SUM(%i)', '5']], 'text' => ['ahoj', 'jak', 'se', ['SUM(%i)', '5']],
'num%i' => ['1', '', 10.3, 1], 'num%i' => ['1', '-1', 10.3, 1],
]; ];
Assert::same( Assert::same(
reformat('INSERT INTO test ([id], [text], [num]) VALUES (1, \'ahoj\', 1), (2, \'jak\', 0), (3, \'se\', 10), (4, SUM(5), 1)'), reformat('INSERT INTO test ([id], [text], [num]) VALUES (1, \'ahoj\', 1), (2, \'jak\', -1), (3, \'se\', 10), (4, SUM(5), 1)'),
$conn->translate('INSERT INTO test %m', $array6) $conn->translate('INSERT INTO test %m', $array6)
); );
@@ -513,10 +542,13 @@ Assert::same(
$conn->translate('INSERT INTO [test.*]') $conn->translate('INSERT INTO [test.*]')
); );
Assert::same( Assert::exception(function () use ($conn) {
reformat('INSERT INTO 0'), $conn->translate('INSERT INTO %i', 'ahoj');
$conn->translate('INSERT INTO %f', 'ahoj') }, 'Dibi\Exception', "Expected number, 'ahoj' given.");
);
Assert::exception(function () use ($conn) {
$conn->translate('INSERT INTO %f', 'ahoj');
}, 'Dibi\Exception', "Expected number, 'ahoj' given.");
Assert::same( Assert::same(
@@ -535,17 +567,23 @@ Assert::same(
); );
setLocale(LC_ALL, 'czech'); 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( Assert::same(
reformat("UPDATE [colors] SET [color]='blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5"), reformat("UPDATE [colors] SET [color]='blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5"),
$conn->translate('UPDATE [colors] SET', [ $conn->translate('UPDATE [colors] SET', [
'color' => 'blue', 'color' => 'blue',
'price' => -12.4, 'price' => -12.4,
'spec%f' => '-9E-005', 'spec%f' => '-9E-005',
'spec2%f' => 1000.00, 'spec2%f' => 1000.00,
'spec3%i' => 10000, 'spec3%i' => 10000,
'spec4' => 10000, 'spec4' => 10000,
], 'WHERE [price]=%f', 123.5) ], 'WHERE [price]=%f', 123.5)
); );

View File

@@ -18,7 +18,7 @@ date_default_timezone_set('Europe/Prague');
try { try {
$config = Tester\Environment::loadData(); $config = Tester\Environment::loadData();
} catch (Exception $e) { } catch (Exception $e) {
$config = parse_ini_file(__DIR__ . '/../databases.ini', TRUE); $config = parse_ini_file(__DIR__ . '/../databases.ini', true);
$config = reset($config); $config = reset($config);
} }
@@ -63,7 +63,7 @@ function reformat($s)
return strtr($s, '[]', '``'); return strtr($s, '[]', '``');
} elseif ($config['system'] === 'postgre') { } elseif ($config['system'] === 'postgre') {
return strtr($s, '[]', '""'); return strtr($s, '[]', '""');
} elseif (in_array($config['system'], ['odbc', 'sqlite', 'sqlsrv'])) { } elseif (in_array($config['system'], ['odbc', 'sqlite', 'sqlsrv'], true)) {
return $s; return $s;
} else { } else {
trigger_error("Unsupported driver $config[system]", E_USER_WARNING); trigger_error("Unsupported driver $config[system]", E_USER_WARNING);

View File

@@ -1,5 +1,3 @@
/*!40102 SET storage_engine = InnoDB */;
DROP DATABASE IF EXISTS dibi_test; DROP DATABASE IF EXISTS dibi_test;
CREATE DATABASE dibi_test; CREATE DATABASE dibi_test;
USE dibi_test; USE dibi_test;

View 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 )

View File

@@ -8,6 +8,7 @@ CREATE TABLE products (
title varchar(50) NOT NULL, title varchar(50) NOT NULL,
PRIMARY KEY(product_id) PRIMARY KEY(product_id)
); );
CREATE INDEX [title] ON [dbo].[products] ([title]);
SET IDENTITY_INSERT products ON; SET IDENTITY_INSERT products ON;
INSERT INTO products (product_id, title) VALUES (1, 'Chair'); INSERT INTO products (product_id, title) VALUES (1, 'Chair');

View File

@@ -15,7 +15,7 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
$e = Assert::exception(function () use ($conn) { $e = Assert::exception(function () use ($conn) {
$conn->query('SELECT'); $conn->query('SELECT');
}, 'Dibi\DriverException', "%a% error in your SQL syntax;%a%", 1064); }, 'Dibi\DriverException', '%a% error in your SQL syntax;%a%', 1064);
Assert::same('SELECT', $e->getSql()); Assert::same('SELECT', $e->getSql());

View File

@@ -22,14 +22,14 @@ Assert::same('SELECT', $e->getSql());
$e = Assert::exception(function () use ($conn) { $e = Assert::exception(function () use ($conn) {
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")'); $conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
}, 'Dibi\UniqueConstraintViolationException', NULL, 19); }, 'Dibi\UniqueConstraintViolationException', null, 19);
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql()); Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
$e = Assert::exception(function () use ($conn) { $e = Assert::exception(function () use ($conn) {
$conn->query('INSERT INTO products (title) VALUES (NULL)'); $conn->query('INSERT INTO products (title) VALUES (NULL)');
}, 'Dibi\NotNullConstraintViolationException', NULL, 19); }, 'Dibi\NotNullConstraintViolationException', null, 19);
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql()); Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
@@ -37,6 +37,6 @@ Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
$e = Assert::exception(function () use ($conn) { $e = Assert::exception(function () use ($conn) {
$conn->query('PRAGMA foreign_keys=true'); $conn->query('PRAGMA foreign_keys=true');
$conn->query('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)'); $conn->query('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)');
}, 'Dibi\ForeignKeyConstraintViolationException', NULL, 19); }, 'Dibi\ForeignKeyConstraintViolationException', null, 19);
Assert::same('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)', $e->getSql()); Assert::same('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)', $e->getSql());

View File

@@ -17,11 +17,12 @@ try {
Tester\Environment::skip($e->getMessage()); Tester\Environment::skip($e->getMessage());
} }
Assert::same(3, count($meta->getTables())); if ($config['system'] !== 'sqlsrv') {
Assert::same(3, count($meta->getTables()));
$names = $meta->getTableNames(); $names = $meta->getTableNames();
sort($names); sort($names);
Assert::equal(['customers', 'orders', 'products'], $names); Assert::equal(['customers', 'orders', 'products'], $names);
}
Assert::false($meta->hasTable('xxxx')); Assert::false($meta->hasTable('xxxx'));
@@ -48,7 +49,7 @@ Assert::same('title', $column->getName());
Assert::same('products', $column->getTable()->getName()); Assert::same('products', $column->getTable()->getName());
Assert::same('s', $column->getType()); Assert::same('s', $column->getType());
Assert::type('string', $column->getNativeType()); 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->isNullable());
Assert::false($column->isAutoIncrement()); Assert::false($column->isAutoIncrement());
//Assert::null($column->getDefault()); //Assert::null($column->getDefault());

18
tests/php5-win.ini Normal file
View 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

View File

@@ -1,10 +1,11 @@
[PHP] [PHP]
extension_dir = "./ext" extension_dir = "./ext"
extension=php_mysql.dll ;extension=php_mssql.dll
extension=php_mysqli.dll extension=php_mysqli.dll
;extension=php_oci8.dll ;extension=php_oci8.dll
;extension=php_oci8_11g.dll ;extension=php_oci8_11g.dll
;extension=php_pdo_firebird.dll ;extension=php_pdo_firebird.dll
;extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll extension=php_pdo_mysql.dll
;extension=php_pdo_oci.dll ;extension=php_pdo_oci.dll
extension=php_pdo_odbc.dll extension=php_pdo_odbc.dll
@@ -12,4 +13,5 @@ extension=php_pdo_pgsql.dll
extension=php_pdo_sqlite.dll extension=php_pdo_sqlite.dll
extension=php_pgsql.dll extension=php_pgsql.dll
extension=php_sqlite3.dll extension=php_sqlite3.dll
;extension=php_sqlsrv_ts.dll extension=php_sqlsrv_ts.dll
extension=php_odbc.dll