Skip to content
This repository was archived by the owner on May 11, 2021. It is now read-only.

Commit

Permalink
Releasing 2.0.2 into master (#178)
Browse files Browse the repository at this point in the history
* Resolve #119 (wrong email in license text)

* develop branch to depend on develop branch

* Update dependency situation

* fixing stability

* add release script

* fix release script

* Added release notes for 1.1.3

* fix release script

* fix release script

* fix release script

* add YEY message for future releases ;)

* fix broken URL link

* rrrgh!!!

* drop php 5.5 and upgrade phpunit

* Added release notes for 1.1.4

* minmor change in release script

* Apply fixes from StyleCI

* Added release notes for 1.1.5

* Update Expression.php

* Apply fixes from StyleCI

* Update Expression.php

* add php 7.2 testing

* Add support for mysql://user:pass@host/db (#120)

* Add verification for foreign objects inside where() values (#122)

* Add verification for foreign objects inside where() values

* Apply fixes from StyleCI

* Feature/oracle support (#124)

* Reference implementation (untested)

* Apply fixes from StyleCI

* bugfix

* Apply fixes from StyleCI

* Cleanup and split up support for regular an 12c oracle

* ok, some first results

* Apply fixes from StyleCI

* Merge

* Add more tests

* Apply fixes from StyleCI

* add 2 more tests

* add test for field alias escaping

* fix issues around rownum aliasing

* add common methods into common parent class

* Apply fixes from StyleCI

* added clarification why we only use "NEXT"

* typo

* fix FIRST/NEXT in tests

* - introduce Expression::escape_char
- make " as default escape char
- make MySQL as exception to standard SQL escaping not vice versa
- clean up code duplication in Oracle classes
- change tests accordingly

* more duplicated code

* For consistency with table render move field_noalias into generic Query class.

* few fixes for oracle 12c limit

* Apply fixes from StyleCI

* happy with implementation

* address Romans review

* Fix expr().

Basically it's all about expr() method which looses it's parent class and Query_MySQL->expr() for example never know that it should return Expression_MySQL not Expression class instance. In case we have Connection (not PDO) then we can get that info from Connection, because Connection class is/should be the one responsible for that. If we don't have Connection, but only PDO, then wehave to make "smart" quess (anyway that's not worse than it's now).

* Apply fixes from StyleCI

* spellcheck and wrapping :) (#125)

* fix Limit for Oracle <12

* fix oracle test

* Create a driver for postgresql

* Code style fix

* Code style fix

* Enable testing in postgresql on travis

* Postgresql doesn't support REPLACE.

* Fix: When creating database for postgresql, identifiers containing - should be quoted

* Fix: SQL does not guarantee the order of retreived rows unless explic… (#131)

* Fix: SQL does not guarantee the order of retreived rows unless explicitly ordered

* Fix code-style

* Feature/connection oracle (#132)

* * new connection for Oracle <12
* new connection for Oracle 12 (not fully ok yet)

* Apply fixes from StyleCI

* to much "return" statements

* Fix tests to run on both mysql and postgresql

* Use lowest supported version of postgresql to test against. (9.3). Default on travis is 9.1 it seems

* With debug-info to track error when run on travis ...

* Fix testExpression() to work on both mysql and pgsql.

* Apply fixes from StyleCI

* typo fix

* * fix bug with uppercase field names
* refactor and fix lastInsertID
* fix date format issue for Oracle
* fix number format issue for Oracle
* move PgSQL connection to separate class for consistency

* Apply fixes from StyleCI

* don't use select max(id) for now.

* Added release notes for 1.2.0

* fix expr()

* rollback

* proper fix for expr()

* Fix: If  is already an instance of PDO, don't just assume mysql

* Fix: If a \PDO instance is passed to connect(), setup according to database type, and don't just assume it to be mysql

* Fix: Missing break

* Make a separate test-class for when using PDO-connections to connect()

* Fix code style

* Fix style

* resolve #136

* Added release notes for 1.2.1

* implement CASE and add full test coverage

* add one more test case and documentation

* Apply fixes from StyleCI

* rename method else() to otherwise() because PHP 5.6 restricts to use reserved names in method name.

* * implement normalizeDSN()
* Connection now use normalizeDSN()
* test coverage

P.S. normalizeDSN() will also be used in \atk4\data\Persistence to avoid code duplication between repos.

* Added release notes for 1.2.2

* * user and password arguments should take precedence
* and dsn string should still be normalized

* make reasonable defaulcts

* * use only core classes for testing
* with one exception (for now) PHPUnit_Extensions_Database_TestCase

* Added release notes for 1.2.3

* Update README.md

* add resource support (#142)

* more informative exception

* support LOB fields - uzse bindParam + 4th parameter trick

* Apply fixes from StyleCI

* Added release notes for 1.2.4

* fix Oracle session timestamp and date format

* Remove restriction for custom ordering keyword because some DB engines like Oracle can have order statement like this: ORDER BY name DESC NULLS LAST (#145)

* add support for port number in DSN connection string

* support DSN as object

* Apply fixes from StyleCI

* fix compatibility with php5

* add test for port number

* make args public (#147)

* Added release notes for 1.2.5

* fix PHPDoc comments

* add comments, make code consistant

* update docs

* code coverage

* travis fix

* coverage

* Update .travis.yml

* Update .travis.yml

* Update phpunit.xml

* Update phpunit-mysql.xml

* Update phpunit-pgsql.xml

* proper method name

* cleanup

* Update README.md

* Update README.md

* Update .travis.yml

* Update .travis.yml

* Update .travis.yml

* Update .travis.yml

* Add support for groupConcat() (#149)

* Add support for groupConcat()

* Apply fixes from StyleCI

* adds comments

* add tests

* Apply fixes from StyleCI

* add docs

* update docs

* add test for expr()

* re-add tests (Random) which I renamed (#150)

* re-add tests (Random) which I renamed

* Apply fixes from StyleCI

* fix DSN normalization for port

* fix test

* only normalize port if it's passed as part of URL

* add interface for ResultSet - something ATK Data can return from action()

* whops, forgot file

* Apply fixes from StyleCI

* add documentation for $q->options() (#153)

* Added release notes for 1.2.6

* add Connection->driver property (#157)

* Use github actions for DSQL (#162)

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Apply fixes from StyleCI

* trigger bulid

* wip

* wip

* Improve release process - create PR automatically

* Update README.md

* Update README.md

* Feature/for data fix 496 (#165)

* correct condition on empty array value (for atk4/data issue 496), fix test-cases

* Apply fixes from StyleCI

* Update unit-tests.yml

* fix #159

* implement `exprNow()`, fix #158

* Apply fixes from StyleCI

* add docs

* fix comment

* one step closer to have lastInsertID for PostGre

* update composer

* Update release-drafter.yml

* Update bundler.yml

* Update composer.json

* Update unit-tests.yml

* Feature/table name dots (#169)

* add test

* Apply fixes from StyleCI

* implements template tags `{{...}}` for soft escape (#170)

* implements template tags `{{...}}` for soft escape

* add doc

* Update release-drafter.yml

* Update release-drafter.yml

* WITH implementation (#171)

* Implements WITH.
fix #117

* Apply fixes from StyleCI

* add proper condition

* Update unit-tests.yml

* Update unit-tests.yml

* Update unit-tests.yml

* Convert scalar class names to ::class (#175)

* Convert scalar class names to ::class

* Fix segfault

* Use null coalescing operator instead of isset (#177)

* Setting release dependencies

Co-authored-by: Romans Malinovskis <[email protected]>
Co-authored-by: DarkSide <[email protected]>
Co-authored-by: Romans Malinovskis <[email protected]>
Co-authored-by: Imants Horsts <[email protected]>
Co-authored-by: Mads Lie Jensen <[email protected]>
Co-authored-by: Michael Voříšek <[email protected]>
Co-authored-by: GitHub Action <[email protected]>
  • Loading branch information
8 people authored Apr 8, 2020
1 parent e729c97 commit 4523ff2
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ jobs:
runs-on: ubuntu-latest
steps:
# Drafts your next Release notes as Pull Requests are merged into "master"
- uses: toolmantim/release-drafter@master
- uses: toolmantim/release-drafter@v5.6.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
11 changes: 6 additions & 5 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
php: ['7.2', '7.3', 'latest']
services:
mysql:
image: mysql:5.7
image: mariadb:10.4.12
env:
MYSQL_ROOT_PASSWORD: password
DB_DATABASE: dsql_test
MYSQL_DATABASE: dsql_test
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
postgres:
image: postgres:10-alpine
Expand All @@ -31,7 +31,7 @@ jobs:
POSTGRES_USER: postgres
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- run: php --version
- name: Get Composer Cache Directory
id: composer-cache
Expand All @@ -49,10 +49,11 @@ jobs:
- name: Run Tests
run: |
mkdir -p build/logs
mysql -uroot -ppassword -h mysql -e 'CREATE DATABASE dsql_test;'
# mysql -uroot -ppassword -h mysql -e 'CREATE DATABASE dsql_test;'
PGPASSWORD=password psql -h postgres -c 'create database "atk4-dsql-test";' -U postgres
- name: SQLite Testing
run: vendor/bin/phpunit --configuration phpunit.xml --coverage-text --exclude-group dns
run: vendor/bin/phpunit -d zend.enable_gc=0 --configuration phpunit.xml --coverage-text --exclude-group dns # remove " -d zend.enable_gc=0" once PHP segfault in GH Actions is fixed, see https://github.com/atk4/dsql/issues/176

- name: MySQL Testing
run: vendor/bin/phpunit --configuration phpunit-mysql-workflow.xml --exclude-group dns
Expand Down
14 changes: 10 additions & 4 deletions docs/expressions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -326,26 +326,32 @@ parts of the query. You must not call them in normal circumstances.
.. php:method:: _escape($sql_code)
Always surrounds `$sql code` with back-ticks.

This escaping method is automatically used for `{...}` expression template tags .

.. php:method:: _escapeSoft($sql_code)
Surrounds `$sql code` with back-ticks.

This escaping method is automatically used for `{{...}}` expression template tags .

It will smartly escape table.field type of strings resulting in `table`.`field`.

Will do nothing if it finds "*", "`" or "(" character in `$sql_code`::

$query->_escape('first_name'); // `first_name`
$query->_escape('first.name'); // `first`.`name`
$query->_escape('(2+2)'); // (2+2)
$query->_escape('*'); // *
$query->_escapeSoft('first_name'); // `first_name`
$query->_escapeSoft('first.name'); // `first`.`name`
$query->_escapeSoft('(2+2)'); // (2+2)
$query->_escapeSoft('*'); // *

.. php:method:: _param($value)
Converts value into parameter and returns reference. Used only during query
rendering. Consider using :php:meth:`_consume()` instead, which will also
handle nested expressions properly.

This escaping method is automatically used for `[...]` expression template tags .


.. _properties:

Expand Down
51 changes: 51 additions & 0 deletions docs/queries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,57 @@ For a more complex join conditions, you can pass second argument as expression::
$q->join('address a', new Expression('a.name like u.pattern'));


Use WITH cursors
---------------------------

.. php:method:: with(Query $cursor, string $alias, ?array $fields = null, bool $recursive = false)
If you want to add `WITH` cursor statement in your SQL, then use this method.
First parameter defines sub-query to use. Second parameter defines alias of this cursor.
By using third, optional argument you can set aliases for columns in cursor.
And finally forth, optional argument set if cursors will be recursive or not.

You can add more than one cursor in your query.

Did you know: you can use these cursors when joining your query to other tables. Just join cursor instead.

.. php:method:: withRecursive(Query $cursor, string $alias, ?array $fields = null)
Same as :php:meth:`with()`, but always sets it as recursive.

Keep in mind that if any of cursors added in your query will be recursive, then all cursors will
be set recursive. That's how SQL want it to be.

Example::

$quotes = $q->table('quotes')
->field('emp_id')
->field($q->expr('sum([])', ['total_net']))
->group('emp_id');
$invoices = $q()->table('invoices')
->field('emp_id')
->field($q->expr('sum([])', ['total_net']))
->group('emp_id');
$employees = $q
->with($quotes, 'q', ['emp','quoted'])
->with($invoices, 'i', ['emp','invoiced'])
->table('employees')
->join('q.emp')
->join('i.emp')
->field(['name', 'salary', 'q.quoted', 'i.invoiced']);

This generates SQL below:

.. code-block:: sql
with
`q` (`emp`,`quoted`) as (select `emp_id`,sum(`total_net`) from `quotes` group by `emp_id`),
`i` (`emp`,`invoiced`) as (select `emp_id`,sum(`total_net`) from `invoices` group by `emp_id`)
select `name`,`salary`,`q`.`quoted`,`i`.`invoiced`
from `employees`
left join `q` on `q`.`emp` = `employees`.`id`
left join `i` on `i`.`emp` = `employees`.`id`
Limiting result-set
-------------------

Expand Down
36 changes: 17 additions & 19 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class Connection
use \atk4\core\DIContainerTrait;

/** @var string Query classname */
protected $query_class = 'atk4\dsql\Query';
protected $query_class = Query::class;

/** @var string Expression classname */
protected $expression_class = 'atk4\dsql\Expression';
protected $expression_class = Expression::class;

/** @var Connection|\PDO Connection or PDO object */
protected $connection = null;
Expand Down Expand Up @@ -71,13 +71,11 @@ public static function normalizeDSN($dsn, $user = null, $pass = null)
// If parts are usable, convert DSN format
if ($parts !== false && isset($parts['host'], $parts['path'])) {
// DSN is using URL-like format, so we need to convert it
$dsn =
$parts['scheme'].
':host='.$parts['host'].
(isset($parts['port']) ? ';port='.$parts['port'] : '').
';dbname='.substr($parts['path'], 1);
$user = $user !== null ? $user : (isset($parts['user']) ? $parts['user'] : null);
$pass = $pass !== null ? $pass : (isset($parts['pass']) ? $parts['pass'] : null);
$dsn = $parts['scheme'].':host='.$parts['host']
.(isset($parts['port']) ? ';port='.$parts['port'] : '')
.';dbname='.substr($parts['path'], 1);
$user = $user ?? ($parts['user'] ?? null);
$pass = $pass ?? ($parts['pass'] ?? null);
}

// If it's still array, then simply use it
Expand Down Expand Up @@ -118,25 +116,25 @@ public static function connect($dsn, $user = null, $password = null, $args = [])
// If it's already PDO object, then we simply use it
if ($dsn instanceof \PDO) {
$driver = $dsn->getAttribute(\PDO::ATTR_DRIVER_NAME);
$connectionClass = '\\atk4\\dsql\\Connection';
$connectionClass = self::class;
$queryClass = null;
$expressionClass = null;
switch ($driver) {
case 'pgsql':
$connectionClass = '\\atk4\\dsql\\Connection_PgSQL';
$queryClass = 'atk4\dsql\Query_PgSQL';
$connectionClass = Connection_PgSQL::class;
$queryClass = Query_PgSQL::class;
break;
case 'oci':
$connectionClass = '\\atk4\\dsql\\Connection_Oracle';
$connectionClass = Connection_Oracle::class;
break;
case 'sqlite':
$queryClass = 'atk4\dsql\Query_SQLite';
$queryClass = Query_SQLite::class;
break;
case 'mysql':
$expressionClass = 'atk4\dsql\Expression_MySQL';
$expressionClass = Expression_MySQL::class;
default:
// Default, for backwards compatibility
$queryClass = 'atk4\dsql\Query_MySQL';
$queryClass = Query_MySQL::class;
break;
}

Expand All @@ -163,16 +161,16 @@ public static function connect($dsn, $user = null, $password = null, $args = [])
case 'mysql':
$c = new static(array_merge([
'connection' => new \PDO($dsn['dsn'], $dsn['user'], $dsn['pass']),
'expression_class' => 'atk4\dsql\Expression_MySQL',
'query_class' => 'atk4\dsql\Query_MySQL',
'expression_class' => Expression_MySQL::class,
'query_class' => Query_MySQL::class,
'driver' => $dsn['driver'],
], $args));
break;

case 'sqlite':
$c = new static(array_merge([
'connection' => new \PDO($dsn['dsn'], $dsn['user'], $dsn['pass']),
'query_class' => 'atk4\dsql\Query_SQLite',
'query_class' => Query_SQLite::class,
'driver' => $dsn['driver'],
], $args));
break;
Expand Down
2 changes: 1 addition & 1 deletion src/Connection_Oracle.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
class Connection_Oracle extends Connection
{
/** @var string Query classname */
protected $query_class = 'atk4\dsql\Query_Oracle';
protected $query_class = Query_Oracle::class;

/**
* Add some configuration for current connection session.
Expand Down
2 changes: 1 addition & 1 deletion src/Connection_Oracle12.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
class Connection_Oracle12 extends Connection_Oracle
{
/** @var string Query classname */
protected $query_class = 'atk4\dsql\Query_Oracle12c';
protected $query_class = Query_Oracle12c::class;
}
2 changes: 1 addition & 1 deletion src/Connection_PgSQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
class Connection_PgSQL extends Connection
{
/** @var string Query classname */
protected $query_class = 'atk4\dsql\Query_PgSQL';
protected $query_class = Query_PgSQL::class;

/**
* Return last inserted ID value.
Expand Down
24 changes: 17 additions & 7 deletions src/Expression.php
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ protected function _escapeSoft($value)
*
* @param string $value
*
* @return string
* @return Expression
*/
public function escape($value)
{
Expand All @@ -390,10 +390,9 @@ protected function _escape($value)
}

// in all other cases we should escape
return
$this->escape_char
.str_replace($this->escape_char, $this->escape_char.$this->escape_char, $value)
.$this->escape_char;
$c = $this->escape_char;

return $c.str_replace($c, $c.$c, $value).$c;
}

/**
Expand Down Expand Up @@ -436,10 +435,21 @@ public function render()
}

$res = preg_replace_callback(
'/\[[a-z0-9_]*\]|{[a-z0-9_]*}/i',
// param | escape | escapeSoft
'/\[[a-z0-9_]*\]|{[a-z0-9_]*}|{{[a-z0-9_]*}}/i',
function ($matches) use (&$nameless_count) {
$identifier = substr($matches[0], 1, -1);
$escaping = ($matches[0][0] == '[') ? 'param' : 'escape';

if ($matches[0][0] == '[') {
$escaping = 'param';
} elseif ($matches[0][0] == '{') {
if ($matches[0][1] == '{') {
$escaping = 'soft-escape';
$identifier = substr($identifier, 1, -1);
} else {
$escaping = 'escape';
}
}

// Allow template to contain []
if ($identifier === '') {
Expand Down
Loading

0 comments on commit 4523ff2

Please sign in to comment.